[ Index ]
 

Code source de eZ Publish 3.9.0

Accédez au Source d'autres logiciels libresSoutenez Angelica Josefina !

title

Body

[fermer]

/lib/ezdbschema/classes/ -> ezlintschema.php (source)

   1  <?php
   2  //
   3  // Definition of eZLintSchema class
   4  //
   5  // Created on: <05-Nov-2004 14:03:27 jb>
   6  //
   7  // SOFTWARE NAME: eZ publish
   8  // SOFTWARE RELEASE: 3.9.0
   9  // BUILD VERSION: 17785
  10  // COPYRIGHT NOTICE: Copyright (C) 1999-2006 eZ systems AS
  11  // SOFTWARE LICENSE: GNU General Public License v2.0
  12  // NOTICE: >
  13  //   This program is free software; you can redistribute it and/or
  14  //   modify it under the terms of version 2.0  of the GNU General
  15  //   Public License as published by the Free Software Foundation.
  16  //
  17  //   This program is distributed in the hope that it will be useful,
  18  //   but WITHOUT ANY WARRANTY; without even the implied warranty of
  19  //   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  20  //   GNU General Public License for more details.
  21  //
  22  //   You should have received a copy of version 2.0 of the GNU General
  23  //   Public License along with this program; if not, write to the Free
  24  //   Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  25  //   MA 02110-1301, USA.
  26  //
  27  //
  28  
  29  /*!
  30    \class eZLintSchema ezlintschema.php
  31    \ingroup eZDBSchema
  32    \brief Provides lint checking of database schemas
  33  
  34    Checks a given schema by going trough all tables, fields and indexes
  35    and corrects any mistakes. The result is a new schema which is returned
  36    in schema(). The new schema can then be used to diff against the original
  37    and output the changes.
  38  
  39    The current rules apply:
  40    - Table names must not exceed 26 characters, configurable in dbschema.ini (LintChecker/TableLimit)
  41    - Field names must not exceed 30 characters, configurable in dbschema.ini (LintChecker/FieldLimit)
  42    - Index names must not exceed 30 characters, configurable in dbschema.ini (LintChecker/IndexLimit)
  43    - Index names must not be the same as table names
  44    - String fields cannot have NOT NULL and an empty string as DEFAULT value.
  45    - Primary keys must be named PRIMARY
  46  
  47    The lint checker works by taking in another DB Schema object as parameter
  48    to the constructor. All calls will be forwarded to this object so it will
  49    work as though it were a real schema.
  50    The exception are the schema(), data() and validate() methods which makes
  51    sure the schema is correct.
  52  
  53    To check if the schema has been checked yet call isLintChecked().
  54    To fetch the DB schema which is checked use otherSchema().
  55  
  56  */
  57  
  58  include_once ( 'lib/ezdbschema/classes/ezdbschemainterface.php' );
  59  
  60  class eZLintSchema extends eZDBSchemaInterface
  61  {
  62      /*!
  63       Initializes the lint checker with a foreign db schema.
  64  
  65       \param $db A dummy parameter, pass \c false.
  66       \param $otherSchema The db schema that should be checked
  67      */
  68      function eZLintSchema( $db, $otherSchema )
  69      {
  70          $this->eZDBSchemaInterface( $db );
  71          $this->OtherSchema = $otherSchema;
  72          $this->CorrectSchema = false;
  73          $this->IsLintChecked = false;
  74      }
  75  
  76      /*!
  77       Runs the lint checker on the database schema in otherSchema()
  78       and returns the new schema that is correct.
  79      */
  80      function schema( $params = array() )
  81      {
  82          if ( $this->IsLintChecked )
  83          {
  84              return $this->CorrectSchema;
  85          }
  86  
  87          $params = array_merge( array( 'meta_data' => false,
  88                                        'format' => 'generic' ),
  89                                 $params );
  90  
  91          $this->CorrectSchema = $this->OtherSchema->schema( $params );
  92          $this->lintCheckSchema( $this->CorrectSchema );
  93          $this->IsLintChecked = true;
  94          return $this->CorrectSchema;
  95      }
  96  
  97      /*!
  98       \reimp
  99       Runs lint checker on all tables, indexes and fields.
 100      */
 101      function validate()
 102      {
 103          return $this->lintCheckSchema( $this->CorrectSchema );
 104      }
 105  
 106      /*!
 107       \return The schema object which is being lint checked.
 108      */
 109      function otherSchema()
 110      {
 111          return $this->OtherSchema;
 112      }
 113  
 114      /*!
 115       \return \c true if the lint checker has been run on the schema.
 116      */
 117      function isLintChecked()
 118      {
 119          return $this->IsLintChecked;
 120      }
 121  
 122      /*!
 123       \return A modified version of \a $identifier that is guaranteed to be shorter than \a $limit
 124      */
 125      function shortenIdentifier( $identifier, $limit, $shortenList )
 126      {
 127          reset( $shortenList );
 128          // Replace one word at a time until we have a string that is short
 129          // enough, or we run out of replace words
 130          while ( strlen( $identifier ) > $limit and
 131                  current( $shortenList ) !== false )
 132          {
 133              $from = key( $shortenList );
 134              $to = current( $shortenList );
 135              next( $shortenList );
 136              $identifier = str_replace( $from, $to, $identifier );
 137          }
 138  
 139          // It is still to large so we just cut it off
 140          if ( strlen( $identifier ) > $limit )
 141          {
 142              $identifier = substr( $identifier, 0, $limit );
 143          }
 144          return $identifier;
 145      }
 146  
 147      /*!
 148       \private
 149       Goes trough all tables, fields and indexes and makes sure they have valid names.
 150       \return \c false if something was fixed, \c true otherwise.
 151      */
 152      function lintCheckSchema( &$schema )
 153      {
 154          $status = true;
 155  
 156          $ini =& eZINI::instance( 'dbschema.ini' );
 157  
 158          // A mapping table that maps from a long name to a short name
 159          // This will be used if an identifier/name is too long
 160          $shortenList = $ini->variable( 'LintChecker', 'NameMap' );
 161  
 162          // Limitation on the length of identifiers/names
 163          // Oracle is the database with the most limit (30 characters) so the
 164          // limit values must be equal or lower to that.
 165          $tableNameLimit = (int)$ini->variable( 'LintChecker', 'TableLimit' );
 166          $fieldNameLimit = (int)$ini->variable( 'LintChecker', 'FieldLimit' );
 167          $indexNameLimit = (int)$ini->variable( 'LintChecker', 'IndexLimit' );
 168  
 169          // Tables which do not get lint checked, they are currently
 170          // handled with workarounds in the various schema handlers
 171          // Note: The fields and indexes are still checked.
 172          $ignoredTableList = $ini->variable( 'LintChecker', 'IgnoredTables' );
 173  
 174          // Fields which do not get lint checked, they are currently
 175          // handled with workarounds in the various schema handlers
 176          $list = $ini->variable( 'LintChecker', 'IgnoredFields' );
 177          $ignoredFieldList = array();
 178          foreach ( $list as $entry )
 179          {
 180              list( $tableName, $fieldName ) = explode( '.', $entry, 2 );
 181              if ( !isset( $ignoredFieldList[$tableName] ) )
 182                  $ignoredFieldList[$tableName] = array();
 183              $ignoredFieldList[$tableName][] = $fieldName;
 184          }
 185  
 186          // Fields which do not get lint checked, they are currently
 187          // handled with workarounds in the various schema handlers
 188          $list = $ini->variable( 'LintChecker', 'IgnoredFieldSyntax' );
 189          $ignoredFieldSyntaxList = array();
 190          foreach ( $list as $entry )
 191          {
 192              list( $tableName, $fieldName ) = explode( '.', $entry, 2 );
 193              if ( !isset( $ignoredFieldList[$tableName] ) )
 194                  $ignoredFieldSyntaxList[$tableName] = array();
 195              $ignoredFieldSyntaxList[$tableName][] = $fieldName;
 196          }
 197  
 198          // Indexes which do not get lint checked, they are currently
 199          // handled with workarounds in the various schema handlers
 200          $ignoredIndexList = $ini->variable( 'LintChecker', 'IgnoredIndexes' );
 201  
 202          $badTables = array();
 203          foreach ( $schema as $tableName => $tableDef )
 204          {
 205              // Skip the info structure, this is not a table
 206              if ( $tableName == '_info' )
 207                  continue;
 208  
 209              $existingTableName = $tableName;
 210              $tableComments = array();
 211  
 212              // If table is not in ignore list we check the name
 213              if ( !in_array( $tableName, $ignoredTableList ) )
 214              {
 215                  // identifiers must be 30 or less
 216                  // for tables we require 26 or less to allow adding suffix or prefix for indexes etc.
 217                  if ( strlen( $tableName ) > $tableNameLimit )
 218                  {
 219                      $tableComment = "Table names must not exceed $tableNameLimit characters,\n'$tableName' is " . strlen( $tableName ) . " characters,\ndatabases like Oracle will have problems with this.";
 220                      $tableName = $this->shortenIdentifier( $tableName, $tableNameLimit, $shortenList );
 221                      $tableComment .= "\nNew name is '$tableName'";
 222                      $tableComments[] = $tableComment;
 223                      $status = false;
 224                  }
 225  
 226                  if ( strcmp( $tableName, $existingTableName ) != 0 )
 227                  {
 228                      $badTables[] = array( 'from' => $existingTableName,
 229                                            'to' => $tableName );
 230                  }
 231              }
 232  
 233              if ( isset( $tableDef['fields'] ) )
 234              {
 235                  $badFields = array();
 236                  foreach ( $tableDef['fields'] as $fieldName => $fieldDef )
 237                  {
 238                      $comments = array();
 239                      $existingFieldName = $fieldName;
 240  
 241                      // Do we ignore the field name?
 242                      if ( !isset( $ignoredFieldList[$existingTableName] ) or
 243                           !in_array( $fieldName, $ignoredFieldList[$existingTableName] ) )
 244                      {
 245  
 246                          // identifiers must be 30 or less
 247                          if ( strlen( $fieldName ) > $fieldNameLimit )
 248                          {
 249                              $comment = "Field names must not exceed $fieldNameLimit characters,\n'$fieldName' in table '$existingTableName' is " . strlen( $fieldName ) . " characters,\ndatabases like Oracle will have problems with this.";
 250                              $fieldName = $this->shortenIdentifier( $fieldName, $fieldNameLimit, $shortenList );
 251                              $comment .= "\nNew name is '$fieldName'";
 252                              $comments[] = $comment;
 253                              $status = false;
 254                          }
 255                      }
 256  
 257                      if ( !isset( $ignoredFieldSyntaxList[$existingTableName] ) or
 258                           !in_array( $fieldName, $ignoredFieldSyntaxList[$existingTableName] ) )
 259                      {
 260                          /* Temporarily disabled
 261                          if ( in_array( $fieldDef['type'],
 262                                         array( 'varchar', 'char',
 263                                                'longtext', 'mediumtext', 'shorttext' ) ) and
 264                               isset( $fieldDef['not_null'] ) and
 265                               $fieldDef['not_null'] and
 266                               $fieldDef['default'] === '' )
 267                          {
 268                              $comments[] = "The string type " . $fieldDef['type'] . " ($existingTableName.$fieldName) cannot have NOT NULL defined and an empty string as DEFAULT value\nDatabase like Oracle will have problems with this.";
 269                              $status = false;
 270                          }
 271                          */
 272                      }
 273  
 274                      if ( strcmp( $existingFieldName, $fieldName ) != 0 )
 275                      {
 276                          $badFields[] = array( 'from' => $existingFieldName,
 277                                                'to' => $fieldName );
 278                      }
 279  
 280                      if ( count( $comments ) > 0 )
 281                      {
 282                          $schema[$existingTableName]['fields'][$existingFieldName]['comments'] = $comments;
 283                          foreach ( $comments as $comment )
 284                          {
 285                              eZDebug::writeWarning( $comment, 'eZLintSchema::fieldComment' );
 286                          }
 287                      }
 288                  }
 289  
 290                  foreach ( $badFields as $badField )
 291                  {
 292                      $schema[$existingTableName]['fields'][$badField['to']] = $schema[$existingTableName]['fields'][$badField['from']];
 293  //                     unset( $schema[$existingTableName]['fields'][$badField['from']] );
 294                      $schema[$existingTableName]['fields'][$badField['from']]['removed'] = true;
 295                  }
 296              }
 297  
 298              if ( isset( $tableDef['indexes'] ) )
 299              {
 300                  $badIndexes = array();
 301                  foreach ( $tableDef['indexes'] as $indexName => $indexDef )
 302                  {
 303                      // Primary key
 304                      if ( $indexDef['type'] == 'primary' )
 305                          continue;
 306  
 307                      // Do we ignore the index?
 308                      if ( in_array( $indexName, $ignoredIndexList ) )
 309                          continue;
 310  
 311                      $comments = array();
 312  
 313                      $existingIndexName = $indexName;
 314                      if ( isset( $schema[$indexName] ) )
 315                      {
 316                          $comment = "Index named '$indexName' has same name as an existing table,\ndatabases like PostgreSQL and Oracle will have problems with this.";
 317                          $indexFieldText = '';
 318                          $i = 0;
 319                          foreach ( $indexDef['fields'] as $fieldDef )
 320                          {
 321                              if ( $i > 0 )
 322                                  $indexFieldText .= '_';
 323                              if ( is_array( $fieldDef ) )
 324                              {
 325                                  $indexFieldText .= $fieldDef['name'];
 326                              }
 327                              else
 328                              {
 329                                  $indexFieldText .= $fieldDef;
 330                              }
 331                          }
 332                          $indexName = $indexName . '_' . $indexFieldText . '_i';
 333                          $comment .= "\nNew name is '$indexName'";
 334                          $comments[] = $comment;
 335                          $status = false;
 336                      }
 337  
 338                      // Primary indexes must be named PRIMARY
 339                      if ( $indexDef['type'] == 'primary' and
 340                           $indexName != 'PRIMARY' )
 341                      {
 342                          $comment = "Index named '$indexName' which is a primary key must be named PRIMARY.";
 343                          $indexName = "PRIMARY";
 344                          $comments[] = $comment;
 345                          $status = false;
 346                      }
 347  
 348                      // identifiers must be 30 or less
 349                      if ( strlen( $indexName ) > $indexNameLimit )
 350                      {
 351                          $comment = "Index names must not exceed $indexNameLimit characters,\n'$indexName' is " . strlen( $indexName ) . " characters,\ndatabases like Oracle will have problems with this.";
 352                          $indexName = $this->shortenIdentifier( $indexName, $indexNameLimit, $shortenList );
 353                          $comment .= "\nNew name is '$indexName'";
 354                          $comments[] = $comment;
 355                          $status = false;
 356                      }
 357  
 358                      // Check if there are some database specific entries
 359                      foreach ( $indexDef['fields'] as $fieldDef )
 360                      {
 361                          if ( is_array( $fieldDef ) )
 362                          {
 363                              $fieldName = $fieldDef['name'];
 364                              foreach ( $fieldDef as $fdName => $fdValue )
 365                              {
 366                                  if ( preg_match( "#^([a-z0-9]+):#", $fdName, $matches ) )
 367                                  {
 368                                      $dbName = $matches[1];
 369                                      $comments[] = "Found database specific entry ($dbName) at index $existingIndexName.$fieldName";
 370                                      $status = false;
 371                                  }
 372                              }
 373                          }
 374                      }
 375  
 376                      if ( strcmp( $existingIndexName, $indexName ) != 0 )
 377                      {
 378                          $badIndexes[] = array( 'from' => $existingIndexName,
 379                                                 'to' => $indexName );
 380                      }
 381                      if ( count( $comments ) > 0 )
 382                      {
 383                          $schema[$existingTableName]['indexes'][$existingIndexName]['comments'] = $comments;
 384                          foreach ( $comments as $comment )
 385                          {
 386                              eZDebug::writeWarning( $comment, 'eZLintSchema::indexComment' );
 387                          }
 388                      }
 389                  }
 390  
 391                  foreach ( $badIndexes as $badIndex )
 392                  {
 393                      $schema[$existingTableName]['indexes'][$badIndex['to']] = $schema[$existingTableName]['indexes'][$badIndex['from']];
 394                      $schema[$existingTableName]['indexes'][$badIndex['from']]['removed'] = true;
 395                  }
 396              }
 397  
 398              if ( count( $tableComments ) > 0 )
 399              {
 400                  $schema[$existingTableName]['comments'] = $tableComments;
 401                  foreach ( $tableComments as $comment )
 402                  {
 403                      eZDebug::writeWarning( $comment, 'eZLintSchema::tableComment' );
 404                  }
 405              }
 406          }
 407          foreach ( $badTables as $badTable )
 408          {
 409              $schema[$badTable['to']] = $schema[$badTable['from']];
 410              $schema[$badTable['from']]['removed'] = true;
 411          }
 412          return $status;
 413      }
 414  
 415      /*!
 416       \reimp
 417       Forwards request to data() on the otherSchema() object.
 418      */
 419      function data( $schema = false, $tableNameList = false )
 420      {
 421          return $this->OtherSchema->data( $schema, $tableNameList );
 422      }
 423  
 424      /*!
 425       \reimp
 426       Forwards request to generateSchemaFile() on the otherSchema() object.
 427      */
 428      function generateSchemaFile( $schema, $params )
 429      {
 430          return $this->OtherSchema->generateSchemaFile( $schema, $params );
 431      }
 432  
 433      /*!
 434       \reimp
 435       Forwards request to generateUpgradeFile() on the otherSchema() object.
 436      */
 437      function generateUpgradeFile( $differences, $params )
 438      {
 439          return $this->OtherSchema->generateUpgradeFile( $differences, $params );
 440      }
 441  
 442      /*!
 443       \reimp
 444       Forwards request to generateDataFile() on the otherSchema() object.
 445      */
 446      function generateDataFile( $schema, $data, $params )
 447      {
 448          return $this->OtherSchema->generateDataFile( $schema, $data, $params );
 449      }
 450  
 451      /*!
 452       \reimp
 453       Forwards request to generateTableSchema() on the otherSchema() object.
 454      */
 455      function generateTableSchema( $table, $tableDef, $params )
 456      {
 457          return $this->OtherSchema->generateTableSchema( $table, $tableDef, $params );
 458      }
 459  
 460      /*!
 461       \reimp
 462       Forwards request to generateTableInsert() on the otherSchema() object.
 463      */
 464      function generateTableInsert( $tableName, $tableDef, $dataEntries, $params )
 465      {
 466          return $this->OtherSchema->generateTableInsert( $tableName, $tableDef, $dataEntries, $params );
 467      }
 468  
 469      /*!
 470       \reimp
 471       Forwards request to generateDropTable() on the otherSchema() object.
 472      */
 473      function generateDropTable( $table, $params )
 474      {
 475          return $this->OtherSchema->generateDropTable( $table, $params );
 476      }
 477  
 478      /*!
 479       \reimp
 480       Forwards request to generateAddFieldSql() on the otherSchema() object.
 481      */
 482      function generateAddFieldSql( $table, $field_name, $added_field, $params )
 483      {
 484          return $this->OtherSchema->generateAddFieldSql( $table, $field_name, $added_field, $params );
 485      }
 486  
 487      /*!
 488       \reimp
 489       Forwards request to generateAlterFieldSql() on the otherSchema() object.
 490      */
 491      function generateAlterFieldSql( $table, $field_name, $changed_field, $params )
 492      {
 493          return $this->OtherSchema->generateAlterFieldSql( $table, $field_name, $changed_field, $params );
 494      }
 495  
 496      /*!
 497       \reimp
 498       Forwards request to generateDropFieldSql() on the otherSchema() object.
 499      */
 500      function generateDropFieldSql( $table, $field_name, $params )
 501      {
 502          return $this->OtherSchema->generateDropFieldSql( $table, $field_name, $params );
 503      }
 504  
 505      /*!
 506       \reimp
 507       Forwards request to generateAddIndexSql() on the otherSchema() object.
 508      */
 509      function generateAddIndexSql( $table, $index_name, $added_index, $params )
 510      {
 511          return $this->OtherSchema->generateAddIndexSql( $table, $index_name, $added_index, $params );
 512      }
 513  
 514      /*!
 515       \reimp
 516       Forwards request to generateDropIndexSql() on the otherSchema() object.
 517      */
 518      function generateDropIndexSql( $table, $index_name, $removed_index, $params )
 519      {
 520          return $this->OtherSchema->generateDropIndexSql( $table, $index_name, $removed_index, $params );
 521      }
 522  
 523      /*!
 524       \reimp
 525       Forwards request to isMultiInsertSupported() on the otherSchema() object.
 526      */
 527      function isMultiInsertSupported()
 528      {
 529          return $this->OtherSchema->isMultiInsertSupported();
 530      }
 531  
 532      /*!
 533       \reimp
 534       Forwards request to generateDataValueTextSQL() on the otherSchema() object.
 535      */
 536      function generateDataValueTextSQL( $fieldDef, $value )
 537      {
 538          return $this->OtherSchema->generateDataValueTextSQL( $fieldDef, $value );
 539      }
 540  
 541      /*!
 542       \reimp
 543       Forwards request to schemaType() on the otherSchema() object.
 544      */
 545      function schemaType()
 546      {
 547          return $this->OtherSchema->schemaType();
 548      }
 549  
 550      /*!
 551       \reimp
 552       Forwards request to schemaName() on the otherSchema() object.
 553      */
 554      function schemaName()
 555      {
 556          return $this->OtherSchema->schemaName();
 557      }
 558  
 559      /// \privatesection
 560      /// eZDBSchemaInterface object which should be lint checked
 561      var $OtherSchema;
 562      /// The corrected schema
 563      var $CorrectSchema;
 564      /// Whether the schema has been checked or not
 565      var $IsLintChecked;
 566  }
 567  
 568  ?>


Généré le : Sat Feb 24 10:30:04 2007 par Balluche grâce à PHPXref 0.7