cakephp /
phinx
| 1 | <?php |
||||||
| 2 | |||||||
| 3 | /** |
||||||
| 4 | * MIT License |
||||||
| 5 | * For full license information, please view the LICENSE file that was distributed with this source code. |
||||||
| 6 | */ |
||||||
| 7 | |||||||
| 8 | namespace Phinx\Util; |
||||||
| 9 | |||||||
| 10 | use DateTime; |
||||||
| 11 | use DateTimeZone; |
||||||
| 12 | use Exception; |
||||||
| 13 | use Symfony\Component\Console\Input\InputInterface; |
||||||
| 14 | use Symfony\Component\Console\Output\OutputInterface; |
||||||
| 15 | |||||||
| 16 | class Util |
||||||
| 17 | { |
||||||
| 18 | /** |
||||||
| 19 | * @var string |
||||||
| 20 | */ |
||||||
| 21 | public const DATE_FORMAT = 'YmdHis'; |
||||||
| 22 | |||||||
| 23 | /** |
||||||
| 24 | * @var string |
||||||
| 25 | */ |
||||||
| 26 | protected const MIGRATION_FILE_NAME_PATTERN = '/^\d+_([a-z][a-z\d]*(?:_[a-z\d]+)*)\.php$/i'; |
||||||
| 27 | |||||||
| 28 | /** |
||||||
| 29 | * @var string |
||||||
| 30 | */ |
||||||
| 31 | protected const MIGRATION_FILE_NAME_NO_NAME_PATTERN = '/^[0-9]{14}\.php$/'; |
||||||
| 32 | |||||||
| 33 | /** |
||||||
| 34 | * @var string |
||||||
| 35 | */ |
||||||
| 36 | protected const SEED_FILE_NAME_PATTERN = '/^([a-z][a-z\d]*)\.php$/i'; |
||||||
| 37 | |||||||
| 38 | /** |
||||||
| 39 | * @var string |
||||||
| 40 | */ |
||||||
| 41 | protected const CLASS_NAME_PATTERN = '/^(?:[A-Z][a-z\d]*)+$/'; |
||||||
| 42 | |||||||
| 43 | /** |
||||||
| 44 | * Gets the current timestamp string, in UTC. |
||||||
| 45 | * |
||||||
| 46 | * @return string |
||||||
| 47 | */ |
||||||
| 48 | public static function getCurrentTimestamp() |
||||||
| 49 | { |
||||||
| 50 | $dt = new DateTime('now', new DateTimeZone('UTC')); |
||||||
| 51 | |||||||
| 52 | return $dt->format(static::DATE_FORMAT); |
||||||
| 53 | 15 | } |
|||||
| 54 | |||||||
| 55 | 15 | /** |
|||||
| 56 | 15 | * Gets an array of all the existing migration class names. |
|||||
| 57 | * |
||||||
| 58 | * @param string $path Path |
||||||
| 59 | * @return string[] |
||||||
| 60 | */ |
||||||
| 61 | public static function getExistingMigrationClassNames($path) |
||||||
| 62 | { |
||||||
| 63 | $classNames = []; |
||||||
| 64 | 15 | ||||||
| 65 | if (!is_dir($path)) { |
||||||
| 66 | 15 | return $classNames; |
|||||
| 67 | } |
||||||
| 68 | 15 | ||||||
| 69 | 1 | // filter the files to only get the ones that match our naming scheme |
|||||
| 70 | $phpFiles = static::getFiles($path); |
||||||
| 71 | |||||||
| 72 | foreach ($phpFiles as $filePath) { |
||||||
| 73 | 14 | $fileName = basename($filePath); |
|||||
| 74 | if (preg_match(static::MIGRATION_FILE_NAME_PATTERN, $fileName)) { |
||||||
| 75 | 14 | $classNames[] = static::mapFileNameToClassName($fileName); |
|||||
| 76 | 3 | } |
|||||
| 77 | 3 | } |
|||||
| 78 | 3 | ||||||
| 79 | 14 | return $classNames; |
|||||
| 80 | } |
||||||
| 81 | 14 | ||||||
| 82 | /** |
||||||
| 83 | * Get the version from the beginning of a file name. |
||||||
| 84 | * |
||||||
| 85 | * @param string $fileName File Name |
||||||
| 86 | * @return string |
||||||
| 87 | */ |
||||||
| 88 | public static function getVersionFromFileName($fileName) |
||||||
| 89 | { |
||||||
| 90 | 395 | $matches = []; |
|||||
| 91 | preg_match('/^[0-9]+/', basename($fileName), $matches); |
||||||
| 92 | 395 | ||||||
| 93 | 395 | return $matches[0]; |
|||||
| 94 | 395 | } |
|||||
| 95 | |||||||
| 96 | /** |
||||||
| 97 | * Turn migration names like 'CreateUserTable' into file names like |
||||||
| 98 | * '12345678901234_create_user_table.php' or 'LimitResourceNamesTo30Chars' into |
||||||
| 99 | * '12345678901234_limit_resource_names_to_30_chars.php'. |
||||||
| 100 | * |
||||||
| 101 | * @param string $className Class Name |
||||||
| 102 | * @return string |
||||||
| 103 | */ |
||||||
| 104 | public static function mapClassNameToFileName($className) |
||||||
| 105 | 14 | { |
|||||
| 106 | $snake = function ($matches) { |
||||||
| 107 | 14 | return '_' . strtolower($matches[0]); |
|||||
| 108 | 14 | }; |
|||||
| 109 | 14 | $fileName = preg_replace_callback('/\d+|[A-Z]/', $snake, $className); |
|||||
| 110 | 14 | $fileName = static::getCurrentTimestamp() . "$fileName.php"; |
|||||
| 111 | |||||||
| 112 | return $fileName; |
||||||
| 113 | } |
||||||
| 114 | |||||||
| 115 | /** |
||||||
| 116 | * Turn file names like '12345678901234_create_user_table.php' into class |
||||||
| 117 | * names like 'CreateUserTable'. |
||||||
| 118 | * |
||||||
| 119 | * @param string $fileName File Name |
||||||
| 120 | 391 | * @return string |
|||||
| 121 | */ |
||||||
| 122 | 391 | public static function mapFileNameToClassName(string $fileName): string |
|||||
| 123 | 391 | { |
|||||
| 124 | 391 | $matches = []; |
|||||
| 125 | 391 | if (preg_match(static::MIGRATION_FILE_NAME_PATTERN, $fileName, $matches)) { |
|||||
| 126 | $fileName = $matches[1]; |
||||||
| 127 | 391 | } elseif (preg_match(static::MIGRATION_FILE_NAME_NO_NAME_PATTERN, $fileName)) { |
|||||
| 128 | return 'V' . substr($fileName, 0, strlen($fileName) - 4); |
||||||
| 129 | } |
||||||
| 130 | |||||||
| 131 | $className = str_replace('_', '', ucwords($fileName, '_')); |
||||||
| 132 | |||||||
| 133 | return $className; |
||||||
| 134 | } |
||||||
| 135 | |||||||
| 136 | /** |
||||||
| 137 | * Check if a migration class name is unique regardless of the |
||||||
| 138 | * timestamp. |
||||||
| 139 | * |
||||||
| 140 | * This method takes a class name and a path to a migrations directory. |
||||||
| 141 | * |
||||||
| 142 | * Migration class names must be in PascalCase format but consecutive |
||||||
| 143 | * capitals are allowed. |
||||||
| 144 | * e.g: AddIndexToPostsTable or CustomHTMLTitle. |
||||||
| 145 | 13 | * |
|||||
| 146 | * @param string $className Class Name |
||||||
| 147 | 13 | * @param string $path Path |
|||||
| 148 | 13 | * @return bool |
|||||
| 149 | */ |
||||||
| 150 | public static function isUniqueMigrationClassName($className, $path) |
||||||
| 151 | { |
||||||
| 152 | $existingClassNames = static::getExistingMigrationClassNames($path); |
||||||
| 153 | |||||||
| 154 | return !in_array($className, $existingClassNames, true); |
||||||
| 155 | } |
||||||
| 156 | |||||||
| 157 | /** |
||||||
| 158 | * Check if a migration/seed class name is valid. |
||||||
| 159 | * |
||||||
| 160 | * Migration & Seed class names must be in CamelCase format. |
||||||
| 161 | * e.g: CreateUserTable, AddIndexToPostsTable or UserSeeder. |
||||||
| 162 | 16 | * |
|||||
| 163 | * Single words are not allowed on their own. |
||||||
| 164 | 16 | * |
|||||
| 165 | * @param string $className Class Name |
||||||
| 166 | * @return bool |
||||||
| 167 | */ |
||||||
| 168 | public static function isValidPhinxClassName($className) |
||||||
| 169 | { |
||||||
| 170 | return (bool)preg_match(static::CLASS_NAME_PATTERN, $className); |
||||||
| 171 | } |
||||||
| 172 | |||||||
| 173 | 387 | /** |
|||||
| 174 | * Check if a migration file name is valid. |
||||||
| 175 | 387 | * |
|||||
| 176 | 387 | * @param string $fileName File Name |
|||||
| 177 | * @return bool |
||||||
| 178 | */ |
||||||
| 179 | public static function isValidMigrationFileName(string $fileName): bool |
||||||
| 180 | { |
||||||
| 181 | return (bool)preg_match(static::MIGRATION_FILE_NAME_PATTERN, $fileName) |
||||||
| 182 | || (bool)preg_match(static::MIGRATION_FILE_NAME_NO_NAME_PATTERN, $fileName); |
||||||
| 183 | } |
||||||
| 184 | |||||||
| 185 | 11 | /** |
|||||
| 186 | * Check if a seed file name is valid. |
||||||
| 187 | 11 | * |
|||||
| 188 | 11 | * @param string $fileName File Name |
|||||
| 189 | * @return bool |
||||||
| 190 | */ |
||||||
| 191 | public static function isValidSeedFileName($fileName) |
||||||
| 192 | { |
||||||
| 193 | return (bool)preg_match(static::SEED_FILE_NAME_PATTERN, $fileName); |
||||||
| 194 | } |
||||||
| 195 | |||||||
| 196 | /** |
||||||
| 197 | 33 | * Expands a set of paths with curly braces (if supported by the OS). |
|||||
| 198 | * |
||||||
| 199 | 33 | * @param string[] $paths Paths |
|||||
| 200 | * @return string[] |
||||||
| 201 | 33 | */ |
|||||
| 202 | 33 | public static function globAll(array $paths) |
|||||
| 203 | 33 | { |
|||||
| 204 | $result = []; |
||||||
| 205 | 33 | ||||||
| 206 | foreach ($paths as $path) { |
||||||
| 207 | $result = array_merge($result, static::glob($path)); |
||||||
| 208 | } |
||||||
| 209 | |||||||
| 210 | return $result; |
||||||
| 211 | } |
||||||
| 212 | |||||||
| 213 | /** |
||||||
| 214 | 431 | * Expands a path with curly braces (if supported by the OS). |
|||||
| 215 | * |
||||||
| 216 | 431 | * @param string $path Path |
|||||
| 217 | * @return string[] |
||||||
| 218 | */ |
||||||
| 219 | public static function glob($path) |
||||||
| 220 | { |
||||||
| 221 | return glob($path, defined('GLOB_BRACE') ? GLOB_BRACE : 0); |
||||||
| 222 | } |
||||||
| 223 | |||||||
| 224 | /** |
||||||
| 225 | * Takes the path to a php file and attempts to include it if readable |
||||||
| 226 | * |
||||||
| 227 | * @param string $filename Filename |
||||||
| 228 | * @param \Symfony\Component\Console\Input\InputInterface|null $input Input |
||||||
| 229 | * @param \Symfony\Component\Console\Output\OutputInterface|null $output Output |
||||||
| 230 | * @param \Phinx\Console\Command\AbstractCommand|mixed|null $context Context |
||||||
| 231 | * @throws \Exception |
||||||
| 232 | * @return string |
||||||
| 233 | */ |
||||||
| 234 | public static function loadPhpFile($filename, ?InputInterface $input = null, ?OutputInterface $output = null, $context = null) |
||||||
|
0 ignored issues
–
show
The parameter
$output is not used and could be removed.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for parameters that have been defined for a function or method, but which are not used in the method body. Loading history...
The parameter
$context is not used and could be removed.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for parameters that have been defined for a function or method, but which are not used in the method body. Loading history...
|
|||||||
| 235 | { |
||||||
| 236 | $filePath = realpath($filename); |
||||||
| 237 | if (!file_exists($filePath)) { |
||||||
| 238 | throw new Exception(sprintf("File does not exist: %s \n", $filename)); |
||||||
| 239 | } |
||||||
| 240 | |||||||
| 241 | /** |
||||||
| 242 | * I lifed this from phpunits FileLoader class |
||||||
| 243 | * |
||||||
| 244 | * @see https://github.com/sebastianbergmann/phpunit/pull/2751 |
||||||
| 245 | */ |
||||||
| 246 | $isReadable = @fopen($filePath, 'r') !== false; |
||||||
| 247 | |||||||
| 248 | if (!$isReadable) { |
||||||
| 249 | throw new Exception(sprintf("Cannot open file %s \n", $filename)); |
||||||
| 250 | } |
||||||
| 251 | |||||||
| 252 | // prevent this to be propagated to the included file |
||||||
| 253 | unset($isReadable); |
||||||
| 254 | |||||||
| 255 | include_once $filePath; |
||||||
| 256 | |||||||
| 257 | return $filePath; |
||||||
| 258 | } |
||||||
| 259 | |||||||
| 260 | /** |
||||||
| 261 | * Given an array of paths, return all unique PHP files that are in them |
||||||
| 262 | * |
||||||
| 263 | * @param string|string[] $paths Path or array of paths to get .php files. |
||||||
| 264 | * @return string[] |
||||||
| 265 | */ |
||||||
| 266 | public static function getFiles($paths) |
||||||
| 267 | { |
||||||
| 268 | $files = static::globAll(array_map(function ($path) { |
||||||
| 269 | return $path . DIRECTORY_SEPARATOR . '*.php'; |
||||||
| 270 | }, (array)$paths)); |
||||||
| 271 | // glob() can return the same file multiple times |
||||||
| 272 | // This will cause the migration to fail with a |
||||||
| 273 | // false assumption of duplicate migrations |
||||||
| 274 | // http://php.net/manual/en/function.glob.php#110340 |
||||||
| 275 | $files = array_unique($files); |
||||||
| 276 | |||||||
| 277 | return $files; |
||||||
| 278 | } |
||||||
| 279 | |||||||
| 280 | /** |
||||||
| 281 | * Attempt to remove the current working directory from a path for output. |
||||||
| 282 | * |
||||||
| 283 | * @param string $path Path to remove cwd prefix from |
||||||
| 284 | * @return string |
||||||
| 285 | */ |
||||||
| 286 | public static function relativePath($path) |
||||||
| 287 | { |
||||||
| 288 | $realpath = realpath($path); |
||||||
| 289 | if ($realpath !== false) { |
||||||
| 290 | $path = $realpath; |
||||||
| 291 | } |
||||||
| 292 | |||||||
| 293 | $cwd = getcwd(); |
||||||
| 294 | if ($cwd !== false) { |
||||||
| 295 | $cwd .= DIRECTORY_SEPARATOR; |
||||||
| 296 | $cwdLen = strlen($cwd); |
||||||
| 297 | |||||||
| 298 | if (substr($path, 0, $cwdLen) === $cwd) { |
||||||
| 299 | $path = substr($path, $cwdLen); |
||||||
| 300 | } |
||||||
| 301 | } |
||||||
| 302 | |||||||
| 303 | return $path; |
||||||
| 304 | } |
||||||
| 305 | |||||||
| 306 | /** |
||||||
| 307 | * Parses DSN string into db config array. |
||||||
| 308 | * |
||||||
| 309 | * @param string $dsn DSN string |
||||||
| 310 | * @return array |
||||||
| 311 | */ |
||||||
| 312 | public static function parseDsn(string $dsn): array |
||||||
| 313 | { |
||||||
| 314 | $pattern = <<<'REGEXP' |
||||||
| 315 | { |
||||||
| 316 | ^ |
||||||
| 317 | (?: |
||||||
| 318 | (?P<adapter>[\w\\\\]+):// |
||||||
| 319 | ) |
||||||
| 320 | (?: |
||||||
| 321 | (?P<user>.*?) |
||||||
| 322 | (?: |
||||||
| 323 | :(?P<pass>.*?) |
||||||
| 324 | )? |
||||||
| 325 | @ |
||||||
| 326 | )? |
||||||
| 327 | (?: |
||||||
| 328 | (?P<host>[^?#/:@]+) |
||||||
| 329 | (?: |
||||||
| 330 | :(?P<port>\d+) |
||||||
| 331 | )? |
||||||
| 332 | )? |
||||||
| 333 | (?: |
||||||
| 334 | /(?P<name>[^?#]*) |
||||||
| 335 | )? |
||||||
| 336 | (?: |
||||||
| 337 | \?(?P<query>[^#]*) |
||||||
| 338 | )? |
||||||
| 339 | $ |
||||||
| 340 | }x |
||||||
| 341 | REGEXP; |
||||||
| 342 | |||||||
| 343 | if (!preg_match($pattern, $dsn, $parsed)) { |
||||||
| 344 | return []; |
||||||
| 345 | } |
||||||
| 346 | |||||||
| 347 | // filter out everything except the matched groups |
||||||
| 348 | $config = array_intersect_key($parsed, array_flip(['adapter', 'user', 'pass', 'host', 'port', 'name'])); |
||||||
| 349 | $config = array_filter($config); |
||||||
| 350 | |||||||
| 351 | parse_str($parsed['query'] ?? '', $query); |
||||||
| 352 | $config = array_merge($query, $config); |
||||||
| 353 | |||||||
| 354 | return $config; |
||||||
| 355 | } |
||||||
| 356 | } |
||||||
| 357 |
This check looks for parameters that have been defined for a function or method, but which are not used in the method body.