From 334cf7d73bbb74c0eb08ed1d6d1479232ecd90f8 Mon Sep 17 00:00:00 2001 From: Guy Sartorelli Date: Fri, 13 Sep 2024 13:00:44 +1200 Subject: [PATCH] API Deprecate Path class --- src/Control/Director.php | 7 ++-- src/Control/SimpleResourceURLGenerator.php | 21 +++++----- src/Core/Manifest/Module.php | 9 +++-- src/Core/Manifest/ModuleResource.php | 9 +++-- src/Core/Path.php | 20 +++++++++- src/Core/TempFolder.php | 17 ++++---- src/View/Requirements_Backend.php | 3 +- src/View/ThemeResourceLoader.php | 13 +++--- src/i18n/Messages/YamlWriter.php | 7 ++-- src/i18n/TextCollection/i18nTextCollector.php | 40 ++++++++++++++----- tests/php/Core/PathTest.php | 7 ++-- 11 files changed, 102 insertions(+), 51 deletions(-) diff --git a/src/Control/Director.php b/src/Control/Director.php index 119d4e746d0..55375246dab 100644 --- a/src/Control/Director.php +++ b/src/Control/Director.php @@ -12,6 +12,7 @@ use SilverStripe\Core\Injector\Injector; use SilverStripe\Core\Kernel; use SilverStripe\Core\Path; +use SilverStripe\Dev\Deprecation; use SilverStripe\Versioned\Versioned; use SilverStripe\View\Requirements; use SilverStripe\View\Requirements_Backend; @@ -662,7 +663,7 @@ public static function publicFolder() $folder = Director::baseFolder(); $publicDir = Director::publicDir(); if ($publicDir) { - return Path::join($folder, $publicDir); + return Deprecation::withNoReplacement(fn () => Path::join($folder, $publicDir)); } return $folder; @@ -855,14 +856,14 @@ public static function getAbsFile($file) // If path is relative to public folder search there first if (Director::publicDir()) { - $path = Path::join(Director::publicFolder(), $file); + $path = Deprecation::withNoReplacement(fn () => Path::join(Director::publicFolder(), $file)); if (file_exists($path ?? '')) { return $path; } } // Default to base folder - return Path::join(Director::baseFolder(), $file); + return Deprecation::withNoReplacement(fn () => Path::join(Director::baseFolder(), $file)); } /** diff --git a/src/Control/SimpleResourceURLGenerator.php b/src/Control/SimpleResourceURLGenerator.php index c41f68ef36c..18121763cff 100644 --- a/src/Control/SimpleResourceURLGenerator.php +++ b/src/Control/SimpleResourceURLGenerator.php @@ -9,6 +9,7 @@ use SilverStripe\Core\Manifest\ModuleResource; use SilverStripe\Core\Manifest\ResourceURLGenerator; use SilverStripe\Core\Path; +use SilverStripe\Dev\Deprecation; /** * Generate URLs assuming that BASE_PATH is also the webroot @@ -77,7 +78,7 @@ public function urlForResource($relativePath) if (strpos($relativePath ?? '', '?') !== false) { list($relativePath, $query) = explode('?', $relativePath ?? ''); } - + list($exists, $absolutePath, $relativePath) = $this->resolvePublicResource($relativePath); } if (!$exists) { @@ -138,12 +139,14 @@ protected function resolveModuleResource(ModuleResource $resource) // Rewrite to _resources with public directory if (Director::publicDir()) { // All resources mapped directly to _resources/ - $relativePath = Path::join(RESOURCES_DIR, $relativePath); + $relativePath = Deprecation::withNoReplacement(fn () => Path::join(RESOURCES_DIR, $relativePath)); } elseif (stripos($relativePath ?? '', ManifestFileFinder::VENDOR_DIR . DIRECTORY_SEPARATOR) === 0) { // If there is no public folder, map to _resources/ but trim leading vendor/ too (4.0 compat) - $relativePath = Path::join( - RESOURCES_DIR, - substr($relativePath ?? '', strlen(ManifestFileFinder::VENDOR_DIR ?? '')) + $relativePath = Deprecation::withNoReplacement( + fn () => Path::join( + RESOURCES_DIR, + substr($relativePath ?? '', strlen(ManifestFileFinder::VENDOR_DIR ?? '')) + ) ); } return [$exists, $absolutePath, $relativePath]; @@ -160,7 +163,7 @@ protected function resolveModuleResource(ModuleResource $resource) protected function inferPublicResourceRequired(&$relativePath) { // Normalise path - $relativePath = Path::normalise($relativePath, true); + $relativePath = Deprecation::withNoReplacement(fn () => Path::normalise($relativePath, true)); // Detect public-only request $publicOnly = stripos($relativePath ?? '', 'public' . DIRECTORY_SEPARATOR) === 0; @@ -183,17 +186,17 @@ protected function resolvePublicResource($relativePath) $publicOnly = $this->inferPublicResourceRequired($relativePath); // Search public folder first, and unless `public/` is prefixed, also private base path - $publicPath = Path::join(Director::publicFolder(), $relativePath); + $publicPath = Deprecation::withNoReplacement(fn () => Path::join(Director::publicFolder(), $relativePath)); if (file_exists($publicPath ?? '')) { // String is a literal url committed directly to public folder return [true, $publicPath, $relativePath]; } // Fall back to private path (and assume expose will make this available to _resources/) - $privatePath = Path::join(Director::baseFolder(), $relativePath); + $privatePath = Deprecation::withNoReplacement(fn () => Path::join(Director::baseFolder(), $relativePath)); if (!$publicOnly && file_exists($privatePath ?? '')) { // String is private but exposed to _resources/, so rewrite to the symlinked base - $relativePath = Path::join(RESOURCES_DIR, $relativePath); + $relativePath = Deprecation::withNoReplacement(fn () => Path::join(RESOURCES_DIR, $relativePath)); return [true, $privatePath, $relativePath]; } diff --git a/src/Core/Manifest/Module.php b/src/Core/Manifest/Module.php index b335dbc2df5..eca835ed295 100644 --- a/src/Core/Manifest/Module.php +++ b/src/Core/Manifest/Module.php @@ -5,6 +5,7 @@ use Exception; use InvalidArgumentException; use SilverStripe\Core\Path; +use SilverStripe\Dev\Deprecation; /** * Abstraction of a PHP Package. Can be used to retrieve information about Silverstripe CMS modules, and other packages @@ -48,8 +49,8 @@ class Module */ public function __construct($path, $basePath) { - $this->path = Path::normalise($path); - $this->basePath = Path::normalise($basePath); + $this->path = Deprecation::withNoReplacement(fn () => Path::normalise($path)); + $this->basePath = Deprecation::withNoReplacement(fn () => Path::normalise($basePath)); $this->loadComposer(); } @@ -175,7 +176,7 @@ public function __unserialize(array $data): void $this->composerData = $data['composerData']; $this->resources = []; } - + /** * Activate _config.php for this module, if one exists */ @@ -213,7 +214,7 @@ protected function loadComposer() */ public function getResource($path) { - $path = Path::normalise($path, true); + $path = Deprecation::withNoReplacement(fn () => Path::normalise($path, true)); if (empty($path)) { throw new InvalidArgumentException('$path is required'); } diff --git a/src/Core/Manifest/ModuleResource.php b/src/Core/Manifest/ModuleResource.php index e89b90ac547..3dce91eafcd 100644 --- a/src/Core/Manifest/ModuleResource.php +++ b/src/Core/Manifest/ModuleResource.php @@ -5,6 +5,7 @@ use InvalidArgumentException; use SilverStripe\Core\Injector\Injector; use SilverStripe\Core\Path; +use SilverStripe\Dev\Deprecation; /** * This object represents a single resource file attached to a module, and can be used @@ -40,7 +41,7 @@ class ModuleResource public function __construct(Module $module, $relativePath) { $this->module = $module; - $this->relativePath = Path::normalise($relativePath, true); + $this->relativePath = Deprecation::withNoReplacement(fn () => Path::normalise($relativePath, true)); if (empty($this->relativePath)) { throw new InvalidArgumentException("Resource cannot have empty path"); } @@ -56,7 +57,7 @@ public function __construct(Module $module, $relativePath) */ public function getPath() { - return Path::join($this->module->getPath(), $this->relativePath); + return Deprecation::withNoReplacement(fn () => Path::join($this->module->getPath(), $this->relativePath)); } /** @@ -74,7 +75,7 @@ public function getRelativePath() if (!$parent) { return $this->relativePath; } - return Path::join($parent, $this->relativePath); + return Deprecation::withNoReplacement(fn () => Path::join($parent, $this->relativePath)); } /** @@ -140,7 +141,7 @@ public function getModule() public function getRelativeResource($path) { // Defer to parent module - $relativeToModule = Path::join($this->relativePath, $path); + $relativeToModule = Deprecation::withNoReplacement(fn () => Path::join($this->relativePath, $path)); return $this->getModule()->getResource($relativeToModule); } } diff --git a/src/Core/Path.php b/src/Core/Path.php index 7d06a1e8516..37924715bcc 100644 --- a/src/Core/Path.php +++ b/src/Core/Path.php @@ -3,14 +3,25 @@ namespace SilverStripe\Core; use InvalidArgumentException; +use SilverStripe\Dev\Deprecation; /** * Path manipulation helpers + * @deprecated 5.4.0 Use Symfony\Component\Filesystem\Path from symfony/filesystem instead */ class Path { const TRIM_CHARS = ' /\\'; + public function __construct() + { + Deprecation::notice( + '5.4.0', + 'Use Symfony\Component\Filesystem\Path from symfony/filesystem instead', + Deprecation::SCOPE_CLASS + ); + } + /** * Joins one or more paths, normalising all separators to DIRECTORY_SEPARATOR * @@ -20,10 +31,12 @@ class Path * @see File::join_paths() for joining file identifiers * * @param array $parts - * @return string Combined path, not including trailing slash (unless it's a single slash) + * @return string Combined path, not including trailing slash (unless it's a single slash)' + * @deprecated 5.4.0 Use Symfony\Component\Filesystem\Path::join() from symfony/filesystem instead */ public static function join(...$parts) { + Deprecation::notice('5.4.0', 'Use Symfony\Component\Filesystem\Path::join() from symfony/filesystem instead'); // In case $parts passed as an array in first parameter if (count($parts ?? []) === 1 && is_array($parts[0])) { $parts = $parts[0]; @@ -48,9 +61,14 @@ public static function join(...$parts) * @param string $path Input path * @param bool $relative * @return string Path with no trailing slash. If $relative is true, also trim leading slashes + * @deprecated 5.4.0 Use Symfony\Component\Filesystem\Path::normalize() from symfony/filesystem instead */ public static function normalise($path, $relative = false) { + Deprecation::notice( + '5.4.0', + 'Use Symfony\Component\Filesystem\Path::normalize() from symfony/filesystem instead' + ); $path = trim(Convert::slashes($path) ?? ''); if ($relative) { return trim($path ?? '', Path::TRIM_CHARS ?? ''); diff --git a/src/Core/TempFolder.php b/src/Core/TempFolder.php index 11a41c66031..641bd2d0979 100644 --- a/src/Core/TempFolder.php +++ b/src/Core/TempFolder.php @@ -3,6 +3,7 @@ namespace SilverStripe\Core; use Exception; +use SilverStripe\Dev\Deprecation; /** * Guesses location for temp folder @@ -20,7 +21,7 @@ public static function getTempFolder($base) $parent = static::getTempParentFolder($base); // The actual temp folder is a subfolder of getTempParentFolder(), named by username - $subfolder = Path::join($parent, static::getTempFolderUsername()); + $subfolder = Deprecation::withNoReplacement(fn () => Path::join($parent, static::getTempFolderUsername())); if (!@file_exists($subfolder ?? '')) { mkdir($subfolder ?? ''); @@ -69,7 +70,7 @@ public static function getTempFolderUsername() protected static function getTempParentFolder($base) { // first, try finding a silverstripe-cache dir built off the base path - $localPath = Path::join($base, 'silverstripe-cache'); + $localPath = Deprecation::withNoReplacement(fn () => Path::join($base, 'silverstripe-cache')); if (@file_exists($localPath ?? '')) { if ((fileperms($localPath ?? '') & 0777) != 0777) { @chmod($localPath ?? '', 0777); @@ -78,11 +79,13 @@ protected static function getTempParentFolder($base) } // failing the above, try finding a namespaced silverstripe-cache dir in the system temp - $tempPath = Path::join( - sys_get_temp_dir(), - 'silverstripe-cache-php' . preg_replace('/[^\w\-\.+]+/', '-', PHP_VERSION) . - str_replace([' ', '/', ':', '\\'], '-', $base ?? '') - ); + $tempPath = Deprecation::withNoReplacement(function () use ($base) { + return Path::join( + sys_get_temp_dir(), + 'silverstripe-cache-php' . preg_replace('/[^\w\-\.+]+/', '-', PHP_VERSION) . + str_replace([' ', '/', ':', '\\'], '-', $base ?? '') + ); + }); if (!@file_exists($tempPath ?? '')) { $oldUMask = umask(0); @mkdir($tempPath ?? '', 0777); diff --git a/src/View/Requirements_Backend.php b/src/View/Requirements_Backend.php index 869f636a289..38d2242c575 100644 --- a/src/View/Requirements_Backend.php +++ b/src/View/Requirements_Backend.php @@ -16,6 +16,7 @@ use SilverStripe\Core\Manifest\ResourceURLGenerator; use SilverStripe\Core\Path; use SilverStripe\Dev\Debug; +use SilverStripe\Dev\Deprecation; use SilverStripe\i18n\i18n; use SilverStripe\ORM\FieldType\DBField; use Symfony\Component\Filesystem\Path as FilesystemPath; @@ -1047,7 +1048,7 @@ function ($candidate) { ); foreach ($candidates as $candidate) { - $relativePath = Path::join($langDir, $candidate); + $relativePath = Deprecation::withNoReplacement(fn () => Path::join($langDir, $candidate)); $absolutePath = Director::getAbsFile($relativePath); if (file_exists($absolutePath ?? '')) { $files[] = $relativePath; diff --git a/src/View/ThemeResourceLoader.php b/src/View/ThemeResourceLoader.php index 8799fe13a66..cce52377c3d 100644 --- a/src/View/ThemeResourceLoader.php +++ b/src/View/ThemeResourceLoader.php @@ -9,6 +9,7 @@ use SilverStripe\Core\Manifest\ModuleLoader; use SilverStripe\Core\Manifest\ModuleResourceLoader; use SilverStripe\Core\Path; +use SilverStripe\Dev\Deprecation; /** * Handles finding templates from a stack of template manifest objects. @@ -113,12 +114,12 @@ public function getPath($identifier) if (count($parts ?? []) > 1) { throw new InvalidArgumentException("Invalid theme identifier {$identifier}"); } - return Path::normalise($identifier, true); + return Deprecation::withNoReplacement(fn () => Path::normalise($identifier, true)); } // If there is no slash / colon it's a legacy theme if ($slashPos === false && count($parts ?? []) === 1) { - return Path::join(THEMES_DIR, $identifier); + return Deprecation::withNoReplacement(fn () => Path::join(THEMES_DIR, $identifier)); } // Extract from /: format. @@ -158,7 +159,7 @@ public function getPath($identifier) } // Join module with subpath - return Path::normalise($modulePath . $subpath, true); + return Deprecation::withNoReplacement(fn () => Path::normalise($modulePath . $subpath, true)); } /** @@ -238,7 +239,7 @@ public function findTemplate($template, $themes = null) // Join path $pathParts = [ $this->base, $themePath, 'templates', $head, $type, $tail ]; try { - $path = Path::join($pathParts) . '.ss'; + $path = Deprecation::withNoReplacement(fn () => Path::join($pathParts)) . '.ss'; if (file_exists($path ?? '')) { $this->getCache()->set($cacheKey, $path); return $path; @@ -326,8 +327,8 @@ public function findThemedResource($resource, $themes = null) $paths = $this->getThemePaths($themes); foreach ($paths as $themePath) { - $relativePath = Path::join($themePath, $resource); - $absolutePath = Path::join($this->base, $relativePath); + $relativePath = Deprecation::withNoReplacement(fn () => Path::join($themePath, $resource)); + $absolutePath = Deprecation::withNoReplacement(fn () => Path::join($this->base, $relativePath)); if (file_exists($absolutePath ?? '')) { return $relativePath; } diff --git a/src/i18n/Messages/YamlWriter.php b/src/i18n/Messages/YamlWriter.php index dbaf2d07c7a..9180c771b8b 100644 --- a/src/i18n/Messages/YamlWriter.php +++ b/src/i18n/Messages/YamlWriter.php @@ -8,6 +8,7 @@ use Symfony\Component\Yaml\Dumper; use SilverStripe\i18n\Messages\Symfony\ModuleYamlLoader; use LogicException; +use SilverStripe\Dev\Deprecation; /** * Write yml files compatible with ModuleYamlLoader @@ -44,17 +45,17 @@ public function write($messages, $locale, $path) } // Create folder for lang files - $langFolder = Path::join($path, 'lang'); + $langFolder = Deprecation::withNoReplacement(fn () => Path::join($path, 'lang')); if (!file_exists($langFolder ?? '')) { Filesystem::makeFolder($langFolder); - touch(Path::join($langFolder, '_manifest_exclude')); + touch(Deprecation::withNoReplacement(fn () => Path::join($langFolder, '_manifest_exclude'))); } // De-normalise messages and convert to yml $content = $this->getYaml($messages, $locale); // Open the English file and write the Master String Table - $langFile = Path::join($langFolder, $locale . '.yml'); + $langFile = Deprecation::withNoReplacement(fn () => Path::join($langFolder, $locale . '.yml')); if ($fh = fopen($langFile ?? '', "w")) { fwrite($fh, $content ?? ''); fclose($fh); diff --git a/src/i18n/TextCollection/i18nTextCollector.php b/src/i18n/TextCollection/i18nTextCollector.php index 3d3d6fa6f8f..e81b63489e9 100644 --- a/src/i18n/TextCollection/i18nTextCollector.php +++ b/src/i18n/TextCollection/i18nTextCollector.php @@ -16,6 +16,7 @@ use SilverStripe\Dev\Debug; use SilverStripe\Control\Director; use ReflectionClass; +use SilverStripe\Dev\Deprecation; use SilverStripe\Forms\FormField; use SilverStripe\i18n\i18n; use SilverStripe\i18n\i18nEntityProvider; @@ -385,7 +386,9 @@ protected function mergeWithExisting($entitiesByModule) $modules = $this->getModulesAndThemes(); foreach ($entitiesByModule as $module => $messages) { // Load existing localisations - $masterFile = Path::join($modules[$module]->getPath(), 'lang', $this->defaultLocale . '.yml'); + $masterFile = Deprecation::withNoReplacement( + fn () => Path::join($modules[$module]->getPath(), 'lang', $this->defaultLocale . '.yml') + ); $existingMessages = $this->getReader()->read($this->defaultLocale, $masterFile); // Merge @@ -473,8 +476,9 @@ private function getModulesAndThemes(): array } if (!empty($themes)) { foreach ($themes as $theme) { - if (is_dir(Path::join(THEMES_PATH, $theme))) { - $modules[i18nTextCollector::THEME_PREFIX . $theme] = new Module(Path::join(THEMES_PATH, $theme), BASE_PATH); + if (is_dir(Deprecation::withNoReplacement(fn () => Path::join(THEMES_PATH, $theme)))) { + $themePath = Deprecation::withNoReplacement(fn () => Path::join(THEMES_PATH, $theme)); + $modules[i18nTextCollector::THEME_PREFIX . $theme] = new Module($themePath, BASE_PATH); } } } @@ -503,7 +507,7 @@ public function write(Module $module, $entities) $this->getWriter()->write( $entities, $this->defaultLocale, - Path::join($this->baseSavePath, $module->getRelativePath()) + Deprecation::withNoReplacement(fn () => Path::join($this->baseSavePath, $module->getRelativePath())) ); return $this; } @@ -563,20 +567,34 @@ protected function getFileListForModule(Module $module) } // If non-standard module structure, search all root files - if (!is_dir(Path::join($modulePath, 'code')) && !is_dir(Path::join($modulePath, 'src'))) { + if (!is_dir(Deprecation::withNoReplacement(fn () => Path::join($modulePath, 'code'))) + && !is_dir(Deprecation::withNoReplacement(fn () => Path::join($modulePath, 'src'))) + ) { return $this->getFilesRecursive($modulePath); } // Get code files if (is_dir(Path::join($modulePath, 'src'))) { - $files = $this->getFilesRecursive(Path::join($modulePath, 'src'), null, 'php'); + $files = $this->getFilesRecursive( + Deprecation::withNoReplacement(fn () => Path::join($modulePath, 'src')), + null, + 'php' + ); } else { - $files = $this->getFilesRecursive(Path::join($modulePath, 'code'), null, 'php'); + $files = $this->getFilesRecursive( + Deprecation::withNoReplacement(fn () => Path::join($modulePath, 'code')), + null, + 'php' + ); } // Search for templates in this module - if (is_dir(Path::join($modulePath, 'templates'))) { - $templateFiles = $this->getFilesRecursive(Path::join($modulePath, 'templates'), null, 'ss'); + if (is_dir(Deprecation::withNoReplacement(fn () => Path::join($modulePath, 'templates')))) { + $templateFiles = $this->getFilesRecursive( + Deprecation::withNoReplacement(fn () => Path::join($modulePath, 'templates')), + null, + 'ss' + ); } else { $templateFiles = $this->getFilesRecursive($modulePath, null, 'ss'); } @@ -1043,7 +1061,9 @@ protected function getFilesRecursive($folder, $fileList = [], $type = null, $fol $fileList = []; } // Skip ignored folders - if (is_file(Path::join($folder, '_manifest_exclude')) || preg_match($folderExclude ?? '', $folder ?? '')) { + if (is_file(Deprecation::withNoReplacement(fn () => Path::join($folder, '_manifest_exclude'))) + || preg_match($folderExclude ?? '', $folder ?? '') + ) { return $fileList; } diff --git a/tests/php/Core/PathTest.php b/tests/php/Core/PathTest.php index 10dff0e8d98..d8079a15b03 100644 --- a/tests/php/Core/PathTest.php +++ b/tests/php/Core/PathTest.php @@ -4,6 +4,7 @@ use InvalidArgumentException; use SilverStripe\Core\Path; +use SilverStripe\Dev\Deprecation; use SilverStripe\Dev\SapphireTest; class PathTest extends SapphireTest @@ -17,7 +18,7 @@ class PathTest extends SapphireTest */ public function testJoinPaths($args, $expected) { - $joined = Path::join($args); + $joined = Deprecation::withNoReplacement(fn () => Path::join($args)); $this->assertEquals($expected, $joined); } @@ -72,7 +73,7 @@ public function testJoinPathsErrors($args, $error) { $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage($error); - Path::join($args); + Deprecation::withNoReplacement(fn () => Path::join($args)); } public function providerTestJoinPathsErrors() @@ -93,7 +94,7 @@ public function providerTestJoinPathsErrors() */ public function testNormalise($input, $expected) { - $output = Path::normalise($input); + $output = Deprecation::withNoReplacement(fn () => Path::normalise($input)); $this->assertEquals($expected, $output, "Expected $input to be normalised to $expected"); }