MediaWiki master
SchemaGenerator.php
Go to the documentation of this file.
1<?php
2
4
5use Doctrine\SqlFormatter\NullHighlighter;
6use Doctrine\SqlFormatter\SqlFormatter;
7use JsonException;
11
23 public function validateAndGetSchema( string $jsonPath ): array {
24 $json = file_get_contents( $jsonPath );
25
26 if ( !$json ) {
27 throw new AbstractSchemaValidationError( "'$jsonPath' does not exist!" );
28 }
29
30 try {
31 $abstractSchema = json_decode( $json, true, 512, JSON_THROW_ON_ERROR );
32 } catch ( JsonException $e ) {
33 throw new AbstractSchemaValidationError( "Invalid JSON schema: " . $e->getMessage(), 0, $e );
34 }
35
36 $validator = new AbstractSchemaValidator();
37 $validator->validate( $jsonPath );
38
39 return $abstractSchema;
40 }
41
45 public function generateSchema( string $platform, string $jsonPath ): string {
46 $abstractSchemaChange = $this->validateAndGetSchema( $jsonPath );
47
48 $sql = $this->makeSQLComment( 'generateSchemaSql.php', $jsonPath );
49
50 $schemaBuilder = ( new DoctrineSchemaBuilderFactory() )->getSchemaBuilder( $platform );
51
52 foreach ( $abstractSchemaChange as $table ) {
53 $schemaBuilder->addTable( $table );
54 }
55 $tableSqls = $schemaBuilder->getSql();
56
57 $sql .= $this->cleanupSqlArray( $platform, $tableSqls );
58
59 return $sql;
60 }
61
65 public function generateSchemaChange( string $platform, string $jsonPath ): string {
66 $abstractSchemaChange = $this->validateAndGetSchema( $jsonPath );
67
68 $sql = $this->makeSQLComment( 'generateSchemaChangeSql.php', $jsonPath );
69
70 $schemaChangeBuilder = ( new DoctrineSchemaBuilderFactory() )->getSchemaChangeBuilder( $platform );
71
72 $schemaChangeSqls = $schemaChangeBuilder->getSchemaChangeSql( $abstractSchemaChange );
73 if ( !$schemaChangeSqls ) {
74 throw new AbstractSchemaValidationError( "No schema changes detected!" );
75 }
76
77 $sql .= $this->cleanupSqlArray( $platform, $schemaChangeSqls );
78
79 return $sql;
80 }
81
82 private function makeSQLComment( string $scriptName, string $jsonPath ): string {
84
85 $canonicalJsonPath = $this->normalizePath( $jsonPath, MW_INSTALL_PATH, $wgExtensionDirectory );
86
87 return "-- This file is automatically generated using maintenance/$scriptName.\n" .
88 "-- Source: $canonicalJsonPath\n" .
89 "-- Do not modify this file directly.\n" .
90 "-- See https://www.mediawiki.org/wiki/Manual:Schema_changes\n";
91 }
92
101 public static function normalizePath(
102 string $jsonPath,
103 string $installPath,
104 string $extensionDirectory
105 ): string {
106 $installPath = realpath( $installPath );
107 $jsonPath = realpath( $jsonPath );
108 $extensionDirectory = realpath( $extensionDirectory );
109
110 // For windows
111 if ( DIRECTORY_SEPARATOR === '\\' ) {
112 $installPath = strtr( $installPath, '\\', '/' );
113 $jsonPath = strtr( $jsonPath, '\\', '/' );
114 $extensionDirectory = strtr( $extensionDirectory, '\\', '/' );
115 }
116
117 if ( str_starts_with( $jsonPath, $extensionDirectory ) ) {
118 // Strip $wgExtensionDirectory which might be configured outside of MW_INSTALL_PATH
119 // /path/to/mw-ext-all/Example/bar -> Example/bar
120 $canonicalJsonPath = str_replace( "$extensionDirectory/", '', $jsonPath );
121
122 // Strip the extension name
123 // Example/bar -> bar
124 $canonicalJsonPath = explode( '/', $canonicalJsonPath, 2 )[1];
125 } else {
126 // For mediawiki/core we strip MW_INSTALL_PATH
127 // /path/to/mediawiki/foo/bar -> foo/bar
128 $canonicalJsonPath = str_replace( "$installPath/", '', $jsonPath );
129 }
130
131 return $canonicalJsonPath;
132 }
133
142 private function cleanupSqlArray( string $platform, array $sqlArray ): string {
143 if ( !$sqlArray ) {
144 return '';
145 }
146
147 // Temporary
148 $sql = implode( ";\n\n", $sqlArray ) . ';';
149 $sql = ( new SqlFormatter( new NullHighlighter() ) )->format( $sql );
150
151 // Postgres hacks
152 if ( $platform === 'postgres' ) {
153 // FIXME: Fix a lot of weird formatting issues caused by
154 // presence of partial index's WHERE clause, this should probably
155 // be done in some better way, but for now this can work temporarily
156 $sql = str_replace(
157 [ "WHERE\n ", "\n /*_*/\n ", " ", " );", "KEY(\n " ],
158 [ "WHERE", ' ', " ", ');', "KEY(\n " ],
159 $sql
160 );
161 }
162
163 // Temporary fixes until the linting issues are resolved upstream.
164 // https://github.com/doctrine/sql-formatter/issues/53
165
166 $sql = preg_replace( "!\s+/\*_\*/\s+!", " /*_*/", $sql );
167 $sql = preg_replace(
168 '!\s+/\*\$wgDBTableOptions\*/\s+;!',
169 ' /*$wgDBTableOptions*/;',
170 $sql
171 );
172
173 $sql = str_replace( ";\n\nCREATE TABLE ", ";\n\n\nCREATE TABLE ", $sql );
174 $sql = preg_replace( '/^(CREATE|DROP|ALTER)\s+(TABLE|VIEW|INDEX)\s+/m', '$1 $2 ', $sql );
175 $sql = preg_replace( '/(?<!\s|;)\s+(ADD|DROP|ALTER|MODIFY|CHANGE|RENAME)\s+/', "\n \$1 ", $sql );
176
177 if ( !str_ends_with( $sql, "\n" ) ) {
178 $sql .= "\n";
179 }
180
181 // Sqlite hacks
182 if ( $platform === 'sqlite' ) {
183 // Doctrine prepends __temp__ to the table name and we set the table with the schema prefix causing invalid
184 // sqlite.
185 $sql = preg_replace( '/__temp__\s*\/\*_\*\//', '/*_*/__temp__', $sql );
186 $sql = str_replace( "\n /*_*/", ' /*_*/', $sql );
187 }
188
189 return $sql;
190 }
191}
if(!defined('MW_SETUP_CALLBACK'))
Definition WebStart.php:71
Validate abstract schema json files against their JSON schema.
Helper to generate abstract schema and schema changes in maintenance scripts.
generateSchemaChange(string $platform, string $jsonPath)
static normalizePath(string $jsonPath, string $installPath, string $extensionDirectory)
Make absolute path relative to the MediaWiki core root or extensions directory.
generateSchema(string $platform, string $jsonPath)
validateAndGetSchema(string $jsonPath)
Fetches the abstract schema.
$wgExtensionDirectory
Config variable stub for the ExtensionDirectory setting, for use by phpdoc and IDEs.
Update the CREDITS list by merging in the list of git commit authors.