jenkins-bot has submitted this change and it was merged.

Change subject: initial release (v 0.1.1)
......................................................................


initial release (v 0.1.1)

There are two PhpTags objects:
* Storage
* PageData

Bug: T94872
Change-Id: Idffbdda34a3dae2c7570691f86b263ae3ea7aafb
---
A PhpTagsStorage.hooks.php
A PhpTagsStorage.json
A PhpTagsStorage.php
A i18n/en.json
A i18n/qqq.json
A includes/PageData.php
A includes/Storage.php
A includes/Storage/Field.php
A includes/Storage/PageDataUpdate.php
A includes/Storage/PageTemplatesUpdate.php
A includes/Storage/Schema.php
A includes/Storage/SchemaUpdate.php
A sql/storage.sql
A tests/phpunit/!!!firstInit_Test.php
A tests/phpunit/StorageWikiPage_Test.php
A tests/phpunit/Storage_Test.php
16 files changed, 1,128 insertions(+), 0 deletions(-)

Approvals:
  Pastakhov: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/PhpTagsStorage.hooks.php b/PhpTagsStorage.hooks.php
new file mode 100644
index 0000000..59ef574
--- /dev/null
+++ b/PhpTagsStorage.hooks.php
@@ -0,0 +1,93 @@
+<?php
+
+
+/**
+ * PhpTags Storage MediaWiki Hooks.
+ *
+ * @file PhpTagsStorage.hooks.php
+ * @ingroup PhpTags
+ * @author Pavel Astakhov <pastak...@yandex.ru>
+ * @licence GNU General Public Licence 2.0 or later
+ */
+class PhpTagsStorageHooks {
+
+       /**
+        *
+        * @return boolean
+        */
+       public static function onParserFirstCallInit() {
+               if ( !defined( 'PHPTAGS_VERSION' ) ) {
+                       throw new MWException( "\n\nYou need to have the 
PhpTags extension installed in order to use the PhpTags Storage extension." );
+               }
+               $needVersion = '5.1.4';
+               if ( version_compare( PHPTAGS_VERSION, $needVersion, '<' ) ) {
+                       throw new MWException( "\n\nThis version of the PhpTags 
Storage extension requires the PhpTags extension $needVersion or above.\n You 
have " . PHPTAGS_VERSION . ". Please update it." );
+               }
+               if ( PHPTAGS_HOOK_RELEASE != 8 ) {
+                       throw new MWException( "\n\nThis version of the PhpTags 
Storage extension is outdated and not compatible with current version of the 
PhpTags extension.\n Please update it." );
+               }
+               return true;
+       }
+
+       /**
+        *
+        * @return boolean
+        */
+       public static function onPhpTagsRuntimeFirstInit() {
+               \PhpTags\Hooks::addJsonFile( __DIR__ . '/PhpTagsStorage.json', 
PHPTAGS_STORAGE_VERSION );
+               return true;
+       }
+
+       /**
+        *
+        * @param \Title $title
+        * @param \Content $old
+        * @param bool $recursive
+        * @param \ParserOutput $parserOutput
+        * @param array $updates
+        * @return boolean
+        */
+       public static function onSecondaryDataUpdates( $title, $old, 
$recursive, $parserOutput, &$updates ) {
+               wfDebugLog( 'PhpTags Storage', __METHOD__ );
+//             echo __METHOD__ . '( ' . $title->getArticleID() . " )\n";
+               \PhpTagsObjects\Storage::onDataUpdates( $title->getArticleID(), 
$updates );
+               return true;
+       }
+
+       /**
+        *
+        * @param \WikiPage $page
+        * @param type $content
+        * @param array $updates
+        */
+       public static function onWikiPageDeletionUpdates( $page, $content, 
&$updates ) {
+               wfDebugLog( 'PhpTags Storage', __METHOD__ );
+               $titleID = $page->getTitle()->getArticleID();
+//             echo __METHOD__ . '( ' . $titleID . " )\n";
+               PhpTagsStorage\Schema::onPageDelete( $titleID );
+               PhpTagsObjects\Storage::onDataUpdates( $titleID, $updates );
+       }
+
+       public static function onLoadExtensionSchemaUpdates( DatabaseUpdater 
$updater ) {
+               $updater->addExtensionTable( 'phptags_schemas', __DIR__ . 
'/sql/storage.sql' );
+               $updater->addExtensionTable( 'phptags_page_templates', __DIR__ 
. '/sql/storage.sql' );
+               return true;
+       }
+
+       /**
+        *
+        * @param array $files
+        * @return boolean
+        */
+       public static function onUnitTestsList( &$files ) {
+               $testDir = __DIR__ . '/tests/phpunit';
+               $files = array_merge( $files, glob( "$testDir/*Test.php" ) );
+               return true;
+       }
+
+       public static function onParserTestTables( &$tables ) {
+               $tables[] = 'phptags_schemas';
+               $tables[] = 'phptags_page_templates';
+       }
+
+}
diff --git a/PhpTagsStorage.json b/PhpTagsStorage.json
new file mode 100644
index 0000000..5d8fef2
--- /dev/null
+++ b/PhpTagsStorage.json
@@ -0,0 +1,48 @@
+{
+       "objects": {
+               "Storage": {
+                       "class": "Storage",
+                       "METHODS": {
+                               "__construct": {
+                                       "parameters": [
+                                               { "type": "array", "name": 
"structure" }
+                                       ],
+                                       "return": "Storage",
+                                       "desc": "Returns new Storage object"
+                               },
+                               "setValues": {
+                                       "parameters": [
+                                               { "type": "array", "name": 
"values" }
+                                       ],
+                                       "desc": "Set values to Storage object"
+                               }
+                       }
+               },
+               "PageData": {
+                       "class": "PageData",
+                       "METHODS": {
+                               "__construct": {
+                                       "parameters": [
+                                               { "type": "mixed", "name": 
"page", "default": "NULL" },
+                                               { "type": "mixed", "name": 
"templates", "default": "NULL" }
+                                       ],
+                                       "return": "PageData",
+                                       "desc": "Returns new PageData object"
+                               },
+                               "getValues": {
+                                       "parameters": [],
+                                       "return": "array",
+                                       "desc": "Get values stored by Storage 
object"
+                               }
+                       }
+               }
+       },
+       "constants": {
+               "PHPTAGS_STORAGE_VERSION": {
+                       "desc": "The current version of the PhpTags Storage 
extension as a string",
+                       "type": "string",
+                       "example": "3.1.7",
+                       "link": 
"https://www.mediawiki.org/wiki/Extension:PhpTags_Storage";
+               }
+       }
+}
diff --git a/PhpTagsStorage.php b/PhpTagsStorage.php
new file mode 100644
index 0000000..0f86668
--- /dev/null
+++ b/PhpTagsStorage.php
@@ -0,0 +1,54 @@
+<?php
+/**
+ * Main entry point for the PhpTags Storage extension.
+ *
+ * @link https://www.mediawiki.org/wiki/Extension:PhpTags_Storage Documentation
+ * @file PhpTagsStorage.php
+ * @defgroup PhpTags
+ * @ingroup Extensions
+ * @author Pavel Astakhov <pastak...@yandex.ru>
+ * @licence GNU General Public Licence 2.0 or later
+ */
+
+// Check to see if we are being called as an extension or directly
+if ( !defined('MEDIAWIKI') ) {
+       die( 'This file is an extension to MediaWiki and thus not a valid entry 
point.' );
+}
+
+const PHPTAGS_STORAGE_VERSION = '0.1.1';
+
+// Register this extension on Special:Version
+$wgExtensionCredits['phptags'][] = array(
+       'path' => __FILE__,
+       'name' => 'PhpTags Storage',
+       'version' => PHPTAGS_STORAGE_VERSION,
+       'url' => 'https://www.mediawiki.org/wiki/Extension:PhpTags_Storage',
+       'author' => '[https://www.mediawiki.org/wiki/User:Pastakhov Pavel 
Astakhov]',
+       'descriptionmsg' => 'phptagsstorage-desc',
+       'license-name' => 'GPL-2.0+',
+);
+
+// Allow translations for this extension
+$wgMessagesDirs['PhpTagsStorage'] = __DIR__ . '/i18n';
+
+// Add hooks
+$wgHooks['ParserFirstCallInit'][] = 
'PhpTagsStorageHooks::onParserFirstCallInit';
+$wgHooks['PhpTagsRuntimeFirstInit'][] = 
'PhpTagsStorageHooks::onPhpTagsRuntimeFirstInit';
+$wgHooks['LoadExtensionSchemaUpdates'][] = 
'PhpTagsStorageHooks::onLoadExtensionSchemaUpdates';
+$wgHooks['SecondaryDataUpdates'][] = 
'PhpTagsStorageHooks::onSecondaryDataUpdates';
+$wgHooks['WikiPageDeletionUpdates'][] = 
'PhpTagsStorageHooks::onWikiPageDeletionUpdates';
+$wgHooks['UnitTestsList'][] = 'PhpTagsStorageHooks::onUnitTestsList';
+$wgHooks['ParserTestTables'][] = 'PhpTagsStorageHooks::onParserTestTables';
+
+// Add parser tests
+# $wgParserTestFiles[] = __DIR__ . '/tests/parser/PhpTagsStorageTests.txt';
+
+// Preparing classes for autoloading
+$wgAutoloadClasses['PhpTagsStorageHooks'] = __DIR__ . 
'/PhpTagsStorage.hooks.php';
+$wgAutoloadClasses['PhpTagsObjects\\Storage'] = __DIR__ . 
'/includes/Storage.php';
+$wgAutoloadClasses['PhpTagsObjects\\PageData'] = __DIR__ . 
'/includes/PageData.php';
+$wgAutoloadClasses['PhpTagsStorage\\Schema'] = __DIR__ . 
'/includes/Storage/Schema.php';
+$wgAutoloadClasses['PhpTagsStorage\\SchemaUpdate'] = __DIR__ . 
'/includes/Storage/SchemaUpdate.php';
+$wgAutoloadClasses['PhpTagsStorage\\PageDataUpdate'] = __DIR__ . 
'/includes/Storage/PageDataUpdate.php';
+$wgAutoloadClasses['PhpTagsStorage\\PageTemplatesUpdate'] = __DIR__ . 
'/includes/Storage/PageTemplatesUpdate.php';
+$wgAutoloadClasses['PhpTagsStorage\\Field'] = __DIR__ . 
'/includes/Storage/Field.php';
diff --git a/i18n/en.json b/i18n/en.json
new file mode 100644
index 0000000..5bae24a
--- /dev/null
+++ b/i18n/en.json
@@ -0,0 +1,8 @@
+{
+    "@metadata": {
+        "authors": [
+            "pastakhov"
+        ]
+    },
+    "phptagsstorage-desc": "Provides a lightweight way to store and query the 
data for the PhpTags extension"
+}
diff --git a/i18n/qqq.json b/i18n/qqq.json
new file mode 100644
index 0000000..519677b
--- /dev/null
+++ b/i18n/qqq.json
@@ -0,0 +1,8 @@
+{
+       "@metadata": {
+               "authors": [
+                       "pastakhov"
+               ]
+       },
+       "phptagsstorage-desc": 
"{{desc|name=PhpTags_Storage|url=https://www.mediawiki.org/wiki/Extension:PhpTags_Storage}}";
+}
diff --git a/includes/PageData.php b/includes/PageData.php
new file mode 100644
index 0000000..16f6fe8
--- /dev/null
+++ b/includes/PageData.php
@@ -0,0 +1,113 @@
+<?php
+namespace PhpTagsObjects;
+
+/**
+ * Description of PageData
+ *
+ * @author pastakhov
+ */
+class PageData extends \PhpTags\GenericObject {
+
+       public static $cache = array();
+       private $pageID;
+       private $templates = array();
+
+       public function m___construct( $page = null, $templates = null ) {
+               wfDebugLog( 'PhpTags Storage', __METHOD__ );
+
+               $this->pageID = $page === null ? $this->pageID = 
\PhpTags\Renderer::getParser()->getTitle()->getArticleID() : self::getID( $page 
);
+               if ( $templates === null ) {
+                       $this->templates = null;
+               } elseif ( is_array( $templates ) ) {
+                       foreach ( $templates as $t ) {
+                               $this->templates[] = self::getID( $t );
+                       }
+               } else {
+                       $this->templates[] = self::getID( $templates );
+               }
+               return true;
+       }
+
+       private static function getID ( $page ) {
+               if ( is_numeric( $page ) && $page > 0 ) {
+                       $title = \Title::newFromID( $page );
+                       return (int)$page;
+               } elseif ( $page instanceof \PhpTags\GenericObject ) {
+                       $value = $page->getValue();
+                       if ( $value instanceof \Title ) {
+                               $title = $value;
+                       } else {
+                               return false;
+                       }
+               } elseif ( is_string( $page) ) {
+                       $title = \Title::newFromText( $page );
+               } else {
+                       return false;
+               }
+               if ( $title->isRedirect() ) {
+                       $redirects = \WikiPage::factory( $title 
)->getContent()->getRedirectChain();
+                       if ( !$redirects ) {
+                               return false;
+                       }
+                       $title = array_pop( $redirects );
+               }
+               if ( $title && $title->exists() && $title->userCan('read') ) {
+                       return $title->getArticleID();
+               }
+               return false;
+       }
+
+       public function m_getValues() {
+               wfDebugLog( 'PhpTags Storage', __METHOD__ );
+               $pageTemplates = \PhpTagsStorage\Schema::getPageTemplates( 
$this->pageID );
+               if ( $pageTemplates === false ) { // Page has no data
+                       return false;
+               }
+
+               $return = array();
+               $templates = $this->templates;
+               if ( $templates ) {
+                       $wrongTemplates = array_diff( $pageTemplates, 
$templates );
+                       foreach ( $wrongTemplates as $k => $wt ) {
+                               $return[$wt] = false;
+                               unset( $templates[$k] );
+                       }
+                       if ( ! $templates ) {
+                               return $return;
+                       }
+               } else {
+                       $templates = $pageTemplates;
+               }
+
+               $pageID = $this->pageID;
+               $pageCache =& self::$cache[$pageID];
+               if ( isset(self::$cache[$pageID]) ) { // Get data from cache
+                       foreach ( $templates as $k => $t ) {
+                               if ( isset( $pageCache[$t] ) ) {
+                                       $return[$t] = $pageCache[$t];
+                                       unset( $templates[$k] );
+                               }
+                       }
+                       if ( ! $templates ) {
+                               return $return;
+                       }
+               }
+
+               \PhpTagsStorage\Schema::loadSchema( $templates );
+               $db = wfGetDB( DB_SLAVE, 'PhpTags' );
+               foreach ( $templates as $t ) {
+                       $fields = \PhpTagsStorage\Schema::getTemplateFields( $t 
);
+                       $res = $db->select( 
\PhpTagsStorage\Schema::TABLE_PREFIX . $t, '*', array('page_id'=>$pageID) );
+                       while ( $row = $res->fetchRow() ) {
+                               $rowID = $row['row_id'];
+                               foreach ( $fields as $f ) {
+                                       $pageCache[$t][$rowID][$f->getName()] = 
$row[$f->getDBName()];
+                               }
+                       }
+                       $res->free();
+                       $return[$t] = $pageCache[$t];
+               }
+               return $return;
+       }
+
+}
diff --git a/includes/Storage.php b/includes/Storage.php
new file mode 100644
index 0000000..d1a37ce
--- /dev/null
+++ b/includes/Storage.php
@@ -0,0 +1,95 @@
+<?php
+namespace PhpTagsObjects;
+
+/**
+ * Description of Storage
+ *
+ * @author pastakhov
+ */
+class Storage extends \PhpTags\GenericObject {
+
+       private $row_id;
+       private static $row_ids = array();
+       private static $data = array();
+
+       public function m___construct( $structure ) {
+               wfDebugLog( 'PhpTags Storage', __METHOD__ );
+               $frameTitle = \PhpTags\Renderer::getFrame()->getTitle();
+//             echo __METHOD__ . '( ' . $frameTitle->getArticleID() . " )\n";
+               $this->value = new \PhpTagsStorage\Schema( $frameTitle, 
$structure );
+               return true;
+       }
+
+       public function m_setValues( $values ) {
+               wfDebugLog( 'PhpTags Storage', __METHOD__ . print_r( $values, 
true ) );
+               $schema = $this->value;
+               $templateID = $schema->getTemplateID();
+               if ( $templateID <= 0 ) {
+                       wfDebugLog( 'PhpTags Storage', __METHOD__ . ' has zerro 
template ID, skipping.');
+                       return;
+               }
+               if ( $templateID === 
\PhpTags\Renderer::getParser()->getTitle()->getArticleID() ) {
+                       wfDebugLog( 'PhpTags Storage', __METHOD__ . ' template 
does not store data for itself, skipping.');
+                       return;
+               }
+
+               $rowID = $this->getRowID();
+               foreach ( $values as $fieldName => $val ) {
+                       $field = $schema->getField( $fieldName );
+                       if ( $field === false ) {
+                               unset( self::$data[$templateID][$rowID] );
+                               throw new \PhpTags\HookException( "Unknown 
field: $fieldName" );
+                       }
+                       self::$data[$templateID][$rowID][$field->getDBName()] = 
$val;
+               }
+
+       }
+
+       private function getRowID() {
+               if ( $this->row_id === null ) {
+                       $templateID = $this->value->getTemplateID();
+                       $scope = \PhpTags\Renderer::getScopeID( 
\PhpTags\Renderer::getFrame() );
+                       if ( false === isset( 
self::$row_ids[$templateID][$scope] ) ) {
+                               self::$row_ids[$templateID][$scope] = isset( 
self::$row_ids[$templateID] ) ? count( self::$row_ids[$templateID] ) + 1 : 1;
+                       }
+                       $this->row_id = self::$row_ids[$templateID][$scope];
+               }
+               return $this->row_id;
+       }
+
+       /**
+        *
+        * @param int $titleID
+        * @param array $updates
+        */
+       public static function onDataUpdates( $titleID, &$updates ) {
+               if ( $titleID <= 0 ) {
+                       wfDebugLog( 'PhpTags Storage', __METHOD__ . ' has zerro 
template ID, skipping.');
+                       return;
+               }
+               \PhpTagsStorage\Schema::onDataUpdates( $titleID, $updates );
+
+               $templates = array();
+               foreach ( self::$data as $templateID => $rows ) {
+                       $templates[] = $templateID;
+                       $updates[] = new \PhpTagsStorage\PageDataUpdate( 
$titleID, $templateID, $rows );
+               }
+
+               $oldTemplates = \PhpTagsStorage\Schema::getPageTemplates( 
$titleID );
+               if ( $oldTemplates !== false ) {
+                       $deleteOldTemplates = array_diff( $oldTemplates, 
$templates );
+                       \PhpTagsStorage\Schema::loadSchema( $deleteOldTemplates 
);
+                       foreach ( $deleteOldTemplates as $templateID ) {
+                               if ( \PhpTagsStorage\Schema::getLoadedRow( 
$templateID ) === true ) { // schema doesn't exists
+                                       continue;
+                               }
+                               $updates[] = new 
\PhpTagsStorage\PageDataUpdate( $titleID, $templateID, false );
+                       }
+               }
+
+               $updates[] = \PhpTagsStorage\Schema::createPageTemplatesUpdate( 
$titleID, $templates );
+               self::$data = array();
+               self::$row_ids = array();
+       }
+
+}
diff --git a/includes/Storage/Field.php b/includes/Storage/Field.php
new file mode 100644
index 0000000..021ba5a
--- /dev/null
+++ b/includes/Storage/Field.php
@@ -0,0 +1,69 @@
+<?php
+namespace PhpTagsStorage;
+
+class Field {
+
+       const PREFIX = 'field_';
+
+       private $name;
+       private $type;
+       private $templateID;
+       private $number;
+
+       public function __construct( $templateID, $name, $type, $number ) {
+               $this->templateID = $templateID;
+               $this->name = $name;
+               $this->type = $type;
+               $this->number = $number;
+       }
+
+       public static function newFromUserString( $templateID, $name, 
$typeString, $number ) {
+               switch ( strtolower( $typeString ) ) {
+                       case 'int':
+                       case 'integer':
+                               $type = self::T_INTEGER;
+                               break;
+                       case 'text':
+                       case 'string':
+                               $type = self::T_TEXT;
+                               break;
+                       default:
+                               throw new \PhpTags\HookException( "unknown 
colum type: $typeString" );
+               }
+               return new self( $templateID, $name, $type, $number );
+       }
+
+       const T_INTEGER = 'INTEGER';
+       const T_TEXT = 'TEXT';
+
+       public static function newFromDB( $templateID, $name, $info ) {
+               return new self( $templateID, $name, $info['type'], 
$info['number'] );
+       }
+
+       public function toCreateSQL() {
+               //global $wgDBtype;
+               $type = $this->type;
+               $name = self::PREFIX . $this->number;
+               return ", $name $type NULL DEFAULT NULL";
+       }
+
+       public function toSchema() {
+               return array(
+                       'type' => $this->type,
+                       'number' => $this->number,
+               );
+       }
+
+       public function getName() {
+               return $this->name;
+       }
+
+       public function getNumber() {
+               return $this->number;
+       }
+
+       public function getDBName() {
+               return self::PREFIX . $this->number;
+       }
+
+}
\ No newline at end of file
diff --git a/includes/Storage/PageDataUpdate.php 
b/includes/Storage/PageDataUpdate.php
new file mode 100644
index 0000000..045f71f
--- /dev/null
+++ b/includes/Storage/PageDataUpdate.php
@@ -0,0 +1,43 @@
+<?php
+namespace PhpTagsStorage;
+
+/**
+ *
+ */
+class PageDataUpdate extends \DataUpdate {
+
+       private $pageID;
+       private $templateID;
+       private $rows;
+
+       function __construct( $pageID, $templateID, $rows ) {
+               parent::__construct();
+
+               $this->pageID = $pageID;
+               $this->templateID = $templateID;
+               $this->rows = $rows;
+       }
+
+       public function doUpdate() {
+               wfDebugLog( 'PhpTags Storage', __METHOD__ );
+
+               $db = wfGetDB( DB_MASTER );
+               wfDebugLog( 'PhpTags Storage', __METHOD__ . ' DELETE ' . 
$this->templateID . ' WHERE page_id=' . $this->pageID );
+               $db->delete( Schema::TABLE_PREFIX . $this->templateID, 
array('page_id'=>$this->pageID) );
+               unset( \PhpTagsObjects\PageData::$cache[$this->pageID] );
+
+               if ( $this->rows === false ) {
+                       return;
+               }
+               $a = array();
+               $pageID = $this->pageID;
+               foreach ( $this->rows as $rowID => $value ) {
+                       $value['page_id'] = $pageID;
+                       $value['row_id'] = $rowID;
+                       $a[] = $value;
+               }
+               wfDebugLog( 'PhpTags Storage', __METHOD__ . ' INSERT ' . 
$this->templateID . ' VALUES ' . print_r( $a, true ) );
+               $db->insert( Schema::TABLE_PREFIX . $this->templateID, $a );
+       }
+
+}
diff --git a/includes/Storage/PageTemplatesUpdate.php 
b/includes/Storage/PageTemplatesUpdate.php
new file mode 100644
index 0000000..2f2a898
--- /dev/null
+++ b/includes/Storage/PageTemplatesUpdate.php
@@ -0,0 +1,43 @@
+<?php
+namespace PhpTagsStorage;
+
+/**
+ *
+ */
+class PageTemplatesUpdate extends \DataUpdate {
+
+       private $pageID;
+       private $templates;
+       private $schemaPageTemplates;
+
+       function __construct( $pageID, $templates, &$pageTemplates ) {
+               parent::__construct();
+
+               $this->pageID = $pageID;
+               $this->templates = $templates;
+               $this->schemaPageTemplates =& $pageTemplates;
+       }
+
+       public function doUpdate() {
+               wfDebugLog( 'PhpTags Storage', __METHOD__ );
+
+               $db = wfGetDB( DB_MASTER );
+               $templates = $this->templates;
+
+               if ( $templates ) {
+                       wfDebugLog( 'PhpTags Storage', __METHOD__ . " REPLACE 
pageTemplates WHERE pageID is " . $this->pageID );
+                       $db->replace(
+                                       Schema::TABLE_PAGE_TEMPLATES,
+                                       array('page_id' => $this->pageID),
+                                       array('page_id' => $this->pageID, 
'templates' => \FormatJson::encode( $templates ))
+                               );
+                       $this->schemaPageTemplates[$this->pageID] = $templates;
+                       return;
+               }
+
+               wfDebugLog( 'PhpTags Storage', __METHOD__ . " DELETE 
pageTemplates WHERE pageID is " . $this->pageID );
+               $db->delete( Schema::TABLE_PAGE_TEMPLATES, 
array('page_id'=>$this->pageID) );
+               $this->schemaPageTemplates[$this->pageID] = null;
+       }
+
+}
diff --git a/includes/Storage/Schema.php b/includes/Storage/Schema.php
new file mode 100644
index 0000000..b2c8ae4
--- /dev/null
+++ b/includes/Storage/Schema.php
@@ -0,0 +1,196 @@
+<?php
+namespace PhpTagsStorage;
+
+class Schema {
+
+       const TABLE_PREFIX = 'phptags_storage_';
+       const TABLE_SCHEMA = 'phptags_schemas';
+       const TABLE_PAGE_TEMPLATES = 'phptags_page_templates';
+
+       /**
+        *
+        * @var \Title
+        */
+       private $title;
+
+       private static $schemaUpdates = array();
+
+       private $templateID = 0;
+       private static $loadedRows = array();
+       private static $templates = array();
+       private static $pageTemplates = array();
+
+       public function __construct( \Title $templateTitle, $newStructure = 
null ) {
+               $this->title = $templateTitle;
+               $templateID = $templateTitle->getArticleID();
+               $this->templateID = $templateID;
+
+               $this->preloadSchemas(); // try to query structures for all 
templates in the page if needed
+               $originFields = self::getTemplateFields( $templateID );
+               $newFields = array();
+               $number = 1;
+               foreach ( $newStructure as $fieldName => $fieldType ) {
+                       $newFields[$fieldName] = Field::newFromUserString( 
$templateID, $fieldName, $fieldType, $number++ );
+               }
+
+               if ( $templateID <= 0 ) {
+                       wfDebugLog( 'PhpTags Storage', __METHOD__ . ' has zerro 
template ID, skipping.');
+                       return;
+               }
+
+               if ( $originFields === false ) {
+                       wfDebugLog( 'PhpTags Storage', __METHOD__ . ' has new 
schema, will be created.');
+                       $fDropTable = false;
+               } else {
+                       if ( $originFields == $newFields ) {
+                               wfDebugLog( 'PhpTags Storage', __METHOD__ . ' 
has no changes in schema, skipping.');
+                               return;
+                       }
+                       wfDebugLog( 'PhpTags Storage', __METHOD__ . ' has 
changes in schema, will be updated.');
+                       $fDropTable = true;
+               }
+
+               self::$templates[$templateID] = $newFields;
+               self::$schemaUpdates[$templateID] = new SchemaUpdate( 
$templateID, $newFields, $fDropTable, self::$loadedRows, self::$templates );
+       }
+
+       private function preloadSchemas() {
+               $pageID = 
\PhpTags\Renderer::getParser()->getTitle()->getArticleID();
+               if ( $pageID <= 0 || isset( self::$pageTemplates[$pageID] ) ) { 
// already preloaded or id = 0
+                       return;
+               }
+
+               $templates = self::getPageTemplates( $pageID );
+               $templateID = $this->templateID;
+               if( $templateID <= 0 ) {
+                       return;
+               }
+
+               if ( $templates === false ) {
+                       $templates = array( $templateID );
+               } elseif( false === in_array( $templateID, $templates ) ) {
+                       $templates[] = $templateID;
+               }
+               return self::loadSchema( $templates );
+       }
+
+       public static function getPageTemplates( $pageID ) {
+               if ( isset(self::$pageTemplates[$pageID]) ) {
+                       if ( self::$pageTemplates[$pageID] === true ) {
+                               return false;
+                       }
+                       return self::$pageTemplates[$pageID];
+               }
+
+               $db = wfGetDB( DB_SLAVE );
+               $rowTemplates = $db->selectRow( self::TABLE_PAGE_TEMPLATES , 
'templates', array('page_id'=>$pageID) );
+               if ( $rowTemplates !== false ) {
+                       $templates = \FormatJson::decode( 
$rowTemplates->templates, true );
+                       self::$pageTemplates[$pageID] = $templates;
+                       return $templates;
+               }
+
+               self::$pageTemplates[$pageID] = true;
+               return false;
+       }
+
+       public static function loadSchema( $templates ) {
+               $tmp = array();
+               foreach ( $templates as $id ) {
+                       if ( isset( self::$loadedRows[$id] ) ) {
+                               continue;
+                       }
+                       $tmp[] = $id;
+               }
+               if ( ! $tmp ) {
+                       return true;
+               }
+
+               $db = wfGetDB( DB_SLAVE );
+               $schemaRows = $db->select( self::TABLE_SCHEMA, 
array('template_id','table_schema'), array('template_id'=>$tmp) );
+               while ( $row = $schemaRows->fetchObject() ) {
+                       self::$loadedRows[$row->template_id] = 
$row->table_schema;
+               }
+               foreach ( $tmp as $id ) {
+                       if ( isset( self::$loadedRows[$id] ) ) {
+                               continue;
+                       }
+                       self::$loadedRows[$id] = true;
+               }
+               return $schemaRows->numRows();
+       }
+
+       /**
+        *
+        * @param type $templateID
+        * @return Field[]
+        */
+       public static function getTemplateFields( $templateID ) {
+               if ( $templateID <= 0 ) {
+                       return false;
+               }
+               if ( !isset( self::$templates[$templateID] ) ) {
+                       if ( !isset( self::$loadedRows[$templateID] ) ) {
+                               self::loadSchema( array($templateID) );
+                       }
+                       if ( self::$loadedRows[$templateID] === true ) {
+                               self::$templates[$templateID] = true;
+                       } else {
+                               $fields = \FormatJson::decode( 
self::$loadedRows[$templateID], true );
+                               $objFields = array();
+                               foreach ( $fields as $fieldName => $fieldInfo ) 
{
+                                       $objFields[$fieldName] = 
Field::newFromDB( $templateID, $fieldName, $fieldInfo );
+                               }
+                               self::$templates[$templateID] = $objFields;
+                       }
+               }
+               return self::$templates[$templateID] === true ? false : 
self::$templates[$templateID];
+       }
+
+       public function getTemplateID() {
+               return $this->templateID;
+       }
+
+       /**
+        * Returns filed by name
+        * @param string $fieldName
+        * @return Field
+        */
+       public function getField( $fieldName ) {
+               return isset( self::$templates[$this->templateID][$fieldName] ) 
? self::$templates[$this->templateID][$fieldName] : false;
+       }
+
+       public static function getLoadedRow( $id ) {
+               return isset( self::$loadedRows[$id] ) ? self::$loadedRows[$id] 
: null;
+       }
+
+       public static function createPageTemplatesUpdate( $pageID, $templates ) 
{
+               return new PageTemplatesUpdate( $pageID, $templates, 
self::$pageTemplates );
+       }
+
+       /**
+        *
+        * @param int $titleID
+        * @param array $updates
+        */
+       public static function onDataUpdates( $titleID, &$updates ) {
+               if ( isset( self::$schemaUpdates[$titleID] ) ) { // need to 
update the schema
+                       wfDebugLog( 'PhpTags Storage', __METHOD__ . ' adds 
UPDATE schema task for ID:' . $titleID );
+                       $updates[] = self::$schemaUpdates[$titleID];
+//                     echo 'UPDATE schema task ' . $titleID;
+                       self::$schemaUpdates[$titleID] = null;
+               } elseif ( isset( self::$templates[$titleID] ) && 
self::$templates[$titleID] !== true ) {
+//                     echo "...SKIP";
+               } elseif ( isset( self::$loadedRows[$titleID] ) || 
self::loadSchema( array($titleID) ) > 0 ) { // need to drop the storage table
+                       wfDebugLog( 'PhpTags Storage', __METHOD__ . ' adds DROP 
TABLE schema task for ID:' . $titleID );
+                       $updates[] = new SchemaUpdate( $titleID, false, true, 
self::$loadedRows, self::$templates ); // drop it
+//                     echo 'DROP TABLE schema task ' . $titleID;
+               }
+//             echo "...\n";
+       }
+
+       public static function onPageDelete( $titleID ) {
+               unset( self::$templates[$titleID] );
+       }
+
+}
diff --git a/includes/Storage/SchemaUpdate.php 
b/includes/Storage/SchemaUpdate.php
new file mode 100644
index 0000000..0d96eaf
--- /dev/null
+++ b/includes/Storage/SchemaUpdate.php
@@ -0,0 +1,74 @@
+<?php
+namespace PhpTagsStorage;
+
+/**
+ *
+ */
+class SchemaUpdate extends \DataUpdate {
+
+       private $templateID;
+       private $fields;
+       private $fDropTable;
+       private $schemaLoadedRows;
+       private $schemaTemplates;
+
+       function __construct( $templateID, $fields, $fDropTable, &$loadedRows, 
&$templates ) {
+               parent::__construct();
+
+               $this->templateID = $templateID;
+               $this->fields = $fields;
+               $this->fDropTable = $fDropTable;
+               $this->schemaLoadedRows =& $loadedRows;
+               $this->schemaTemplates =& $templates;
+       }
+
+       public function doUpdate() {
+               wfDebugLog( 'PhpTags Storage', __METHOD__ );
+               $templateID = $this->templateID;
+               $db = wfGetDB( DB_MASTER );
+
+               if ( $this->fDropTable ) {
+                       try {
+                               wfDebugLog( 'PhpTags Storage', __METHOD__ . " 
DROP TABLE $templateID");
+                               $db->dropTable( Schema::TABLE_PREFIX . 
$templateID );
+                       } catch ( Exception $e ) {
+                               throw new MWException( "Caught exception ($e) 
while trying to drop PhpTags Storage table. "
+                               . "Please make sure that your database user 
account has the DROP permission." );
+                       }
+               }
+
+               if ( $this->fields === false ) {
+                       wfDebugLog( 'PhpTags Storage', __METHOD__ . " DELETE 
TABLE_SCHEMA $templateID");
+                       $db->delete( Schema::TABLE_SCHEMA, 
array('template_id'=>$templateID) );
+                       $this->schemaLoadedRows[$templateID] = true;
+                       $this->schemaTemplates[$templateID] = true;
+                       return;
+               }
+
+               # create table for template's data
+               $tableName = $db->tableName( Schema::TABLE_PREFIX . $templateID 
);
+               $fields = array(); // for update table Schema::TABLE_SCHEMA
+               $createSQL = "CREATE TABLE $tableName ( page_id int NOT NULL, 
row_id int NOT NULL";
+               foreach ( $this->fields as $f ) {
+                       $createSQL .= $f->toCreateSQL();
+                       $fields[$f->getName()] = $f->toSchema();
+               }
+               $createSQL .= ')';
+               try {
+                       wfDebugLog( 'PhpTags Storage', __METHOD__ . " CREATE 
TABLE $templateID");
+                       $db->query( $createSQL );
+                       $db->query( "CREATE UNIQUE INDEX page_row_id ON 
$tableName ( page_id, row_id )" );
+               } catch ( Exception $e ) {
+                       throw new MWException( "Caught exception ($e) while 
trying to create PhpTags Storage table. "
+                               . "Please make sure that your database user 
account has the CREATE permission." );
+               }
+
+               # update table Schema::TABLE_SCHEMA
+               $schema = \FormatJson::encode( $fields );
+               wfDebugLog( 'PhpTags Storage', __METHOD__ . " REPLACE 
TABLE_SCHEMA $templateID");
+               $db->replace( Schema::TABLE_SCHEMA, 
array('template_id'=>$templateID), array('template_id'=>$templateID, 
'table_schema'=>$schema) );
+               $this->schemaTemplates[$templateID] = null;
+               $this->schemaLoadedRows[$templateID] = $schema;
+       }
+
+}
diff --git a/sql/storage.sql b/sql/storage.sql
new file mode 100644
index 0000000..289abac
--- /dev/null
+++ b/sql/storage.sql
@@ -0,0 +1,13 @@
+CREATE TABLE /*_*/phptags_schemas (
+       template_id int NOT NULL,
+       table_schema text NOT NULL
+) /*$wgDBTableOptions*/;
+
+CREATE UNIQUE INDEX /*i*/template_id ON /*_*/phptags_schemas (template_id);
+
+CREATE TABLE /*_*/phptags_page_templates (
+       page_id int NOT NULL,
+       templates text NOT NULL
+) /*$wgDBTableOptions*/;
+
+CREATE UNIQUE INDEX /*i*/page_id ON /*_*/phptags_page_templates (page_id);
diff --git "a/tests/phpunit/\041\041\041firstInit_Test.php" 
"b/tests/phpunit/\041\041\041firstInit_Test.php"
new file mode 100644
index 0000000..74f988a
--- /dev/null
+++ "b/tests/phpunit/\041\041\041firstInit_Test.php"
@@ -0,0 +1,8 @@
+<?php
+
+if ( PhpTags\Renderer::$needInitRuntime ) {
+       wfRunHooks( 'PhpTagsRuntimeFirstInit' );
+       \PhpTags\Hooks::loadData();
+       \PhpTags\Runtime::$loopsLimit = 1000;
+       PhpTags\Renderer::$needInitRuntime = false;
+}
diff --git a/tests/phpunit/StorageWikiPage_Test.php 
b/tests/phpunit/StorageWikiPage_Test.php
new file mode 100644
index 0000000..5c2076f
--- /dev/null
+++ b/tests/phpunit/StorageWikiPage_Test.php
@@ -0,0 +1,250 @@
+<?php
+
+class StorageWikiPageTest extends MediaWikiLangTestCase {
+
+       protected function setUp() {
+               parent::setUp();
+               $this->pages_to_delete = array();
+
+               LinkCache::singleton()->clear(); # avoid cached redirect 
status, etc
+       }
+
+
+
+       protected function tearDown() {
+               foreach ( $this->pages_to_delete as $p ) {
+                       /* @var $p WikiPage */
+
+                       try {
+                               if ( $p->exists() ) {
+                                       $p->doDeleteArticle( "testing done." );
+                               }
+                       } catch ( MWException $ex ) {
+                               // fail silently
+                       }
+               }
+               parent::tearDown();
+       }
+
+       /**
+        * @param Title|string $title
+        * @param string|null $model
+        * @return WikiPage
+        */
+       protected function newPage( $title ) {
+               if ( is_string( $title ) ) {
+                       $title = Title::newFromText( $title );
+               }
+
+               $page = WikiPage::factory( $title );
+
+               $this->pages_to_delete[] = $page;
+
+               return $page;
+       }
+
+       /**
+        * @param string|Title|WikiPage $page
+        * @param string $text
+        * @param int $model
+        *
+        * @return WikiPage
+        */
+       protected function createPage( $page, $text ) {
+               if ( is_string( $page ) || $page instanceof Title ) {
+                       $page = $this->newPage( $page );
+               }
+
+               if ( $text instanceof Content ) {
+                       $content = $text;
+               } else {
+                       $content = ContentHandler::makeContent( $text, 
$page->getTitle() );
+               }
+               $page->doEditContent( ContentHandler::makeContent( '', 
$page->getTitle() ), "create empty page" );
+               $page->doEditContent( $content, "testing" );
+               return $page;
+       }
+
+       public function test_template_StorageTag() {
+
+               ####### Create Template:StorageTag #######
+               $text = '
+<phptag>
+$s = new Storage( ["tag"=>"text"] );
+if( !isset( $argv[1] ) ) {
+       break;
+}
+$s->setValues( [ "tag"=>$argv[1] ] );
+</phptag>';
+
+               $titleStorageTag = Title::newFromText( 'StorageTag', 
NS_TEMPLATE );
+               $templateStorageTagId = $this->createPage( $titleStorageTag, 
$text, CONTENT_MODEL_WIKITEXT )->getId();
+
+               # ------------------------
+               $dbr = wfGetDB( DB_SLAVE );
+               $res = $dbr->select( PhpTagsStorage\Schema::TABLE_SCHEMA, '*', 
array('template_id' => $templateStorageTagId) );
+               $n = $res->numRows();
+               $res->free();
+
+               $this->assertEquals( 1, $n, 'TABLE_SCHEMA should contain one 
record' );
+
+               # ------------------------
+               $this->assertTrue( $dbr->tableExists( 
PhpTagsStorage\Schema::TABLE_PREFIX . $templateStorageTagId ), 'Table for 
template data was not created' );
+
+               ####### Create Page1 (transclude one template StorageTag) 
#######
+               $text = '{{StorageTag|It is TAG!}}';
+
+               $page_1 = $this->createPage( "Page1", $text, 
CONTENT_MODEL_WIKITEXT );
+               $page_1_ID = $page_1->getId();
+
+               # ------------------------
+               $template_table_name = PhpTagsStorage\Schema::TABLE_PREFIX . 
$templateStorageTagId;
+               $field_1_DB_Name = PhpTagsStorage\Field::PREFIX . 1;
+               $res = $dbr->select( $template_table_name, '*', 
array('page_id'=>$page_1_ID) );
+               $n = $res->numRows();
+               $this->assertEquals( 1, $n, 'template TABLE should contain one 
record for Page1' );
+
+               $row = $res->fetchRow();
+               $this->assertEquals( $row[$field_1_DB_Name], 'It is TAG!' );
+               $res->free();
+
+               ####### Create Page2 (transclude three templates StorageTag) 
#######
+               $text = 
'{{StorageTag|one}}{{StorageTag|two}}{{StorageTag|three}}';
+
+               $pageID = $this->createPage( "Page2", $text, 
CONTENT_MODEL_WIKITEXT )->getId();
+
+               # ------------------------
+               $res = $dbr->select( $template_table_name, '*', 
array('page_id'=>$pageID) );
+               $n = $res->numRows();
+               $this->assertEquals( 3, $n, 'template TABLE should contain 
three record for Page2' );
+
+               $row = $res->fetchRow();
+               $this->assertEquals( $row[$field_1_DB_Name], 'one' );
+               $row = $res->fetchRow();
+               $this->assertEquals( $row[$field_1_DB_Name], 'two' );
+               $row = $res->fetchRow();
+               $this->assertEquals( $row[$field_1_DB_Name], 'three' );
+               $res->free();
+
+               ####### Create Template:DumpTags #######
+               $text = '
+<phptag>
+$title = isset( $argv[1] ) ? new WTitle( $argv[1] ) : null;
+$templateTitle = new WTitle( "StorageTag", NS_TEMPLATE );
+$pd = new PageData( $title, $templateTitle );
+$values = $pd->getValues();
+$rows = current( $values );
+if ( $rows ) {
+    $tags = array();
+    foreach ( $rows as $tagRow ) {
+        $tags[] = $tagRow["tag"];
+    }
+    echo "TAGS: ", implode( ", ", $tags ), ".\n";
+} else {
+       echo "There is no TAG\n";
+}
+</phptag>';
+
+               $templateDumpTagsId = $this->createPage( "Template:DumpTags", 
$text, CONTENT_MODEL_WIKITEXT )->getId();
+
+               ####### Create Page 'Test template DumpTags' for Page2 data 
#######
+               $text = '{{DumpTags|Page2}}';
+
+               $page = $this->createPage( "Test template DumpTags", $text, 
CONTENT_MODEL_WIKITEXT );
+
+               $options = new ParserOptions();
+               $options->enableLimitReport( false );
+
+               $output = $page->getContent()-> getParserOutput( 
$page->getTitle(), null, $options );
+               $this->assertEquals($output->getText(), "<p>TAGS: one, two, 
three.\n</p>", "Page 'Test template DumpTags'" );
+
+               ####### Move Template:StorageTag to Template:NewStorageTag and 
create redirect #######
+//             var_dump( "-= MOVE TEMPLATE =-" );
+               global $wgUser;
+               $titleNewStorageTag = Title::newFromText( "NewStorageTag", 
NS_TEMPLATE );
+               $mp = new MovePage( $titleStorageTag, $titleNewStorageTag, true 
);
+               $status = $mp->move( $wgUser, 'Test move Storage', true );
+               $this->assertTrue( $status->isOK() );
+               $this->assertEquals( $titleNewStorageTag->getArticleID(), 
$templateStorageTagId, 'ID should not change when moving the template' );
+               $this->assertTrue( $dbr->tableExists( 
PhpTagsStorage\Schema::TABLE_PREFIX . $templateStorageTagId ), 'Template table 
was dropped, ID:' . $templateStorageTagId );
+
+               # ------------------------
+               $res = $dbr->select( $template_table_name, '*', 
array('page_id'=>$page_1_ID) );
+               $n = $res->numRows();
+               $this->assertEquals( 1, $n, 'template TABLE should contain one 
record for Page1 after template move' );
+               $row = $res->fetchRow();
+               $this->assertEquals( $row[$field_1_DB_Name], 'It is TAG!', 
'After template move' );
+               $res->free();
+
+               ####### Create Page 'Test template DumpTags after move 
template' for Page2 data #######
+               $text = '{{DumpTags|Page2}}';
+
+               $page = $this->createPage( "Test template DumpTags after move 
template", $text, CONTENT_MODEL_WIKITEXT );
+               $output = $page->getContent()-> getParserOutput( 
$page->getTitle(), null, $options );
+               $this->assertEquals($output->getText(), "<p>TAGS: one, two, 
three.\n</p>", "Page 'Test template DumpTags after move template'" );
+
+               ####### Create Page3 (transclude redirect StorageTag) #######
+               $text = '{{StorageTag|I use #redirect to template 
NewStorageTag}}';
+               $pageID = $this->createPage( "Page3", $text, 
CONTENT_MODEL_WIKITEXT )->getId();
+               $res = $dbr->select( $template_table_name, '*', 
array('page_id'=>$pageID) );
+               $n = $res->numRows();
+               $this->assertEquals( 1, $n, 'template TABLE should contain 
record when redirect is transcluded (Page3)' );
+               $row = $res->fetchRow();
+               $this->assertEquals( $row[$field_1_DB_Name], 'I use #redirect 
to template NewStorageTag' );
+               $res->free();
+
+               ####### Test template DumpTags for redirect page #######
+               $wch = new WikitextContentHandler();
+               $text = $wch->makeRedirectContent( Title::newFromText( "Page3" 
) );
+               $page = $this->createPage( "Redirect to Page3", $text, 
CONTENT_MODEL_WIKITEXT );
+               $page->insertRedirect();
+
+               $text = '{{DumpTags|Redirect to Page3}}';
+               $page = $this->createPage( "Test DumpTags for redirect page", 
$text, CONTENT_MODEL_WIKITEXT );
+
+               $output = $page->getContent()-> getParserOutput( 
$page->getTitle(), null, $options );
+               $this->assertEquals($output->getText(), "<p>TAGS: I use 
#redirect to template NewStorageTag.\n</p>" );
+
+               ####### Delete Page1 #######
+//             echo "Test delete Page1 $page_1_ID\n";
+               $page_1->doDeleteArticle( 'test delete page' );
+               $res = $dbr->select( $template_table_name, '*', 
array('page_id'=>$page_1_ID) );
+               $n = $res->numRows();
+               $this->assertEquals( 0, $n, 'template TABLE should not contain 
a record for Page1 after the page deletion' );
+               $res->free();
+
+               ####### Undelete Page1 #######
+               $archive = new PageArchive( $page_1->getTitle() );
+               $archive->undelete( array(), 'restore Page1 after test delete' 
);
+               $page_1 = WikiPage::factory( $page_1->getTitle() );
+               $page_1_ID = $page_1->getId();
+
+               # ------------------------
+               $template_table_name = PhpTagsStorage\Schema::TABLE_PREFIX . 
$templateStorageTagId;
+               $field_1_DB_Name = PhpTagsStorage\Field::PREFIX . 1;
+               $res = $dbr->select( $template_table_name, '*', 
array('page_id'=>$page_1_ID) );
+               $n = $res->numRows();
+               $this->assertEquals( 1, $n, 'template TABLE should contain one 
record for Page1 after undelete' );
+
+               $row = $res->fetchRow();
+               $this->assertEquals( $row[$field_1_DB_Name], 'It is TAG!', 
'after undelete Page1' );
+               $res->free();
+
+               ####### Delete Template:NewStorageTag #######
+//             var_dump( "\$titleNewStorageTag " . 
$titleNewStorageTag->getArticleID() );
+               WikiPage::factory( $titleNewStorageTag )->doDeleteArticle( 
'test delete template' );
+//             var_dump( 'test delete template' );
+
+               # ------------------------
+               $res = $dbr->select( PhpTagsStorage\Schema::TABLE_SCHEMA, '*', 
array('template_id' => $templateStorageTagId) );
+               $n = $res->numRows();
+               $res->free();
+               //$this->assertEquals( 0, $n, 'TABLE_SCHEMA should not contain 
a record after template delete, ID:' . $templateStorageTagId );
+
+               # ------------------------
+               $this->assertFalse( $dbr->tableExists( 
PhpTagsStorage\Schema::TABLE_PREFIX . $templateStorageTagId ), 'Table for 
template data was not deleted, ID:' . $templateStorageTagId );
+               //$j = new RunJobs();
+               //$j->execute();
+       }
+
+}
diff --git a/tests/phpunit/Storage_Test.php b/tests/phpunit/Storage_Test.php
new file mode 100644
index 0000000..2563a69
--- /dev/null
+++ b/tests/phpunit/Storage_Test.php
@@ -0,0 +1,13 @@
+<?php
+namespace PhpTags;
+
+class PhpTagsStorage_Test extends \PHPUnit_Framework_TestCase {
+
+       public function testRun_PHPTAGS_STORAGE_VERSION_constant() {
+               $this->assertEquals(
+                               Runtime::runSource('echo 
PHPTAGS_STORAGE_VERSION;'),
+                               array(PHPTAGS_STORAGE_VERSION)
+                               );
+       }
+
+}

-- 
To view, visit https://gerrit.wikimedia.org/r/218118
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: Idffbdda34a3dae2c7570691f86b263ae3ea7aafb
Gerrit-PatchSet: 11
Gerrit-Project: mediawiki/extensions/PhpTagsStorage
Gerrit-Branch: master
Gerrit-Owner: Pastakhov <pastak...@yandex.ru>
Gerrit-Reviewer: JoelKP <joelkpetters...@gmail.com>
Gerrit-Reviewer: Pastakhov <pastak...@yandex.ru>
Gerrit-Reviewer: Siebrand <siebr...@kitano.nl>
Gerrit-Reviewer: Springle <sprin...@wikimedia.org>
Gerrit-Reviewer: jenkins-bot <>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to