From 4acad8b1682f8d1b6ce2fb935a6fdb85ccec7c59 Mon Sep 17 00:00:00 2001 From: Rias Date: Tue, 18 Aug 2026 22:31:28 +0200 Subject: [PATCH 1/6] Move alias support to Yii adapter --- CHANGELOG.md | 1 + composer.json | 1 - composer.lock | 151 +---------- config/general.php | 6 +- packages/craftcms-playwright/php/DbBackup.php | 3 +- .../modules/install/components/SiteFields.vue | 2 +- .../_components/utilities/SystemReport.twig | 21 -- resources/templates/_includes/forms.twig | 16 +- .../_includes/forms/autosuggest.twig | 5 +- .../_includes/forms/editableTable.twig | 1 - .../_layouts/components/system-info.twig | 2 +- scripts/copyicons.php | 32 --- src/Announcement/Announcements.php | 4 +- src/Asset/AssetsHelper.php | 3 +- src/Asset/Elements/Asset.php | 7 +- src/Config/ConfigServiceProvider.php | 5 - src/Config/GeneralConfig.php | 61 +---- src/Cp/FormFields.php | 15 +- src/Cp/Icons.php | 51 +--- src/Cp/SelectOptions.php | 40 +-- src/Database/DatabaseServiceProvider.php | 3 - src/Database/Migrator.php | 7 +- src/Entry/Commands/MergeEntryTypesCommand.php | 4 +- src/Field/Fields.php | 4 +- src/Field/Icon.php | 5 +- src/Filesystem/Filesystems/Local.php | 8 +- .../Assets/TransformController.php | 4 +- src/Http/Controllers/IconController.php | 10 +- src/Http/Controllers/InstallController.php | 11 +- .../Controllers/Settings/SitesController.php | 4 +- src/Http/Middleware/HandleInertiaRequests.php | 3 +- src/Http/Middleware/ShowBrokenImage.php | 7 +- src/License/License.php | 3 +- src/Plugin/Plugins.php | 13 +- src/Providers/AppServiceProvider.php | 35 +-- src/Providers/CraftServiceProvider.php | 1 - src/Providers/IconServiceProvider.php | 75 ------ src/Route/RouteServiceProvider.php | 3 +- src/Support/Composer.php | 36 +-- src/Support/Env.php | 10 +- src/Support/Facades/Path.php | 2 + src/Support/Html.php | 16 +- src/Support/Json.php | 3 - src/Support/Path.php | 44 ++-- src/Support/Url.php | 10 +- .../TranslationServiceProvider.php | 3 +- src/Twig/Extensions/CoreTwigExtension.php | 2 - src/Twig/Extensions/CpExtension.php | 4 +- src/Twig/Extensions/HtmlTwigExtension.php | 3 +- src/Twig/Variables/Cp.php | 6 +- src/Utility/Utilities/ClearCaches.php | 7 +- src/Utility/Utilities/SystemReport.php | 16 -- src/View/TemplateMode.php | 4 +- src/View/ViewServiceProvider.php | 9 +- testbench.yaml | 1 - tests/Feature/Cp/SelectOptionsTest.php | 21 -- .../Http/Controllers/IconControllerTest.php | 20 +- .../Controllers/SiteRouteControllerTest.php | 3 +- .../Middleware/HandleTemplateRequestTest.php | 3 +- .../Http/Middleware/ShowBrokenImageTest.php | 17 +- tests/Feature/Support/ComposerTest.php | 10 +- tests/Feature/Support/EnvTest.php | 3 +- tests/Feature/Support/JsonTest.php | 9 +- tests/Feature/Support/PathTest.php | 235 ++++++------------ tests/Feature/Support/URLTest.php | 3 +- .../SystemMessageMailableTest.php | 7 +- tests/Unit/AliasesArchitectureTest.php | 31 +++ .../LayoutElements/TemplateTest.php | 9 +- tests/Unit/HelpersTest.php | 3 +- tests/Unit/Route/TemplateRouteTest.php | 3 +- tests/Unit/Support/TypecastTest.php | 1 - tests/Unit/Twig/TemplateLoaderTest.php | 3 +- tests/Unit/Twig/TemplateResolverTest.php | 3 +- tests/Unit/Twig/TwigRendererTest.php | 3 +- .../Validation/Rules/EnvValueRuleTest.php | 3 - tests/Unit/View/BladeRendererTest.php | 3 +- yii2-adapter/composer.json | 1 + yii2-adapter/composer.lock | 14 +- yii2-adapter/legacy/Craft.php | 2 +- yii2-adapter/legacy/base/Plugin.php | 1 + .../behaviors/EnvAttributeParserBehavior.php | 2 +- yii2-adapter/legacy/config/GeneralConfig.php | 22 ++ yii2-adapter/legacy/debug/Module.php | 2 +- yii2-adapter/legacy/helpers/App.php | 2 +- yii2-adapter/legacy/helpers/Html.php | 7 +- yii2-adapter/legacy/helpers/Json.php | 3 +- yii2-adapter/legacy/log/MonologTarget.php | 2 +- .../legacy/mail/transportadapters/Gmail.php | 2 +- .../mail/transportadapters/Sendmail.php | 2 +- .../legacy/mail/transportadapters/Smtp.php | 2 +- yii2-adapter/legacy/services/Path.php | 2 +- .../src/Config/GeneralConfigCompatibility.php | 30 ++- yii2-adapter/src/Plugin/Plugins.php | 23 ++ yii2-adapter/src/Support/Composer.php | 32 +++ yii2-adapter/src/Support/Env.php | 20 ++ yii2-adapter/src/Twig/AliasesExtension.php | 21 ++ yii2-adapter/src/Yii2ServiceProvider.php | 77 ++++++ .../Legacy/AliasesCompatibilityTest.php | 32 +++ .../Legacy/ConfigCompatibilityTest.php | 29 +++ .../PluginLifecycleCompatibilityTest.php | 4 +- 100 files changed, 621 insertions(+), 909 deletions(-) delete mode 100644 src/Providers/IconServiceProvider.php create mode 100644 tests/Unit/AliasesArchitectureTest.php create mode 100644 yii2-adapter/src/Plugin/Plugins.php create mode 100644 yii2-adapter/src/Support/Composer.php create mode 100644 yii2-adapter/src/Support/Env.php create mode 100644 yii2-adapter/src/Twig/AliasesExtension.php create mode 100644 yii2-adapter/tests-laravel/Legacy/AliasesCompatibilityTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 2582e79f7e3..41df7646ea3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - Added `CraftCms\Cms\Plugin\Plugin::settingsForm()` for defining standard plugin settings pages with the Control Panel Form system. ([#19439](https://github.com/craftcms/cms/pull/19439)) - Removed `CraftCms\Cms\Plugin\Plugin::settingsHtml()`. `settingsForm()` should be used instead; Yii-era plugin settings HTML remains supported by `craftcms/yii2-adapter`. ([#19439](https://github.com/craftcms/cms/pull/19439)) +- Removed path alias support from core in favor of Laravel application paths. Legacy path aliases remain supported by `craftcms/yii2-adapter`. - Safe HTML elements are now allowed within Markdown field layout elements. ([#19426](https://github.com/craftcms/cms/pull/19426)) - Added a slideout system for the Inertia/Vue Control Panel, which renders any `CpScreenResponse`-based screen as an in-page panel from a normal Inertia response, alongside the existing legacy `Craft.CpScreenSlideout`. ([#19354](https://github.com/craftcms/cms/pull/19354)) - Added `CraftCms\Cms\Http\Responses\CpScreenResponse::screenData()`. ([#19354](https://github.com/craftcms/cms/pull/19354)) diff --git a/composer.json b/composer.json index a784cab2e1e..f3c93378189 100644 --- a/composer.json +++ b/composer.json @@ -38,7 +38,6 @@ "commerceguys/addressing": "^2.1.1", "composer/semver": "^3.3.2", "craftcms/cms-assets": "self.version", - "craftcms/laravel-aliases": "^2.0", "craftcms/laravel-dependency-aware-cache": "^1.2.3", "craftcms/laravel-ruleset-validation": "^1.1", "craftcms/plugin-installer": "~1.6.0", diff --git a/composer.lock b/composer.lock index 6d572e1f088..2a8fecbfc07 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "a70fee4a1b126c8967a01d48132ddb4f", + "content-hash": "103e1d5b597b341a98610b51a8c90931", "packages": [ { "name": "bacon/bacon-qr-code", @@ -423,78 +423,6 @@ "relative": true } }, - { - "name": "craftcms/laravel-aliases", - "version": "2.1.0", - "source": { - "type": "git", - "url": "https://github.com/craftcms/laravel-aliases.git", - "reference": "734ea28e7fc7acfc3988f64475b416ecbc46419e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/craftcms/laravel-aliases/zipball/734ea28e7fc7acfc3988f64475b416ecbc46419e", - "reference": "734ea28e7fc7acfc3988f64475b416ecbc46419e", - "shasum": "" - }, - "require": { - "illuminate/contracts": "^12.0||^13.0", - "php": "^8.2", - "yiisoft/aliases": "^3.0" - }, - "require-dev": { - "larastan/larastan": "^2.9||^3.0", - "laravel/pint": "^1.14", - "nunomaduro/collision": "^8.1.1||^7.10.0", - "orchestra/testbench": "^10.0.0||^11.0", - "pestphp/pest": "^4.0", - "pestphp/pest-plugin-arch": "^4.0", - "pestphp/pest-plugin-laravel": "^4.0", - "phpstan/extension-installer": "^1.3||^2.0", - "phpstan/phpstan-deprecation-rules": "^1.1||^2.0", - "phpstan/phpstan-phpunit": "^1.3||^2.0" - }, - "type": "library", - "extra": { - "laravel": { - "aliases": { - "Aliases": "CraftCms\\Aliases\\Facades\\Aliases" - }, - "providers": [ - "CraftCms\\Aliases\\AliasesServiceProvider" - ] - } - }, - "autoload": { - "psr-4": { - "CraftCms\\Aliases\\": "src/", - "CraftCms\\Aliases\\Database\\Factories\\": "database/factories/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Pixel & Tonic", - "homepage": "https://pixelandtonic.com/" - } - ], - "description": "A Laravel wrapper around yiisoft/aliases", - "homepage": "https://github.com/craftcms/laravel-aliases", - "keywords": [ - "craftcms", - "laravel", - "laravel-aliases", - "yii" - ], - "support": { - "issues": "https://github.com/craftcms/laravel-aliases/issues", - "source": "https://github.com/craftcms/laravel-aliases/tree/2.1.0" - }, - "time": "2026-03-17T14:59:00+00:00" - }, { "name": "craftcms/laravel-dependency-aware-cache", "version": "1.2.3", @@ -9219,83 +9147,6 @@ ], "time": "2026-06-17T06:05:59+00:00" }, - { - "name": "yiisoft/aliases", - "version": "3.1.1", - "source": { - "type": "git", - "url": "https://github.com/yiisoft/aliases.git", - "reference": "6f876dbf899f604fd3aefa3b3fd37e2ff2549ead" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/yiisoft/aliases/zipball/6f876dbf899f604fd3aefa3b3fd37e2ff2549ead", - "reference": "6f876dbf899f604fd3aefa3b3fd37e2ff2549ead", - "shasum": "" - }, - "require": { - "php": "8.1 - 8.5" - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.3", - "maglnet/composer-require-checker": "^4.7.1", - "phpunit/phpunit": "^10.5.48", - "psr/container": "^2.0.2", - "rector/rector": "^2.1.2", - "spatie/phpunit-watcher": "^1.24.0", - "yiisoft/definitions": "^3.4", - "yiisoft/di": "^1.4", - "yiisoft/test-support": "^3.0.2" - }, - "type": "library", - "extra": { - "bamarni-bin": { - "bin-links": true, - "forward-command": true, - "target-directory": "tools" - }, - "config-plugin": { - "di": "di.php", - "params": "params.php" - }, - "config-plugin-options": { - "source-directory": "config" - } - }, - "autoload": { - "psr-4": { - "Yiisoft\\Aliases\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "description": "Named paths and URLs storage", - "homepage": "https://www.yiiframework.com/", - "keywords": [ - "alias" - ], - "support": { - "chat": "https://t.me/yii3en", - "forum": "https://www.yiiframework.com/forum/", - "irc": "ircs://irc.libera.chat:6697/yii", - "issues": "https://github.com/yiisoft/aliases/issues?state=open", - "source": "https://github.com/yiisoft/aliases", - "wiki": "https://www.yiiframework.com/wiki/" - }, - "funding": [ - { - "url": "https://github.com/sponsors/yiisoft", - "type": "github" - }, - { - "url": "https://opencollective.com/yiisoft", - "type": "opencollective" - } - ], - "time": "2025-12-04T12:53:43+00:00" - }, { "name": "yiisoft/arrays", "version": "3.2.1", diff --git a/config/general.php b/config/general.php index 97fb38227f4..f6bc2b5e309 100644 --- a/config/general.php +++ b/config/general.php @@ -18,8 +18,4 @@ // Preload Single entries as Twig variables ->preloadSingles() // Prevent user enumeration attacks - ->preventUserEnumeration() - // Set the @webroot alias so the clear-caches command knows where to find CP resources - ->aliases([ - '@webroot' => public_path(), - ]); + ->preventUserEnumeration(); diff --git a/packages/craftcms-playwright/php/DbBackup.php b/packages/craftcms-playwright/php/DbBackup.php index c820153a5db..77e4ddf680d 100644 --- a/packages/craftcms-playwright/php/DbBackup.php +++ b/packages/craftcms-playwright/php/DbBackup.php @@ -9,7 +9,6 @@ use craft\db\Table; use craft\events\BackupEvent; use craft\helpers\ArrayHelper; -use CraftCms\Aliases\Aliases; use yii\base\Event; use yii\base\Module; @@ -19,7 +18,7 @@ class DbBackup extends Module public function init() { // Set a @modules alias pointed to the modules/ directory - Aliases::set('@modules', __DIR__); + Craft::setAlias('@modules', __DIR__); parent::init(); Craft::$app->onInit(function () { diff --git a/resources/js/modules/install/components/SiteFields.vue b/resources/js/modules/install/components/SiteFields.vue index 54bdfa1816b..78307635240 100644 --- a/resources/js/modules/install/components/SiteFields.vue +++ b/resources/js/modules/install/components/SiteFields.vue @@ -64,7 +64,7 @@ class="p-0" icon="lightbulb" > - {{ t('This can begin with an environment variable or alias.') }} + {{ t('This can begin with an environment variable.') }} {{ t('Learn more') }} -
- -

{{ 'Aliases'|t('app') }}

-

- {{ 'The following aliases are defined:'|t('app', { - url: 'https://craftcms.com/docs/5.x/configure.html#aliases', - })|raw }} -

- - - - {% for alias, value in aliases %} - - - - - {% endfor %} - -
{{ alias }}{{ value }}
-
-

{{ "Requirements"|t('app') }}

diff --git a/resources/templates/_includes/forms.twig b/resources/templates/_includes/forms.twig index 181d89dbac5..267d8b45040 100644 --- a/resources/templates/_includes/forms.twig +++ b/resources/templates/_includes/forms.twig @@ -287,9 +287,7 @@ {% set config = config|merge({id: config.id ?? "selectize#{random()}"}) %} {% if (config.includeEnvVars ?? false) and config.tip is not defined and (config.value ?? '')[0:1] != '$' %} {% set config = config|merge({ - tip: (config.allowedEnvValues is not defined) - ? 'This can begin with an environment variable or alias.'|t('app') - : 'This can begin with an environment variable.'|t('app'), + tip: 'This can begin with an environment variable.'|t('app'), }) %} {% endif %} {{ _self.field(config, 'template:_includes/forms/selectize') }} @@ -411,23 +409,17 @@ id: config.id ?? "autosuggest#{random()}", }) %} - {# Suggest an environment variable / alias? #} + {# Suggest an environment variable? #} {% if (config.suggestEnvVars ?? false) %} {% set value = config.value ?? '' %} - {% if config.tip is not defined and value[0:1] not in ['$', '@'] %} + {% if config.tip is not defined and value[0:1] != '$' %} {% set config = config|merge({ - tip: ((config.suggestAliases ?? false) - ? 'This can begin with an environment variable or alias.'|t('app') - : 'This can begin with an environment variable.'|t('app')) ~ ' ' ~ tag('a', { + tip: 'This can begin with an environment variable.'|t('app') ~ ' ' ~ tag('a', { href: 'https://craftcms.com/docs/5.x/configure.html#control-panel-settings', class: 'go', text: 'Learn more'|t('app'), }), }) %} - {% elseif config.warning is not defined and (value == '@web' or value[0:5] == '@web/') %} - {% set config = config|merge({ - warning: 'The `@web` alias is not recommended.'|t('app'), - }) %} {% endif %} {% endif %} diff --git a/resources/templates/_includes/forms/autosuggest.twig b/resources/templates/_includes/forms/autosuggest.twig index 6f9f6bdc531..260249d49da 100644 --- a/resources/templates/_includes/forms/autosuggest.twig +++ b/resources/templates/_includes/forms/autosuggest.twig @@ -4,10 +4,7 @@ {% set suggestions = (suggestions ?? []) |merge((suggestTemplates ?? false) ? cpTemplateSuggestions() : []) - |merge((suggestEnvVars ?? false) ? cpEnvSuggestions( - suggestAliases ?? false, - suggestionFilter ?? null - ) : []) %} + |merge((suggestEnvVars ?? false) ? cpEnvSuggestions(suggestionFilter ?? null) : []) %} {%- set id = id ?? "autosuggest#{random()}" %} {%- set previewInputId = "#{id}-preview" %} diff --git a/resources/templates/_includes/forms/editableTable.twig b/resources/templates/_includes/forms/editableTable.twig index b6e6b0c25bb..01eda212504 100644 --- a/resources/templates/_includes/forms/editableTable.twig +++ b/resources/templates/_includes/forms/editableTable.twig @@ -218,7 +218,6 @@ name: cellName, suggestTemplates: col.type == 'template', suggestEnvVars: col.suggestEnvVars ?? false, - suggestAliases: col.suggestAliases ?? false, value: value, disabled: static, labelledBy: headingId, diff --git a/resources/templates/_layouts/components/system-info.twig b/resources/templates/_layouts/components/system-info.twig index 90d87a27874..59aa991cd4f 100644 --- a/resources/templates/_layouts/components/system-info.twig +++ b/resources/templates/_layouts/components/system-info.twig @@ -7,7 +7,7 @@ } %}
{% if config('craft.general.cpIconUrl') %} - + {% else %} {{ iconSvg('c-outline') }} {% endif %} diff --git a/scripts/copyicons.php b/scripts/copyicons.php index d5c100a0631..3df12db8b61 100644 --- a/scripts/copyicons.php +++ b/scripts/copyicons.php @@ -84,23 +84,6 @@ $metaPath = "$kitDir/icons/metadata/icons.json"; $meta = json_decode(file_get_contents($metaPath), true); $index = []; -$aliasesPhp = <<set every - * time incurs a high performance cost. - */ -\$reflectionProperty = new ReflectionProperty(\$aliases, 'aliases'); -\$reflectionProperty->setValue(\$aliases, array_merge_recursive(\$reflectionProperty->getValue(\$aliases), [ - '@appicons' => [ - -PHP; - $skipped = 0; $wrote = 0; @@ -168,19 +151,8 @@ ]; } - if ($style !== 'solid') { - $aliasesPhp .= << "@icons/$dir/$name.svg", - -PHP; - } } -$aliasesPhp .= <<<'PHP' - ] -])); -PHP; - echo "Finished writing $wrote icons ($skipped skipped).\n"; $missing = array_filter( @@ -216,7 +188,3 @@ PHP; file_put_contents($indexPath, $indexContents); echo "done\n"; - -echo 'Writing aliases ... '; -file_put_contents("$iconsDir/aliases.php", $aliasesPhp); -echo "done\n"; diff --git a/src/Announcement/Announcements.php b/src/Announcement/Announcements.php index 65e5513c1de..2fd0b04b81d 100644 --- a/src/Announcement/Announcements.php +++ b/src/Announcement/Announcements.php @@ -4,9 +4,9 @@ namespace CraftCms\Cms\Announcement; -use CraftCms\Aliases\Aliases; use CraftCms\Cms\Announcement\Jobs\SendAnnouncement; use CraftCms\Cms\Announcement\Models\Announcement; +use CraftCms\Cms\Cp\Icons; use CraftCms\Cms\Plugin\Contracts\PluginInterface; use CraftCms\Cms\Plugin\Plugins; use CraftCms\Cms\Support\Facades\I18N; @@ -87,7 +87,7 @@ public function get(): array $icon = $this->plugins->getPluginIconSvg($plugin->handle); $label = $plugin->name; } else { - $icon = @file_get_contents(Aliases::get('@appicons/craft-cms.svg')); + $icon = @file_get_contents(Icons::resolveIconPath('craft-cms')); $label = 'Craft CMS'; } diff --git a/src/Asset/AssetsHelper.php b/src/Asset/AssetsHelper.php index 051e9693bde..a32a247908f 100644 --- a/src/Asset/AssetsHelper.php +++ b/src/Asset/AssetsHelper.php @@ -4,7 +4,6 @@ namespace CraftCms\Cms\Asset; -use CraftCms\Aliases\Aliases; use CraftCms\Cms\Asset\Data\Volume; use CraftCms\Cms\Asset\Data\VolumeFolder; use CraftCms\Cms\Asset\Elements\Asset; @@ -493,7 +492,7 @@ public static function iconSvg(string $extension): string return $path; } - $svg = file_get_contents(Aliases::get('@resources/public/images/thumbs/file.svg')); + $svg = file_get_contents(Path::resources('public/images/thumbs/file.svg')); $extLength = strlen($extension); diff --git a/src/Asset/Elements/Asset.php b/src/Asset/Elements/Asset.php index b4500051ec8..1ee1383f2f2 100644 --- a/src/Asset/Elements/Asset.php +++ b/src/Asset/Elements/Asset.php @@ -5,7 +5,6 @@ namespace CraftCms\Cms\Asset\Elements; use Closure; -use CraftCms\Aliases\Aliases; use CraftCms\Cms\Asset\Actions\CopyReferenceTag; use CraftCms\Cms\Asset\Actions\CopyUrl; use CraftCms\Cms\Asset\Actions\DeleteAssets; @@ -2154,7 +2153,7 @@ protected function thumbUrl(int $size): ?string protected function thumbSvg(): string { if ($this->isFolder) { - return file_get_contents(Aliases::get('@resources/public/images/thumbs/folder.svg')); + return file_get_contents(Path::resources('public/images/thumbs/folder.svg')); } return AssetsHelper::iconSvg($this->getExtension()); @@ -3502,8 +3501,8 @@ private function _validateTempFilePath(): bool [Path::temp(), true], [Path::tempAssetUploads(), true], [sys_get_temp_dir(), true], - [Aliases::get('@root', false), false], - [Aliases::get('@storage', false), false], + [base_path(), false], + [storage_path(), false], ]; $inAllowedRoot = false; diff --git a/src/Config/ConfigServiceProvider.php b/src/Config/ConfigServiceProvider.php index ee4cb43f482..9929b15caa6 100644 --- a/src/Config/ConfigServiceProvider.php +++ b/src/Config/ConfigServiceProvider.php @@ -4,7 +4,6 @@ namespace CraftCms\Cms\Config; -use CraftCms\Aliases\Aliases; use CraftCms\Cms\Support\Env; use CraftCms\Cms\Support\HtmlSanitizer\HtmlSanitizerManager; use CraftCms\Cms\Support\Typecast; @@ -110,10 +109,6 @@ private function loadGeneralConfig(ConfigRepository $repository): GeneralConfig $this->applyGeneralConfigSetting($config, $setting, $value); } - foreach ($config->aliases as $name => $value) { - Aliases::set($name, $value); - } - $repository->set('craft.general', $config); return $config; diff --git a/src/Config/GeneralConfig.php b/src/Config/GeneralConfig.php index 0cb5e7f02b7..e3cbaf17009 100644 --- a/src/Config/GeneralConfig.php +++ b/src/Config/GeneralConfig.php @@ -123,19 +123,6 @@ class GeneralConfig extends BaseConfig */ public bool $addTrailingSlashesToUrls = false; - /** - * @var array Any custom Yii [aliases](https://www.yiiframework.com/doc/guide/2.0/en/concept-aliases) that should be defined for every request. - * - * ```php Static Config - * ->aliases([ - * '@webroot' => '/var/www/', - * ]) - * ``` - * - * @group Environment - */ - public array $aliases = []; - /** * @var string|null The server path to the directory where Craft should store compiled Twig templates. * @@ -3211,38 +3198,13 @@ public function addTrailingSlashesToUrls(bool $value = true): self return $this; } - /** - * Any custom Yii [aliases](https://www.yiiframework.com/doc/guide/2.0/en/concept-aliases) that should be defined for every request. - * - * ```php - * ->aliases([ - * '@webroot' => '/var/www/', - * ]) - * ``` - * - * @group Environment - * - * @param array $value - * - * @see $aliases - */ - public function aliases(array $value): self - { - $this->aliases = []; - foreach ($value as $name => $path) { - $this->addAlias($name, $path); - } - - return $this; - } - /** * The server path to the directory where Craft should store compiled Twig templates. * * If this is set to `null`, Craft will store compiled templates in `storage/runtime/compiled_templates`. * * ```php - * ->compiledTemplatesPath('@storage/runtime/templates') + * ->compiledTemplatesPath(storage_path('runtime/templates')) * ``` * * @group Environment @@ -3257,27 +3219,6 @@ public function compiledTemplatesPath(?string $value): self return $this; } - /** - * Adds a custom Yii [alias](https://www.yiiframework.com/doc/guide/2.0/en/concept-aliases) that should be defined for every request. - * - * ```php - * ->addAlias('@webroot', '/var/www/') - * ``` - * - * @group Environment - * - * @see $aliases - */ - public function addAlias(string $name, ?string $path): self - { - if (! str_starts_with($name, '@')) { - $name = "@$name"; - } - $this->aliases[$name] = $path; - - return $this; - } - /** * Whether admins should be allowed to make administrative changes to the system. * diff --git a/src/Cp/FormFields.php b/src/Cp/FormFields.php index 7722ef6b951..7ff0edf06bb 100644 --- a/src/Cp/FormFields.php +++ b/src/Cp/FormFields.php @@ -1331,24 +1331,15 @@ public static function autosuggestFieldHtml(array $config): string { $config['id'] ??= 'autosuggest'.mt_rand(); - // Suggest an environment variable / alias? + // Suggest an environment variable? if ($config['suggestEnvVars'] ?? false) { $value = $config['value'] ?? ''; - if (! isset($config['tip']) && (! isset($value[0]) || ! in_array($value[0], ['$', '@']))) { - if ($config['suggestAliases'] ?? false) { - $config['tip'] = t('This can begin with an environment variable or alias.'); - } else { - $config['tip'] = t('This can begin with an environment variable.'); - } + if (! isset($config['tip']) && (! isset($value[0]) || $value[0] !== '$')) { + $config['tip'] = t('This can begin with an environment variable.'); $config['tip'] .= ' '. Html::a(t('Learn more'), 'https://craftcms.com/docs/5.x/configure.html#control-panel-settings', [ 'class' => 'go', ]); - } elseif ( - ! isset($config['warning']) && - ($value === '@web' || str_starts_with((string) $value, '@web/')) - ) { - $config['warning'] = t('The `@web` alias is not recommended.'); } } diff --git a/src/Cp/Icons.php b/src/Cp/Icons.php index 1cbd1628f67..f4aac3e76a6 100644 --- a/src/Cp/Icons.php +++ b/src/Cp/Icons.php @@ -4,11 +4,10 @@ namespace CraftCms\Cms\Cp; -use CraftCms\Aliases\Aliases; use CraftCms\Cms\Cms; +use CraftCms\Cms\Support\CmsAssets; use CraftCms\Cms\Support\Facades\I18N; use CraftCms\Cms\Support\Html; -use CraftCms\Cms\Support\Str; use Illuminate\Support\Facades\Log; use InvalidArgumentException; @@ -82,34 +81,6 @@ private const array EARTH_ALIASES = ['world', 'earth']; - private const array CUSTOM_ICONS = [ - 'asterisk-slash', - 'c-debug', - 'c-outline', - 'clone-dashed', - 'diamond-slash', - 'duplicate', - 'element-card', - 'element-card-slash', - 'element-cards', - 'gear-slash', - 'graphql', - 'grip-dots', - 'image-slash', - 'language', - 'list-flip', - 'list-tree-flip', - 'notification-bottom-left', - 'notification-bottom-right', - 'notification-top-left', - 'notification-top-right', - 'share-flip', - 'slideout-left', - 'slideout-right', - 'thumb-left', - 'thumb-right', - ]; - public static function resolveIconName(string $icon): string { if (isset(self::LEGACY_ICON_MAP[$icon])) { @@ -131,23 +102,19 @@ public static function resolveIconName(string $icon): string public static function resolveIconFamily(string $icon): string { - if (in_array($icon, self::CUSTOM_ICONS)) { - return 'custom-icons'; + foreach (['custom-icons', 'solid', 'brands', 'regular'] as $family) { + if (is_file(CmsAssets::resourcesPath("icons/$family/$icon.svg"))) { + return $family; + } } - return Str::of(self::resolveIconPath($icon)) - ->replace(Aliases::get('@icons'), '') - ->ltrim('/', '') - ->before('/') - ->toString(); + return 'solid'; } /** @return array{name: string, family: string} */ public static function resolveIconData(string $icon): array { - // Resolve the family from the resolved name, not the raw one — an - // alias can point at a custom icon (e.g. `move` → `grip-dots`), whose - // family would otherwise resolve as if it were a system icon. + // Resolve the family from the resolved name, not the raw one. $name = self::resolveIconName($icon); return [ @@ -158,9 +125,7 @@ public static function resolveIconData(string $icon): array public static function resolveIconPath(string $icon): string { - return in_array($icon, self::CUSTOM_ICONS) - ? Aliases::get("@cmsAssets/resources/icons/custom-icons/$icon.svg") - : Aliases::get("@appicons/$icon.svg"); + return CmsAssets::resourcesPath(sprintf('icons/%s/%s.svg', self::resolveIconFamily($icon), $icon)); } public static function svg(?string $icon, ?string $fallbackLabel = null, ?string $altText = null): ?string diff --git a/src/Cp/SelectOptions.php b/src/Cp/SelectOptions.php index 6dc57715bb0..36b6bdd6f7e 100644 --- a/src/Cp/SelectOptions.php +++ b/src/Cp/SelectOptions.php @@ -4,7 +4,6 @@ namespace CraftCms\Cms\Cp; -use CraftCms\Aliases\Aliases; use CraftCms\Cms\Asset\AssetsHelper; use CraftCms\Cms\Asset\Data\Volume; use CraftCms\Cms\Filesystem\Contracts\FsInterface; @@ -34,18 +33,16 @@ class SelectOptions { /** - * Returns the available environment variable and alias suggestions for + * Returns the available environment variable suggestions for * inputs that support them. * - * @param bool $includeAliases Whether aliases should be included in the list - * (only enable this if the setting defines a URL or file path) * @param callable|null $filter A function that returns whether a given value should be included * * @phpstan-param callable(scalar):bool|null $filter * - * @phpstan-return array{0: array{type: 'optgroup', label: string, options: list>}, 1?: array{type: 'optgroup', label: string, options: list>}} + * @phpstan-return array{0: array{type: 'optgroup', label: string, options: list>}} */ - public static function getEnvSuggestions(bool $includeAliases = false, ?callable $filter = null) + public static function getEnvSuggestions(?callable $filter = null): array { $suggestions = []; @@ -61,7 +58,7 @@ public static function getEnvSuggestions(bool $includeAliases = false, ?callable 'label' => '$'.$var, 'value' => '$'.$var, 'data' => [ - 'hint' => Security::redactIfSensitive($var, Aliases::get((string) $env, false)), + 'hint' => Security::redactIfSensitive($var, $env), ], ]; } @@ -73,33 +70,6 @@ public static function getEnvSuggestions(bool $includeAliases = false, ?callable 'options' => array_values(Arr::sort($envSuggestions, 'name')), ]; - if ($includeAliases) { - $aliasSuggestions = []; - foreach (Aliases::getAll() as $alias => $path) { - // Don't ever suggest @web - if ($alias === '@web') { - continue; - } - if (str_starts_with($alias, '@web/')) { - continue; - } - if (! $filter || $filter($path)) { - $aliasSuggestions[] = [ - 'label' => $alias, - 'value' => $alias, - 'data' => [ - 'hint' => $path, - ], - ]; - } - } - $suggestions[] = [ - 'type' => 'optgroup', - 'label' => t('Aliases'), - 'options' => array_values(Arr::sort($aliasSuggestions, 'name')), - ]; - } - return $suggestions; } @@ -131,7 +101,7 @@ public static function getEnvOptions(?array $allowedValues = null): array ) { $data = []; if ($value !== '') { - $data['hint'] = Security::redactIfSensitive($var, Aliases::get($value, false)); + $data['hint'] = Security::redactIfSensitive($var, $value); } $options[] = array_filter([ diff --git a/src/Database/DatabaseServiceProvider.php b/src/Database/DatabaseServiceProvider.php index 8b08edc374c..0d8b7faa842 100644 --- a/src/Database/DatabaseServiceProvider.php +++ b/src/Database/DatabaseServiceProvider.php @@ -4,7 +4,6 @@ namespace CraftCms\Cms\Database; -use CraftCms\Aliases\Aliases; use CraftCms\Cms\Cms; use CraftCms\Cms\Database\Commands\BackupCommand; use CraftCms\Cms\Database\Commands\ConvertCharsetCommand; @@ -110,8 +109,6 @@ public function boot(Repository $config, Connection $db, \Illuminate\Cache\Repos $db->useWriteConnectionWhenReading(); } - Aliases::set('@migrations', '@package/Database/Migrations'); - $this->commands([ BackupCommand::class, ConvertCharsetCommand::class, diff --git a/src/Database/Migrator.php b/src/Database/Migrator.php index e6949159f2d..54241b84b6e 100644 --- a/src/Database/Migrator.php +++ b/src/Database/Migrator.php @@ -4,7 +4,6 @@ namespace CraftCms\Cms\Database; -use CraftCms\Aliases\Aliases; use Override; /** @@ -25,7 +24,7 @@ public function track(string $track): self $this->repository->track($track); if ($track === 'craft') { - $this->setPaths(['@migrations']); + $this->setPaths([__DIR__.'/Migrations']); } if (is_null($track)) { @@ -55,8 +54,6 @@ public function run($paths = [], array $options = []): array $paths = $this->paths; } - $paths = array_map(Aliases::get(...), $paths); - if (! $this->repository->repositoryExists()) { $this->repository->createRepository(); } @@ -96,8 +93,6 @@ public function getPendingMigrations(array $paths = []): array $paths = $this->paths; } - $paths = array_map(Aliases::get(...), $paths); - $files = $this->getMigrationFiles($paths); return $this->pendingMigrations( diff --git a/src/Entry/Commands/MergeEntryTypesCommand.php b/src/Entry/Commands/MergeEntryTypesCommand.php index ade9810f3d0..9fd239ac7ed 100644 --- a/src/Entry/Commands/MergeEntryTypesCommand.php +++ b/src/Entry/Commands/MergeEntryTypesCommand.php @@ -4,7 +4,6 @@ namespace CraftCms\Cms\Entry\Commands; -use CraftCms\Aliases\Aliases; use CraftCms\Cms\Console\CraftCommand; use CraftCms\Cms\Database\Migrator; use CraftCms\Cms\Entry\Data\EntryType; @@ -17,6 +16,7 @@ use CraftCms\Cms\FieldLayout\FieldLayoutTab; use CraftCms\Cms\Section\Data\Section; use CraftCms\Cms\Support\Arr; +use CraftCms\Cms\Support\Facades\Path; use CraftCms\Cms\Support\Facades\Sections; use CraftCms\Cms\Support\File; use CraftCms\Cms\Support\Str; @@ -251,7 +251,7 @@ public function handle(EntryTypes $entryTypes, Fields $fields, Migrator $migrato $uidMap, ) { ob_start(); - File::getRequire(Aliases::get('@craftcms/stubs/entry-type-merge.php.stub'), [ + File::getRequire(Path::package('stubs/entry-type-merge.php.stub'), [ 'persistingEntryTypeUid' => $persistingEntryType->uid, 'outgoingEntryTypeUid' => $outgoingEntryType->uid, 'layoutElementUidMap' => $uidMap, diff --git a/src/Field/Fields.php b/src/Field/Fields.php index 29adfce2d46..9484e3eb528 100644 --- a/src/Field/Fields.php +++ b/src/Field/Fields.php @@ -4,7 +4,6 @@ namespace CraftCms\Cms\Field; -use CraftCms\Aliases\Aliases; use CraftCms\Cms\Cms; use CraftCms\Cms\Component\ComponentHelper; use CraftCms\Cms\Component\Contracts\Iconic; @@ -41,6 +40,7 @@ use CraftCms\Cms\ProjectConfig\ProjectConfig; use CraftCms\Cms\ProjectConfig\ProjectConfigHelper; use CraftCms\Cms\Support\Arr; +use CraftCms\Cms\Support\Facades\Path; use CraftCms\Cms\Support\Facades\ProjectConfig as ProjectConfigFacade; use CraftCms\Cms\Support\File; use CraftCms\Cms\Support\Json as JsonHelper; @@ -852,7 +852,7 @@ public function merge( $migrationPath = database_path("migrations/{$migrationName}.php"); ob_start(); - File::getRequire(Aliases::get('@craftcms/stubs/field-merge.php.stub'), [ + File::getRequire(Path::package('stubs/field-merge.php.stub'), [ 'persistingFieldUid' => $persistingField->uid, 'outgoingFieldUid' => $outgoingField->uid, ]); diff --git a/src/Field/Icon.php b/src/Field/Icon.php index 1d875bf2459..e4f2a8bb2bf 100644 --- a/src/Field/Icon.php +++ b/src/Field/Icon.php @@ -4,7 +4,6 @@ namespace CraftCms\Cms\Field; -use CraftCms\Aliases\Aliases; use CraftCms\Cms\Cms; use CraftCms\Cms\Cp\FormFields; use CraftCms\Cms\Cp\Icons; @@ -24,6 +23,7 @@ use CraftCms\Cms\Form\Nodes\Field as FormField; use CraftCms\Cms\Gql\Types\Generators\IconDataType; use CraftCms\Cms\Support\Arr; +use CraftCms\Cms\Support\CmsAssets; use CraftCms\Cms\Support\Html; use CraftCms\Cms\Support\Query; use GraphQL\Type\Definition\Type; @@ -75,8 +75,7 @@ public static function dbType(): string private static function iconStyles(string $name): array { if (! isset(self::$_icons)) { - $indexPath = '@cmsAssets/resources/icons/index.php'; - self::$_icons = require Aliases::get($indexPath); + self::$_icons = require CmsAssets::resourcesPath('icons/index.php'); } return self::$_icons[$name]['styles'] ?? []; diff --git a/src/Filesystem/Filesystems/Local.php b/src/Filesystem/Filesystems/Local.php index c578e8d2d25..9e080cf9637 100644 --- a/src/Filesystem/Filesystems/Local.php +++ b/src/Filesystem/Filesystems/Local.php @@ -127,20 +127,20 @@ public function settingsForm(FormContext $context = new FormContext): ?Form if ($this->hasUrls && $this->getShowUrlSetting()) { $form->add(Field::make(t('Base URL')) - ->instructions(t('The base URL to the files in this filesystem. This can begin with an environment variable or alias.')) + ->instructions(t('The base URL to the files in this filesystem. This can begin with an environment variable.')) ->required() ->control(Combobox::make('url') ->value($this->url) - ->options(SelectOptions::getEnvSuggestions(true, fn ($value): bool => Str::isUrl($value))) + ->options(SelectOptions::getEnvSuggestions(fn ($value): bool => Str::isUrl($value))) ->placeholder('//example.com/path/to/folder'))); } return $form->add(Field::make(t('Base Path')) - ->instructions(t('The base folder path that should be used as the root of the filesystem. This can begin with an environment variable or alias.')) + ->instructions(t('The base folder path that should be used as the root of the filesystem. This can begin with an environment variable.')) ->required() ->control(Combobox::make('path') ->value($this->path) - ->options(SelectOptions::getEnvSuggestions(true)) + ->options(SelectOptions::getEnvSuggestions()) ->placeholder('/path/to/folder'))); } diff --git a/src/Http/Controllers/Assets/TransformController.php b/src/Http/Controllers/Assets/TransformController.php index 010b725d96e..33b7dda5deb 100644 --- a/src/Http/Controllers/Assets/TransformController.php +++ b/src/Http/Controllers/Assets/TransformController.php @@ -4,9 +4,9 @@ namespace CraftCms\Cms\Http\Controllers\Assets; -use CraftCms\Aliases\Aliases; use CraftCms\Cms\Asset\Elements\Asset; use CraftCms\Cms\Auth\Concerns\EnforcesPermissions; +use CraftCms\Cms\Cp\Icons; use CraftCms\Cms\Http\RespondsWithFlash; use CraftCms\Cms\Image\Data\ImageTransform; use CraftCms\Cms\Image\ImageTransformer; @@ -139,7 +139,7 @@ private function asBrokenImage(?Throwable $e = null): Response { $statusCode = $e instanceof HttpException && $e->getStatusCode() ? $e->getStatusCode() : 500; - return response()->file(Aliases::get('@appicons/broken-image.svg'), [ + return response()->file(Icons::resolveIconPath('image-slash'), [ 'Content-Type' => 'image/svg+xml', ])->setStatusCode($statusCode); } diff --git a/src/Http/Controllers/IconController.php b/src/Http/Controllers/IconController.php index 234ce24ef00..06240387556 100644 --- a/src/Http/Controllers/IconController.php +++ b/src/Http/Controllers/IconController.php @@ -4,8 +4,8 @@ namespace CraftCms\Cms\Http\Controllers; -use CraftCms\Aliases\Aliases; use CraftCms\Cms\Cp\Icons; +use CraftCms\Cms\Support\CmsAssets; use CraftCms\Cms\Support\Html; use CraftCms\Cms\Support\Search; use CraftCms\DependencyAwareCache\Dependency\FileDependency; @@ -51,12 +51,14 @@ public function pickerOptions(Request $request): JsonResponse $searchTerms = explode(' ', Search::normalizeKeywords($search)); } - $indexPath = '@cmsAssets/resources/icons/index.php'; - $icons = require Aliases::get($indexPath); + $indexPath = CmsAssets::resourcesPath('icons/index.php'); + $icons = require $indexPath; $output = []; $scores = []; foreach ($icons as $name => $icon) { + $name = (string) $name; + if ($freeOnly && $icon['pro']) { continue; } @@ -69,7 +71,7 @@ public function pickerOptions(Request $request): JsonResponse $scores[] = $score; } - $file = Aliases::get("@appicons/$name.svg"); + $file = Icons::resolveIconPath($name); $output[] = Html::beginTag('li'). Html::button(file_get_contents($file), [ 'class' => 'icon-picker--icon', diff --git a/src/Http/Controllers/InstallController.php b/src/Http/Controllers/InstallController.php index b94c1e5a1cb..38b6e2d4246 100644 --- a/src/Http/Controllers/InstallController.php +++ b/src/Http/Controllers/InstallController.php @@ -4,7 +4,6 @@ namespace CraftCms\Cms\Http\Controllers; -use CraftCms\Aliases\Aliases; use CraftCms\Cms\Cms; use CraftCms\Cms\Config\GeneralConfig; use CraftCms\Cms\Cp\SelectOptions; @@ -16,6 +15,7 @@ use CraftCms\Cms\Site\Data\Site; use CraftCms\Cms\Support\Env; use CraftCms\Cms\Support\Facades\I18N; +use CraftCms\Cms\Support\Facades\Path; use CraftCms\Cms\Support\Str; use CraftCms\Cms\Support\Url; use CraftCms\Cms\Validation\Rules\EnvValueRule; @@ -86,7 +86,7 @@ public function index(GeneralConfig $generalConfig): \Inertia\Response 'showDbScreen' => $showDbScreen, 'postCpLoginRedirect' => $postCpLoginRedirect, 'licenseHtml' => Inertia::defer(function () { - $licensePath = Aliases::get('@craftcms/LICENSE.md'); + $licensePath = Path::package('LICENSE.md'); $license = file_get_contents($licensePath); return Str::markdown($license); @@ -102,7 +102,7 @@ public function index(GeneralConfig $generalConfig): \Inertia\Response return array_merge($timezoneOptions, SelectOptions::getEnvOptions(array_column($timezoneOptions, 'value'))); }), - 'baseUrlSuggestions' => SelectOptions::getEnvSuggestions(true, fn ($value) => Str::isUrl($value)), + 'baseUrlSuggestions' => SelectOptions::getEnvSuggestions(fn ($value) => Str::isUrl($value)), 'defaultSystemName' => $defaultSystemName, 'defaultSiteUrl' => $defaultSiteUrl, 'defaultSiteLanguage' => $defaultSiteLanguage, @@ -221,11 +221,6 @@ public function install(Request $request, Migrator $migrator, LaravelMigrations $username = $request->input('account.username', $email); $siteUrl = $request->input('site.baseUrl'); - // Don't save @web even if they chose it - if ($siteUrl === '@web') { - $siteUrl = Aliases::get($siteUrl); - } - if (! in_array($siteUrl[0], ['@', '$']) && ! app()->isEphemeral()) { try { Env::writeVariable('APP_URL', $siteUrl, $path, overwrite: true); diff --git a/src/Http/Controllers/Settings/SitesController.php b/src/Http/Controllers/Settings/SitesController.php index 8b2e5035d78..9e4879920fb 100644 --- a/src/Http/Controllers/Settings/SitesController.php +++ b/src/Http/Controllers/Settings/SitesController.php @@ -357,11 +357,11 @@ private function siteForm(array $values): FormPayload if ($values['hasUrls'] ?? false) { $nodes[] = Field::make(t('Base URL'), Combobox::make('baseUrl') - ->options(SelectOptions::getEnvSuggestions(true, Str::isUrl(...)))) + ->options(SelectOptions::getEnvSuggestions(Str::isUrl(...)))) ->instructions(t('The base URL for the site.')) ->tip(sprintf( '%s [%s](%s)', - t('This can begin with an environment variable or alias.'), + t('This can begin with an environment variable.'), t('Learn more'), 'https://craftcms.com/docs/5.x/configure.html#control-panel-settings', )); diff --git a/src/Http/Middleware/HandleInertiaRequests.php b/src/Http/Middleware/HandleInertiaRequests.php index e141da4fb1d..658191064d6 100644 --- a/src/Http/Middleware/HandleInertiaRequests.php +++ b/src/Http/Middleware/HandleInertiaRequests.php @@ -5,7 +5,6 @@ namespace CraftCms\Cms\Http\Middleware; use Closure; -use CraftCms\Aliases\Aliases; use CraftCms\Cms\Cms; use CraftCms\Cms\Config\GeneralConfig; use CraftCms\Cms\Cp\Cp; @@ -121,7 +120,7 @@ public function share(Request $request): array } $systemIcon = ($generalConfig->cpIconUrl && Edition::isAtLeast(Edition::Pro)) - ? Html::img(Aliases::get($generalConfig->cpIconUrl))->render() + ? Html::img($generalConfig->cpIconUrl)->render() : Icons::svg('c-outline'); return [ diff --git a/src/Http/Middleware/ShowBrokenImage.php b/src/Http/Middleware/ShowBrokenImage.php index 47a488ec97a..0fc9a0d1cb4 100644 --- a/src/Http/Middleware/ShowBrokenImage.php +++ b/src/Http/Middleware/ShowBrokenImage.php @@ -5,7 +5,6 @@ namespace CraftCms\Cms\Http\Middleware; use Closure; -use CraftCms\Aliases\Aliases; use CraftCms\Cms\Cms; use CraftCms\Cms\Support\File; use Illuminate\Http\Request; @@ -45,7 +44,11 @@ public function handle(Request $request, Closure $next): mixed } $generalConfig = Cms::config(); - $imagePath = Aliases::get($generalConfig->brokenImagePath); + $imagePath = $generalConfig->brokenImagePath; + + if (str_starts_with($imagePath, '@')) { + throw new RuntimeException('Path aliases require craftcms/yii2-adapter. Use an absolute path instead.'); + } if (! is_file($imagePath)) { throw new RuntimeException("Invalid broken image path: $generalConfig->brokenImagePath"); diff --git a/src/License/License.php b/src/License/License.php index 5a95ce2b7eb..a72d70dbdc8 100644 --- a/src/License/License.php +++ b/src/License/License.php @@ -4,7 +4,6 @@ namespace CraftCms\Cms\License; -use CraftCms\Aliases\Aliases; use CraftCms\Cms\Cms; use CraftCms\Cms\Config\GeneralConfig; use CraftCms\Cms\Edition; @@ -323,7 +322,7 @@ private function statusIssueMismatched(LicenseData $licenseData, array $only): a $keyPath = File::normalizePath($this->keyPath(), '/'); // If the license key path starts with the root project path, trim the project path off - $rootPath = File::normalizePath(Aliases::get('@root'), '/'); + $rootPath = File::normalizePath(base_path(), '/'); if (str_starts_with($keyPath, $rootPath.'/')) { $keyPath = substr($keyPath, strlen($rootPath) + 1); } diff --git a/src/Plugin/Plugins.php b/src/Plugin/Plugins.php index 9979c479e90..82af0c379c3 100644 --- a/src/Plugin/Plugins.php +++ b/src/Plugin/Plugins.php @@ -4,9 +4,9 @@ namespace CraftCms\Cms\Plugin; -use CraftCms\Aliases\Aliases; use CraftCms\Cms\Cms; use CraftCms\Cms\Config\GeneralConfig; +use CraftCms\Cms\Cp\Icons; use CraftCms\Cms\Database\Table; use CraftCms\Cms\Edition; use CraftCms\Cms\License\License; @@ -893,14 +893,6 @@ public function createPlugin(string $handle, ?array $info = null): ?PluginInterf $config = $this->composerPluginInfo[$handle]; - if (isset($config['aliases'])) { - foreach ($config['aliases'] as $alias => $path) { - Aliases::set($alias, $path); - } - - unset($config['aliases']); - } - /** @var class-string|class-string $class */ $class = $config['class']; @@ -1125,7 +1117,6 @@ public function getPluginIconSvg(string $handle): string } if (($basePath = $this->composerPluginInfo[$handle]['basePath'] ?? false) !== false) { - $basePath = Aliases::get($basePath); $basePaths[] = $basePath; $basePaths[] = dirname($basePath).'/resources'; } @@ -1136,7 +1127,7 @@ public function getPluginIconSvg(string $handle): string ); if ($iconPath === null) { - $iconPath = Aliases::get('@appicons/default-plugin.svg'); + $iconPath = Icons::resolveIconPath('default-plugin'); } else { $iconPath .= '/icon.svg'; } diff --git a/src/Providers/AppServiceProvider.php b/src/Providers/AppServiceProvider.php index 35f674c41de..2724f86439d 100644 --- a/src/Providers/AppServiceProvider.php +++ b/src/Providers/AppServiceProvider.php @@ -4,7 +4,6 @@ namespace CraftCms\Cms\Providers; -use CraftCms\Aliases\Aliases; use CraftCms\Cms\Auth\Enums\CpAuthPath; use CraftCms\Cms\Cms; use CraftCms\Cms\Edition; @@ -16,7 +15,6 @@ use CraftCms\Cms\Support\CmsAssets; use CraftCms\Cms\Support\Env; use CraftCms\Cms\Support\Facades\Updates; -use CraftCms\Cms\Support\File; use CraftCms\Cms\Support\Url; use CraftCms\Cms\Update\Data\Update as UpdateData; use CraftCms\Cms\Update\Data\UpdateRelease; @@ -59,8 +57,6 @@ class AppServiceProvider extends ServiceProvider public static int $maxPasswordLength = UserPasswordRule::MAX_PASSWORD_LENGTH; - private string $root = __DIR__.'/../..'; - #[Override] public function register(): void { @@ -97,7 +93,6 @@ public function boot(): void ]); $this->setNamespace(); - $this->bootAliases(); Cms::setDefaultTimezone(); $this->app->booted(function () { @@ -122,6 +117,12 @@ public function boot(): void CmsAssets::resourcesPath('build') => public_path('vendor/craft/build'), CmsAssets::resourcesPath('legacy') => public_path('vendor/craft/legacy'), ], ['craftcms', 'craftcms-assets']); + + foreach (glob(CmsAssets::resourcesPath('icons/*'), GLOB_ONLYDIR) ?: [] as $path) { + $this->publishes([ + $path => public_path('vendor/craft/icons/'.basename($path)), + ], ['craftcms', 'craftcms-assets', 'craftcms-icons']); + } } private function registerMacros(): void @@ -243,30 +244,6 @@ private function setNamespace(): void } } - private function bootAliases(): void - { - Aliases::set('@root', Env::get('CRAFT_ROOT_PATH', $this->app->basePath())); - Aliases::set('@craftcms', File::normalizePath($this->root)); - Aliases::set('@cmsAssets', CmsAssets::packagePath()); - Aliases::set('@package', '@craftcms/src'); - Aliases::set('@resources', "{$this->root}/resources"); - Aliases::set('@vendor', '@root/vendor'); - Aliases::set('@storage', $this->app->storagePath()); - Aliases::set('@runtime', '@storage/runtime'); - - if (Aliases::get('@templates', false) === false) { - Aliases::set('@templates', is_dir($this->app->resourcePath('views')) - ? $this->app->resourcePath('views') - : $this->app->basePath('templates')); - } - - if ($webUrl = Env::get('CRAFT_WEB_URL')) { - Aliases::set('@web', $webUrl); - } else { - Aliases::set('@web', config('app.url')); - } - } - private function registerThrottleExceptionHandler(): void { $this->callAfterResolving(ExceptionHandler::class, function (ExceptionHandler $handler): void { diff --git a/src/Providers/CraftServiceProvider.php b/src/Providers/CraftServiceProvider.php index b981c02f4f1..31264ab1e10 100644 --- a/src/Providers/CraftServiceProvider.php +++ b/src/Providers/CraftServiceProvider.php @@ -49,7 +49,6 @@ class CraftServiceProvider extends AggregateServiceProvider LicenseServiceProvider::class, RouteServiceProvider::class, AppServiceProvider::class, - IconServiceProvider::class, ConsoleServiceProvider::class, ElementServiceProvider::class, EmailServiceProvider::class, diff --git a/src/Providers/IconServiceProvider.php b/src/Providers/IconServiceProvider.php deleted file mode 100644 index b476cc65a80..00000000000 --- a/src/Providers/IconServiceProvider.php +++ /dev/null @@ -1,75 +0,0 @@ -set('@icons', '@cmsAssets/resources/icons'); - $aliases->set('@appicons', '@icons/solid'); - - $customIconsPath = '@icons/custom-icons'; - - // Icons - $aliases->set('@appicons/c-debug.svg', "$customIconsPath/c-debug.svg"); - $aliases->set('@appicons/c-outline.svg', "$customIconsPath/c-outline.svg"); - $aliases->set('@appicons/craft-cms.svg', "$customIconsPath/craft-cms.svg"); - $aliases->set('@appicons/craft-partners.svg', "$customIconsPath/craft-partners.svg"); - $aliases->set('@appicons/craft-stack-exchange.svg', "$customIconsPath/craft-stack-exchange.svg"); - $aliases->set('@appicons/default-plugin.svg', "$customIconsPath/default-plugin.svg"); - - if (file_exists($aliasesFile = $aliases->get('@icons/aliases.php'))) { - require $aliasesFile; - } - - $solidIconsPath = '@icons/solid'; - - // Renamed icon aliases - $aliases->set('@appicons/alert.svg', "$solidIconsPath/triangle-exclamation.svg"); - $aliases->set('@appicons/broken-image', "$solidIconsPath/image-slash.svg"); - $aliases->set('@appicons/buoey.svg', "$solidIconsPath/life-ring.svg"); - $aliases->set('@appicons/draft.svg', "$solidIconsPath/scribble.svg"); - $aliases->set('@appicons/entry-types', "$solidIconsPath/files.svg"); - $aliases->set('@appicons/excite.svg', "$solidIconsPath/certificate.svg"); - $aliases->set('@appicons/feed.svg', "$solidIconsPath/rss.svg"); - $aliases->set('@appicons/field.svg', "$solidIconsPath/pen-to-square.svg"); - $aliases->set('@appicons/hash.svg', "$solidIconsPath/hashtag.svg"); - $aliases->set('@appicons/info-circle', "$solidIconsPath/circle-info.svg"); - $aliases->set('@appicons/info-circle.svg', "$solidIconsPath/circle-info.svg"); - $aliases->set('@appicons/info.svg', "$solidIconsPath/circle-info.svg"); - $aliases->set('@appicons/location.svg', "$solidIconsPath/location-dot.svg"); - $aliases->set('@appicons/photo.svg', "$solidIconsPath/image.svg"); - $aliases->set('@appicons/plugin.svg', "$solidIconsPath/plug.svg"); - $aliases->set('@appicons/routes.svg', "$solidIconsPath/signs-post.svg"); - $aliases->set('@appicons/search.svg', "$solidIconsPath/magnifying-glass.svg"); - $aliases->set('@appicons/shopping-cart', "$solidIconsPath/cart-shopping.svg"); - $aliases->set('@appicons/template.svg', "$solidIconsPath/file-code.svg"); - $aliases->set('@appicons/tip.svg', "$solidIconsPath/lightbulb.svg"); - $aliases->set('@appicons/tools.svg', "$solidIconsPath/screwdriver-wrench.svg"); - $aliases->set('@appicons/tree.svg', "$solidIconsPath/sitemap.svg"); - $aliases->set('@appicons/upgrade.svg', "$solidIconsPath/square-arrow-up.svg"); - $aliases->set('@appicons/wand.svg', "$solidIconsPath/wand-magic-sparkles.svg"); - $aliases->set('@appicons/world.svg', "$solidIconsPath/earth-americas.svg"); - - if (! $this->app->runningInConsole()) { - return; - } - - $iconsDir = $aliases->get('@icons'); - $icons = []; - - if ($iconsDir) { - foreach (glob("{$iconsDir}/*", GLOB_ONLYDIR) ?: [] as $path) { - $icons[$path] = public_path('vendor/craft/icons/'.basename($path)); - } - } - - $this->publishes($icons, ['craftcms', 'craftcms-assets', 'craftcms-icons']); - } -} diff --git a/src/Route/RouteServiceProvider.php b/src/Route/RouteServiceProvider.php index c4a75a5f6b0..36663fc2f87 100644 --- a/src/Route/RouteServiceProvider.php +++ b/src/Route/RouteServiceProvider.php @@ -34,6 +34,7 @@ use CraftCms\Cms\Http\Middleware\UseWriteConnection; use CraftCms\Cms\Route\Data\Route; use CraftCms\Cms\Site\Events\SiteDeleted; +use CraftCms\Cms\Support\Facades\Path; use CraftCms\Cms\Support\Facades\ProjectConfig; use CraftCms\Cms\Support\Str; use Illuminate\Cache\RateLimiting\Limit; @@ -85,7 +86,7 @@ public function boot(Router $router, Routes $routes): void $router->patterns($routes->tokens); $this->bootMiddleware($router); - $this->loadRoutesFrom(dirname(__DIR__).'/../routes/routes.php'); + $this->loadRoutesFrom(Path::package('routes/routes.php')); if (! $this->app->routesAreCached()) { $this->bootMaintenanceModeExceptions(); diff --git a/src/Support/Composer.php b/src/Support/Composer.php index f16edf49eb8..d4eed77454a 100644 --- a/src/Support/Composer.php +++ b/src/Support/Composer.php @@ -4,10 +4,10 @@ namespace CraftCms\Cms\Support; -use CraftCms\Aliases\Aliases; use CraftCms\Cms\Support\Facades\Path; use Illuminate\Container\Attributes\Singleton; use Illuminate\Contracts\Filesystem\FileNotFoundException; +use RuntimeException; use Symfony\Component\Process\Exception\ProcessFailedException; use Symfony\Component\Process\ExecutableFinder; use Symfony\Component\Process\Process; @@ -42,7 +42,7 @@ public function getJsonPath(): string return CRAFT_COMPOSER_PATH; } - $jsonPath = File::normalizePath(Aliases::get('@root/composer.json')); + $jsonPath = File::normalizePath(base_path('composer.json')); if (! File::exists($jsonPath)) { throw new FileNotFoundException("No Composer config found at $jsonPath."); @@ -164,23 +164,11 @@ public function uninstall(array $packages, ?callable $callback = null): void */ private function runComposerCommand(string $jsonPath, array $command, ?callable $callback): void { - $composerPath = new ExecutableFinder()->find('composer'); - $pharPath = ''; - - if (! $composerPath) { - $runtimePath = Path::runtime(); - - // Copy composer.phar into the runtime folder - $pharPath = join_paths($runtimePath, 'composer.phar'); - copy(Aliases::get('@lib/composer.phar'), $pharPath); - - $homePath = join_paths($runtimePath, 'composer'); - File::ensureDirectoryExists($homePath); - } + [$composerPath, $homePath, $temporaryPath] = $this->composerCommand(); $command = array_merge([ PHP::executable() ?? 'php', - $composerPath ?? $pharPath, + $composerPath, ], $command, [ '--working-dir', dirname($jsonPath), @@ -196,8 +184,8 @@ private function runComposerCommand(string $jsonPath, array $command, ?callable ->setTimeout(null) ->mustRun($callback); } finally { - if ($pharPath && File::exists($pharPath)) { - File::delete($pharPath); + if ($temporaryPath !== null && File::exists($temporaryPath)) { + File::delete($temporaryPath); } } @@ -207,6 +195,18 @@ private function runComposerCommand(string $jsonPath, array $command, ?callable } } + /** @return array{string, string|null, string|null} */ + protected function composerCommand(): array + { + $composerPath = new ExecutableFinder()->find('composer'); + + if ($composerPath === null) { + throw new RuntimeException('Composer could not be found.'); + } + + return [$composerPath, null, null]; + } + /** * Ensures composer.craftcms.com is listed as a repository in composer.json */ diff --git a/src/Support/Env.php b/src/Support/Env.php index bf15d803be8..983b4082e51 100644 --- a/src/Support/Env.php +++ b/src/Support/Env.php @@ -4,7 +4,6 @@ namespace CraftCms\Cms\Support; -use CraftCms\Aliases\Aliases; use CraftCms\Cms\Support\Attributes\EnvName; use Illuminate\Contracts\Filesystem\FileNotFoundException; use Illuminate\Filesystem\Filesystem; @@ -77,7 +76,7 @@ public static function configValue(string $key, array $fallbackEnvs = []): ?stri /** * Checks if a string references an environment variable (`$VARIABLE_NAME`) - * and/or an alias (`@aliasName`), and returns the referenced value. + * and returns the referenced value. * * If the string references an environment variable with a value of `true` * or `false`, a boolean value will be returned. @@ -89,11 +88,10 @@ public static function configValue(string $key, array $fallbackEnvs = []): ?stri * * ```php * $value1 = Env::parse('$SMTP_PASSWORD'); - * $value2 = Env::parse('@webroot'); * ``` * * @return string|null The parsed value, or the original value if it didn’t - * reference an environment variable and/or alias. + * reference an environment variable. */ public static function parse(?string $value): ?string { @@ -123,10 +121,6 @@ function ($m) { return null; } - if (str_starts_with((string) $value, '@') && $alias = Aliases::get($value)) { - return $alias; - } - return $value; } diff --git a/src/Support/Facades/Path.php b/src/Support/Facades/Path.php index 6dd097f66cc..1aa4b095bbd 100644 --- a/src/Support/Facades/Path.php +++ b/src/Support/Facades/Path.php @@ -27,6 +27,8 @@ * @method static string imageTransforms(string $path = '', bool $create = true) * @method static string pluginIcons(string $path = '', bool $create = true) * @method static string logs(string $path = '', bool $create = true) + * @method static string package(string $path = '') + * @method static string resources(string $path = '') * @method static string cpTranslations(string $path = '') * @method static string siteTranslations(string $path = '') * @method static string cpTemplates(string $path = '') diff --git a/src/Support/Html.php b/src/Support/Html.php index 20a2ffd0209..bed1eca55b8 100644 --- a/src/Support/Html.php +++ b/src/Support/Html.php @@ -4,7 +4,6 @@ namespace CraftCms\Cms\Support; -use CraftCms\Aliases\Aliases; use CraftCms\Cms\Asset\Elements\Asset; use CraftCms\Cms\Cms; use CraftCms\Cms\Http\Middleware\SetHeaders; @@ -1184,10 +1183,10 @@ public static function dataUrl(string $file, ?string $mimeType = null): string throw new InvalidArgumentException("Invalid file path: $file"); } - $file = File::absolutePath(Aliases::get($file), '/'); + $file = File::absolutePath($file, '/'); // make sure it's contained within the project root - $rootPath = File::absolutePath(Aliases::get('@root'), '/'); + $rootPath = File::absolutePath(base_path(), '/'); if (! str_starts_with($file, "$rootPath/")) { throw new InvalidArgumentException(sprintf('%s cannot be passed a path outside of the project root.', __METHOD__)); } @@ -1328,17 +1327,6 @@ public static function svg( } } elseif (stripos($svg, ' tag, so it's probably a file path - try { - $svg = Aliases::get($svg); - } catch (InvalidArgumentException $e) { - if ($throwException) { - throw $e; - } - Log::error("Could not get the contents of $svg: {$e->getMessage()}", [__METHOD__]); - report($e); - - return ''; - } if (! is_file($svg) || ! File::isSvg($svg)) { if ($throwException) { throw new InvalidArgumentException("Invalid SVG path: $svg"); diff --git a/src/Support/Json.php b/src/Support/Json.php index 50b654fbad0..23cc973fe80 100644 --- a/src/Support/Json.php +++ b/src/Support/Json.php @@ -4,7 +4,6 @@ namespace CraftCms\Cms\Support; -use CraftCms\Aliases\Aliases; use InvalidArgumentException; use Throwable; @@ -84,8 +83,6 @@ public static function decodeIfJson(mixed $str, bool $asArray = true): mixed */ public static function decodeFromFile(string $file, bool $asArray = true): mixed { - $file = Aliases::get($file); - if (! File::exists($file)) { throw new InvalidArgumentException("`$file` doesn’t exist."); } diff --git a/src/Support/Path.php b/src/Support/Path.php index b36cac86000..91535968d33 100644 --- a/src/Support/Path.php +++ b/src/Support/Path.php @@ -4,12 +4,12 @@ namespace CraftCms\Cms\Support; -use CraftCms\Aliases\Aliases; use CraftCms\Cms\Config\GeneralConfig; use CraftCms\Cms\License\License; use CraftCms\Cms\ProjectConfig\ProjectConfig; use Illuminate\Container\Attributes\Singleton; use Illuminate\Contracts\Foundation\Application; +use InvalidArgumentException; use function Illuminate\Filesystem\join_paths; @@ -56,14 +56,14 @@ public function projectConfig(string $path = '', bool $create = true): string public function storage(string $path = '', bool $create = true): string { - $this->storagePath ??= $this->aliasOrDefault('@storage', $this->app->storagePath()); + $this->storagePath ??= File::normalizePath($this->app->storagePath()); return $this->directory($this->storagePath, $path, $create); } public function tests(string $path = ''): string { - $this->testsPath ??= $this->aliasOrDefault('@tests', $this->app->basePath('tests')); + $this->testsPath ??= File::normalizePath($this->app->basePath('tests')); return $this->path($this->testsPath, $path); } @@ -85,7 +85,7 @@ public function configDelta(string $path = '', bool $create = true): string public function vendor(string $path = ''): string { - $this->vendorPath ??= $this->aliasOrDefault('@vendor', $this->app->basePath('vendor')); + $this->vendorPath ??= File::normalizePath($this->app->basePath('vendor')); return $this->path($this->vendorPath, $path); } @@ -145,26 +145,42 @@ public function logs(string $path = '', bool $create = true): string return $this->subdirectory($this->storage(create: $create), 'logs', $path, $create); } + public function package(string $path = ''): string + { + return $this->path(File::normalizePath(__DIR__.'/../..'), $path); + } + + public function resources(string $path = ''): string + { + return $this->path($this->package('resources'), $path); + } + public function cpTranslations(string $path = ''): string { - return $this->path(File::normalizePath(Aliases::get('@craftcms/resources/translations')), $path); + return $this->path($this->resources('translations'), $path); } public function siteTranslations(string $path = ''): string { - $this->siteTranslationsPath ??= $this->aliasOrDefault('@translations', $this->app->langPath()); + $this->siteTranslationsPath ??= File::normalizePath($this->app->langPath()); return $this->path($this->siteTranslationsPath, $path); } public function cpTemplates(string $path = ''): string { - return $this->path(File::normalizePath(Aliases::get('@craftcms/resources/templates')), $path); + return $this->path($this->resources('templates'), $path); } public function siteTemplates(string $path = ''): string { - return $this->path($this->aliasOrDefault('@templates', $this->app->basePath('templates')), $path); + $templatesPath = config('view.paths.0'); + + if (! is_string($templatesPath) || ! is_dir($templatesPath)) { + $templatesPath = $this->app->basePath('templates'); + } + + return $this->path(File::normalizePath($templatesPath), $path); } public function compiledClasses(string $path = '', bool $create = true): string @@ -178,6 +194,10 @@ public function compiledTemplates(string $path = '', bool $create = true): strin $compiledTemplatesPath = Env::parse($this->generalConfig->compiledTemplatesPath); if ($compiledTemplatesPath !== null && $compiledTemplatesPath !== '') { + if (str_starts_with($compiledTemplatesPath, '@')) { + throw new InvalidArgumentException('Path aliases require craftcms/yii2-adapter. Use an absolute path instead.'); + } + return $this->directory( File::normalizePath($compiledTemplatesPath), $path, @@ -208,8 +228,7 @@ public function licenseKey(): string public function system(): array { return [ - $this->aliasOrDefault('@contentMigrations', $this->app->basePath('migrations')), - $this->aliasOrDefault('@lib', $this->app->basePath('yii2-adapter/lib')), + $this->app->basePath('migrations'), $this->composerBackups(create: false), $this->configBackup(create: false), $this->configDelta(create: false), @@ -250,11 +269,6 @@ public static function ensurePathIsContained(string $path): bool return true; } - private function aliasOrDefault(string $alias, string $path): string - { - return File::normalizePath(Aliases::get($alias, false) ?: $path); - } - private function path(string $basePath, string $path = ''): string { return $path === '' ? $basePath : File::normalizePath(join_paths($basePath, $path)); diff --git a/src/Support/Url.php b/src/Support/Url.php index dd8f944e88a..d3aad8b32c1 100644 --- a/src/Support/Url.php +++ b/src/Support/Url.php @@ -4,7 +4,6 @@ namespace CraftCms\Cms\Support; -use CraftCms\Aliases\Aliases; use CraftCms\Cms\Cms; use CraftCms\Cms\Cp\RequestedSite; use CraftCms\Cms\RouteToken\RouteTokens; @@ -452,8 +451,7 @@ public static function baseSiteUrl(): string } } - // Use @web as a fallback - return Aliases::get('@web'); + return rtrim((string) config('app.url'), '/').'/'; } /** @@ -472,11 +470,9 @@ public static function baseCpUrl(): string private static function fallbackBaseUrl(): string { - // Use @web as a fallback, unless it's a console request and @web was defined dynamically, - // in which case it's totally unreliable so go with the base site URL return app()->runningInConsole() ? static::baseSiteUrl() - : Aliases::get('@web'); + : url('/'); } /** @@ -622,7 +618,7 @@ private static function _createUrl( // Put it all together if ($path) { - $url = rtrim($baseUrl, '/').'/'.trim((string) $path, '/'); + $url = rtrim($baseUrl, '/').'/'.trim($path, '/'); if (! $cpUrl && $generalConfig->addTrailingSlashesToUrls && ! preg_match('/\.[^\/]+$/', $url)) { $url .= '/'; diff --git a/src/Translation/TranslationServiceProvider.php b/src/Translation/TranslationServiceProvider.php index b1aee2cbcea..9c825e2ef45 100644 --- a/src/Translation/TranslationServiceProvider.php +++ b/src/Translation/TranslationServiceProvider.php @@ -4,6 +4,7 @@ namespace CraftCms\Cms\Translation; +use CraftCms\Cms\Support\Facades\Path; use Illuminate\Contracts\Config\Repository as ConfigRepository; use Illuminate\Support\ServiceProvider; use Yiisoft\Translator\CategorySource; @@ -29,7 +30,7 @@ public function boot(): void $this->callAfterResolving(I18N::class, function (I18N $i18n): void { $appCategory = new CategorySource( name: 'app', - reader: new MessageSource(dirname(__DIR__, 2).'/resources/translations'), + reader: new MessageSource(Path::resources('translations')), formatter: new IntlMessageFormatter, ); diff --git a/src/Twig/Extensions/CoreTwigExtension.php b/src/Twig/Extensions/CoreTwigExtension.php index db20b0c5552..1399f8fa21c 100644 --- a/src/Twig/Extensions/CoreTwigExtension.php +++ b/src/Twig/Extensions/CoreTwigExtension.php @@ -5,7 +5,6 @@ namespace CraftCms\Cms\Twig\Extensions; use CommerceGuys\Addressing\Formatter\FormatterInterface; -use CraftCms\Aliases\Aliases; use CraftCms\Cms\Address\Addresses; use CraftCms\Cms\Address\Elements\Address; use CraftCms\Cms\Component\Component; @@ -263,7 +262,6 @@ public function getFunctions(): array { return [ new TwigFunction('actionUrl', Url::actionUrl(...)), - new TwigFunction('alias', Aliases::get(...)), new TwigFunction('asset', asset(...)), new TwigFunction('ceil', 'ceil'), new TwigFunction('className', 'get_class'), diff --git a/src/Twig/Extensions/CpExtension.php b/src/Twig/Extensions/CpExtension.php index 34c87ce6fc4..164bfb8824c 100644 --- a/src/Twig/Extensions/CpExtension.php +++ b/src/Twig/Extensions/CpExtension.php @@ -83,9 +83,9 @@ public function envOptions(?array $allowedValues = null): array } /** @return list> */ - public function envSuggestions(bool $includeAliases = false, ?callable $filter = null): array + public function envSuggestions(?callable $filter = null): array { - return $this->formatLegacySuggestions(SelectOptions::getEnvSuggestions($includeAliases, $filter)); + return $this->formatLegacySuggestions(SelectOptions::getEnvSuggestions($filter)); } /** @return list> */ diff --git a/src/Twig/Extensions/HtmlTwigExtension.php b/src/Twig/Extensions/HtmlTwigExtension.php index 0108aa08e7b..72934629dfe 100644 --- a/src/Twig/Extensions/HtmlTwigExtension.php +++ b/src/Twig/Extensions/HtmlTwigExtension.php @@ -4,7 +4,6 @@ namespace CraftCms\Cms\Twig\Extensions; -use CraftCms\Aliases\Aliases; use CraftCms\Cms\Asset\Elements\Asset; use CraftCms\Cms\Asset\Exceptions\AssetException; use CraftCms\Cms\Field\Data\MarkdownData; @@ -219,7 +218,7 @@ public function dataUrlFunction(Asset|string $file, ?string $mimeType = null): s return $file->getDataUrl(); } - return Html::dataUrl(Aliases::get($file), $mimeType); + return Html::dataUrl($file, $mimeType); } catch (InvalidArgumentException $e) { Log::warning($e->getMessage(), [__METHOD__]); diff --git a/src/Twig/Variables/Cp.php b/src/Twig/Variables/Cp.php index 0c012da24a0..5cad3914bdf 100644 --- a/src/Twig/Variables/Cp.php +++ b/src/Twig/Variables/Cp.php @@ -173,8 +173,6 @@ public function trialInfo(): ?array * Returns the available environment variable and alias suggestions for * inputs that support them. * - * @param bool $includeAliases Whether aliases should be included in the list - * (only enable this if the setting defines a URL or file path) * @param callable|null $filter A function that returns whether a given value should be included * * @phpstan-param callable(scalar):bool|null $filter @@ -182,9 +180,9 @@ public function trialInfo(): ?array * @return list> */ #[Deprecated(message: 'in 6.0.0. [[\CraftCms\Cms\Cp\SelectOptions::getEnvSuggestions]] should be used instead.')] - public function getEnvSuggestions(bool $includeAliases = false, ?callable $filter = null): array + public function getEnvSuggestions(?callable $filter = null): array { - return $this->formatLegacySuggestions(SelectOptions::getEnvSuggestions($includeAliases, $filter)); + return $this->formatLegacySuggestions(SelectOptions::getEnvSuggestions($filter)); } /** diff --git a/src/Utility/Utilities/ClearCaches.php b/src/Utility/Utilities/ClearCaches.php index 75d907da1d1..955e9b008fe 100644 --- a/src/Utility/Utilities/ClearCaches.php +++ b/src/Utility/Utilities/ClearCaches.php @@ -5,7 +5,6 @@ namespace CraftCms\Cms\Utility\Utilities; use Closure; -use CraftCms\Aliases\Aliases; use CraftCms\Cms\Cms; use CraftCms\Cms\Database\Table; use CraftCms\Cms\Support\Arr; @@ -207,7 +206,7 @@ private static function defaultCacheOptions(): array 'key' => 'compiled-templates', 'label' => t('Compiled templates'), 'info' => t('Contents of {path}', [ - 'path' => sprintf('`%s/`', File::relativePath($pathService->compiledTemplates(create: false), Aliases::get('@root'))), + 'path' => sprintf('`%s/`', File::relativePath($pathService->compiledTemplates(create: false), base_path())), ]), 'action' => $pathService->compiledTemplates(create: false), ], @@ -215,7 +214,7 @@ private static function defaultCacheOptions(): array 'key' => 'compiled-classes', 'label' => t('Compiled classes'), 'info' => t('Contents of {path}', [ - 'path' => sprintf('`%s/`', File::relativePath($pathService->compiledClasses(create: false), Aliases::get('@root'))), + 'path' => sprintf('`%s/`', File::relativePath($pathService->compiledClasses(create: false), base_path())), ]), 'action' => $pathService->compiledClasses(create: false), ], @@ -223,7 +222,7 @@ private static function defaultCacheOptions(): array 'key' => 'temp-files', 'label' => t('Temp files'), 'info' => t('Contents of {path}', [ - 'path' => sprintf('`%s/`', File::relativePath($pathService->temp(), Aliases::get('@root'))), + 'path' => sprintf('`%s/`', File::relativePath($pathService->temp(), base_path())), ]), 'action' => $pathService->temp(), ], diff --git a/src/Utility/Utilities/SystemReport.php b/src/Utility/Utilities/SystemReport.php index 7e6e4177161..d420a751237 100644 --- a/src/Utility/Utilities/SystemReport.php +++ b/src/Utility/Utilities/SystemReport.php @@ -5,7 +5,6 @@ namespace CraftCms\Cms\Utility\Utilities; use Composer\InstalledVersions; -use CraftCms\Aliases\Aliases; use CraftCms\Cms\Cms; use CraftCms\Cms\Database\ConnectionConfig; use CraftCms\Cms\Edition; @@ -49,24 +48,9 @@ public static function icon(): string #[Override] public static function contentHtml(): string { - $aliases = []; - foreach (Aliases::getAll() as $alias => $value) { - if (is_array($value)) { - foreach ($value as $a => $v) { - if (! str_starts_with((string) $a, '@appicons/')) { - $aliases[$a] = $v; - } - } - } elseif (! str_starts_with((string) $alias, '@appicons/')) { - $aliases[$alias] = $value; - } - } - ksort($aliases); - return template('_components/utilities/SystemReport', [ 'appInfo' => self::appInfo(), 'plugins' => app(Plugins::class)->getAllPlugins(), - 'aliases' => $aliases, 'requirements' => self::requirementResults(), ]); } diff --git a/src/View/TemplateMode.php b/src/View/TemplateMode.php index d0af7bfe80a..b5857638c4b 100644 --- a/src/View/TemplateMode.php +++ b/src/View/TemplateMode.php @@ -38,8 +38,8 @@ public static function set(self $mode): void * work without a 'craftcms::' prefix. */ if (TemplateMode::is(TemplateMode::Cp)) { - $templates = dirname(__DIR__, 2).'/resources/templates'; - $views = dirname(__DIR__, 2).'/resources/views'; + $templates = Path::resources('templates'); + $views = Path::resources('views'); /** @var FileViewFinder $finder */ $finder = view()->getFinder(); diff --git a/src/View/ViewServiceProvider.php b/src/View/ViewServiceProvider.php index a8be81aca8d..1edade41558 100644 --- a/src/View/ViewServiceProvider.php +++ b/src/View/ViewServiceProvider.php @@ -12,6 +12,7 @@ use CraftCms\Cms\Blade\Directives\PaginationDirective; use CraftCms\Cms\Blade\Directives\ResourceDirective; use CraftCms\Cms\Blade\Directives\ResponseDirective; +use CraftCms\Cms\Support\Facades\Path; use CraftCms\Cms\View\Events\ViewAssetsRendering; use CraftCms\Cms\View\LegacyAssets\InternalAssetRegistry; use Illuminate\Contracts\View\Factory as ViewFactory; @@ -25,14 +26,9 @@ class ViewServiceProvider extends ServiceProvider { - private string $root = __DIR__.'/../..'; - #[Override] public function register(): void { - $this->loadViewsFrom("{$this->root}/resources/templates", 'craftcms'); - $this->loadViewsFrom("{$this->root}/resources/views", 'c'); - $this->app->make(ViewFactory::class)->addExtension( 'twig', 'twig', @@ -42,6 +38,9 @@ public function register(): void public function boot(): void { + $this->loadViewsFrom(Path::resources('templates'), 'craftcms'); + $this->loadViewsFrom(Path::resources('views'), 'c'); + Event::listen(function (ViewAssetsRendering $event) { app(InternalAssetRegistry::class)->flush(); }); diff --git a/testbench.yaml b/testbench.yaml index f61b1625f09..5b0b59c3871 100644 --- a/testbench.yaml +++ b/testbench.yaml @@ -2,7 +2,6 @@ providers: - Inertia\ServiceProvider - Laravel\Tinker\TinkerServiceProvider - Laravel\Socialite\SocialiteServiceProvider - - CraftCms\Aliases\AliasesServiceProvider - CraftCms\DependencyAwareCache\CacheServiceProvider - CraftCms\Cms\Providers\CraftServiceProvider - Laravel\Wayfinder\WayfinderServiceProvider diff --git a/tests/Feature/Cp/SelectOptionsTest.php b/tests/Feature/Cp/SelectOptionsTest.php index bc24255d50a..20ce52b3d90 100644 --- a/tests/Feature/Cp/SelectOptionsTest.php +++ b/tests/Feature/Cp/SelectOptionsTest.php @@ -18,16 +18,6 @@ ->and($suggestions[0]['options'])->toBeArray(); }); - it('returns suggestions with aliases when includeAliases is true', function () { - $suggestions = SelectOptions::getEnvSuggestions(includeAliases: true); - - expect($suggestions)->toHaveCount(2) - ->and($suggestions[0])->toHaveKey('label') - ->and($suggestions[1])->toHaveKey('label') - ->and($suggestions[0]['options'])->toBeArray() - ->and($suggestions[1]['options'])->toBeArray(); - }); - it('filters suggestions based on filter callback', function () { $_SERVER['TEST_SHORT_ENV'] = 'no'; $_SERVER['TEST_LONG_ENV'] = 'longer'; @@ -60,17 +50,6 @@ unset($_SERVER['TEST_NON_HTTP_VAR'], $_SERVER['HTTP_TEST_VAR']); }); - it('excludes @web aliases when includeAliases is true', function () { - $suggestions = SelectOptions::getEnvSuggestions(includeAliases: true); - - if (isset($suggestions[1]['options'])) { - foreach ($suggestions[1]['options'] as $suggestion) { - expect($suggestion['label'])->not->toBe('@web') - ->and($suggestion['label'])->not->toStartWith('@web/'); - } - } - }); - it('formats env var names with $ prefix', function () { $_SERVER['TEST_FORMAT_ENV_VAR'] = 'test'; diff --git a/tests/Feature/Http/Controllers/IconControllerTest.php b/tests/Feature/Http/Controllers/IconControllerTest.php index f641e4b71ce..3d6f289b921 100644 --- a/tests/Feature/Http/Controllers/IconControllerTest.php +++ b/tests/Feature/Http/Controllers/IconControllerTest.php @@ -2,8 +2,8 @@ declare(strict_types=1); -use CraftCms\Aliases\Aliases; use CraftCms\Cms\Http\Controllers\IconController; +use CraftCms\Cms\Support\CmsAssets; use CraftCms\Cms\User\Elements\User; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\File; @@ -16,10 +16,10 @@ beforeEach(function () { actingAs(User::findOne()); - File::ensureDirectoryExists(Aliases::get('@cmsAssets/resources/icons/solid')); - File::ensureDirectoryExists(Aliases::get('@cmsAssets/resources/icons/custom-icons')); + File::ensureDirectoryExists(CmsAssets::resourcesPath('icons/solid')); + File::ensureDirectoryExists(CmsAssets::resourcesPath('icons/custom-icons')); - $indexPath = Aliases::get('@cmsAssets/resources/icons/index.php'); + $indexPath = CmsAssets::resourcesPath('icons/index.php'); if (! File::exists($indexPath)) { File::put($indexPath, <<<'php_WRAP' @@ -42,18 +42,18 @@ } // Free - if (! File::exists(Aliases::get('@cmsAssets/resources/icons/solid/gear.svg'))) { - File::put(Aliases::get('@cmsAssets/resources/icons/solid/gear.svg'), ''); + if (! File::exists(CmsAssets::resourcesPath('icons/solid/gear.svg'))) { + File::put(CmsAssets::resourcesPath('icons/solid/gear.svg'), ''); } // Pro - if (! File::exists(Aliases::get('@cmsAssets/resources/icons/solid/00.svg'))) { - File::put(Aliases::get('@cmsAssets/resources/icons/solid/00.svg'), ''); + if (! File::exists(CmsAssets::resourcesPath('icons/solid/00.svg'))) { + File::put(CmsAssets::resourcesPath('icons/solid/00.svg'), ''); } // Custom - if (! File::exists(Aliases::get('@cmsAssets/resources/icons/custom-icons/element-card.svg'))) { - File::put(Aliases::get('@cmsAssets/resources/icons/custom-icons/element-card.svg'), ''); + if (! File::exists(CmsAssets::resourcesPath('icons/custom-icons/element-card.svg'))) { + File::put(CmsAssets::resourcesPath('icons/custom-icons/element-card.svg'), ''); } }); diff --git a/tests/Feature/Http/Controllers/SiteRouteControllerTest.php b/tests/Feature/Http/Controllers/SiteRouteControllerTest.php index a81962834b2..48e2d0e3f33 100644 --- a/tests/Feature/Http/Controllers/SiteRouteControllerTest.php +++ b/tests/Feature/Http/Controllers/SiteRouteControllerTest.php @@ -2,7 +2,6 @@ declare(strict_types=1); -use CraftCms\Aliases\Aliases; use CraftCms\Cms\Cms; use CraftCms\Cms\Database\Table; use CraftCms\Cms\Element\Contracts\ElementInterface; @@ -30,7 +29,7 @@ beforeEach(function () { Cms::config()->isSystemLive = true; TemplateMode::set(TemplateMode::Site); - Aliases::set('@templates', dirname(__DIR__, 3).'/Support/templates'); + config(['view.paths' => [dirname(__DIR__, 3).'/Support/templates']]); app(TemplateRoots::class)->register(TemplateMode::Site, '', dirname(__DIR__, 3).'/Support/templates'); diff --git a/tests/Feature/Http/Middleware/HandleTemplateRequestTest.php b/tests/Feature/Http/Middleware/HandleTemplateRequestTest.php index 4ded5cbf84a..cc6d0b19617 100644 --- a/tests/Feature/Http/Middleware/HandleTemplateRequestTest.php +++ b/tests/Feature/Http/Middleware/HandleTemplateRequestTest.php @@ -2,7 +2,6 @@ declare(strict_types=1); -use CraftCms\Aliases\Aliases; use CraftCms\Cms\Cms; use CraftCms\Cms\View\TemplateMode; use Illuminate\Support\Facades\File; @@ -16,7 +15,7 @@ File::ensureDirectoryExists($this->tempDir); File::put($this->tempDir.'/index.twig', 'homepage-template'); - Aliases::set('@templates', $this->tempDir); + config(['view.paths' => [$this->tempDir]]); TemplateMode::set(TemplateMode::Site); }); diff --git a/tests/Feature/Http/Middleware/ShowBrokenImageTest.php b/tests/Feature/Http/Middleware/ShowBrokenImageTest.php index c59c30a84e0..5951a631bb0 100644 --- a/tests/Feature/Http/Middleware/ShowBrokenImageTest.php +++ b/tests/Feature/Http/Middleware/ShowBrokenImageTest.php @@ -2,7 +2,6 @@ declare(strict_types=1); -use CraftCms\Aliases\Aliases; use CraftCms\Cms\Cms; use CraftCms\Cms\Http\Middleware\ShowBrokenImage; use Illuminate\Http\Request; @@ -16,8 +15,7 @@ File::ensureDirectoryExists(dirname($this->imagePath)); File::put($this->imagePath, ''); - Cms::config()->brokenImagePath = '@tests/broken-image-test.svg'; - Aliases::set('@tests', dirname($this->imagePath)); + Cms::config()->brokenImagePath = $this->imagePath; }); afterEach(function () { @@ -74,10 +72,19 @@ }); it('throws when the configured broken image path is invalid', function () { - Cms::config()->brokenImagePath = '@tests/does-not-exist.svg'; + Cms::config()->brokenImagePath = storage_path('framework/testing/does-not-exist.svg'); $request = Request::create('/missing'); $request->headers->set('Accept', 'image/svg+xml'); $this->middleware->handle($request, fn () => new Response('missing', 404)); -})->throws(RuntimeException::class, 'Invalid broken image path: @tests/does-not-exist.svg'); +})->throws(RuntimeException::class, 'Invalid broken image path:'); + +it('rejects an alias as the configured broken image path', function () { + Cms::config()->brokenImagePath = '@storage/broken-image.svg'; + + $request = Request::create('/missing'); + $request->headers->set('Accept', 'image/svg+xml'); + + $this->middleware->handle($request, fn () => new Response('missing', 404)); +})->throws(RuntimeException::class, 'Path aliases require craftcms/yii2-adapter.'); diff --git a/tests/Feature/Support/ComposerTest.php b/tests/Feature/Support/ComposerTest.php index 704fcad0ee0..a46ac5eb957 100644 --- a/tests/Feature/Support/ComposerTest.php +++ b/tests/Feature/Support/ComposerTest.php @@ -2,7 +2,6 @@ declare(strict_types=1); -use CraftCms\Aliases\Aliases; use CraftCms\Cms\Support\Composer; use CraftCms\Cms\Support\File; use Illuminate\Contracts\Filesystem\FileNotFoundException; @@ -16,9 +15,14 @@ }); it('throws a FileNotFoundException when composer.json does not exist', function () { - Aliases::set('@root/composer.json', __DIR__.'/composer.json'); + $basePath = app()->basePath(); + app()->setBasePath(__DIR__); - expect($this->composer->getJsonPath()); + try { + expect($this->composer->getJsonPath()); + } finally { + app()->setBasePath($basePath); + } })->throws(FileNotFoundException::class); it('can get the composer.lock path', function () { diff --git a/tests/Feature/Support/EnvTest.php b/tests/Feature/Support/EnvTest.php index 953190d7817..37a01bde6c5 100644 --- a/tests/Feature/Support/EnvTest.php +++ b/tests/Feature/Support/EnvTest.php @@ -2,7 +2,6 @@ declare(strict_types=1); -use CraftCms\Aliases\Aliases; use CraftCms\Cms\Config\GeneralConfig; use CraftCms\Cms\Support\Env; use CraftCms\Cms\Support\Facades\Sites; @@ -118,7 +117,7 @@ expect(Env::parse('$CRAFT_TESTS_PATH'))->toBe(CRAFT_TESTS_PATH); expect(Env::parse('$CRAFT_TESTS_PATH/foo/bar'))->toBe(CRAFT_TESTS_PATH.'/foo/bar'); expect(Env::parse('CRAFT_TESTS_PATH'))->toBe('CRAFT_TESTS_PATH'); - expect(Env::parse('@vendor/foo/bar'))->toBe(Aliases::get('@vendor/foo/bar')); + expect(Env::parse('@vendor/foo/bar'))->toBe('@vendor/foo/bar'); expect(Env::parse('$TEST_MISSING'))->toBeNull(); expect(Env::parse(null))->toBeNull(); diff --git a/tests/Feature/Support/JsonTest.php b/tests/Feature/Support/JsonTest.php index 56bcd6b3d01..2a63b55a494 100644 --- a/tests/Feature/Support/JsonTest.php +++ b/tests/Feature/Support/JsonTest.php @@ -2,7 +2,6 @@ declare(strict_types=1); -use CraftCms\Aliases\Aliases; use CraftCms\Cms\Support\Json; use Illuminate\Support\Facades\File; @@ -38,11 +37,13 @@ }); test('decodeFromFile', function () { - File::put(Aliases::get('@runtime/test.json'), '{"test":"test"}'); + $path = storage_path('runtime/test.json'); + File::ensureDirectoryExists(dirname($path)); + File::put($path, '{"test":"test"}'); - expect(Json::decodeFromFile('@runtime/test.json'))->toEqualCanonicalizing(['test' => 'test']); + expect(Json::decodeFromFile($path))->toEqualCanonicalizing(['test' => 'test']); - File::delete(Aliases::get('@runtime/test.json')); + File::delete($path); }); test('detectIndent', function (string $expected, string $json) { diff --git a/tests/Feature/Support/PathTest.php b/tests/Feature/Support/PathTest.php index d2cde96c876..dfe20a1c76e 100644 --- a/tests/Feature/Support/PathTest.php +++ b/tests/Feature/Support/PathTest.php @@ -2,7 +2,6 @@ declare(strict_types=1); -use CraftCms\Aliases\Aliases; use CraftCms\Cms\Config\GeneralConfig; use CraftCms\Cms\License\License; use CraftCms\Cms\ProjectConfig\ProjectConfig; @@ -12,207 +11,135 @@ beforeEach(function () { $this->sandboxPath = File::normalizePath(storage_path('framework/testing/path-service/'.uniqid('', true))); - $ds = DIRECTORY_SEPARATOR; - $this->aliases = [ - '@storage' => $this->sandboxPath.$ds.'storage', - '@tests' => $this->sandboxPath.$ds.'tests', - '@vendor' => $this->sandboxPath.$ds.'vendor', - '@templates' => $this->sandboxPath.$ds.'templates', - '@translations' => $this->sandboxPath.$ds.'translations', - '@contentMigrations' => $this->sandboxPath.$ds.'content-migrations', - '@lib' => $this->sandboxPath.$ds.'lib', - ]; - - File::ensureDirectoryExists($this->sandboxPath); - - $this->originalAliases = []; - foreach ($this->aliases as $alias => $path) { - $this->originalAliases[$alias] = Aliases::get($alias, false) ?: ''; - Aliases::set($alias, $path); - File::ensureDirectoryExists($path); - } - + $this->originalStoragePath = app()->storagePath(); + $this->originalLangPath = app()->langPath(); + $this->originalViewPaths = config('view.paths'); $this->originalProjectConfigFolderName = app(ProjectConfig::class)->folderName; - app(ProjectConfig::class)->folderName = 'project'; $this->originalCompiledTemplatesPath = app(GeneralConfig::class)->compiledTemplatesPath; - $this->laravelPath = function (): CraftCms\Cms\Support\Path { - $laravelPathClass = CraftCms\Cms\Support\Path::class; + app()->useStoragePath($this->sandboxPath.'/storage'); + app()->useLangPath($this->sandboxPath.'/translations'); + config(['view.paths' => [$this->sandboxPath.'/templates']]); + + foreach (['storage', 'translations', 'templates'] as $directory) { + File::ensureDirectoryExists($this->sandboxPath."/$directory"); + } + + app(ProjectConfig::class)->folderName = 'project'; - app()->forgetInstance($laravelPathClass); - Facade::clearResolvedInstance($laravelPathClass); + $this->path = function (): CraftCms\Cms\Support\Path { + app()->forgetInstance(CraftCms\Cms\Support\Path::class); + Facade::clearResolvedInstance(CraftCms\Cms\Support\Path::class); - return app($laravelPathClass); + return app(CraftCms\Cms\Support\Path::class); }; }); afterEach(function () { + app()->useStoragePath($this->originalStoragePath); + app()->useLangPath($this->originalLangPath); + config(['view.paths' => $this->originalViewPaths]); app(ProjectConfig::class)->folderName = $this->originalProjectConfigFolderName; app(GeneralConfig::class)->compiledTemplatesPath = $this->originalCompiledTemplatesPath; - foreach ($this->originalAliases as $alias => $path) { - Aliases::set($alias, $path); - } - File::deleteDirectory($this->sandboxPath); File::deleteDirectory(config_path('craft/__path-test__')); }); -test('base getters resolve expected alias-backed and fixed paths', function () { - $path = ($this->laravelPath)(); +test('base getters use Laravel and package paths', function () { + $path = ($this->path)(); + $packagePath = dirname(__DIR__, 3); expect($path->config())->toBe(File::normalizePath(config_path('craft'))) - ->and($path->tests())->toBe($this->aliases['@tests']) - ->and($path->vendor())->toBe($this->aliases['@vendor']) - ->and($path->cpTranslations())->toBe(File::normalizePath(Aliases::get('@craftcms/resources/translations'))) - ->and($path->siteTranslations())->toBe($this->aliases['@translations']) - ->and($path->cpTemplates())->toBe(File::normalizePath(Aliases::get('@craftcms/resources/templates'))) - ->and($path->siteTemplates())->toBe($this->aliases['@templates']) + ->and($path->storage(create: false))->toBe(File::normalizePath(app()->storagePath())) + ->and($path->tests())->toBe(File::normalizePath(base_path('tests'))) + ->and($path->vendor())->toBe(File::normalizePath(base_path('vendor'))) + ->and($path->package())->toBe(File::normalizePath($packagePath)) + ->and($path->resources())->toBe(File::normalizePath($packagePath.'/resources')) + ->and($path->cpTranslations())->toBe(File::normalizePath($packagePath.'/resources/translations')) + ->and($path->siteTranslations())->toBe(File::normalizePath(app()->langPath())) + ->and($path->cpTemplates())->toBe(File::normalizePath($packagePath.'/resources/templates')) + ->and($path->siteTemplates())->toBe(File::normalizePath($this->sandboxPath.'/templates')) ->and($path->licenseKey())->toBe(app(License::class)->keyPath()) ->and($path->projectConfigFile())->toBe(File::normalizePath(config_path('craft/project/'.ProjectConfig::CONFIG_FILENAME))); }); test('project config path respects folder name and create flag', function () { - $path = ($this->laravelPath)(); - $config = app(ProjectConfig::class); - $config->folderName = '__path-test__'; + $path = ($this->path)(); + app(ProjectConfig::class)->folderName = '__path-test__'; $expectedPath = File::normalizePath(config_path('craft/__path-test__')); expect($path->projectConfig(create: false))->toBe($expectedPath) - ->and(File::exists($expectedPath))->toBeFalse(); - - expect($path->projectConfig())->toBe($expectedPath) - ->and(File::isDirectory($expectedPath))->toBeTrue() - ->and($path->projectConfigFile())->toBe($expectedPath.DIRECTORY_SEPARATOR.ProjectConfig::CONFIG_FILENAME); + ->and(File::exists($expectedPath))->toBeFalse() + ->and($path->projectConfig())->toBe($expectedPath) + ->and(File::isDirectory($expectedPath))->toBeTrue(); }); -test('directory getters return the expected path and creation side effects', function ( - string $method, - string $relativePath, - bool $writesGitignore, -) { - $path = ($this->laravelPath)(); - $expectedPath = $this->sandboxPath.DIRECTORY_SEPARATOR.str_replace('/', DIRECTORY_SEPARATOR, $relativePath); +test('directory getters use the Laravel storage path', function (string $method, string $relativePath, bool $writesGitignore) { + $path = ($this->path)(); + $expectedPath = File::normalizePath($this->sandboxPath."/$relativePath"); File::deleteDirectory($expectedPath); expect($path->$method(create: false))->toBe($expectedPath) ->and(File::exists($expectedPath))->toBeFalse() - ->and(File::exists($expectedPath.DIRECTORY_SEPARATOR.'.gitignore'))->toBeFalse(); - - expect($path->$method())->toBe($expectedPath) + ->and($path->$method())->toBe($expectedPath) ->and(File::isDirectory($expectedPath))->toBeTrue() - ->and(File::exists($expectedPath.DIRECTORY_SEPARATOR.'.gitignore'))->toBe($writesGitignore); + ->and(File::exists($expectedPath.'/.gitignore'))->toBe($writesGitignore); })->with([ - 'storage path' => ['storage', 'storage', false], - 'composer backups path' => ['composerBackups', 'storage/composer-backups', true], - 'config backup path' => ['configBackup', 'storage/config-backups', true], - 'config delta path' => ['configDelta', 'storage/config-deltas', true], - 'runtime path' => ['runtime', 'storage/runtime', true], - 'db backup path' => ['dbBackup', 'storage/backups', false], - 'temp path' => ['temp', 'storage/runtime/temp', false], - 'assets path' => ['assets', 'storage/runtime/assets', false], - 'temp asset uploads path' => ['tempAssetUploads', 'storage/runtime/assets/tempuploads', false], - 'asset sources path' => ['assetSources', 'storage/runtime/assets/sources', false], - 'image editor sources path' => ['imageEditorSources', 'storage/runtime/assets/imageeditor', false], - 'assets icons path' => ['assetsIcons', 'storage/runtime/assets/icons', false], - 'image transforms path' => ['imageTransforms', 'storage/runtime/assets/imagetransforms', false], - 'plugin icons path' => ['pluginIcons', 'storage/runtime/pluginicons', false], - 'log path' => ['logs', 'storage/logs', false], - 'compiled classes path' => ['compiledClasses', 'storage/runtime/compiled_classes', false], - 'compiled templates path' => ['compiledTemplates', 'storage/runtime/compiled_templates', false], - 'session path' => ['sessions', 'storage/runtime/sessions', false], - 'cache path' => ['cache', 'storage/runtime/cache', false], + 'storage' => ['storage', 'storage', false], + 'composer backups' => ['composerBackups', 'storage/composer-backups', true], + 'runtime' => ['runtime', 'storage/runtime', true], + 'temp' => ['temp', 'storage/runtime/temp', false], + 'asset sources' => ['assetSources', 'storage/runtime/assets/sources', false], + 'compiled templates' => ['compiledTemplates', 'storage/runtime/compiled_templates', false], ]); -test('compiled templates path can be configured', function () { - app(GeneralConfig::class)->compiledTemplatesPath = '@storage/custom-compiled-templates'; +test('compiled templates path accepts an absolute path', function () { + $expectedPath = $this->sandboxPath.'/custom-compiled-templates'; + app(GeneralConfig::class)->compiledTemplatesPath = $expectedPath; - $path = ($this->laravelPath)(); - $expectedPath = $this->aliases['@storage'].DIRECTORY_SEPARATOR.'custom-compiled-templates'; + $path = ($this->path)(); - File::deleteDirectory($expectedPath); + expect($path->compiledTemplates(create: false))->toBe(File::normalizePath($expectedPath)) + ->and(File::exists($expectedPath))->toBeFalse() + ->and($path->compiledTemplates())->toBe(File::normalizePath($expectedPath)) + ->and(File::isDirectory($expectedPath))->toBeTrue(); +}); - expect($path->compiledTemplates(create: false))->toBe($expectedPath) - ->and(File::exists($expectedPath))->toBeFalse(); +test('compiled templates path rejects aliases', function () { + app(GeneralConfig::class)->compiledTemplatesPath = '@storage/compiled-templates'; - expect($path->compiledTemplates())->toBe($expectedPath) - ->and(File::isDirectory($expectedPath))->toBeTrue() - ->and($path->compiledTemplates('foo.php'))->toBe($expectedPath.DIRECTORY_SEPARATOR.'foo.php'); -}); + ($this->path)()->compiledTemplates(); +})->throws(InvalidArgumentException::class, 'Path aliases require craftcms/yii2-adapter.'); -test('system paths return the expected ordered list', function () { - $path = ($this->laravelPath)(); - $storagePath = $this->aliases['@storage']; - $ds = DIRECTORY_SEPARATOR; +test('system paths return native application paths', function () { + $path = ($this->path)(); + $storage = File::normalizePath(app()->storagePath()); expect($path->system())->toBe([ - $this->aliases['@contentMigrations'], - $this->aliases['@lib'], - $storagePath.$ds.'composer-backups', - $storagePath.$ds.'config-backups', - $storagePath.$ds.'config-deltas', + base_path('migrations'), + "$storage/composer-backups", + "$storage/config-backups", + "$storage/config-deltas", File::normalizePath(config_path('craft')), - $storagePath.$ds.'backups', - $storagePath.$ds.'logs', - $storagePath.$ds.'runtime', - $this->aliases['@templates'], - $this->aliases['@translations'], - $this->aliases['@tests'], - $this->aliases['@vendor'], + "$storage/backups", + "$storage/logs", + "$storage/runtime", + File::normalizePath($this->sandboxPath.'/templates'), + File::normalizePath(app()->langPath()), + File::normalizePath(base_path('tests')), + File::normalizePath(base_path('vendor')), ]); }); -test('laravel path service and facade return the same values', function () { - $laravelPath = ($this->laravelPath)(); - - expect(Path::projectConfigFile())->toBe($laravelPath->projectConfigFile()) - ->and(Path::temp(create: false))->toBe($laravelPath->temp(create: false)) - ->and(Path::system())->toBe($laravelPath->system()); -}); - -test('laravel path service falls back to application paths when aliases are unavailable', function () { - Aliases::remove('@storage'); - Aliases::remove('@tests'); - Aliases::remove('@vendor'); - Aliases::remove('@translations'); - Aliases::remove('@contentMigrations'); - Aliases::remove('@lib'); - - $path = ($this->laravelPath)(); - - expect($path->storage(create: false))->toBe(File::normalizePath(app()->storagePath())) - ->and($path->tests())->toBe(File::normalizePath(app()->basePath('tests'))) - ->and($path->vendor())->toBe(File::normalizePath(app()->basePath('vendor'))) - ->and($path->siteTranslations())->toBe(File::normalizePath(app()->langPath())) - ->and($path->system()[0])->toBe(File::normalizePath(app()->basePath('migrations'))) - ->and($path->system()[1])->toBe(File::normalizePath(app()->basePath('yii2-adapter/lib'))); -}); - -test('laravel path service accepts subpaths for representative roots', function () { - $path = ($this->laravelPath)(); - $ds = DIRECTORY_SEPARATOR; - - expect($path->temp('foo.zip', create: false))->toBe($this->aliases['@storage'].$ds.'runtime'.$ds.'temp'.$ds.'foo.zip') - ->and($path->assetSources('123.jpg', create: false))->toBe($this->aliases['@storage'].$ds.'runtime'.$ds.'assets'.$ds.'sources'.$ds.'123.jpg') - ->and($path->vendor('composer/InstalledVersions.php'))->toBe($this->aliases['@vendor'].$ds.'composer'.$ds.'InstalledVersions.php') - ->and($path->projectConfig('foo/bar.yaml', create: false))->toBe(File::normalizePath(config_path('craft/project/foo/bar.yaml'))); -}); - -test('laravel path service only creates the base directory when a subpath is provided', function () { - $path = ($this->laravelPath)(); - $ds = DIRECTORY_SEPARATOR; - - $tempFilePath = $path->temp('foo.zip'); - $composerBackupPath = $path->composerBackups('composer.json'); +test('path service and facade return the same values', function () { + $path = ($this->path)(); - expect($tempFilePath)->toBe($this->aliases['@storage'].$ds.'runtime'.$ds.'temp'.$ds.'foo.zip') - ->and(File::isDirectory(dirname((string) $tempFilePath)))->toBeTrue() - ->and(File::exists($tempFilePath))->toBeFalse() - ->and($composerBackupPath)->toBe($this->aliases['@storage'].$ds.'composer-backups'.$ds.'composer.json') - ->and(File::isDirectory(dirname((string) $composerBackupPath)))->toBeTrue() - ->and(File::exists($composerBackupPath))->toBeFalse() - ->and(File::exists(dirname((string) $composerBackupPath).$ds.'.gitignore'))->toBeTrue(); + expect(Path::projectConfigFile())->toBe($path->projectConfigFile()) + ->and(Path::package('LICENSE.md'))->toBe($path->package('LICENSE.md')) + ->and(Path::temp(create: false))->toBe($path->temp(create: false)) + ->and(Path::system())->toBe($path->system()); }); test('ensurePathIsContained', function (bool $expected, string $path) { @@ -223,7 +150,5 @@ [true, 'in/a/path'], [false, '../test'], [true, './test'], - [true, 'test'], [false, 'foo////../../bar'], - [true, 'foo/0/0/0/../../bar'], ]); diff --git a/tests/Feature/Support/URLTest.php b/tests/Feature/Support/URLTest.php index bc0f39a694f..3fcf9152a5c 100644 --- a/tests/Feature/Support/URLTest.php +++ b/tests/Feature/Support/URLTest.php @@ -2,13 +2,12 @@ declare(strict_types=1); -use CraftCms\Aliases\Aliases; use CraftCms\Cms\Cms; use CraftCms\Cms\RouteToken\RouteTokens; use CraftCms\Cms\Support\Url; beforeEach(function () { - Aliases::set('@web', 'https://localhost'); + config(['app.url' => 'https://localhost']); swapUrlRequest('https://localhost/news'); }); diff --git a/tests/Feature/SystemMessage/SystemMessageMailableTest.php b/tests/Feature/SystemMessage/SystemMessageMailableTest.php index 167835cfdaf..30253c4c9e9 100644 --- a/tests/Feature/SystemMessage/SystemMessageMailableTest.php +++ b/tests/Feature/SystemMessage/SystemMessageMailableTest.php @@ -2,7 +2,6 @@ declare(strict_types=1); -use CraftCms\Aliases\Aliases; use CraftCms\Cms\Support\Facades\ProjectConfig; use CraftCms\Cms\Support\Facades\Sites; use CraftCms\Cms\SystemMessage\SystemMessages; @@ -38,7 +37,7 @@ }); it('renders system messages through the configured site template and preserves the plain-text body', function () { - Aliases::set('@templates', dirname(__DIR__, 2).'/Support/templates'); + config(['view.paths' => [dirname(__DIR__, 2).'/Support/templates']]); ProjectConfig::set('email', array_merge( ProjectConfig::get('email') ?? [], ['template' => 'mail/custom-system-message.twig'], @@ -68,7 +67,7 @@ it('renders system messages through a site-specific template override', function () { $site = Sites::getPrimarySite(); - Aliases::set('@templates', dirname(__DIR__, 2).'/Support/templates'); + config(['view.paths' => [dirname(__DIR__, 2).'/Support/templates']]); ProjectConfig::set('email', array_merge( ProjectConfig::get('email') ?? [], [ @@ -100,7 +99,7 @@ it('falls back to the global template when no site-specific override is set', function () { $site = Sites::getPrimarySite(); - Aliases::set('@templates', dirname(__DIR__, 2).'/Support/templates'); + config(['view.paths' => [dirname(__DIR__, 2).'/Support/templates']]); ProjectConfig::set('email', array_merge( ProjectConfig::get('email') ?? [], [ diff --git a/tests/Unit/AliasesArchitectureTest.php b/tests/Unit/AliasesArchitectureTest.php new file mode 100644 index 00000000000..41d01aadd47 --- /dev/null +++ b/tests/Unit/AliasesArchitectureTest.php @@ -0,0 +1,31 @@ +filter(function (SplFileInfo|string $file) use ($references): bool { + if (! in_array(pathinfo((string) $file, PATHINFO_EXTENSION), ['json', 'php', 'yaml'], true)) { + return false; + } + + $contents = File::get((string) $file); + + return collect($references)->contains(fn (string $reference): bool => str_contains($contents, $reference)); + }); + + expect($matches->map(fn (SplFileInfo|string $file): string => (string) $file)->values()->all())->toBeEmpty(); +}); diff --git a/tests/Unit/FieldLayout/LayoutElements/TemplateTest.php b/tests/Unit/FieldLayout/LayoutElements/TemplateTest.php index 0aa693639d1..8d8ae74c3d4 100644 --- a/tests/Unit/FieldLayout/LayoutElements/TemplateTest.php +++ b/tests/Unit/FieldLayout/LayoutElements/TemplateTest.php @@ -2,7 +2,6 @@ declare(strict_types=1); -use CraftCms\Aliases\Aliases; use CraftCms\Cms\FieldLayout\FieldLayoutElementContext; use CraftCms\Cms\FieldLayout\LayoutElements\Template; use CraftCms\Cms\Form\Form; @@ -14,10 +13,10 @@ use Symfony\Component\DomCrawler\Crawler; it('renders sanitized non-interactive template content', function () { - $templatesPath = Aliases::getAll()['@templates'] ?? null; + $templatePaths = config('view.paths'); try { - Aliases::set('@templates', dirname(__DIR__, 3).'/Support/templates'); + config(['view.paths' => [dirname(__DIR__, 3).'/Support/templates']]); $element = new Template([ 'uid' => 'template-test', @@ -28,9 +27,7 @@ $context = new FormContext; $node = $element->formNode(new FieldLayoutElementContext(null, $context)); } finally { - $templatesPath === null - ? Aliases::remove('@templates') - : Aliases::set('@templates', $templatesPath); + config(['view.paths' => $templatePaths]); } $payload = app(FormResolver::class)->resolve(Form::make([$node]), $context); diff --git a/tests/Unit/HelpersTest.php b/tests/Unit/HelpersTest.php index 5c97f7053d2..20b1cabfdee 100644 --- a/tests/Unit/HelpersTest.php +++ b/tests/Unit/HelpersTest.php @@ -2,7 +2,6 @@ declare(strict_types=1); -use CraftCms\Aliases\Aliases; use CraftCms\Cms\Cms; use CraftCms\Cms\View\TemplateEngine; use CraftCms\Cms\View\TemplateMode; @@ -89,7 +88,7 @@ beforeEach(function () { $this->tempDir = sys_get_temp_dir().'/craft-template-helper-test-'.uniqid(); File::ensureDirectoryExists($this->tempDir); - Aliases::set('@templates', $this->tempDir); + config(['view.paths' => [$this->tempDir]]); TemplateMode::set(TemplateMode::Site); app()->forgetScopedInstances(); }); diff --git a/tests/Unit/Route/TemplateRouteTest.php b/tests/Unit/Route/TemplateRouteTest.php index b6f41af003c..98bb752cc03 100644 --- a/tests/Unit/Route/TemplateRouteTest.php +++ b/tests/Unit/Route/TemplateRouteTest.php @@ -2,7 +2,6 @@ declare(strict_types=1); -use CraftCms\Aliases\Aliases; use CraftCms\Cms\Cms; use CraftCms\Cms\Route\TemplateRoute; use CraftCms\Cms\Twig\Exceptions\TemplateLoaderException; @@ -15,7 +14,7 @@ $this->tempDir = sys_get_temp_dir().'/craft-template-route-test-'.uniqid(); File::ensureDirectoryExists($this->tempDir); - Aliases::set('@templates', $this->tempDir); + config(['view.paths' => [$this->tempDir]]); TemplateMode::set(TemplateMode::Site); Cms::config()->headlessMode(false); config()->set('app.debug', false); diff --git a/tests/Unit/Support/TypecastTest.php b/tests/Unit/Support/TypecastTest.php index 28522ffeb35..f808a891b2b 100644 --- a/tests/Unit/Support/TypecastTest.php +++ b/tests/Unit/Support/TypecastTest.php @@ -20,7 +20,6 @@ $property => $expected, ]); })->with([ - [GeneralConfig::class, 'aliases', ['foo', 'bar'], 'foo,bar'], [GeneralConfig::class, 'allowAdminChanges', true, 'yes'], [GeneralConfig::class, 'allowAdminChanges', false, 'no'], [GeneralConfig::class, 'allowAdminChanges', true, 'on'], diff --git a/tests/Unit/Twig/TemplateLoaderTest.php b/tests/Unit/Twig/TemplateLoaderTest.php index 255a0ec9daa..85dd6156766 100644 --- a/tests/Unit/Twig/TemplateLoaderTest.php +++ b/tests/Unit/Twig/TemplateLoaderTest.php @@ -2,7 +2,6 @@ declare(strict_types=1); -use CraftCms\Aliases\Aliases; use CraftCms\Cms\Cms; use CraftCms\Cms\Support\File as Path; use CraftCms\Cms\Twig\Exceptions\TemplateLoaderException; @@ -17,7 +16,7 @@ $this->tempDir = sys_get_temp_dir().'/craft-template-loader-test-'.uniqid(); File::ensureDirectoryExists($this->tempDir); - Aliases::set('@templates', $this->tempDir); + config(['view.paths' => [$this->tempDir]]); TemplateMode::set(TemplateMode::Site); Cms::setIsInstalled(false); diff --git a/tests/Unit/Twig/TemplateResolverTest.php b/tests/Unit/Twig/TemplateResolverTest.php index 56d10069182..0cc4241b65c 100644 --- a/tests/Unit/Twig/TemplateResolverTest.php +++ b/tests/Unit/Twig/TemplateResolverTest.php @@ -2,7 +2,6 @@ declare(strict_types=1); -use CraftCms\Aliases\Aliases; use CraftCms\Cms\Cms; use CraftCms\Cms\Support\File as Path; use CraftCms\Cms\View\TemplateMode; @@ -17,7 +16,7 @@ $this->resolver = new TemplateResolver; - Aliases::set('@templates', $this->tempDir); + config(['view.paths' => [$this->tempDir]]); TemplateMode::set(TemplateMode::Site); Cms::setIsInstalled(false); }); diff --git a/tests/Unit/Twig/TwigRendererTest.php b/tests/Unit/Twig/TwigRendererTest.php index 3aee3275251..1698b0af326 100644 --- a/tests/Unit/Twig/TwigRendererTest.php +++ b/tests/Unit/Twig/TwigRendererTest.php @@ -2,7 +2,6 @@ declare(strict_types=1); -use CraftCms\Aliases\Aliases; use CraftCms\Cms\Twig\Twig; use CraftCms\Cms\View\Events\TemplateRendered; use CraftCms\Cms\View\Events\TemplateRendering; @@ -18,7 +17,7 @@ $this->tempDir = sys_get_temp_dir().'/craft-template-renderer-test-'.uniqid(); File::ensureDirectoryExists($this->tempDir); - Aliases::set('@templates', $this->tempDir); + config(['view.paths' => [$this->tempDir]]); TemplateMode::set(TemplateMode::Site); file_put_contents($this->tempDir.'/test-template.twig', 'Hello from test template'); diff --git a/tests/Unit/Validation/Rules/EnvValueRuleTest.php b/tests/Unit/Validation/Rules/EnvValueRuleTest.php index b931ba8cc80..038a345a2f5 100644 --- a/tests/Unit/Validation/Rules/EnvValueRuleTest.php +++ b/tests/Unit/Validation/Rules/EnvValueRuleTest.php @@ -2,7 +2,6 @@ declare(strict_types=1); -use CraftCms\Aliases\Aliases; use CraftCms\Cms\Validation\Concerns\Validates; use CraftCms\Cms\Validation\Contracts\Validatable; use CraftCms\Cms\Validation\Rules\EnvValueRule; @@ -60,7 +59,6 @@ function makeEnvValueRuleValidator(mixed $value, array $rules) putenv('ENV_VALUE_RULE_MISSING'); putenv('ENV_VALUE_RULE_BOOL'); putenv('PASSWORD'); - Aliases::remove('@env-value-rule-email'); }); it('passes validation for resolved values', function (mixed $value, array $rules, ?Closure $setup = null) { @@ -70,7 +68,6 @@ function makeEnvValueRuleValidator(mixed $value, array $rules) })->with([ 'literal email' => ['test@example.com', ['required', 'email']], 'environment variable email' => ['$ENV_VALUE_RULE_EMAIL', ['required', 'email'], fn () => putenv('ENV_VALUE_RULE_EMAIL=test@example.com')], - 'alias email' => ['@env-value-rule-email', ['required', 'email'], fn () => Aliases::set('@env-value-rule-email', 'test@example.com')], 'nullable missing environment variable' => ['$ENV_VALUE_RULE_MISSING', ['nullable', 'email']], ]); diff --git a/tests/Unit/View/BladeRendererTest.php b/tests/Unit/View/BladeRendererTest.php index 67c1e1d61ac..f14cb614e35 100644 --- a/tests/Unit/View/BladeRendererTest.php +++ b/tests/Unit/View/BladeRendererTest.php @@ -2,7 +2,6 @@ declare(strict_types=1); -use CraftCms\Aliases\Aliases; use CraftCms\Cms\Support\File as Path; use CraftCms\Cms\View\Events\TemplateRendering; use CraftCms\Cms\View\TemplateEngine; @@ -35,7 +34,7 @@ File::ensureDirectoryExists($this->tempDir.'/nested'); File::put($this->tempDir.'/nested/partial.blade.php', 'Nested {{ $name }}'); File::put($this->tempDir.'/twig-partial.twig', 'Twig {{ name }}'); - Aliases::set('@templates', $this->tempDir); + config(['view.paths' => [$this->tempDir]]); view()->addNamespace('blade-test', $this->tempDir); view()->addLocation($this->tempDir); diff --git a/yii2-adapter/composer.json b/yii2-adapter/composer.json index 7d6380eeb0f..5c493c45eae 100644 --- a/yii2-adapter/composer.json +++ b/yii2-adapter/composer.json @@ -35,6 +35,7 @@ "ext-pdo": "*", "ext-zip": "*", "craftcms/cms": "self.version", + "craftcms/laravel-aliases": "^2.0", "creocoder/yii2-nested-sets": "~0.9.0", "mikehaertl/php-shellcommand": "^1.6.3", "samdark/yii2-psr-log-target": "^1.1.3", diff --git a/yii2-adapter/composer.lock b/yii2-adapter/composer.lock index 8d390ed918e..5dae504d133 100644 --- a/yii2-adapter/composer.lock +++ b/yii2-adapter/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "4cbc75dfba221e3fc791d08652e2b08b", + "content-hash": "fbc3981ef4a6f24eb7966a3ac8a04623", "packages": [ { "name": "bacon/bacon-qr-code", @@ -476,14 +476,13 @@ "dist": { "type": "path", "url": "..", - "reference": "473a60eed015b88151d6a5c5c489796fdb3b5b7c" + "reference": "2e1c64589e3c7e3adb45f5886643d340ec71aced" }, "require": { "bacon/bacon-qr-code": "^3.0", "commerceguys/addressing": "^2.1.1", "composer/semver": "^3.3.2", "craftcms/cms-assets": "self.version", - "craftcms/laravel-aliases": "^2.0", "craftcms/laravel-dependency-aware-cache": "^1.2.3", "craftcms/laravel-ruleset-validation": "^1.1", "craftcms/plugin-installer": "~1.6.0", @@ -533,6 +532,7 @@ }, "require-dev": { "barryvdh/laravel-debugbar": "^4.2", + "craftcms/test-plugin": "1.0.1", "dg/bypass-finals": "^1.9", "driftingly/rector-laravel": "^2.1", "fakerphp/faker": "^1.19.0", @@ -542,9 +542,9 @@ "laravel/pint": "^1.22", "laravel/socialite": "^5.23", "orchestra/testbench": "^11.0", - "pestphp/pest": "^4.0", - "pestphp/pest-plugin-arch": "^4.0", - "pestphp/pest-plugin-laravel": "^4.0", + "pestphp/pest": "^5.0", + "pestphp/pest-plugin-arch": "^5.0", + "pestphp/pest-plugin-laravel": "^5.0", "phpstan/phpstan": "^2.1", "rector/rector": "^2.3", "spatie/laravel-typescript-transformer": "^3.2" @@ -651,7 +651,7 @@ "./vendor/bin/pest --compact" ], "tests-adapter": [ - "./vendor/bin/pest --configuration ./yii2-adapter/phpunit.xml.dist --test-directory ./yii2-adapter/tests-laravel" + "./yii2-adapter/vendor/bin/pest --configuration ./yii2-adapter/phpunit.xml.dist --test-directory ./tests-laravel" ], "post-autoload-dump": [ "@clear", diff --git a/yii2-adapter/legacy/Craft.php b/yii2-adapter/legacy/Craft.php index 676358d662d..6ae385e4e6d 100644 --- a/yii2-adapter/legacy/Craft.php +++ b/yii2-adapter/legacy/Craft.php @@ -12,9 +12,9 @@ use CraftCms\Cms\Cms; use CraftCms\Cms\Field\Fields; use CraftCms\Cms\Support\Arr; -use CraftCms\Cms\Support\Env; use CraftCms\Cms\Support\Facades\I18N; use CraftCms\Cms\Support\Typecast; +use CraftCms\Yii2Adapter\Support\Env; use GuzzleHttp\Client; use Illuminate\Support\Facades\Http; use Symfony\Component\VarDumper\Cloner\VarCloner; diff --git a/yii2-adapter/legacy/base/Plugin.php b/yii2-adapter/legacy/base/Plugin.php index 6686b41742b..ce261ff0d6c 100644 --- a/yii2-adapter/legacy/base/Plugin.php +++ b/yii2-adapter/legacy/base/Plugin.php @@ -353,6 +353,7 @@ public static function create(array $config): PluginInterface { // Merge in the plugin’s dynamic config $config = Arr::merge($config, static::config()); + unset($config['aliases']); $config['class'] = static::class; diff --git a/yii2-adapter/legacy/behaviors/EnvAttributeParserBehavior.php b/yii2-adapter/legacy/behaviors/EnvAttributeParserBehavior.php index 37d2bd66212..31a29d2e0b6 100644 --- a/yii2-adapter/legacy/behaviors/EnvAttributeParserBehavior.php +++ b/yii2-adapter/legacy/behaviors/EnvAttributeParserBehavior.php @@ -7,9 +7,9 @@ namespace craft\behaviors; -use CraftCms\Cms\Support\Env; use CraftCms\Cms\Support\Facades\Security; use CraftCms\Cms\Support\Str; +use CraftCms\Yii2Adapter\Support\Env; use yii\base\Behavior; use yii\base\Model; use yii\validators\UrlValidator; diff --git a/yii2-adapter/legacy/config/GeneralConfig.php b/yii2-adapter/legacy/config/GeneralConfig.php index 454e634b8fc..0a484ca57f1 100644 --- a/yii2-adapter/legacy/config/GeneralConfig.php +++ b/yii2-adapter/legacy/config/GeneralConfig.php @@ -32,6 +32,9 @@ */ class GeneralConfig extends \CraftCms\Cms\Config\GeneralConfig { + /** @var array */ + public array $aliases = []; + protected static array $renamedSettings = [ 'activateAccountFailurePath' => 'invalidUserTokenPath', 'allowAutoUpdates' => 'allowUpdates', @@ -47,6 +50,25 @@ class GeneralConfig extends \CraftCms\Cms\Config\GeneralConfig public const string EVENT_DEFINE_BEHAVIORS = 'defineBehaviors'; + /** @param array $value */ + public function aliases(array $value): self + { + $this->aliases = []; + + foreach ($value as $name => $path) { + $this->addAlias($name, $path); + } + + return $this; + } + + public function addAlias(string $name, ?string $path): self + { + $this->aliases[str_starts_with($name, '@') ? $name : "@$name"] = $path; + + return $this; + } + /** * @var string|array|null|false Configures Craft to send all system emails to either a single email address or an array of email addresses * for testing purposes. diff --git a/yii2-adapter/legacy/debug/Module.php b/yii2-adapter/legacy/debug/Module.php index 9472fbb28f3..4f83d5b8c0e 100644 --- a/yii2-adapter/legacy/debug/Module.php +++ b/yii2-adapter/legacy/debug/Module.php @@ -13,8 +13,8 @@ use craft\web\View; use CraftCms\Cms\Filesystem\Contracts\FsInterface; use CraftCms\Cms\Filesystem\Filesystems\DiskFilesystem; -use CraftCms\Cms\Support\Env; use CraftCms\Cms\Support\Facades\Filesystems; +use CraftCms\Yii2Adapter\Support\Env; use Illuminate\Filesystem\FilesystemAdapter; use Illuminate\Support\Facades\Storage; use yii\base\InvalidConfigException; diff --git a/yii2-adapter/legacy/helpers/App.php b/yii2-adapter/legacy/helpers/App.php index 8c4dc2f8aab..3bfcf2a0daf 100644 --- a/yii2-adapter/legacy/helpers/App.php +++ b/yii2-adapter/legacy/helpers/App.php @@ -31,7 +31,6 @@ use CraftCms\Cms\Edition; use CraftCms\Cms\License\License; use CraftCms\Cms\ProjectConfig\ProjectConfig as ProjectConfigService; -use CraftCms\Cms\Support\Env; use CraftCms\Cms\Support\Facades\Deprecator; use CraftCms\Cms\Support\Facades\I18N; use CraftCms\Cms\Support\PHP; @@ -40,6 +39,7 @@ use CraftCms\Cms\Translation\Locale; use CraftCms\Cms\User\Elements\User; use CraftCms\Yii2Adapter\Cache; +use CraftCms\Yii2Adapter\Support\Env; use InvalidArgumentException; use yii\base\Event; use yii\base\Exception; diff --git a/yii2-adapter/legacy/helpers/Html.php b/yii2-adapter/legacy/helpers/Html.php index 009fee9e556..81d649f7d44 100644 --- a/yii2-adapter/legacy/helpers/Html.php +++ b/yii2-adapter/legacy/helpers/Html.php @@ -7,6 +7,7 @@ namespace craft\helpers; +use CraftCms\Aliases\Aliases; use CraftCms\Cms\Asset\Elements\Asset; use CraftCms\Cms\Support\Exceptions\InvalidHtmlTagException; use InvalidArgumentException; @@ -482,7 +483,7 @@ public static function sanitizeSvg(string $svg): string public static function dataUrl(string $file, ?string $mimeType = null): string { try { - return \CraftCms\Cms\Support\Html::dataUrl($file, $mimeType); + return \CraftCms\Cms\Support\Html::dataUrl(Aliases::get($file), $mimeType); } catch (InvalidArgumentException $e) { throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e); } @@ -576,6 +577,10 @@ public static function svg( ?bool $namespace = null, bool $throwException = false, ): string { + if (is_string($svg) && str_starts_with($svg, '@')) { + $svg = Aliases::get($svg); + } + return \CraftCms\Cms\Support\Html::svg($svg, $sanitize, $namespace, $throwException); } } diff --git a/yii2-adapter/legacy/helpers/Json.php b/yii2-adapter/legacy/helpers/Json.php index dfb8f959b3e..d6a9a82f924 100644 --- a/yii2-adapter/legacy/helpers/Json.php +++ b/yii2-adapter/legacy/helpers/Json.php @@ -7,6 +7,7 @@ namespace craft\helpers; +use CraftCms\Aliases\Aliases; use CraftCms\Cms\Support\Str; use InvalidArgumentException; @@ -65,7 +66,7 @@ public static function decodeIfJson(mixed $str, bool $asArray = true): mixed */ public static function decodeFromFile(string $file, bool $asArray = true): mixed { - return \CraftCms\Cms\Support\Json::decodeFromFile($file, $asArray); + return \CraftCms\Cms\Support\Json::decodeFromFile(Aliases::get($file), $asArray); } /** diff --git a/yii2-adapter/legacy/log/MonologTarget.php b/yii2-adapter/legacy/log/MonologTarget.php index 8a0afd031fb..68232d6f27c 100644 --- a/yii2-adapter/legacy/log/MonologTarget.php +++ b/yii2-adapter/legacy/log/MonologTarget.php @@ -5,7 +5,7 @@ use craft\helpers\App; use CraftCms\Cms\Cms; use CraftCms\Cms\Support\Arr; -use CraftCms\Cms\Support\Env; +use CraftCms\Yii2Adapter\Support\Env; use DateTimeZone; use Illuminate\Support\Collection; use Monolog\Formatter\FormatterInterface; diff --git a/yii2-adapter/legacy/mail/transportadapters/Gmail.php b/yii2-adapter/legacy/mail/transportadapters/Gmail.php index 5188d23d5ca..518a91bb6ee 100644 --- a/yii2-adapter/legacy/mail/transportadapters/Gmail.php +++ b/yii2-adapter/legacy/mail/transportadapters/Gmail.php @@ -9,7 +9,7 @@ use Craft; use craft\behaviors\EnvAttributeParserBehavior; -use CraftCms\Cms\Support\Env; +use CraftCms\Yii2Adapter\Support\Env; use Symfony\Component\Mailer\Transport\AbstractTransport; use function CraftCms\Cms\t; diff --git a/yii2-adapter/legacy/mail/transportadapters/Sendmail.php b/yii2-adapter/legacy/mail/transportadapters/Sendmail.php index 17276e7d04e..8d779749739 100644 --- a/yii2-adapter/legacy/mail/transportadapters/Sendmail.php +++ b/yii2-adapter/legacy/mail/transportadapters/Sendmail.php @@ -10,8 +10,8 @@ use Craft; use craft\behaviors\EnvAttributeParserBehavior; use CraftCms\Cms\ProjectConfig\ProjectConfig; -use CraftCms\Cms\Support\Env; use CraftCms\Cms\Support\Html; +use CraftCms\Yii2Adapter\Support\Env; use Symfony\Component\Mailer\Transport\AbstractTransport; use function CraftCms\Cms\t; diff --git a/yii2-adapter/legacy/mail/transportadapters/Smtp.php b/yii2-adapter/legacy/mail/transportadapters/Smtp.php index a28ffebfa69..c685dbb54cd 100644 --- a/yii2-adapter/legacy/mail/transportadapters/Smtp.php +++ b/yii2-adapter/legacy/mail/transportadapters/Smtp.php @@ -9,7 +9,7 @@ use Craft; use craft\behaviors\EnvAttributeParserBehavior; -use CraftCms\Cms\Support\Env; +use CraftCms\Yii2Adapter\Support\Env; use Symfony\Component\Mailer\Transport\AbstractTransport; use function CraftCms\Cms\t; diff --git a/yii2-adapter/legacy/services/Path.php b/yii2-adapter/legacy/services/Path.php index 8a237613aa2..f272d4f6bed 100644 --- a/yii2-adapter/legacy/services/Path.php +++ b/yii2-adapter/legacy/services/Path.php @@ -7,9 +7,9 @@ namespace craft\services; -use CraftCms\Cms\Support\Env; use CraftCms\Cms\Support\File; use CraftCms\Cms\Support\Path as LaravelPath; +use CraftCms\Yii2Adapter\Support\Env; use yii\base\Component; /** diff --git a/yii2-adapter/src/Config/GeneralConfigCompatibility.php b/yii2-adapter/src/Config/GeneralConfigCompatibility.php index 77f86f02a42..f75c76380cc 100644 --- a/yii2-adapter/src/Config/GeneralConfigCompatibility.php +++ b/yii2-adapter/src/Config/GeneralConfigCompatibility.php @@ -5,6 +5,7 @@ namespace CraftCms\Yii2Adapter\Config; use craft\config\GeneralConfig as LegacyGeneralConfig; +use CraftCms\Aliases\Aliases; use CraftCms\Cms\Config\GeneralConfig; use CraftCms\Cms\Support\Facades\Deprecator; use CraftCms\Cms\Support\Typecast; @@ -49,10 +50,37 @@ private function resolve(mixed $config, GeneralConfig $default): GeneralConfig|a $config = $config($default); } - if ($config instanceof GeneralConfig || is_array($config)) { + if ($config instanceof LegacyGeneralConfig) { + $this->registerAliases($config->aliases); + + return $config; + } + + if (is_array($config)) { + $aliases = array_merge( + $config['environmentVariables'] ?? [], + $config['aliases'] ?? [], + ); + unset($config['environmentVariables'], $config['aliases']); + $this->registerAliases($aliases); + + return $config; + } + + if ($config instanceof GeneralConfig) { return $config; } return []; } + + /** @param array $aliases */ + private function registerAliases(array $aliases): void + { + foreach ($aliases as $name => $path) { + $path === null + ? Aliases::remove($name) + : Aliases::set($name, $path); + } + } } diff --git a/yii2-adapter/src/Plugin/Plugins.php b/yii2-adapter/src/Plugin/Plugins.php new file mode 100644 index 00000000000..6db7dc07b38 --- /dev/null +++ b/yii2-adapter/src/Plugin/Plugins.php @@ -0,0 +1,23 @@ +getComposerPluginInfo($handle)['aliases'] ?? [] as $alias => $path) { + Aliases::set($alias, $path); + } + + return parent::createPlugin($handle, $info); + } +} diff --git a/yii2-adapter/src/Support/Composer.php b/yii2-adapter/src/Support/Composer.php new file mode 100644 index 00000000000..423f5349252 --- /dev/null +++ b/yii2-adapter/src/Support/Composer.php @@ -0,0 +1,32 @@ +registerAliases(); + $this->app->bind(CoreMigrator::class, Migrator::class); $this->app ->when(Migrator::class) @@ -121,6 +132,8 @@ public function register(): void $this->app->scoped(SystemMessages::class, LegacySystemMessages::class); $this->app->scoped(UserPermissions::class, LegacyUserPermissions::class); $this->app->singleton(UtilityTypes::class, LegacyUtilityTypes::class); + $this->app->singleton(Plugins::class, LegacyPlugins::class); + $this->app->singleton(CoreComposer::class, Composer::class); $this->callAfterResolving(FormNodeTypes::class, fn(FormNodeTypes $types) => $types->register(LegacyHtmlField::class)); $this->callAfterResolving(FormControlTypes::class, fn(FormControlTypes $types) => $types->register(LegacyHtmlControl::class)); /** @@ -140,6 +153,68 @@ public function register(): void $this->registerExceptionHandling(); } + private function registerAliases(): void + { + $cmsPath = InstalledVersions::getInstallPath('craftcms/cms') ?? dirname(__DIR__, 2); + $iconsPath = CmsAssets::resourcesPath('icons'); + + Aliases::set('@root', Env::get('CRAFT_ROOT_PATH', $this->app->basePath())); + Aliases::set('@craftcms', $cmsPath); + Aliases::set('@cmsAssets', CmsAssets::packagePath()); + Aliases::set('@package', "$cmsPath/src"); + Aliases::set('@resources', "$cmsPath/resources"); + Aliases::set('@vendor', $this->app->basePath('vendor')); + Aliases::set('@storage', $this->app->storagePath()); + Aliases::set('@runtime', $this->app->storagePath('runtime')); + Aliases::set('@templates', is_dir($this->app->resourcePath('views')) + ? $this->app->resourcePath('views') + : $this->app->basePath('templates')); + Aliases::set('@web', Env::get('CRAFT_WEB_URL', config('app.url'))); + Aliases::set('@icons', $iconsPath); + + $iconAliases = ['@appicons' => "$iconsPath/solid"]; + + foreach (['brands', 'regular', 'custom-icons'] as $family) { + foreach (glob("$iconsPath/$family/*.svg") ?: [] as $path) { + $iconAliases['@appicons/' . basename($path)] = $path; + } + } + + foreach ([ + 'alert.svg' => 'solid/triangle-exclamation.svg', + 'broken-image' => 'solid/image-slash.svg', + 'buoey.svg' => 'solid/life-ring.svg', + 'draft.svg' => 'solid/scribble.svg', + 'entry-types' => 'solid/files.svg', + 'excite.svg' => 'solid/certificate.svg', + 'feed.svg' => 'solid/rss.svg', + 'field.svg' => 'solid/pen-to-square.svg', + 'hash.svg' => 'solid/hashtag.svg', + 'info-circle' => 'solid/circle-info.svg', + 'info-circle.svg' => 'solid/circle-info.svg', + 'info.svg' => 'solid/circle-info.svg', + 'location.svg' => 'solid/location-dot.svg', + 'photo.svg' => 'solid/image.svg', + 'plugin.svg' => 'solid/plug.svg', + 'routes.svg' => 'solid/signs-post.svg', + 'search.svg' => 'solid/magnifying-glass.svg', + 'shopping-cart' => 'solid/cart-shopping.svg', + 'template.svg' => 'solid/file-code.svg', + 'tip.svg' => 'solid/lightbulb.svg', + 'tools.svg' => 'solid/screwdriver-wrench.svg', + 'tree.svg' => 'solid/sitemap.svg', + 'upgrade.svg' => 'solid/square-arrow-up.svg', + 'wand.svg' => 'solid/wand-magic-sparkles.svg', + 'world.svg' => 'solid/earth-americas.svg', + ] as $name => $path) { + $iconAliases["@appicons/$name"] = "$iconsPath/$path"; + } + + foreach ($iconAliases as $alias => $path) { + Aliases::set($alias, $path); + } + } + protected function registerConstants(): void { /* @@ -244,6 +319,8 @@ private function toLegacyException(Throwable $exception): Throwable public function boot(): void { + $this->app->make(Twig::class)->registerExtension(new AliasesExtension()); + $kernel = $this->app->make(HttpKernel::class); $middleware = array_values(array_filter( $kernel->getGlobalMiddleware(), diff --git a/yii2-adapter/tests-laravel/Legacy/AliasesCompatibilityTest.php b/yii2-adapter/tests-laravel/Legacy/AliasesCompatibilityTest.php new file mode 100644 index 00000000000..a755ae56422 --- /dev/null +++ b/yii2-adapter/tests-laravel/Legacy/AliasesCompatibilityTest.php @@ -0,0 +1,32 @@ +toBe('/path/to/uploads/image.jpg'); + } finally { + Aliases::remove('@uploads'); + } +}); + +it('registers the legacy Twig alias function', function() { + expect(collect(new AliasesExtension()->getFunctions())->map->getName()->all())->toContain('alias'); +}); + +it('keeps the bundled Composer fallback in the adapter', function() { + expect(app(CoreComposer::class))->toBeInstanceOf(Composer::class); +}); + +it('registers icon aliases from CMS assets', function() { + expect(Aliases::get('@appicons/craft-cms.svg'))->toEndWith('/icons/custom-icons/craft-cms.svg') + ->and(Aliases::get('@appicons/github.svg'))->toEndWith('/icons/brands/github.svg'); +}); diff --git a/yii2-adapter/tests-laravel/Legacy/ConfigCompatibilityTest.php b/yii2-adapter/tests-laravel/Legacy/ConfigCompatibilityTest.php index 0a0d29f351f..a7bccd06e16 100644 --- a/yii2-adapter/tests-laravel/Legacy/ConfigCompatibilityTest.php +++ b/yii2-adapter/tests-laravel/Legacy/ConfigCompatibilityTest.php @@ -3,6 +3,7 @@ declare(strict_types=1); use craft\config\GeneralConfig; +use CraftCms\Aliases\Aliases; use CraftCms\Yii2Adapter\Config\GeneralConfigCompatibility; use CraftCms\Yii2Adapter\Config\MultiEnvironmentConfigCompatibility; use Illuminate\Support\Facades\Config; @@ -60,3 +61,31 @@ expect($config)->toBeInstanceOf(GeneralConfig::class) ->and($config->cpTrigger)->toBe('console'); }); + +it('registers aliases from fluent general config', function(): void { + $config = GeneralConfig::create()->aliases([ + 'uploads' => '/path/to/uploads', + ]); + + try { + new GeneralConfigCompatibility()->convert($config); + + expect(Aliases::get('@uploads'))->toBe('/path/to/uploads'); + } finally { + Aliases::remove('@uploads'); + } +}); + +it('registers and removes aliases from array general config', function(): void { + try { + $config = new GeneralConfigCompatibility()->convert([ + 'aliases' => ['@uploads' => '/path/to/uploads'], + 'cpTrigger' => 'control', + ]); + + expect($config)->toBe(['cpTrigger' => 'control']) + ->and(Aliases::get('@uploads'))->toBe('/path/to/uploads'); + } finally { + Aliases::remove('@uploads'); + } +}); diff --git a/yii2-adapter/tests-laravel/Legacy/PluginLifecycleCompatibilityTest.php b/yii2-adapter/tests-laravel/Legacy/PluginLifecycleCompatibilityTest.php index 84239c4e7b8..4237e8a7cf1 100644 --- a/yii2-adapter/tests-laravel/Legacy/PluginLifecycleCompatibilityTest.php +++ b/yii2-adapter/tests-laravel/Legacy/PluginLifecycleCompatibilityTest.php @@ -25,7 +25,7 @@ it('creates and runs adapter plugins through the shared plugin interface', function() { $plugins = app(Plugins::class); - new ReflectionProperty($plugins, 'composerPluginInfo')->setValue($plugins, [ + new ReflectionProperty(Plugins::class, 'composerPluginInfo')->setValue($plugins, [ 'legacy' => [ 'class' => AdapterLifecycleTestPlugin::class, 'handle' => 'legacy', @@ -53,7 +53,7 @@ $plugins = app(Plugins::class); AdapterRegistrationTestPlugin::$modernTypeWasVisible = false; - new ReflectionProperty($plugins, 'composerPluginInfo')->setValue($plugins, [ + new ReflectionProperty(Plugins::class, 'composerPluginInfo')->setValue($plugins, [ 'legacy-registration' => [ 'class' => AdapterRegistrationTestPlugin::class, 'handle' => 'legacy-registration', From 04fafe03d3abf4a7abcce684ba44e4cd733e5c69 Mon Sep 17 00:00:00 2001 From: Rias Date: Fri, 21 Aug 2026 20:49:24 +0200 Subject: [PATCH 2/6] Fix alias compatibility CI failures --- tests/Feature/Support/PathTest.php | 12 ++++++------ yii2-adapter/bootstrap/bootstrap.php | 2 +- yii2-adapter/legacy/services/Path.php | 3 ++- .../Legacy/AliasesCompatibilityTest.php | 19 +++++++++++++++++-- 4 files changed, 26 insertions(+), 10 deletions(-) diff --git a/tests/Feature/Support/PathTest.php b/tests/Feature/Support/PathTest.php index dfe20a1c76e..af9dd1011ca 100644 --- a/tests/Feature/Support/PathTest.php +++ b/tests/Feature/Support/PathTest.php @@ -119,13 +119,13 @@ expect($path->system())->toBe([ base_path('migrations'), - "$storage/composer-backups", - "$storage/config-backups", - "$storage/config-deltas", + File::normalizePath("$storage/composer-backups"), + File::normalizePath("$storage/config-backups"), + File::normalizePath("$storage/config-deltas"), File::normalizePath(config_path('craft')), - "$storage/backups", - "$storage/logs", - "$storage/runtime", + File::normalizePath("$storage/backups"), + File::normalizePath("$storage/logs"), + File::normalizePath("$storage/runtime"), File::normalizePath($this->sandboxPath.'/templates'), File::normalizePath(app()->langPath()), File::normalizePath(base_path('tests')), diff --git a/yii2-adapter/bootstrap/bootstrap.php b/yii2-adapter/bootstrap/bootstrap.php index be65404a15e..7ff052a1d59 100644 --- a/yii2-adapter/bootstrap/bootstrap.php +++ b/yii2-adapter/bootstrap/bootstrap.php @@ -102,7 +102,7 @@ ? CRAFT_TEMPLATES_PATH : Aliases::get('@templates', is_dir($app->resourcePath('views')) ? $app->resourcePath('views') : $app->basePath('templates'))); Aliases::set('@translations', Env::get('CRAFT_TRANSLATIONS_PATH', $app->langPath())); -Aliases::set('@tests', Env::get('CRAFT_TESTS_PATH', $app->basePath('tests'))); +Aliases::set('@tests', defined('CRAFT_TESTS_PATH') ? CRAFT_TESTS_PATH : Env::get('CRAFT_TESTS_PATH', $app->basePath('tests'))); // Load the config $config = Arr::merge( diff --git a/yii2-adapter/legacy/services/Path.php b/yii2-adapter/legacy/services/Path.php index f272d4f6bed..cbbca474c68 100644 --- a/yii2-adapter/legacy/services/Path.php +++ b/yii2-adapter/legacy/services/Path.php @@ -7,6 +7,7 @@ namespace craft\services; +use CraftCms\Aliases\Aliases; use CraftCms\Cms\Support\File; use CraftCms\Cms\Support\Path as LaravelPath; use CraftCms\Yii2Adapter\Support\Env; @@ -60,7 +61,7 @@ public function getStoragePath(bool $create = true): string */ public function getTestsPath(): string { - return $this->service()->tests(); + return File::normalizePath(Aliases::get('@tests')); } /** diff --git a/yii2-adapter/tests-laravel/Legacy/AliasesCompatibilityTest.php b/yii2-adapter/tests-laravel/Legacy/AliasesCompatibilityTest.php index a755ae56422..b9a320ca94a 100644 --- a/yii2-adapter/tests-laravel/Legacy/AliasesCompatibilityTest.php +++ b/yii2-adapter/tests-laravel/Legacy/AliasesCompatibilityTest.php @@ -2,8 +2,10 @@ declare(strict_types=1); +use craft\services\Path; use CraftCms\Aliases\Aliases; use CraftCms\Cms\Support\Composer as CoreComposer; +use CraftCms\Cms\Support\File; use CraftCms\Yii2Adapter\Support\Composer; use CraftCms\Yii2Adapter\Support\Env; use CraftCms\Yii2Adapter\Twig\AliasesExtension; @@ -22,11 +24,24 @@ expect(collect(new AliasesExtension()->getFunctions())->map->getName()->all())->toContain('alias'); }); +it('resolves legacy path service paths through aliases', function() { + $original = Aliases::get('@tests', false); + $testsPath = File::normalizePath(__DIR__ . '/../Fixtures'); + Aliases::set('@tests', $testsPath); + + try { + expect(new Path()->getTestsPath())->toBe($testsPath); + } finally { + $original === false + ? Aliases::remove('@tests') + : Aliases::set('@tests', $original); + } +}); + it('keeps the bundled Composer fallback in the adapter', function() { expect(app(CoreComposer::class))->toBeInstanceOf(Composer::class); }); it('registers icon aliases from CMS assets', function() { - expect(Aliases::get('@appicons/craft-cms.svg'))->toEndWith('/icons/custom-icons/craft-cms.svg') - ->and(Aliases::get('@appicons/github.svg'))->toEndWith('/icons/brands/github.svg'); + expect(Aliases::get('@appicons/craft-cms.svg'))->toEndWith('/icons/custom-icons/craft-cms.svg'); }); From ba7a1f4bfa29adee3381e69f9a26fa1aa63bae8c Mon Sep 17 00:00:00 2001 From: Rias Date: Fri, 21 Aug 2026 20:54:02 +0200 Subject: [PATCH 3/6] Align legacy storage path with Laravel --- yii2-adapter/bootstrap/bootstrap.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/yii2-adapter/bootstrap/bootstrap.php b/yii2-adapter/bootstrap/bootstrap.php index 7ff052a1d59..49f4b8e53fb 100644 --- a/yii2-adapter/bootstrap/bootstrap.php +++ b/yii2-adapter/bootstrap/bootstrap.php @@ -35,6 +35,10 @@ // Get the Laravel application instance $app = Application::getInstance(); +if (defined('CRAFT_STORAGE_PATH')) { + $app->useStoragePath(CRAFT_STORAGE_PATH); +} + // Load the general config // ----------------------------------------------------------------------------- From 4bd9c99171c3b70fc7cabf64753c316bac667b23 Mon Sep 17 00:00:00 2001 From: Rias Date: Fri, 21 Aug 2026 20:58:27 +0200 Subject: [PATCH 4/6] Align legacy application paths with Laravel --- yii2-adapter/bootstrap/bootstrap.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/yii2-adapter/bootstrap/bootstrap.php b/yii2-adapter/bootstrap/bootstrap.php index 49f4b8e53fb..301cc29a373 100644 --- a/yii2-adapter/bootstrap/bootstrap.php +++ b/yii2-adapter/bootstrap/bootstrap.php @@ -35,10 +35,22 @@ // Get the Laravel application instance $app = Application::getInstance(); +if (defined('CRAFT_CONFIG_PATH')) { + $app->useConfigPath(CRAFT_CONFIG_PATH); +} + if (defined('CRAFT_STORAGE_PATH')) { $app->useStoragePath(CRAFT_STORAGE_PATH); } +if (defined('CRAFT_TEMPLATES_PATH')) { + $app->get('config')->set('view.paths', [CRAFT_TEMPLATES_PATH]); +} + +if (defined('CRAFT_TRANSLATIONS_PATH')) { + $app->useLangPath(CRAFT_TRANSLATIONS_PATH); +} + // Load the general config // ----------------------------------------------------------------------------- From e106272384413691f12948c0109191ce695dfe68 Mon Sep 17 00:00:00 2001 From: Rias Date: Fri, 21 Aug 2026 21:03:32 +0200 Subject: [PATCH 5/6] Restore dynamic template alias compatibility --- yii2-adapter/src/Support/Path.php | 25 +++++++++++++++++++ yii2-adapter/src/Yii2ServiceProvider.php | 3 +++ .../Legacy/AliasesCompatibilityTest.php | 19 ++++++++++++-- 3 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 yii2-adapter/src/Support/Path.php diff --git a/yii2-adapter/src/Support/Path.php b/yii2-adapter/src/Support/Path.php new file mode 100644 index 00000000000..c6b39679276 --- /dev/null +++ b/yii2-adapter/src/Support/Path.php @@ -0,0 +1,25 @@ +app->singleton(UtilityTypes::class, LegacyUtilityTypes::class); $this->app->singleton(Plugins::class, LegacyPlugins::class); $this->app->singleton(CoreComposer::class, Composer::class); + $this->app->singleton(CorePath::class, LegacyPath::class); $this->callAfterResolving(FormNodeTypes::class, fn(FormNodeTypes $types) => $types->register(LegacyHtmlField::class)); $this->callAfterResolving(FormControlTypes::class, fn(FormControlTypes $types) => $types->register(LegacyHtmlControl::class)); /** diff --git a/yii2-adapter/tests-laravel/Legacy/AliasesCompatibilityTest.php b/yii2-adapter/tests-laravel/Legacy/AliasesCompatibilityTest.php index b9a320ca94a..03e70e33c25 100644 --- a/yii2-adapter/tests-laravel/Legacy/AliasesCompatibilityTest.php +++ b/yii2-adapter/tests-laravel/Legacy/AliasesCompatibilityTest.php @@ -2,10 +2,11 @@ declare(strict_types=1); -use craft\services\Path; +use craft\services\Path as LegacyPath; use CraftCms\Aliases\Aliases; use CraftCms\Cms\Support\Composer as CoreComposer; use CraftCms\Cms\Support\File; +use CraftCms\Cms\Support\Path as CorePath; use CraftCms\Yii2Adapter\Support\Composer; use CraftCms\Yii2Adapter\Support\Env; use CraftCms\Yii2Adapter\Twig\AliasesExtension; @@ -30,7 +31,7 @@ Aliases::set('@tests', $testsPath); try { - expect(new Path()->getTestsPath())->toBe($testsPath); + expect(new LegacyPath()->getTestsPath())->toBe($testsPath); } finally { $original === false ? Aliases::remove('@tests') @@ -38,6 +39,20 @@ } }); +it('resolves the core site templates path through the legacy alias', function() { + $original = Aliases::get('@templates', false); + $templatesPath = File::normalizePath(__DIR__ . '/../Fixtures'); + Aliases::set('@templates', $templatesPath); + + try { + expect(app(CorePath::class)->siteTemplates())->toBe($templatesPath); + } finally { + $original === false + ? Aliases::remove('@templates') + : Aliases::set('@templates', $original); + } +}); + it('keeps the bundled Composer fallback in the adapter', function() { expect(app(CoreComposer::class))->toBeInstanceOf(Composer::class); }); From c7b70c890c0434f23f8d310b992564f9f4448e7a Mon Sep 17 00:00:00 2001 From: Rias Date: Fri, 21 Aug 2026 21:10:28 +0200 Subject: [PATCH 6/6] Restore dynamic vendor alias compatibility --- yii2-adapter/src/Support/Path.php | 11 +++++++++++ .../Legacy/AliasesCompatibilityTest.php | 14 ++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/yii2-adapter/src/Support/Path.php b/yii2-adapter/src/Support/Path.php index c6b39679276..6949e640c4d 100644 --- a/yii2-adapter/src/Support/Path.php +++ b/yii2-adapter/src/Support/Path.php @@ -12,6 +12,17 @@ class Path extends CorePath { + public function vendor(string $path = ''): string + { + $vendorPath = Aliases::get('@vendor', false); + + if ($vendorPath === false) { + return parent::vendor($path); + } + + return File::normalizePath($path === '' ? $vendorPath : join_paths($vendorPath, $path)); + } + public function siteTemplates(string $path = ''): string { $templatesPath = Aliases::get('@templates', false); diff --git a/yii2-adapter/tests-laravel/Legacy/AliasesCompatibilityTest.php b/yii2-adapter/tests-laravel/Legacy/AliasesCompatibilityTest.php index 03e70e33c25..480dc80c6dd 100644 --- a/yii2-adapter/tests-laravel/Legacy/AliasesCompatibilityTest.php +++ b/yii2-adapter/tests-laravel/Legacy/AliasesCompatibilityTest.php @@ -53,6 +53,20 @@ } }); +it('resolves the core vendor path through the legacy alias', function() { + $original = Aliases::get('@vendor', false); + $vendorPath = File::normalizePath(__DIR__ . '/../Fixtures'); + Aliases::set('@vendor', $vendorPath); + + try { + expect(app(CorePath::class)->vendor())->toBe($vendorPath); + } finally { + $original === false + ? Aliases::remove('@vendor') + : Aliases::set('@vendor', $original); + } +}); + it('keeps the bundled Composer fallback in the adapter', function() { expect(app(CoreComposer::class))->toBeInstanceOf(Composer::class); });