├── .scrutinizer.yml
├── composer.json
├── LICENSE.md
├── CHANGELOG.md
├── README.md
├── Plugin.php
└── Installer.php
/.scrutinizer.yml:
--------------------------------------------------------------------------------
1 | checks:
2 | php: true
3 |
4 | filter:
5 | paths:
6 | - "src/*"
7 |
8 | tools:
9 | php_code_coverage:
10 | enabled: true
11 |
12 | build:
13 | nodes:
14 | analysis:
15 | environment:
16 | php: 7.4.12
17 |
18 | tests:
19 | override:
20 | - php-scrutinizer-run
21 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "yiisoft/yii2-composer",
3 | "description": "The composer plugin for Yii extension installer",
4 | "keywords": ["yii2", "composer", "extension installer"],
5 | "type": "composer-plugin",
6 | "license": "BSD-3-Clause",
7 | "support": {
8 | "issues": "https://github.com/yiisoft/yii2-composer/issues",
9 | "forum": "https://www.yiiframework.com/forum/",
10 | "wiki": "https://www.yiiframework.com/wiki/",
11 | "irc": "ircs://irc.libera.chat:6697/yii",
12 | "source": "https://github.com/yiisoft/yii2-composer"
13 | },
14 | "authors": [
15 | {
16 | "name": "Qiang Xue",
17 | "email": "qiang.xue@gmail.com"
18 | },
19 | {
20 | "name": "Carsten Brandt",
21 | "email": "mail@cebe.cc"
22 | }
23 | ],
24 | "require": {
25 | "composer-plugin-api": "^1.0 | ^2.0"
26 | },
27 | "require-dev": {
28 | "composer/composer": "^1.0 | ^2.0@dev",
29 | "phpunit/phpunit": "<7"
30 | },
31 | "autoload": {
32 | "psr-4": { "yii\\composer\\": "" }
33 | },
34 | "autoload-dev": {
35 | "psr-4": { "tests\\": "tests" }
36 | },
37 | "extra": {
38 | "class": "yii\\composer\\Plugin",
39 | "branch-alias": {
40 | "dev-master": "2.0.x-dev"
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Copyright © 2008 by Yii Software LLC (http://www.yiisoft.com)
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions
6 | are met:
7 |
8 | * Redistributions of source code must retain the above copyright
9 | notice, this list of conditions and the following disclaimer.
10 | * Redistributions in binary form must reproduce the above copyright
11 | notice, this list of conditions and the following disclaimer in
12 | the documentation and/or other materials provided with the
13 | distribution.
14 | * Neither the name of Yii Software LLC nor the names of its
15 | contributors may be used to endorse or promote products derived
16 | from this software without specific prior written permission.
17 |
18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21 | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22 | COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 | POSSIBILITY OF SUCH DAMAGE.
30 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | Yii Framework 2 composer extension Change Log
2 | =============================================
3 |
4 | 2.0.12 under development
5 | ------------------------
6 |
7 | - no changes in this release.
8 |
9 |
10 | 2.0.11 February 13, 2025
11 | ------------------------
12 |
13 | - Enh #38: Use `random_bytes` instead of `openssl_random_pseudo_bytes` if available (fetus_hina)
14 |
15 |
16 | 2.0.10 June 24, 2020
17 | --------------------
18 |
19 | - Enh #31: Add Composer 2 parallel unzip compatibility (samdark)
20 |
21 |
22 | 2.0.9 April 20, 2020
23 | --------------------
24 |
25 | - Bug #30: Fix PHP error when upgrading/downgrading a Yii2 extension (brandonkelly)
26 | - Enh #27: Support for Composer 2 (brandonkelly, cebe)
27 |
28 |
29 | 2.0.8 July 16, 2019
30 | -------------------
31 |
32 | - Bug #23: Fixed another an error that would occur if the Zend OPcache extension was installed, but its "restrict_api" setting was enabled (Lachee)
33 |
34 |
35 | 2.0.7 July 05, 2018
36 | -------------------
37 |
38 | - Bug #18: Fixed an error that would occur if the Zend OPcache extension was installed, but its "restrict_api" setting was enabled (angrybrad)
39 |
40 |
41 | 2.0.6 March 21, 2018
42 | --------------------
43 |
44 | - Bug #16: Upgrade notes were not shown when upgrading from a patch version (cebe)
45 |
46 |
47 | 2.0.5 December 20, 2016
48 | -----------------------
49 |
50 | - Bug #11: `generateCookieValidationKey()` now saves config file only when `cookieValidationKey` was generated (rob006)
51 | - Enh #10: Added `yii\composer\Installer::postInstall()` method (rob006)
52 | - Enh #12: Added `yii\composer\Installer::copyFiles()` method (rob006)
53 | - Enh #14: A note about yii UPGRADE notes file is shown after upgrading Yii to make user aware of it (cebe)
54 |
55 |
56 | 2.0.4 February 06, 2016
57 | -----------------------
58 |
59 | - Bug #7735: Composer failed to install extensions with multiple base paths in "psr-4" autoload section (cebe)
60 | - Enh #2: Better error handling for the case when installer is unable to change permissions (dbavscc)
61 | - Enh #3: `loadExtensions()` and `saveExtensions()` now access `EXTENSION_FILE` constant with late static binding (karneds)
62 |
63 |
64 | 2.0.3 March 01, 2015
65 | --------------------
66 |
67 | - no changes in this release.
68 |
69 |
70 | 2.0.2 January 11, 2015
71 | ----------------------
72 |
73 | - no changes in this release.
74 |
75 |
76 | 2.0.1 December 07, 2014
77 | -----------------------
78 |
79 | - no changes in this release.
80 |
81 |
82 | 2.0.0 October 12, 2014
83 | ----------------------
84 |
85 | - no changes in this release.
86 |
87 |
88 | 2.0.0-rc September 27, 2014
89 | ---------------------------
90 |
91 | - Bug #3438: Fixed support for non-lowercase package names (cebe)
92 | - Chg: Added `yii\composer\Installer::postCreateProject()` and modified the syntax of calling installer methods in composer.json (qiangxue)
93 |
94 | 2.0.0-beta April 13, 2014
95 | -------------------------
96 |
97 | - Bug #1480: Fixed issue with creating extensions.php when php opcache is enabled (cebe)
98 | - Enh: Added support for installing packages conforming to PSR-4 standard (qiangxue)
99 |
100 | 2.0.0-alpha, December 1, 2013
101 | -----------------------------
102 |
103 | - Initial release.
104 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
Yii 2 Composer Installer
6 |
7 |
8 |
9 | This is the composer installer for [Yii framework 2.0](https://www.yiiframework.com) extensions.
10 | It implements a new composer package type named `yii2-extension`,
11 | which should be used by all Yii 2 extensions if they are distributed as composer packages.
12 |
13 | For license information check the [LICENSE](LICENSE.md)-file.
14 |
15 | [](https://packagist.org/packages/yiisoft/yii2-composer)
16 | [](https://packagist.org/packages/yiisoft/yii2-composer)
17 | [](https://github.com/yiisoft/yii2-composer/actions?query=workflow%3Abuild)
18 |
19 |
20 | Usage
21 | -----
22 |
23 | The Yii 2 Composer Installer is automatically installed with when installing the framework via Composer.
24 |
25 | To use Yii 2 composer installer, simply set the package `type` to be `yii2-extension` in your `composer.json`,
26 | like the following:
27 |
28 | ```json
29 | {
30 | "type": "yii2-extension",
31 | "require": {
32 | "yiisoft/yii2": "~2.0.0"
33 | },
34 | ...
35 | }
36 | ```
37 |
38 | You may specify a bootstrapping class in the `extra` section. The `init()` method of the class will be executed each time
39 | the Yii 2 application is responding to a request. For example,
40 |
41 | ```json
42 | {
43 | "type": "yii2-extension",
44 | ...,
45 | "extra": {
46 | "bootstrap": "yii\\jui\\Extension"
47 | }
48 | }
49 | ```
50 |
51 | The `Installer` class also implements a static method `postCreateProject()` that can be called after
52 | a Yii 2 project is created, through the `post-create-project-cmd` composer script.
53 | A similar method exists for running tasks after each `composer install` call, which is `postInstall()`.
54 | These methods allow to run other `Installer` class methods like `setPermission()` or `generateCookieValidationKey()`,
55 | depending on the corresponding parameters set in the `extra` section of the `composer.json` file.
56 | For example,
57 |
58 | ```json
59 | {
60 | "name": "yiisoft/yii2-app-basic",
61 | "type": "project",
62 | ...
63 | "scripts": {
64 | "post-create-project-cmd": [
65 | "yii\\composer\\Installer::postCreateProject"
66 | ],
67 | "post-install-cmd": [
68 | "yii\\composer\\Installer::postInstall"
69 | ]
70 | },
71 | "extra": {
72 | "yii\\composer\\Installer::postCreateProject": {
73 | "setPermission": [
74 | {
75 | "runtime": "0777",
76 | "web/assets": "0777",
77 | "yii": "0755"
78 | }
79 | ]
80 | },
81 | "yii\\composer\\Installer::postInstall": {
82 | "copyFiles": [
83 | {
84 | "config/templates/console-local.php": "config/console-local.php",
85 | "config/templates/web-local.php": "config/web-local.php",
86 | "config/templates/db-local.php": "config/db-local.php",
87 | "config/templates/cache.json": ["runtime/cache.json", true]
88 | }
89 | ],
90 | "generateCookieValidationKey": [
91 | "config/web-local.php"
92 | ]
93 | }
94 | }
95 | }
96 | ```
97 |
--------------------------------------------------------------------------------
/Plugin.php:
--------------------------------------------------------------------------------
1 |
25 | * @since 2.0
26 | */
27 | class Plugin implements PluginInterface, EventSubscriberInterface
28 | {
29 | /**
30 | * @var Installer
31 | */
32 | private $_installer;
33 | /**
34 | * @var array noted package updates.
35 | */
36 | private $_packageUpdates = [];
37 | /**
38 | * @var string path to the vendor directory.
39 | */
40 | private $_vendorDir;
41 |
42 |
43 | /**
44 | * @inheritdoc
45 | */
46 | public function activate(Composer $composer, IOInterface $io)
47 | {
48 | $this->_installer = new Installer($io, $composer);
49 | $composer->getInstallationManager()->addInstaller($this->_installer);
50 | $this->_vendorDir = rtrim($composer->getConfig()->get('vendor-dir'), '/');
51 | $file = $this->_vendorDir . '/yiisoft/extensions.php';
52 | if (!is_file($file)) {
53 | @mkdir(dirname($file), 0777, true);
54 | file_put_contents($file, "getInstallationManager()->removeInstaller($this->_installer);
64 | }
65 |
66 | /**
67 | * @inheritdoc
68 | */
69 | public function uninstall(Composer $composer, IOInterface $io)
70 | {
71 | }
72 |
73 | /**
74 | * @inheritdoc
75 | * @return array The event names to listen to.
76 | */
77 | public static function getSubscribedEvents()
78 | {
79 | return [
80 | PackageEvents::POST_PACKAGE_UPDATE => 'checkPackageUpdates',
81 | ScriptEvents::POST_UPDATE_CMD => 'showUpgradeNotes',
82 | ];
83 | }
84 |
85 |
86 | /**
87 | * Listen to POST_PACKAGE_UPDATE event and take note of the package updates.
88 | * @param PackageEvent $event
89 | */
90 | public function checkPackageUpdates(PackageEvent $event)
91 | {
92 | $operation = $event->getOperation();
93 | if ($operation instanceof UpdateOperation) {
94 | $this->_packageUpdates[$operation->getInitialPackage()->getName()] = [
95 | 'from' => $operation->getInitialPackage()->getVersion(),
96 | 'fromPretty' => $operation->getInitialPackage()->getPrettyVersion(),
97 | 'to' => $operation->getTargetPackage()->getVersion(),
98 | 'toPretty' => $operation->getTargetPackage()->getPrettyVersion(),
99 | 'direction' => $this->_isUpgrade($event, $operation) ? 'up' : 'down',
100 | ];
101 | }
102 | }
103 |
104 | /**
105 | * @param PackageEvent $event
106 | * @param UpdateOperation $operation
107 | * @return bool
108 | */
109 | private function _isUpgrade(PackageEvent $event, UpdateOperation $operation)
110 | {
111 | // Composer 1.7.0+
112 | if (method_exists('Composer\Package\Version\VersionParser', 'isUpgrade')) {
113 | return VersionParser::isUpgrade(
114 | $operation->getInitialPackage()->getVersion(),
115 | $operation->getTargetPackage()->getVersion()
116 | );
117 | }
118 |
119 | return $event->getPolicy()->versionCompare(
120 | $operation->getInitialPackage(),
121 | $operation->getTargetPackage(),
122 | '<'
123 | );
124 | }
125 |
126 | /**
127 | * Listen to POST_UPDATE_CMD event to display information about upgrade notes if appropriate.
128 | * @param Script\Event $event
129 | */
130 | public function showUpgradeNotes(Script\Event $event)
131 | {
132 | $packageName = 'yiisoft/yii2';
133 | if (!isset($this->_packageUpdates[$packageName])) {
134 | return;
135 | }
136 |
137 | $package = $this->_packageUpdates['yiisoft/yii2'];
138 |
139 | // do not show a notice on up/downgrades between dev versions
140 | // avoid messages like from version dev-master to dev-master
141 | if ($package['fromPretty'] == $package['toPretty']) {
142 | return;
143 | }
144 |
145 | $io = $event->getIO();
146 |
147 | // print the relevant upgrade notes for the upgrade
148 | // - only on upgrade, not on downgrade
149 | // - only if the "from" version is non-dev, otherwise we have no idea which notes to show
150 | if ($package['direction'] === 'up' && $this->isNumericVersion($package['fromPretty'])) {
151 |
152 | $notes = $this->findUpgradeNotes($packageName, $package['fromPretty']);
153 | if ($notes !== false && empty($notes)) {
154 | // no relevent upgrade notes, do not show anything.
155 | return;
156 | }
157 |
158 | $this->printUpgradeIntro($io, $package);
159 |
160 | if ($notes) {
161 | // safety check: do not display notes if they are too many
162 | if (count($notes) > 250) {
163 | $io->write("\n The relevant notes for your upgrade are too long to be displayed here.>");
164 | } else {
165 | $io->write("\n " . trim(implode("\n ", $notes)));
166 | }
167 | }
168 |
169 | $io->write("\n You can find the upgrade notes for all versions online at:");
170 | } else {
171 | $this->printUpgradeIntro($io, $package);
172 | $io->write("\n You can find the upgrade notes online at:");
173 | }
174 | $this->printUpgradeLink($io, $package);
175 | }
176 |
177 | /**
178 | * Print link to upgrade notes
179 | * @param IOInterface $io
180 | * @param array $package
181 | */
182 | private function printUpgradeLink($io, $package)
183 | {
184 | $maxVersion = $package['direction'] === 'up' ? $package['toPretty'] : $package['fromPretty'];
185 | // make sure to always show a valid link, even if $maxVersion is something like dev-master
186 | if (!$this->isNumericVersion($maxVersion)) {
187 | $maxVersion = 'master';
188 | }
189 | $io->write(" https://github.com/yiisoft/yii2/blob/$maxVersion/framework/UPGRADE.md\n");
190 | }
191 |
192 | /**
193 | * Print upgrade intro
194 | * @param IOInterface $io
195 | * @param array $package
196 | */
197 | private function printUpgradeIntro($io, $package)
198 | {
199 | $io->write("\n Seems you have "
200 | . ($package['direction'] === 'up' ? 'upgraded' : 'downgraded')
201 | . ' Yii Framework from version '
202 | . $package['fromPretty'] . ' to ' . $package['toPretty'] . '.>'
203 | );
204 | $io->write("\n Please check the upgrade notes for possible incompatible changes");
205 | $io->write(' and adjust your application code accordingly.>');
206 | }
207 |
208 | /**
209 | * Read upgrade notes from a files and returns an array of lines
210 | * @param string $packageName
211 | * @param string $fromVersion until which version to read the notes
212 | * @return array|false
213 | */
214 | private function findUpgradeNotes($packageName, $fromVersion)
215 | {
216 | if (preg_match('/^([0-9]\.[0-9]+\.?[0-9]*)/', $fromVersion, $m)) {
217 | $fromVersionMajor = $m[1];
218 | } else {
219 | $fromVersionMajor = $fromVersion;
220 | }
221 |
222 | $upgradeFile = $this->_vendorDir . '/' . $packageName . '/UPGRADE.md';
223 | if (!is_file($upgradeFile) || !is_readable($upgradeFile)) {
224 | return false;
225 | }
226 | $lines = preg_split('~\R~', file_get_contents($upgradeFile));
227 | $relevantLines = [];
228 | $consuming = false;
229 | // whether an exact match on $fromVersion has been encountered
230 | $foundExactMatch = false;
231 | foreach($lines as $line) {
232 | if (preg_match('/^Upgrade from Yii ([0-9]\.[0-9]+\.?[0-9\.]*)/i', $line, $matches)) {
233 | if ($matches[1] === $fromVersion) {
234 | $foundExactMatch = true;
235 | }
236 | if (version_compare($matches[1], $fromVersion, '<') && ($foundExactMatch || version_compare($matches[1], $fromVersionMajor, '<'))) {
237 | break;
238 | }
239 | $consuming = true;
240 | }
241 | if ($consuming) {
242 | $relevantLines[] = $line;
243 | }
244 | }
245 | return $relevantLines;
246 | }
247 |
248 | /**
249 | * Check whether a version is numeric, e.g. 2.0.10.
250 | * @param string $version
251 | * @return bool
252 | */
253 | private function isNumericVersion($version)
254 | {
255 | return (bool) preg_match('~^([0-9]\.[0-9]+\.?[0-9\.]*)~', $version);
256 | }
257 | }
258 |
--------------------------------------------------------------------------------
/Installer.php:
--------------------------------------------------------------------------------
1 |
20 | * @since 2.0
21 | */
22 | class Installer extends LibraryInstaller
23 | {
24 | const EXTRA_BOOTSTRAP = 'bootstrap';
25 | const EXTENSION_FILE = 'yiisoft/extensions.php';
26 |
27 |
28 | /**
29 | * @inheritdoc
30 | */
31 | public function supports($packageType)
32 | {
33 | return $packageType === 'yii2-extension';
34 | }
35 |
36 | /**
37 | * @inheritdoc
38 | */
39 | public function install(InstalledRepositoryInterface $repo, PackageInterface $package)
40 | {
41 | $afterInstall = function () use ($package) {
42 | // add the package to yiisoft/extensions.php
43 | $this->addPackage($package);
44 | // ensure the yii2-dev package also provides Yii.php in the same place as yii2 does
45 | if ($package->getName() == 'yiisoft/yii2-dev') {
46 | $this->linkBaseYiiFiles();
47 | }
48 | };
49 |
50 | // install the package the normal composer way
51 | $promise = parent::install($repo, $package);
52 |
53 | // Composer v2 might return a promise here
54 | if ($promise instanceof PromiseInterface) {
55 | return $promise->then($afterInstall);
56 | }
57 |
58 | // If not, execute the code right away as parent::install executed synchronously (composer v1, or v2 without async)
59 | $afterInstall();
60 | }
61 |
62 | /**
63 | * @inheritdoc
64 | */
65 | public function update(InstalledRepositoryInterface $repo, PackageInterface $initial, PackageInterface $target)
66 | {
67 | $afterUpdate = function () use ($initial, $target) {
68 | $this->removePackage($initial);
69 | $this->addPackage($target);
70 | // ensure the yii2-dev package also provides Yii.php in the same place as yii2 does
71 | if ($initial->getName() == 'yiisoft/yii2-dev') {
72 | $this->linkBaseYiiFiles();
73 | }
74 | };
75 |
76 | // update the package the normal composer way
77 | $promise = parent::update($repo, $initial, $target);
78 |
79 | // Composer v2 might return a promise here
80 | if ($promise instanceof PromiseInterface) {
81 | return $promise->then($afterUpdate);
82 | }
83 |
84 | // If not, execute the code right away as parent::update executed synchronously (composer v1, or v2 without async)
85 | $afterUpdate();
86 | }
87 |
88 | /**
89 | * @inheritdoc
90 | */
91 | public function uninstall(InstalledRepositoryInterface $repo, PackageInterface $package)
92 | {
93 | $afterUninstall = function () use ($package) {
94 | // remove the package from yiisoft/extensions.php
95 | $this->removePackage($package);
96 | // remove links for Yii.php
97 | if ($package->getName() == 'yiisoft/yii2-dev') {
98 | $this->removeBaseYiiFiles();
99 | }
100 | };
101 |
102 | // uninstall the package the normal composer way
103 | $promise = parent::uninstall($repo, $package);
104 |
105 | // Composer v2 might return a promise here
106 | if ($promise instanceof PromiseInterface) {
107 | return $promise->then($afterUninstall);
108 | }
109 |
110 | // If not, execute the code right away as parent::uninstall executed synchronously (composer v1, or v2 without async)
111 | $afterUninstall();
112 | }
113 |
114 | protected function addPackage(PackageInterface $package)
115 | {
116 | $extension = [
117 | 'name' => $package->getName(),
118 | 'version' => $package->getVersion(),
119 | ];
120 |
121 | $alias = $this->generateDefaultAlias($package);
122 | if (!empty($alias)) {
123 | $extension['alias'] = $alias;
124 | }
125 | $extra = $package->getExtra();
126 | if (isset($extra[self::EXTRA_BOOTSTRAP])) {
127 | $extension['bootstrap'] = $extra[self::EXTRA_BOOTSTRAP];
128 | }
129 |
130 | $extensions = $this->loadExtensions();
131 | $extensions[$package->getName()] = $extension;
132 | $this->saveExtensions($extensions);
133 | }
134 |
135 | protected function generateDefaultAlias(PackageInterface $package)
136 | {
137 | $fs = new Filesystem;
138 | $vendorDir = $fs->normalizePath($this->vendorDir);
139 | $autoload = $package->getAutoload();
140 |
141 | $aliases = [];
142 |
143 | if (!empty($autoload['psr-0'])) {
144 | foreach ($autoload['psr-0'] as $name => $path) {
145 | $name = str_replace('\\', '/', trim($name, '\\'));
146 | if (!$fs->isAbsolutePath($path)) {
147 | $path = $this->vendorDir . '/' . $package->getPrettyName() . '/' . $path;
148 | }
149 | $path = $fs->normalizePath($path);
150 | if (strpos($path . '/', $vendorDir . '/') === 0) {
151 | $aliases["@$name"] = '' . substr($path, strlen($vendorDir)) . '/' . $name;
152 | } else {
153 | $aliases["@$name"] = $path . '/' . $name;
154 | }
155 | }
156 | }
157 |
158 | if (!empty($autoload['psr-4'])) {
159 | foreach ($autoload['psr-4'] as $name => $path) {
160 | if (is_array($path)) {
161 | // ignore psr-4 autoload specifications with multiple search paths
162 | // we can not convert them into aliases as they are ambiguous
163 | continue;
164 | }
165 | $name = str_replace('\\', '/', trim($name, '\\'));
166 | if (!$fs->isAbsolutePath($path)) {
167 | $path = $this->vendorDir . '/' . $package->getPrettyName() . '/' . $path;
168 | }
169 | $path = $fs->normalizePath($path);
170 | if (strpos($path . '/', $vendorDir . '/') === 0) {
171 | $aliases["@$name"] = '' . substr($path, strlen($vendorDir));
172 | } else {
173 | $aliases["@$name"] = $path;
174 | }
175 | }
176 | }
177 |
178 | return $aliases;
179 | }
180 |
181 | protected function removePackage(PackageInterface $package)
182 | {
183 | $packages = $this->loadExtensions();
184 | unset($packages[$package->getName()]);
185 | $this->saveExtensions($packages);
186 | }
187 |
188 | protected function loadExtensions()
189 | {
190 | $file = $this->vendorDir . '/' . static::EXTENSION_FILE;
191 | if (!is_file($file)) {
192 | return [];
193 | }
194 | // invalidate opcache of extensions.php if exists
195 | if (function_exists('opcache_invalidate')) {
196 | @opcache_invalidate($file, true);
197 | }
198 | $extensions = require($file);
199 |
200 | $vendorDir = str_replace('\\', '/', $this->vendorDir);
201 | $n = strlen($vendorDir);
202 |
203 | foreach ($extensions as &$extension) {
204 | if (isset($extension['alias'])) {
205 | foreach ($extension['alias'] as $alias => $path) {
206 | $path = str_replace('\\', '/', $path);
207 | if (strpos($path . '/', $vendorDir . '/') === 0) {
208 | $extension['alias'][$alias] = '' . substr($path, $n);
209 | }
210 | }
211 | }
212 | }
213 |
214 | return $extensions;
215 | }
216 |
217 | protected function saveExtensions(array $extensions)
218 | {
219 | $file = $this->vendorDir . '/' . static::EXTENSION_FILE;
220 | if (!file_exists(dirname($file))) {
221 | mkdir(dirname($file), 0777, true);
222 | }
223 | $array = str_replace("'", '$vendorDir . \'', var_export($extensions, true));
224 | file_put_contents($file, "vendorDir . '/yiisoft/yii2';
234 | if (!file_exists($yiiDir)) {
235 | mkdir($yiiDir, 0777, true);
236 | }
237 | foreach (['Yii.php', 'BaseYii.php', 'classes.php'] as $file) {
238 | file_put_contents($yiiDir . '/' . $file, <<vendorDir . '/yiisoft/yii2';
258 | foreach (['Yii.php', 'BaseYii.php', 'classes.php'] as $file) {
259 | if (file_exists($yiiDir . '/' . $file)) {
260 | unlink($yiiDir . '/' . $file);
261 | }
262 | }
263 | if (file_exists($yiiDir)) {
264 | rmdir($yiiDir);
265 | }
266 | }
267 |
268 | /**
269 | * Special method to run tasks defined in `[extra][yii\composer\Installer::postCreateProject]` key in `composer.json`
270 | *
271 | * @param Event $event
272 | */
273 | public static function postCreateProject($event)
274 | {
275 | static::runCommands($event, __METHOD__);
276 | }
277 |
278 | /**
279 | * Special method to run tasks defined in `[extra][yii\composer\Installer::postInstall]` key in `composer.json`
280 | *
281 | * @param Event $event
282 | * @since 2.0.5
283 | */
284 | public static function postInstall($event)
285 | {
286 | static::runCommands($event, __METHOD__);
287 | }
288 |
289 | /**
290 | * Special method to run tasks defined in `[extra][$extraKey]` key in `composer.json`
291 | *
292 | * @param Event $event
293 | * @param string $extraKey
294 | * @since 2.0.5
295 | */
296 | protected static function runCommands($event, $extraKey)
297 | {
298 | $params = $event->getComposer()->getPackage()->getExtra();
299 | if (isset($params[$extraKey]) && is_array($params[$extraKey])) {
300 | foreach ($params[$extraKey] as $method => $args) {
301 | call_user_func_array([__CLASS__, $method], (array) $args);
302 | }
303 | }
304 | }
305 |
306 | /**
307 | * Sets the correct permission for the files and directories listed in the extra section.
308 | * @param array $paths the paths (keys) and the corresponding permission octal strings (values)
309 | */
310 | public static function setPermission(array $paths)
311 | {
312 | foreach ($paths as $path => $permission) {
313 | echo "chmod('$path', $permission)...";
314 | if (is_dir($path) || is_file($path)) {
315 | try {
316 | if (chmod($path, octdec($permission))) {
317 | echo "done.\n";
318 | };
319 | } catch (\Exception $e) {
320 | echo $e->getMessage() . "\n";
321 | }
322 | } else {
323 | echo "file not found.\n";
324 | }
325 | }
326 | }
327 |
328 | /**
329 | * Generates a cookie validation key for every app config listed in "config" in extra section.
330 | * You can provide one or multiple parameters as the configuration files which need to have validation key inserted.
331 | */
332 | public static function generateCookieValidationKey()
333 | {
334 | $configs = func_get_args();
335 | $key = self::generateRandomString();
336 | foreach ($configs as $config) {
337 | if (is_file($config)) {
338 | $content = preg_replace('/(("|\')cookieValidationKey("|\')\s*=>\s*)(""|\'\')/', "\\1'$key'", file_get_contents($config), -1, $count);
339 | if ($count > 0) {
340 | file_put_contents($config, $content);
341 | }
342 | }
343 | }
344 | }
345 |
346 | protected static function generateRandomString()
347 | {
348 | $length = 32;
349 | $bytes = self::generateRandomBytes($length);
350 | return strtr(substr(base64_encode($bytes), 0, $length), '+/=', '_-.');
351 | }
352 |
353 | protected static function generateRandomBytes($length)
354 | {
355 | if (function_exists('random_bytes')) {
356 | return random_bytes($length);
357 | }
358 |
359 | if (extension_loaded('openssl')) {
360 | return openssl_random_pseudo_bytes($length);
361 | }
362 |
363 | throw new \Exception('PHP >= 7.0 or the OpenSSL PHP extension is required by Yii2.');
364 | }
365 |
366 | /**
367 | * Copy files to specified locations.
368 | * @param array $paths The source files paths (keys) and the corresponding target locations
369 | * for copied files (values). Location can be specified as an array - first element is target
370 | * location, second defines whether file can be overwritten (by default method don't overwrite
371 | * existing files).
372 | * @since 2.0.5
373 | */
374 | public static function copyFiles(array $paths)
375 | {
376 | foreach ($paths as $source => $target) {
377 | // handle file target as array [path, overwrite]
378 | $target = (array) $target;
379 | echo "Copying file $source to $target[0] - ";
380 |
381 | if (!is_file($source)) {
382 | echo "source file not found.\n";
383 | continue;
384 | }
385 |
386 | if (is_file($target[0]) && empty($target[1])) {
387 | echo "target file exists - skip.\n";
388 | continue;
389 | } elseif (is_file($target[0]) && !empty($target[1])) {
390 | echo "target file exists - overwrite - ";
391 | }
392 |
393 | try {
394 | if (!is_dir(dirname($target[0]))) {
395 | mkdir(dirname($target[0]), 0777, true);
396 | }
397 | if (copy($source, $target[0])) {
398 | echo "done.\n";
399 | }
400 | } catch (\Exception $e) {
401 | echo $e->getMessage() . "\n";
402 | }
403 | }
404 | }
405 | }
406 |
--------------------------------------------------------------------------------