├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── box.json
├── composer.json
├── composer.lock
├── phpunit.xml.dist
├── src
└── Symfony
│ └── Installer
│ ├── AboutCommand.php
│ ├── Application.php
│ ├── DemoCommand.php
│ ├── DownloadCommand.php
│ ├── Exception
│ └── AbortException.php
│ ├── Manager
│ └── ComposerManager.php
│ ├── NewCommand.php
│ └── SelfUpdateCommand.php
├── symfony
└── tests
└── Symfony
└── Installer
└── Tests
├── IntegrationTest.php
└── Manager
└── ComposerManagerTest.php
/.gitignore:
--------------------------------------------------------------------------------
1 | /vendor/
2 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 | sudo: false
3 |
4 | cache:
5 | directories:
6 | - $HOME/.composer/cache/files
7 |
8 | matrix:
9 | fast_finish: true
10 | include:
11 | - php: 5.4
12 | - php: 5.5
13 | - php: 5.6
14 | - php: 7.0
15 | - php: 7.1
16 |
17 | before_install:
18 | - composer self-update
19 | - curl -LSs https://box-project.github.io/box2/installer.php | php
20 | - mv box.phar box
21 | - chmod 755 box
22 |
23 | install:
24 | - composer install
25 | - ~/.phpenv/versions/5.6/bin/php box build
26 |
27 | script:
28 | - ./vendor/bin/simple-phpunit
29 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2014-2016 Fabien Potencier
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is furnished
8 | to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Symfony Installer
2 | =================
3 |
4 | This is the official installer to start new projects based on the Symfony
5 | full-stack framework. The installer is only compatible with Symfony 2 and 3.
6 |
7 | Creating Symfony 4 projects
8 | ---------------------------
9 |
10 | **This installer is not compatible with Symfony 4** and newer versions. Instead,
11 | use [Composer](https://getcomposer.org/) and create your Symfony 4 project as follows:
12 |
13 | ```bash
14 | $ composer create-project symfony/skeleton my_project_name
15 | ```
16 |
17 | See the [Symfony Installation article](https://symfony.com/doc/current/setup.html)
18 | on the official Symfony Documentation for more details.
19 |
20 | Installing the installer
21 | ------------------------
22 |
23 | This step is only needed the first time you use the installer:
24 |
25 | ### Linux and Mac OS X
26 |
27 | ```bash
28 | $ sudo curl -LsS https://symfony.com/installer -o /usr/local/bin/symfony
29 | $ sudo chmod a+x /usr/local/bin/symfony
30 | ```
31 |
32 | ### Windows
33 |
34 | ```bash
35 | c:\> php -r "file_put_contents('symfony', file_get_contents('https://symfony.com/installer'));"
36 | ```
37 |
38 | Move the downloaded `symfony` file to your projects directory and execute
39 | it as follows:
40 |
41 | ```bash
42 | c:\> php symfony
43 | ```
44 |
45 | If you prefer to create a global `symfony` command, execute the following:
46 |
47 | ```bash
48 | c:\> (echo @ECHO OFF & echo php "%~dp0symfony" %*) > symfony.bat
49 | ```
50 |
51 | Then, move both files (`symfony` and `symfony.bat`) to any location included
52 | in your execution path. Now you can run the `symfony` command anywhere on your
53 | system.
54 |
55 | Using the installer
56 | -------------------
57 |
58 | **1. Start a new project with the latest stable Symfony version**
59 |
60 | Execute the `new` command and provide the name of your project as the only
61 | argument:
62 |
63 | ```bash
64 | # Linux, Mac OS X
65 | $ symfony new my_project
66 |
67 | # Windows
68 | c:\> php symfony new my_project
69 | ```
70 |
71 | **2. Start a new project with the latest Symfony LTS (Long Term Support) version**
72 |
73 | Execute the `new` command and provide the name of your project as the first
74 | argument and `lts` as the second argument. The installer will automatically
75 | select the most recent LTS (*Long Term Support*) version available:
76 |
77 | ```bash
78 | # Linux, Mac OS X
79 | $ symfony new my_project lts
80 |
81 | # Windows
82 | c:\> php symfony new my_project lts
83 | ```
84 |
85 | **3. Start a new project based on a specific Symfony branch**
86 |
87 | Execute the `new` command and provide the name of your project as the first
88 | argument and the branch number as the second argument. The installer will
89 | automatically select the most recent version available for the given branch:
90 |
91 | ```bash
92 | # Linux, Mac OS X
93 | $ symfony new my_project 2.8
94 |
95 | # Windows
96 | c:\> php symfony new my_project 2.8
97 | ```
98 |
99 | **4. Start a new project based on a specific Symfony version**
100 |
101 | Execute the `new` command and provide the name of your project as the first
102 | argument and the exact Symfony version as the second argument:
103 |
104 | ```bash
105 | # Linux, Mac OS X
106 | $ symfony new my_project 2.8.1
107 |
108 | # Windows
109 | c:\> php symfony new my_project 2.8.1
110 | ```
111 |
112 | **5. Install the Symfony demo application**
113 |
114 | The Symfony Demo is a reference application developed using the official Symfony
115 | Best Practices:
116 |
117 | ```bash
118 | # Linux, Mac OS X
119 | $ symfony demo
120 |
121 | # Windows
122 | c:\> php symfony demo
123 | ```
124 |
125 | Updating the installer
126 | ----------------------
127 |
128 | New versions of the Symfony Installer are released regularly. To update your
129 | installer version, execute the following command:
130 |
131 | ```bash
132 | # Linux, Mac OS X
133 | $ symfony self-update
134 |
135 | # Windows
136 | c:\> php symfony self-update
137 | ```
138 |
139 | > **NOTE**
140 | >
141 | > If your system requires the use of a proxy server to download contents, the
142 | > installer tries to guess the best proxy settings from the `HTTP_PROXY` and
143 | > `http_proxy` environment variables. Make sure any of them is set before
144 | > executing the Symfony Installer.
145 |
146 | Troubleshooting
147 | ---------------
148 |
149 | ### SSL and certificates issues on Windows systems
150 |
151 | If you experience any error related with SSL or security certificates when using
152 | the Symfony Installer on Windows systems:
153 |
154 | 1) Check that the OpenSSL extension is enabled in your `php.ini` configuration:
155 |
156 | ```ini
157 | ; make sure that the following line is uncommented
158 | extension=php_openssl.dll
159 | ```
160 |
161 | 2) Check that the path to the file that contains the security certificates
162 | exists and is defined in `php.ini`:
163 |
164 | ```ini
165 | openssl.cafile=C:/path/to/cacert.pem
166 | ```
167 |
168 | If you can't locate the `cacert.pem` file anywhere on your system, you can
169 | safely download it from the official website of the cURL project:
170 | http://curl.haxx.se/ca/cacert.pem
171 |
--------------------------------------------------------------------------------
/box.json:
--------------------------------------------------------------------------------
1 | {
2 | "directories": ["src/"],
3 | "finder": [
4 | {
5 | "name": "*.php",
6 | "exclude": [
7 | ".gitignore",
8 | ".md",
9 | "phpunit",
10 | "Tester",
11 | "Tests",
12 | "tests",
13 | "yaml"
14 | ],
15 | "in": "vendor"
16 | }
17 | ],
18 | "compactors": [
19 | "Herrera\\Box\\Compactor\\Json",
20 | "Herrera\\Box\\Compactor\\Php"
21 | ],
22 | "compression": "GZ",
23 | "git-version": "package_version",
24 | "main": "symfony",
25 | "output": "symfony.phar",
26 | "stub": true,
27 | "chmod": "0755"
28 | }
29 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "symfony/symfony-installer",
3 | "description": "The official installer to create projects based on the Symfony full-stack framework.",
4 | "keywords": ["symfony", "framework", "installer", "php"],
5 | "license": "MIT",
6 | "authors": [
7 | {
8 | "name": "Symfony Community",
9 | "homepage": "https://symfony.com/contributors"
10 | }
11 | ],
12 | "autoload": {
13 | "psr-4": { "Symfony\\": "src/Symfony/" }
14 | },
15 | "require": {
16 | "php": ">=5.4.0",
17 | "guzzlehttp/guzzle": "^5.3.1",
18 | "symfony/console": "~2.6",
19 | "symfony/filesystem": "~2.5",
20 | "raulfraile/distill": "~0.9,!=0.9.3,!=0.9.4"
21 | },
22 | "require-dev": {
23 | "symfony/process": "~2.5",
24 | "symfony/phpunit-bridge": "^4.0"
25 | },
26 | "extra": {
27 | "branch-alias": {
28 | "dev-master": "1.0-dev"
29 | }
30 | },
31 | "bin": [
32 | "symfony"
33 | ]
34 | }
35 |
--------------------------------------------------------------------------------
/composer.lock:
--------------------------------------------------------------------------------
1 | {
2 | "_readme": [
3 | "This file locks the dependencies of your project to a known state",
4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
5 | "This file is @generated automatically"
6 | ],
7 | "content-hash": "8f72036c453520314de3967a312a6733",
8 | "packages": [
9 | {
10 | "name": "guzzlehttp/guzzle",
11 | "version": "5.3.1",
12 | "source": {
13 | "type": "git",
14 | "url": "https://github.com/guzzle/guzzle.git",
15 | "reference": "70f1fa53b71c4647bf2762c09068a95f77e12fb8"
16 | },
17 | "dist": {
18 | "type": "zip",
19 | "url": "https://api.github.com/repos/guzzle/guzzle/zipball/70f1fa53b71c4647bf2762c09068a95f77e12fb8",
20 | "reference": "70f1fa53b71c4647bf2762c09068a95f77e12fb8",
21 | "shasum": ""
22 | },
23 | "require": {
24 | "guzzlehttp/ringphp": "^1.1",
25 | "php": ">=5.4.0"
26 | },
27 | "require-dev": {
28 | "ext-curl": "*",
29 | "phpunit/phpunit": "^4.0"
30 | },
31 | "type": "library",
32 | "autoload": {
33 | "psr-4": {
34 | "GuzzleHttp\\": "src/"
35 | }
36 | },
37 | "notification-url": "https://packagist.org/downloads/",
38 | "license": [
39 | "MIT"
40 | ],
41 | "authors": [
42 | {
43 | "name": "Michael Dowling",
44 | "email": "mtdowling@gmail.com",
45 | "homepage": "https://github.com/mtdowling"
46 | }
47 | ],
48 | "description": "Guzzle is a PHP HTTP client library and framework for building RESTful web service clients",
49 | "homepage": "http://guzzlephp.org/",
50 | "keywords": [
51 | "client",
52 | "curl",
53 | "framework",
54 | "http",
55 | "http client",
56 | "rest",
57 | "web service"
58 | ],
59 | "time": "2016-07-15T19:28:39+00:00"
60 | },
61 | {
62 | "name": "guzzlehttp/ringphp",
63 | "version": "1.1.0",
64 | "source": {
65 | "type": "git",
66 | "url": "https://github.com/guzzle/RingPHP.git",
67 | "reference": "dbbb91d7f6c191e5e405e900e3102ac7f261bc0b"
68 | },
69 | "dist": {
70 | "type": "zip",
71 | "url": "https://api.github.com/repos/guzzle/RingPHP/zipball/dbbb91d7f6c191e5e405e900e3102ac7f261bc0b",
72 | "reference": "dbbb91d7f6c191e5e405e900e3102ac7f261bc0b",
73 | "shasum": ""
74 | },
75 | "require": {
76 | "guzzlehttp/streams": "~3.0",
77 | "php": ">=5.4.0",
78 | "react/promise": "~2.0"
79 | },
80 | "require-dev": {
81 | "ext-curl": "*",
82 | "phpunit/phpunit": "~4.0"
83 | },
84 | "suggest": {
85 | "ext-curl": "Guzzle will use specific adapters if cURL is present"
86 | },
87 | "type": "library",
88 | "extra": {
89 | "branch-alias": {
90 | "dev-master": "1.1-dev"
91 | }
92 | },
93 | "autoload": {
94 | "psr-4": {
95 | "GuzzleHttp\\Ring\\": "src/"
96 | }
97 | },
98 | "notification-url": "https://packagist.org/downloads/",
99 | "license": [
100 | "MIT"
101 | ],
102 | "authors": [
103 | {
104 | "name": "Michael Dowling",
105 | "email": "mtdowling@gmail.com",
106 | "homepage": "https://github.com/mtdowling"
107 | }
108 | ],
109 | "description": "Provides a simple API and specification that abstracts away the details of HTTP into a single PHP function.",
110 | "time": "2015-05-20T03:37:09+00:00"
111 | },
112 | {
113 | "name": "guzzlehttp/streams",
114 | "version": "3.0.0",
115 | "source": {
116 | "type": "git",
117 | "url": "https://github.com/guzzle/streams.git",
118 | "reference": "47aaa48e27dae43d39fc1cea0ccf0d84ac1a2ba5"
119 | },
120 | "dist": {
121 | "type": "zip",
122 | "url": "https://api.github.com/repos/guzzle/streams/zipball/47aaa48e27dae43d39fc1cea0ccf0d84ac1a2ba5",
123 | "reference": "47aaa48e27dae43d39fc1cea0ccf0d84ac1a2ba5",
124 | "shasum": ""
125 | },
126 | "require": {
127 | "php": ">=5.4.0"
128 | },
129 | "require-dev": {
130 | "phpunit/phpunit": "~4.0"
131 | },
132 | "type": "library",
133 | "extra": {
134 | "branch-alias": {
135 | "dev-master": "3.0-dev"
136 | }
137 | },
138 | "autoload": {
139 | "psr-4": {
140 | "GuzzleHttp\\Stream\\": "src/"
141 | }
142 | },
143 | "notification-url": "https://packagist.org/downloads/",
144 | "license": [
145 | "MIT"
146 | ],
147 | "authors": [
148 | {
149 | "name": "Michael Dowling",
150 | "email": "mtdowling@gmail.com",
151 | "homepage": "https://github.com/mtdowling"
152 | }
153 | ],
154 | "description": "Provides a simple abstraction over streams of data",
155 | "homepage": "http://guzzlephp.org/",
156 | "keywords": [
157 | "Guzzle",
158 | "stream"
159 | ],
160 | "time": "2014-10-12T19:18:40+00:00"
161 | },
162 | {
163 | "name": "pimple/pimple",
164 | "version": "v3.0.2",
165 | "source": {
166 | "type": "git",
167 | "url": "https://github.com/silexphp/Pimple.git",
168 | "reference": "a30f7d6e57565a2e1a316e1baf2a483f788b258a"
169 | },
170 | "dist": {
171 | "type": "zip",
172 | "url": "https://api.github.com/repos/silexphp/Pimple/zipball/a30f7d6e57565a2e1a316e1baf2a483f788b258a",
173 | "reference": "a30f7d6e57565a2e1a316e1baf2a483f788b258a",
174 | "shasum": ""
175 | },
176 | "require": {
177 | "php": ">=5.3.0"
178 | },
179 | "type": "library",
180 | "extra": {
181 | "branch-alias": {
182 | "dev-master": "3.0.x-dev"
183 | }
184 | },
185 | "autoload": {
186 | "psr-0": {
187 | "Pimple": "src/"
188 | }
189 | },
190 | "notification-url": "https://packagist.org/downloads/",
191 | "license": [
192 | "MIT"
193 | ],
194 | "authors": [
195 | {
196 | "name": "Fabien Potencier",
197 | "email": "fabien@symfony.com"
198 | }
199 | ],
200 | "description": "Pimple, a simple Dependency Injection Container",
201 | "homepage": "http://pimple.sensiolabs.org",
202 | "keywords": [
203 | "container",
204 | "dependency injection"
205 | ],
206 | "time": "2015-09-11T15:10:35+00:00"
207 | },
208 | {
209 | "name": "raulfraile/distill",
210 | "version": "v0.9.9",
211 | "source": {
212 | "type": "git",
213 | "url": "https://github.com/raulfraile/distill.git",
214 | "reference": "8bc2fb68b570c07e9fa35e4b5d631bca68fcb193"
215 | },
216 | "dist": {
217 | "type": "zip",
218 | "url": "https://api.github.com/repos/raulfraile/distill/zipball/8bc2fb68b570c07e9fa35e4b5d631bca68fcb193",
219 | "reference": "8bc2fb68b570c07e9fa35e4b5d631bca68fcb193",
220 | "shasum": ""
221 | },
222 | "require": {
223 | "php": ">=5.4.0",
224 | "pimple/pimple": "~3.0",
225 | "symfony/filesystem": "~2.4",
226 | "symfony/process": "~2.4"
227 | },
228 | "require-dev": {
229 | "mockery/mockery": "0.9.1",
230 | "raulfraile/ladybug": "~1.0",
231 | "symfony/finder": "~2.4"
232 | },
233 | "suggest": {
234 | "ext-rar": "Allows to uncompress rar files using RarArchive",
235 | "ext-zip": "Allows to uncompress zip files using ZipArchive"
236 | },
237 | "type": "library",
238 | "autoload": {
239 | "psr-4": {
240 | "Distill\\": "src/"
241 | }
242 | },
243 | "notification-url": "https://packagist.org/downloads/",
244 | "license": [
245 | "MIT"
246 | ],
247 | "authors": [
248 | {
249 | "name": "Raul Fraile",
250 | "email": "raulfraile@gmail.com"
251 | }
252 | ],
253 | "description": "Smart compressed files extractor",
254 | "keywords": [
255 | "7zip",
256 | "archive",
257 | "bz2",
258 | "bzip",
259 | "bzip2",
260 | "cab",
261 | "compression",
262 | "epub",
263 | "extractor",
264 | "gzip",
265 | "phar",
266 | "rar",
267 | "strategy",
268 | "tar.gz",
269 | "tar.xz",
270 | "tgz",
271 | "unzip",
272 | "xz",
273 | "zip"
274 | ],
275 | "time": "2015-10-17T06:41:00+00:00"
276 | },
277 | {
278 | "name": "react/promise",
279 | "version": "v2.2.1",
280 | "source": {
281 | "type": "git",
282 | "url": "https://github.com/reactphp/promise.git",
283 | "reference": "3b6fca09c7d56321057fa8867c8dbe1abf648627"
284 | },
285 | "dist": {
286 | "type": "zip",
287 | "url": "https://api.github.com/repos/reactphp/promise/zipball/3b6fca09c7d56321057fa8867c8dbe1abf648627",
288 | "reference": "3b6fca09c7d56321057fa8867c8dbe1abf648627",
289 | "shasum": ""
290 | },
291 | "require": {
292 | "php": ">=5.4.0"
293 | },
294 | "type": "library",
295 | "extra": {
296 | "branch-alias": {
297 | "dev-master": "2.0-dev"
298 | }
299 | },
300 | "autoload": {
301 | "psr-4": {
302 | "React\\Promise\\": "src/"
303 | },
304 | "files": [
305 | "src/functions_include.php"
306 | ]
307 | },
308 | "notification-url": "https://packagist.org/downloads/",
309 | "license": [
310 | "MIT"
311 | ],
312 | "authors": [
313 | {
314 | "name": "Jan Sorgalla",
315 | "email": "jsorgalla@gmail.com"
316 | }
317 | ],
318 | "description": "A lightweight implementation of CommonJS Promises/A for PHP",
319 | "time": "2015-07-03T13:48:55+00:00"
320 | },
321 | {
322 | "name": "symfony/console",
323 | "version": "v2.7.5",
324 | "source": {
325 | "type": "git",
326 | "url": "https://github.com/symfony/console.git",
327 | "reference": "06cb17c013a82f94a3d840682b49425cd00a2161"
328 | },
329 | "dist": {
330 | "type": "zip",
331 | "url": "https://api.github.com/repos/symfony/console/zipball/06cb17c013a82f94a3d840682b49425cd00a2161",
332 | "reference": "06cb17c013a82f94a3d840682b49425cd00a2161",
333 | "shasum": ""
334 | },
335 | "require": {
336 | "php": ">=5.3.9"
337 | },
338 | "require-dev": {
339 | "psr/log": "~1.0",
340 | "symfony/event-dispatcher": "~2.1",
341 | "symfony/phpunit-bridge": "~2.7",
342 | "symfony/process": "~2.1"
343 | },
344 | "suggest": {
345 | "psr/log": "For using the console logger",
346 | "symfony/event-dispatcher": "",
347 | "symfony/process": ""
348 | },
349 | "type": "library",
350 | "extra": {
351 | "branch-alias": {
352 | "dev-master": "2.7-dev"
353 | }
354 | },
355 | "autoload": {
356 | "psr-4": {
357 | "Symfony\\Component\\Console\\": ""
358 | }
359 | },
360 | "notification-url": "https://packagist.org/downloads/",
361 | "license": [
362 | "MIT"
363 | ],
364 | "authors": [
365 | {
366 | "name": "Fabien Potencier",
367 | "email": "fabien@symfony.com"
368 | },
369 | {
370 | "name": "Symfony Community",
371 | "homepage": "https://symfony.com/contributors"
372 | }
373 | ],
374 | "description": "Symfony Console Component",
375 | "homepage": "https://symfony.com",
376 | "time": "2015-09-25T08:32:23+00:00"
377 | },
378 | {
379 | "name": "symfony/filesystem",
380 | "version": "v2.7.5",
381 | "source": {
382 | "type": "git",
383 | "url": "https://github.com/symfony/filesystem.git",
384 | "reference": "a17f8a17c20e8614c15b8e116e2f4bcde102cfab"
385 | },
386 | "dist": {
387 | "type": "zip",
388 | "url": "https://api.github.com/repos/symfony/filesystem/zipball/a17f8a17c20e8614c15b8e116e2f4bcde102cfab",
389 | "reference": "a17f8a17c20e8614c15b8e116e2f4bcde102cfab",
390 | "shasum": ""
391 | },
392 | "require": {
393 | "php": ">=5.3.9"
394 | },
395 | "require-dev": {
396 | "symfony/phpunit-bridge": "~2.7"
397 | },
398 | "type": "library",
399 | "extra": {
400 | "branch-alias": {
401 | "dev-master": "2.7-dev"
402 | }
403 | },
404 | "autoload": {
405 | "psr-4": {
406 | "Symfony\\Component\\Filesystem\\": ""
407 | }
408 | },
409 | "notification-url": "https://packagist.org/downloads/",
410 | "license": [
411 | "MIT"
412 | ],
413 | "authors": [
414 | {
415 | "name": "Fabien Potencier",
416 | "email": "fabien@symfony.com"
417 | },
418 | {
419 | "name": "Symfony Community",
420 | "homepage": "https://symfony.com/contributors"
421 | }
422 | ],
423 | "description": "Symfony Filesystem Component",
424 | "homepage": "https://symfony.com",
425 | "time": "2015-09-09T17:42:36+00:00"
426 | },
427 | {
428 | "name": "symfony/process",
429 | "version": "v2.7.5",
430 | "source": {
431 | "type": "git",
432 | "url": "https://github.com/symfony/process.git",
433 | "reference": "b27c8e317922cd3cdd3600850273cf6b82b2e8e9"
434 | },
435 | "dist": {
436 | "type": "zip",
437 | "url": "https://api.github.com/repos/symfony/process/zipball/b27c8e317922cd3cdd3600850273cf6b82b2e8e9",
438 | "reference": "b27c8e317922cd3cdd3600850273cf6b82b2e8e9",
439 | "shasum": ""
440 | },
441 | "require": {
442 | "php": ">=5.3.9"
443 | },
444 | "require-dev": {
445 | "symfony/phpunit-bridge": "~2.7"
446 | },
447 | "type": "library",
448 | "extra": {
449 | "branch-alias": {
450 | "dev-master": "2.7-dev"
451 | }
452 | },
453 | "autoload": {
454 | "psr-4": {
455 | "Symfony\\Component\\Process\\": ""
456 | }
457 | },
458 | "notification-url": "https://packagist.org/downloads/",
459 | "license": [
460 | "MIT"
461 | ],
462 | "authors": [
463 | {
464 | "name": "Fabien Potencier",
465 | "email": "fabien@symfony.com"
466 | },
467 | {
468 | "name": "Symfony Community",
469 | "homepage": "https://symfony.com/contributors"
470 | }
471 | ],
472 | "description": "Symfony Process Component",
473 | "homepage": "https://symfony.com",
474 | "time": "2015-09-19T19:59:23+00:00"
475 | }
476 | ],
477 | "packages-dev": [
478 | {
479 | "name": "symfony/phpunit-bridge",
480 | "version": "v4.0.2",
481 | "source": {
482 | "type": "git",
483 | "url": "https://github.com/symfony/phpunit-bridge.git",
484 | "reference": "61c84ebdce0d4c289413a222ee545f0114e60120"
485 | },
486 | "dist": {
487 | "type": "zip",
488 | "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/61c84ebdce0d4c289413a222ee545f0114e60120",
489 | "reference": "61c84ebdce0d4c289413a222ee545f0114e60120",
490 | "shasum": ""
491 | },
492 | "require": {
493 | "php": ">=5.3.3"
494 | },
495 | "conflict": {
496 | "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0"
497 | },
498 | "suggest": {
499 | "ext-zip": "Zip support is required when using bin/simple-phpunit",
500 | "symfony/debug": "For tracking deprecated interfaces usages at runtime with DebugClassLoader"
501 | },
502 | "bin": [
503 | "bin/simple-phpunit"
504 | ],
505 | "type": "symfony-bridge",
506 | "extra": {
507 | "branch-alias": {
508 | "dev-master": "4.0-dev"
509 | }
510 | },
511 | "autoload": {
512 | "files": [
513 | "bootstrap.php"
514 | ],
515 | "psr-4": {
516 | "Symfony\\Bridge\\PhpUnit\\": ""
517 | },
518 | "exclude-from-classmap": [
519 | "/Tests/"
520 | ]
521 | },
522 | "notification-url": "https://packagist.org/downloads/",
523 | "license": [
524 | "MIT"
525 | ],
526 | "authors": [
527 | {
528 | "name": "Nicolas Grekas",
529 | "email": "p@tchwork.com"
530 | },
531 | {
532 | "name": "Symfony Community",
533 | "homepage": "https://symfony.com/contributors"
534 | }
535 | ],
536 | "description": "Symfony PHPUnit Bridge",
537 | "homepage": "https://symfony.com",
538 | "time": "2017-12-14T19:48:22+00:00"
539 | }
540 | ],
541 | "aliases": [],
542 | "minimum-stability": "stable",
543 | "stability-flags": [],
544 | "prefer-stable": false,
545 | "prefer-lowest": false,
546 | "platform": {
547 | "php": ">=5.4.0"
548 | },
549 | "platform-dev": []
550 | }
551 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ./tests
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/Symfony/Installer/AboutCommand.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Installer;
13 |
14 | use Symfony\Component\Console\Command\Command;
15 | use Symfony\Component\Console\Input\InputInterface;
16 | use Symfony\Component\Console\Output\OutputInterface;
17 |
18 | /**
19 | * This command provides information about the Symfony installer.
20 | *
21 | * @author Javier Eguiluz
22 | */
23 | class AboutCommand extends Command
24 | {
25 | /**
26 | * @var string The current version of the Symfony installer
27 | */
28 | private $appVersion;
29 |
30 | /**
31 | * Constructor.
32 | *
33 | * @param string $appVersion The current version of the Symfony installer
34 | */
35 | public function __construct($appVersion)
36 | {
37 | parent::__construct();
38 |
39 | $this->appVersion = $appVersion;
40 | }
41 |
42 | /**
43 | * {@inheritdoc}
44 | */
45 | protected function configure()
46 | {
47 | $this
48 | ->setName('about')
49 | ->setDescription('Symfony Installer Help.')
50 | ;
51 | }
52 |
53 | /**
54 | * {@inheritdoc}
55 | */
56 | protected function execute(InputInterface $input, OutputInterface $output)
57 | {
58 | $commandHelp = <<blog in the current directory using
67 | the latest stable version of Symfony, execute the following command:
68 |
69 | %s new blog
70 |
71 | Create a project based on the Symfony Long Term Support version (LTS):
72 |
73 | %3\$s new blog lts
74 |
75 | Create a project based on a specific Symfony branch:
76 |
77 | %3\$s new blog 2.8 or %3\$s new blog 3.0
78 |
79 | Create a project based on a specific Symfony version:
80 |
81 | %3\$s new blog 2.8.1 or %3\$s new blog 3.0.1
82 |
83 | Create a demo application to learn how a Symfony application works:
84 |
85 | %3\$s demo
86 |
87 | COMMAND_HELP;
88 |
89 | // show the self-update information only when using the PHAR file
90 | if ('phar://' === substr(__DIR__, 0, 7)) {
91 | $commandUpdateHelp = <<update your
97 | installer version, execute the following command:
98 |
99 | %3\$s self-update
100 |
101 | COMMAND_UPDATE_HELP;
102 |
103 | $commandHelp .= $commandUpdateHelp;
104 | }
105 |
106 | $output->writeln(sprintf($commandHelp,
107 | $this->appVersion,
108 | str_repeat('=', 20 + strlen($this->appVersion)),
109 | $this->getExecutedCommand()
110 | ));
111 | }
112 |
113 | /**
114 | * Returns the executed command.
115 | *
116 | * @return string The executed command
117 | */
118 | private function getExecutedCommand()
119 | {
120 | $pathDirs = explode(PATH_SEPARATOR, $_SERVER['PATH']);
121 | $executedCommand = $_SERVER['PHP_SELF'];
122 | $executedCommandDir = dirname($executedCommand);
123 |
124 | if (in_array($executedCommandDir, $pathDirs)) {
125 | $executedCommand = basename($executedCommand);
126 | }
127 |
128 | return $executedCommand;
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/src/Symfony/Installer/Application.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Installer;
13 |
14 | use Symfony\Component\Console\Input\InputInterface;
15 | use Symfony\Component\Console\Output\OutputInterface;
16 | use Symfony\Component\Console\Application as ConsoleApplication;
17 |
18 | /**
19 | * This is main Symfony Installer console application class.
20 | *
21 | * @author Jerzy Zawadzki
22 | */
23 | class Application extends ConsoleApplication
24 | {
25 | const VERSIONS_URL = 'https://get.symfony.com/symfony.version';
26 |
27 | /**
28 | * {@inheritdoc}
29 | */
30 | public function doRun(InputInterface $input, OutputInterface $output)
31 | {
32 | return parent::doRun($input, $output);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/Symfony/Installer/DemoCommand.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Installer;
13 |
14 | use Symfony\Component\Console\Input\InputArgument;
15 | use Symfony\Component\Console\Input\InputInterface;
16 | use Symfony\Component\Console\Output\OutputInterface;
17 | use Symfony\Installer\Exception\AbortException;
18 | use Symfony\Installer\Manager\ComposerManager;
19 |
20 | /**
21 | * This command creates a full-featured Symfony demo application.
22 | *
23 | * @author Javier Eguiluz
24 | */
25 | class DemoCommand extends DownloadCommand
26 | {
27 | /**
28 | * {@inheritdoc}
29 | */
30 | protected function configure()
31 | {
32 | $this
33 | ->setName('demo')
34 | ->addArgument('directory', InputArgument::OPTIONAL, 'Directory where the new project will be created.')
35 | ->setDescription('Creates a demo Symfony project.')
36 | ;
37 | }
38 |
39 | /**
40 | * {@inheritdoc}
41 | */
42 | protected function initialize(InputInterface $input, OutputInterface $output)
43 | {
44 | parent::initialize($input, $output);
45 |
46 | $this->version = 'lts';
47 |
48 | if (!$input->getArgument('directory')) {
49 | $this->projectDir = getcwd();
50 |
51 | $i = 1;
52 | $projectName = 'symfony_demo';
53 | while (file_exists($this->projectDir.DIRECTORY_SEPARATOR.$projectName)) {
54 | $projectName = 'symfony_demo_'.(++$i);
55 | }
56 |
57 | $this->projectName = $projectName;
58 | $this->projectDir = $this->projectDir.DIRECTORY_SEPARATOR.$projectName;
59 | } else {
60 | $directory = rtrim(trim($input->getArgument('directory')), DIRECTORY_SEPARATOR);
61 | $this->projectDir = $this->fs->isAbsolutePath($directory) ? $directory : getcwd().DIRECTORY_SEPARATOR.$directory;
62 | $this->projectName = basename($directory);
63 | }
64 |
65 | $this->composerManager = new ComposerManager($this->projectDir);
66 | }
67 |
68 | /**
69 | * {@inheritdoc}
70 | */
71 | protected function execute(InputInterface $input, OutputInterface $output)
72 | {
73 | try {
74 | $this
75 | ->checkInstallerVersion()
76 | ->checkProjectName()
77 | ->checkPermissions()
78 | ->download()
79 | ->extract()
80 | ->cleanUp()
81 | ->updateComposerConfig()
82 | ->createGitIgnore()
83 | ->checkSymfonyRequirements()
84 | ->displayInstallationResult()
85 | ;
86 | } catch (AbortException $e) {
87 | aborted:
88 |
89 | $output->writeln('');
90 | $output->writeln('Aborting download and cleaning up temporary directories.>');
91 |
92 | $this->cleanUp();
93 |
94 | return 1;
95 | } catch (\Exception $e) {
96 | // Guzzle can wrap the AbortException in a GuzzleException
97 | if ($e->getPrevious() instanceof AbortException) {
98 | goto aborted;
99 | }
100 |
101 | $this->cleanUp();
102 | throw $e;
103 | }
104 | }
105 |
106 | /**
107 | * Removes all the temporary files and directories created to
108 | * download the demo application.
109 | *
110 | * @return $this
111 | */
112 | private function cleanUp()
113 | {
114 | $this->fs->remove(dirname($this->downloadedFilePath));
115 |
116 | return $this;
117 | }
118 |
119 | /**
120 | * It displays the message with the result of installing the Symfony Demo
121 | * application and provides some pointers to the user.
122 | *
123 | * @return $this
124 | */
125 | private function displayInstallationResult()
126 | {
127 | if (empty($this->requirementsErrors)) {
128 | $this->output->writeln(sprintf(
129 | " %s Symfony Demo Application was successfully installed. Now you can:\n",
130 | defined('PHP_WINDOWS_VERSION_BUILD') ? 'OK' : '✔'
131 | ));
132 | } else {
133 | $this->output->writeln(sprintf(
134 | " %s Symfony Demo Application was successfully installed but your system doesn't meet the\n".
135 | " technical requirements to run Symfony applications! Fix the following issues before executing it:\n",
136 | defined('PHP_WINDOWS_VERSION_BUILD') ? 'FAILED' : '✕'
137 | ));
138 |
139 | foreach ($this->requirementsErrors as $helpText) {
140 | $this->output->writeln(' * '.$helpText);
141 | }
142 |
143 | $this->output->writeln(sprintf(
144 | " After fixing these issues, re-check Symfony requirements executing this command:\n\n".
145 | " php %s/bin/symfony_requirements\n\n".
146 | " Then, you can:\n",
147 | $this->projectName
148 | ));
149 | }
150 |
151 | $serverRunCommand = extension_loaded('pcntl') ? 'server:start' : 'server:run';
152 |
153 | $this->output->writeln(sprintf(
154 | " 1. Change your current directory to %s\n\n".
155 | " 2. Execute the php bin/console %s command to run the demo application.\n\n".
156 | " 3. Browse to the http://localhost:8000 URL to see the demo application in action.\n\n",
157 | $this->projectDir, $serverRunCommand
158 | ));
159 |
160 | $this->output->writeln(
161 | " WARNING >\n\n".
162 | " This installer downloads the old Symfony Demo version based on Symfony 3.\n".
163 | " If you prefer to install the new version based on Symfony 4 and Symfony Flex,\n".
164 | " execute the following command:\n\n".
165 | " composer create-project symfony/symfony-demo\n");
166 |
167 | return $this;
168 | }
169 |
170 | /**
171 | * {@inheritdoc}
172 | */
173 | protected function getDownloadedApplicationType()
174 | {
175 | return 'the Symfony Demo Application';
176 | }
177 |
178 | /**
179 | * {@inheritdoc}
180 | */
181 | protected function getRemoteFileUrl()
182 | {
183 | return 'https://symfony.com/download?v=Symfony_Demo';
184 | }
185 | }
186 |
--------------------------------------------------------------------------------
/src/Symfony/Installer/DownloadCommand.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Installer;
13 |
14 | use Distill\Distill;
15 | use Distill\Exception\IO\Input\FileCorruptedException;
16 | use Distill\Exception\IO\Input\FileEmptyException;
17 | use Distill\Exception\IO\Output\TargetDirectoryNotWritableException;
18 | use Distill\Strategy\MinimumSize;
19 | use GuzzleHttp\Client;
20 | use GuzzleHttp\Exception\ClientException;
21 | use GuzzleHttp\Event\ProgressEvent;
22 | use GuzzleHttp\Utils;
23 | use Symfony\Component\Console\Command\Command;
24 | use Symfony\Component\Console\Helper\Helper;
25 | use Symfony\Component\Console\Helper\ProgressBar;
26 | use Symfony\Component\Console\Input\InputInterface;
27 | use Symfony\Component\Console\Output\OutputInterface;
28 | use Symfony\Component\Filesystem\Exception\IOException;
29 | use Symfony\Component\Filesystem\Filesystem;
30 | use Symfony\Component\Intl\Exception\MethodArgumentValueNotImplementedException;
31 | use Symfony\Installer\Exception\AbortException;
32 | use Symfony\Installer\Manager\ComposerManager;
33 |
34 | /**
35 | * Abstract command used by commands which download and extract compressed Symfony files.
36 | *
37 | * @author Christophe Coevoet
38 | * @author Javier Eguiluz
39 | */
40 | abstract class DownloadCommand extends Command
41 | {
42 | /**
43 | * @var Filesystem To dump content to a file
44 | */
45 | protected $fs;
46 |
47 | /**
48 | * @var OutputInterface To output content
49 | */
50 | protected $output;
51 |
52 | /**
53 | * @var string The project name
54 | */
55 | protected $projectName;
56 |
57 | /**
58 | * @var string The project dir
59 | */
60 | protected $projectDir;
61 |
62 | /**
63 | * @var string The version to install
64 | */
65 | protected $version = 'latest';
66 |
67 | /**
68 | * @var string The latest installer version
69 | */
70 | protected $latestInstallerVersion;
71 |
72 | /**
73 | * @var string The version of the local installer being executed
74 | */
75 | protected $localInstallerVersion;
76 |
77 | /**
78 | * @var string The path to the downloaded file
79 | */
80 | protected $downloadedFilePath;
81 |
82 | /**
83 | * @var array The requirement errors
84 | */
85 | protected $requirementsErrors = array();
86 |
87 | /** @var ComposerManager */
88 | protected $composerManager;
89 |
90 | /**
91 | * Returns the type of the downloaded application in a human readable format.
92 | * It's mainly used to display readable error messages.
93 | *
94 | * @return string The type of the downloaded application in a human readable format
95 | */
96 | abstract protected function getDownloadedApplicationType();
97 |
98 | /**
99 | * Returns the absolute URL of the remote file downloaded by the command.
100 | *
101 | * @return string The absolute URL of the remote file downloaded by the command
102 | */
103 | abstract protected function getRemoteFileUrl();
104 |
105 | /**
106 | * {@inheritdoc}
107 | */
108 | protected function initialize(InputInterface $input, OutputInterface $output)
109 | {
110 | $this->output = $output;
111 | $this->fs = new Filesystem();
112 |
113 | $this->latestInstallerVersion = $this->getUrlContents(Application::VERSIONS_URL);
114 | $this->localInstallerVersion = $this->getApplication()->getVersion();
115 |
116 | $this->enableSignalHandler();
117 | }
118 |
119 | /**
120 | * Chooses the best compressed file format to download (ZIP or TGZ) depending upon the
121 | * available operating system uncompressing commands and the enabled PHP extensions
122 | * and it downloads the file.
123 | *
124 | * @return $this
125 | *
126 | * @throws \RuntimeException If the Symfony archive could not be downloaded
127 | */
128 | protected function download()
129 | {
130 | $this->output->writeln(sprintf("\n Downloading %s...\n", $this->getDownloadedApplicationType()));
131 |
132 | // decide which is the best compressed version to download
133 | $distill = new Distill();
134 | $symfonyArchiveFile = $distill
135 | ->getChooser()
136 | ->setStrategy(new MinimumSize())
137 | ->addFilesWithDifferentExtensions($this->getRemoteFileUrl(), array('tgz', 'zip'))
138 | ->getPreferredFile()
139 | ;
140 |
141 | /** @var ProgressBar|null $progressBar */
142 | $progressBar = null;
143 | $downloadCallback = function (ProgressEvent $event) use (&$progressBar) {
144 | $downloadSize = $event->downloadSize;
145 | $downloaded = $event->downloaded;
146 |
147 | // progress bar is only displayed for files larger than 1MB
148 | if ($downloadSize < 1 * 1024 * 1024) {
149 | return;
150 | }
151 |
152 | if (null === $progressBar) {
153 | ProgressBar::setPlaceholderFormatterDefinition('max', function (ProgressBar $bar) {
154 | return Helper::formatMemory($bar->getMaxSteps());
155 | });
156 | ProgressBar::setPlaceholderFormatterDefinition('current', function (ProgressBar $bar) {
157 | return str_pad(Helper::formatMemory($bar->getProgress()), 11, ' ', STR_PAD_LEFT);
158 | });
159 |
160 | $progressBar = new ProgressBar($this->output, $downloadSize);
161 | $progressBar->setFormat('%current%/%max% %bar% %percent:3s%%');
162 | $progressBar->setRedrawFrequency(max(1, floor($downloadSize / 1000)));
163 | $progressBar->setBarWidth(60);
164 |
165 | if (!defined('PHP_WINDOWS_VERSION_BUILD')) {
166 | $progressBar->setEmptyBarCharacter('░'); // light shade character \u2591
167 | $progressBar->setProgressCharacter('');
168 | $progressBar->setBarCharacter('▓'); // dark shade character \u2593
169 | }
170 |
171 | $progressBar->start();
172 | }
173 |
174 | $progressBar->setProgress($downloaded);
175 | };
176 |
177 | $client = $this->getGuzzleClient();
178 |
179 | // store the file in a temporary hidden directory with a random name
180 | $this->downloadedFilePath = rtrim(getcwd(), DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.'.'.uniqid(time()).DIRECTORY_SEPARATOR.'symfony.'.pathinfo($symfonyArchiveFile, PATHINFO_EXTENSION);
181 |
182 | try {
183 | $request = $client->createRequest('GET', $symfonyArchiveFile);
184 | $request->getEmitter()->on('progress', $downloadCallback);
185 | $response = $client->send($request);
186 | } catch (ClientException $e) {
187 | if ('new' === $this->getName() && (403 === $e->getCode() || 404 === $e->getCode())) {
188 | throw new \RuntimeException(sprintf(
189 | "The selected version (%s) cannot be installed because it does not exist.\n".
190 | "Execute the following command to install the latest stable Symfony release:\n".
191 | '%s new %s',
192 | $this->version,
193 | $_SERVER['PHP_SELF'],
194 | str_replace(getcwd().DIRECTORY_SEPARATOR, '', $this->projectDir)
195 | ));
196 | } else {
197 | throw new \RuntimeException(sprintf(
198 | "There was an error downloading %s from symfony.com server:\n%s",
199 | $this->getDownloadedApplicationType(),
200 | $e->getMessage()
201 | ), null, $e);
202 | }
203 | }
204 |
205 | $this->fs->dumpFile($this->downloadedFilePath, $response->getBody());
206 |
207 | if (null !== $progressBar) {
208 | $progressBar->finish();
209 | $this->output->writeln("\n");
210 | }
211 |
212 | return $this;
213 | }
214 |
215 | /**
216 | * Checks the project name.
217 | *
218 | * @return $this
219 | *
220 | * @throws \RuntimeException If there is already a projet in the specified directory
221 | */
222 | protected function checkProjectName()
223 | {
224 | if (is_dir($this->projectDir) && !$this->isEmptyDirectory($this->projectDir)) {
225 | throw new \RuntimeException(sprintf(
226 | "There is already a '%s' project in this directory (%s).\n".
227 | 'Change your project name or create it in another directory.',
228 | $this->projectName, $this->projectDir
229 | ));
230 | }
231 |
232 | if ('demo' === $this->projectName && 'new' === $this->getName()) {
233 | $this->output->writeln("\n TIP > If you want to download the Symfony Demo app, execute 'symfony demo' instead of 'symfony new demo'");
234 | }
235 |
236 | return $this;
237 | }
238 |
239 | /**
240 | * Returns the Guzzle client configured according to the system environment
241 | * (e.g. it takes into account whether it should use a proxy server or not).
242 | *
243 | * @return Client The configured Guzzle client
244 | *
245 | * @throws \RuntimeException If the php-curl is not installed or the allow_url_fopen ini setting is not set
246 | */
247 | protected function getGuzzleClient()
248 | {
249 | $defaults = array();
250 |
251 | // check if the client must use a proxy server
252 | if (!empty($_SERVER['HTTP_PROXY']) || !empty($_SERVER['http_proxy'])) {
253 | $defaults['proxy'] = !empty($_SERVER['http_proxy']) ? $_SERVER['http_proxy'] : $_SERVER['HTTP_PROXY'];
254 | }
255 |
256 | if ($this->output->getVerbosity() >= OutputInterface::VERBOSITY_DEBUG) {
257 | $defaults['debug'] = true;
258 | }
259 |
260 | try {
261 | $handler = Utils::getDefaultHandler();
262 | } catch (\RuntimeException $e) {
263 | throw new \RuntimeException('The Symfony installer requires the php-curl extension or the allow_url_fopen ini setting.');
264 | }
265 |
266 | return new Client(array('defaults' => $defaults, 'handler' => $handler));
267 | }
268 |
269 | /**
270 | * Extracts the compressed Symfony file (ZIP or TGZ) using the
271 | * native operating system commands if available or PHP code otherwise.
272 | *
273 | * @return $this
274 | *
275 | * @throws \RuntimeException If the downloaded archive could not be extracted
276 | */
277 | protected function extract()
278 | {
279 | $this->output->writeln(" Preparing project...\n");
280 |
281 | try {
282 | $distill = new Distill();
283 | $extractionSucceeded = $distill->extractWithoutRootDirectory($this->downloadedFilePath, $this->projectDir);
284 | } catch (FileCorruptedException $e) {
285 | throw new \RuntimeException(sprintf(
286 | "%s can't be installed because the downloaded package is corrupted.\n".
287 | "To solve this issue, try executing this command again:\n%s",
288 | ucfirst($this->getDownloadedApplicationType()), $this->getExecutedCommand()
289 | ));
290 | } catch (FileEmptyException $e) {
291 | throw new \RuntimeException(sprintf(
292 | "%s can't be installed because the downloaded package is empty.\n".
293 | "To solve this issue, try executing this command again:\n%s",
294 | ucfirst($this->getDownloadedApplicationType()), $this->getExecutedCommand()
295 | ));
296 | } catch (TargetDirectoryNotWritableException $e) {
297 | throw new \RuntimeException(sprintf(
298 | "%s can't be installed because the installer doesn't have enough\n".
299 | "permissions to uncompress and rename the package contents.\n".
300 | "To solve this issue, check the permissions of the %s directory and\n".
301 | "try executing this command again:\n%s",
302 | ucfirst($this->getDownloadedApplicationType()), getcwd(), $this->getExecutedCommand()
303 | ));
304 | } catch (\Exception $e) {
305 | throw new \RuntimeException(sprintf(
306 | "%s can't be installed because the downloaded package is corrupted\n".
307 | "or because the installer doesn't have enough permissions to uncompress and\n".
308 | "rename the package contents.\n".
309 | "To solve this issue, check the permissions of the %s directory and\n".
310 | "try executing this command again:\n%s",
311 | ucfirst($this->getDownloadedApplicationType()), getcwd(), $this->getExecutedCommand()
312 | ), null, $e);
313 | }
314 |
315 | if (!$extractionSucceeded) {
316 | throw new \RuntimeException(sprintf(
317 | "%s can't be installed because the downloaded package is corrupted\n".
318 | "or because the uncompress commands of your operating system didn't work.",
319 | ucfirst($this->getDownloadedApplicationType())
320 | ));
321 | }
322 |
323 | return $this;
324 | }
325 |
326 | /**
327 | * Checks if environment meets symfony requirements.
328 | *
329 | * @return $this
330 | */
331 | protected function checkSymfonyRequirements()
332 | {
333 | if (null === $requirementsFile = $this->getSymfonyRequirementsFilePath()) {
334 | return $this;
335 | }
336 |
337 | try {
338 | require $requirementsFile;
339 | $symfonyRequirements = new \SymfonyRequirements();
340 | $this->requirementsErrors = array();
341 | foreach ($symfonyRequirements->getRequirements() as $req) {
342 | if ($helpText = $this->getErrorMessage($req)) {
343 | $this->requirementsErrors[] = $helpText;
344 | }
345 | }
346 | } catch (MethodArgumentValueNotImplementedException $e) {
347 | // workaround https://github.com/symfony/symfony-installer/issues/163
348 | }
349 |
350 | return $this;
351 | }
352 |
353 | private function getSymfonyRequirementsFilePath()
354 | {
355 | $paths = array(
356 | $this->projectDir.'/app/SymfonyRequirements.php',
357 | $this->projectDir.'/var/SymfonyRequirements.php',
358 | );
359 |
360 | foreach ($paths as $path) {
361 | if (file_exists($path)) {
362 | return $path;
363 | }
364 | }
365 |
366 | return null;
367 | }
368 |
369 | /**
370 | * Updates the composer.json file to provide better values for some of the
371 | * default configuration values.
372 | *
373 | * @return $this
374 | */
375 | protected function updateComposerConfig()
376 | {
377 | $this->composerManager->initializeProjectConfig();
378 |
379 | return $this;
380 | }
381 |
382 | /**
383 | * Creates the appropriate .gitignore file for a Symfony project if it doesn't exist.
384 | *
385 | * @return $this
386 | */
387 | protected function createGitIgnore()
388 | {
389 | if (!is_file($path = $this->projectDir.'/.gitignore')) {
390 | try {
391 | $client = $this->getGuzzleClient();
392 |
393 | $response = $client->get(sprintf(
394 | 'https://raw.githubusercontent.com/symfony/symfony-standard/v%s/.gitignore',
395 | $this->getInstalledSymfonyVersion()
396 | ));
397 |
398 | $this->fs->dumpFile($path, $response->getBody()->getContents());
399 | } catch (\Exception $e) {
400 | // don't throw an exception in case the .gitignore file cannot be created,
401 | // because this is just an enhancement, not something mandatory for the project
402 | }
403 | }
404 |
405 | return $this;
406 | }
407 |
408 | /**
409 | * Returns the full Symfony version number of the project by getting
410 | * it from the composer.lock file.
411 | *
412 | * @return string The installed Symfony version
413 | */
414 | protected function getInstalledSymfonyVersion()
415 | {
416 | $symfonyVersion = $this->composerManager->getPackageVersion('symfony/symfony');
417 |
418 | if (!empty($symfonyVersion) && 'v' === substr($symfonyVersion, 0, 1)) {
419 | return substr($symfonyVersion, 1);
420 | }
421 |
422 | return $symfonyVersion;
423 | }
424 |
425 | /**
426 | * Checks if the installer has enough permissions to create the project.
427 | *
428 | * @return $this
429 | *
430 | * @throws IOException If the installer does not have enough permissions to write to the project parent directory
431 | */
432 | protected function checkPermissions()
433 | {
434 | $projectParentDirectory = dirname($this->projectDir);
435 |
436 | if (!is_writable($projectParentDirectory)) {
437 | throw new IOException(sprintf('Installer does not have enough permissions to write to the "%s" directory.', $projectParentDirectory));
438 | }
439 |
440 | return $this;
441 | }
442 |
443 | /**
444 | * Formats the error message contained in the given Requirement item
445 | * using the optional line length provided.
446 | *
447 | * @param \Requirement $requirement The Symfony requirements
448 | * @param int $lineSize The maximum line length
449 | *
450 | * @return string The formatted error message
451 | */
452 | protected function getErrorMessage(\Requirement $requirement, $lineSize = 70)
453 | {
454 | if ($requirement->isFulfilled()) {
455 | return;
456 | }
457 |
458 | $errorMessage = wordwrap($requirement->getTestMessage(), $lineSize - 3, PHP_EOL.' ').PHP_EOL;
459 | $errorMessage .= ' > '.wordwrap($requirement->getHelpText(), $lineSize - 5, PHP_EOL.' > ').PHP_EOL;
460 |
461 | return $errorMessage;
462 | }
463 |
464 | /**
465 | * Generates a good random value for Symfony's 'secret' option.
466 | *
467 | * @return string The randomly generated secret
468 | */
469 | protected function generateRandomSecret()
470 | {
471 | if (function_exists('openssl_random_pseudo_bytes')) {
472 | return hash('sha1', openssl_random_pseudo_bytes(23));
473 | }
474 |
475 | return hash('sha1', uniqid(mt_rand(), true));
476 | }
477 |
478 | /**
479 | * Returns the executed command with all its arguments
480 | * (e.g. "symfony new blog 2.8.1").
481 | *
482 | * @return string The executed command with all its arguments
483 | */
484 | protected function getExecutedCommand()
485 | {
486 | $commandBinary = $_SERVER['PHP_SELF'];
487 | $commandBinaryDir = dirname($commandBinary);
488 | $pathDirs = explode(PATH_SEPARATOR, $_SERVER['PATH']);
489 | if (in_array($commandBinaryDir, $pathDirs)) {
490 | $commandBinary = basename($commandBinary);
491 | }
492 |
493 | $commandName = $this->getName();
494 |
495 | if ('new' === $commandName) {
496 | $commandArguments = sprintf('%s %s', $this->projectName, ('latest' !== $this->version) ? $this->version : '');
497 | } elseif ('demo' === $commandName) {
498 | $commandArguments = '';
499 | }
500 |
501 | return sprintf('%s %s %s', $commandBinary, $commandName, $commandArguments);
502 | }
503 |
504 | /**
505 | * Checks whether the given directory is empty or not.
506 | *
507 | * @param string $dir the path of the directory to check
508 | *
509 | * @return bool Whether the given directory is empty
510 | */
511 | protected function isEmptyDirectory($dir)
512 | {
513 | // glob() cannot be used because it doesn't take into account hidden files
514 | // scandir() returns '.' and '..' for an empty dir
515 | return 2 === count(scandir($dir.'/'));
516 | }
517 |
518 | /**
519 | * Checks that the asked version is in the 3.x branch.
520 | *
521 | * @return bool Whether is Symfony3
522 | */
523 | protected function isSymfony3()
524 | {
525 | return '3' === $this->version[0] || 'latest' === $this->version;
526 | }
527 |
528 | /**
529 | * Checks if the installed version is the latest one and displays some
530 | * warning messages if not.
531 | *
532 | * @return $this
533 | */
534 | protected function checkInstallerVersion()
535 | {
536 | // check update only if installer is running via a PHAR file
537 | if ('phar://' !== substr(__DIR__, 0, 7)) {
538 | return $this;
539 | }
540 |
541 | if (!$this->isInstallerUpdated()) {
542 | $this->output->writeln(sprintf(
543 | "\n WARNING > Your Symfony Installer version (%s) is outdated.\n".
544 | ' Execute the command "%s selfupdate" to get the latest version (%s).',
545 | $this->localInstallerVersion, $_SERVER['PHP_SELF'], $this->latestInstallerVersion
546 | ));
547 | }
548 |
549 | return $this;
550 | }
551 |
552 | /**
553 | * @return bool Whether the installed version is the latest one
554 | */
555 | protected function isInstallerUpdated()
556 | {
557 | return version_compare($this->localInstallerVersion, $this->latestInstallerVersion, '>=');
558 | }
559 |
560 | /**
561 | * Returns the contents obtained by making a GET request to the given URL.
562 | *
563 | * @param string $url The URL to get the contents from
564 | *
565 | * @return string The obtained contents of $url
566 | */
567 | protected function getUrlContents($url)
568 | {
569 | $client = $this->getGuzzleClient();
570 |
571 | return $client->get($url)->getBody()->getContents();
572 | }
573 |
574 | /**
575 | * Enables the signal handler.
576 | *
577 | * @throws AbortException if the execution has been aborted with SIGINT signal
578 | */
579 | private function enableSignalHandler()
580 | {
581 | if (!function_exists('pcntl_signal')) {
582 | return;
583 | }
584 |
585 | declare(ticks=1);
586 |
587 | pcntl_signal(SIGINT, function () {
588 | error_reporting(0);
589 |
590 | throw new AbortException();
591 | });
592 | }
593 | }
594 |
--------------------------------------------------------------------------------
/src/Symfony/Installer/Exception/AbortException.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | class AbortException extends \RuntimeException
11 | {
12 | }
13 |
--------------------------------------------------------------------------------
/src/Symfony/Installer/Manager/ComposerManager.php:
--------------------------------------------------------------------------------
1 | projectDir = $projectDir;
15 | $this->fs = new Filesystem();
16 | }
17 |
18 | public function initializeProjectConfig()
19 | {
20 | $composerConfig = $this->getProjectConfig();
21 |
22 | if (isset($composerConfig['config']['platform']['php'])) {
23 | unset($composerConfig['config']['platform']['php']);
24 |
25 | if (empty($composerConfig['config']['platform'])) {
26 | unset($composerConfig['config']['platform']);
27 | }
28 |
29 | if (empty($composerConfig['config'])) {
30 | unset($composerConfig['config']);
31 | }
32 | }
33 |
34 | $this->saveProjectConfig($composerConfig);
35 | }
36 |
37 | public function updateProjectConfig(array $newConfig)
38 | {
39 | $oldConfig = $this->getProjectConfig();
40 | $projectConfig = array_replace_recursive($oldConfig, $newConfig);
41 |
42 | // remove null values from project's config
43 | $projectConfig = array_filter($projectConfig, function($value) { return !is_null($value); });
44 |
45 | $this->saveProjectConfig($projectConfig);
46 | }
47 |
48 | public function getPackageVersion($packageName)
49 | {
50 | $composerLockFileContents = json_decode(file_get_contents($this->projectDir.'/composer.lock'), true);
51 |
52 | foreach ($composerLockFileContents['packages'] as $packageConfig) {
53 | if ($packageName === $packageConfig['name']) {
54 | return $packageConfig['version'];
55 | }
56 | }
57 | }
58 |
59 | /**
60 | * Generates a good Composer project name based on the application name
61 | * and on the user name.
62 | *
63 | * @param $projectName
64 | *
65 | * @return string The generated Composer package name
66 | */
67 | public function createPackageName($projectName)
68 | {
69 | if (!empty($_SERVER['USERNAME'])) {
70 | $packageName = $_SERVER['USERNAME'].'/'.$projectName;
71 | } elseif (true === extension_loaded('posix') && $user = posix_getpwuid(posix_getuid())) {
72 | $packageName = $user['name'].'/'.$projectName;
73 | } elseif (get_current_user()) {
74 | $packageName = get_current_user().'/'.$projectName;
75 | } else {
76 | // package names must be in the format foo/bar
77 | $packageName = $projectName.'/'.$projectName;
78 | }
79 |
80 | return $this->fixPackageName($packageName);
81 | }
82 |
83 | /**
84 | * It returns the project's Composer config as a PHP array.
85 | *
86 | * @return array
87 | */
88 | private function getProjectConfig()
89 | {
90 | $composerJsonPath = $this->projectDir.'/composer.json';
91 | if (!is_writable($composerJsonPath)) {
92 | return [];
93 | }
94 |
95 | return json_decode(file_get_contents($composerJsonPath), true);
96 | }
97 |
98 | /**
99 | * It saves the given PHP array as the project's Composer config. In addition
100 | * to JSON-serializing the contents, it synchronizes the composer.lock file to
101 | * avoid out-of-sync Composer errors.
102 | *
103 | * @param array $config
104 | */
105 | private function saveProjectConfig(array $config)
106 | {
107 | $composerJsonPath = $this->projectDir.'/composer.json';
108 | $this->fs->dumpFile($composerJsonPath, json_encode($config, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)."\n");
109 |
110 | $this->syncComposerLockFile();
111 | }
112 |
113 | /**
114 | * Updates the hash values stored in composer.lock to avoid out-of-sync
115 | * problems when the composer.json file contents are changed.
116 | */
117 | private function syncComposerLockFile()
118 | {
119 | $composerJsonFileContents = file_get_contents($this->projectDir.'/composer.json');
120 | $composerLockFileContents = json_decode(file_get_contents($this->projectDir.'/composer.lock'), true);
121 |
122 | if (array_key_exists('hash', $composerLockFileContents)) {
123 | $composerLockFileContents['hash'] = md5($composerJsonFileContents);
124 | }
125 |
126 | if (array_key_exists('content-hash', $composerLockFileContents)) {
127 | $composerLockFileContents['content-hash'] = $this->getComposerContentHash($composerJsonFileContents);
128 | }
129 |
130 | $this->fs->dumpFile($this->projectDir.'/composer.lock', json_encode($composerLockFileContents, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)."\n");
131 | }
132 |
133 | /**
134 | * Returns the md5 hash of the sorted content of the composer file.
135 | *
136 | * @see https://github.com/composer/composer/blob/master/src/Composer/Package/Locker.php (getContentHash() method)
137 | *
138 | * @param string $composerJsonFileContents The contents of the composer.json file.
139 | *
140 | * @return string The hash of the composer file content.
141 | */
142 | private function getComposerContentHash($composerJsonFileContents)
143 | {
144 | $composerConfig = json_decode($composerJsonFileContents, true);
145 |
146 | $relevantKeys = array(
147 | 'name',
148 | 'version',
149 | 'require',
150 | 'require-dev',
151 | 'conflict',
152 | 'replace',
153 | 'provide',
154 | 'minimum-stability',
155 | 'prefer-stable',
156 | 'repositories',
157 | 'extra',
158 | );
159 |
160 | $relevantComposerConfig = array();
161 |
162 | foreach (array_intersect($relevantKeys, array_keys($composerConfig)) as $key) {
163 | $relevantComposerConfig[$key] = $composerConfig[$key];
164 | }
165 |
166 | if (isset($composerConfig['config']['platform'])) {
167 | $relevantComposerConfig['config']['platform'] = $composerConfig['config']['platform'];
168 | }
169 |
170 | ksort($relevantComposerConfig);
171 |
172 | return md5(json_encode($relevantComposerConfig));
173 | }
174 |
175 | /**
176 | * Transforms a project name into a valid Composer package name.
177 | *
178 | * @param string $name The project name to transform
179 | *
180 | * @return string The valid Composer package name
181 | */
182 | private function fixPackageName($name)
183 | {
184 | $name = str_replace(
185 | ['à', 'á', 'â', 'ä', 'æ', 'ã', 'å', 'ā', 'é', 'è', 'ê', 'ë', 'ę', 'ė', 'ē', 'ī', 'į', 'í', 'ì', 'ï', 'î', 'ō', 'ø', 'œ', 'õ', 'ó', 'ò', 'ö', 'ô', 'ū', 'ú', 'ù', 'ü', 'û', 'ç', 'ć', 'č', 'ł', 'ñ', 'ń', 'ß', 'ś', 'š', 'ŵ', 'ŷ', 'ÿ', 'ź', 'ž', 'ż'],
186 | ['a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'e', 'e', 'e', 'e', 'e', 'e', 'e', 'i', 'i', 'i', 'i', 'i', 'i', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'u', 'u', 'u', 'u', 'u', 'c', 'c', 'c', 'l', 'n', 'n', 's', 's', 's', 'w', 'y', 'y', 'z', 'z', 'z'],
187 | $name
188 | );
189 | $name = preg_replace('#[^A-Za-z0-9_./-]+#', '', $name);
190 |
191 | return strtolower($name);
192 | }
193 | }
194 |
--------------------------------------------------------------------------------
/src/Symfony/Installer/NewCommand.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Installer;
13 |
14 | use Symfony\Component\Console\Input\InputArgument;
15 | use Symfony\Component\Console\Input\InputInterface;
16 | use Symfony\Component\Console\Output\OutputInterface;
17 | use Symfony\Installer\Exception\AbortException;
18 | use Symfony\Installer\Manager\ComposerManager;
19 |
20 | /**
21 | * This command creates new Symfony projects for the given Symfony version.
22 | *
23 | * @author Christophe Coevoet
24 | * @author Javier Eguiluz
25 | */
26 | class NewCommand extends DownloadCommand
27 | {
28 | /**
29 | * {@inheritdoc}
30 | */
31 | protected function configure()
32 | {
33 | $this
34 | ->setName('new')
35 | ->setDescription('Creates a new Symfony project.')
36 | ->addArgument('directory', InputArgument::REQUIRED, 'Directory where the new project will be created.')
37 | ->addArgument('version', InputArgument::OPTIONAL, 'The Symfony version to be installed (defaults to the latest stable version).', 'latest')
38 | ;
39 | }
40 |
41 | /**
42 | * {@inheritdoc}
43 | */
44 | protected function initialize(InputInterface $input, OutputInterface $output)
45 | {
46 | parent::initialize($input, $output);
47 |
48 | $directory = rtrim(trim($input->getArgument('directory')), DIRECTORY_SEPARATOR);
49 | $this->version = trim($input->getArgument('version'));
50 | $this->projectDir = $this->fs->isAbsolutePath($directory) ? $directory : getcwd().DIRECTORY_SEPARATOR.$directory;
51 | $this->projectName = basename($directory);
52 |
53 | $this->composerManager = new ComposerManager($this->projectDir);
54 | }
55 |
56 | /**
57 | * {@inheritdoc}
58 | */
59 | protected function execute(InputInterface $input, OutputInterface $output)
60 | {
61 | try {
62 | $this
63 | ->checkInstallerVersion()
64 | ->checkProjectName()
65 | ->checkSymfonyVersionIsInstallable()
66 | ->checkPermissions()
67 | ->download()
68 | ->extract()
69 | ->cleanUp()
70 | ->dumpReadmeFile()
71 | ->updateParameters()
72 | ->updateComposerConfig()
73 | ->createGitIgnore()
74 | ->checkSymfonyRequirements()
75 | ->displayInstallationResult()
76 | ;
77 | } catch (AbortException $e) {
78 | aborted:
79 |
80 | $output->writeln('');
81 | $output->writeln('Aborting download and cleaning up temporary directories.>');
82 |
83 | $this->cleanUp();
84 |
85 | return 1;
86 | } catch (\Exception $e) {
87 | // Guzzle can wrap the AbortException in a GuzzleException
88 | if ($e->getPrevious() instanceof AbortException) {
89 | goto aborted;
90 | }
91 |
92 | $this->cleanUp();
93 | throw $e;
94 | }
95 | }
96 |
97 | /**
98 | * Checks whether the given Symfony version is installable by the installer.
99 | * Due to the changes introduced in the Icu/Intl components
100 | * (see https://symfony.com/blog/new-in-symfony-2-6-farewell-to-icu-component)
101 | * not all the previous Symfony versions are installable by the installer.
102 | *
103 | * The rules to decide if the version is installable are as follows:
104 | *
105 | * - 2.0, 2.1, 2.2 and 2.4 cannot be installed because they are unmaintained.
106 | * - 2.3 can be installed starting from version 2.3.21 (inclusive)
107 | * - 2.5 can be installed starting from version 2.5.6 (inclusive)
108 | * - 2.6, 2.7, 2.8 and 2.9 can be installed regardless the version.
109 | *
110 | * @return $this
111 | *
112 | * @throws \RuntimeException If the given Symfony version is not compatible with this installer
113 | */
114 | protected function checkSymfonyVersionIsInstallable()
115 | {
116 | // validate the given version syntax
117 | if (!preg_match('/^latest|lts|[2-9]\.\d(?:\.\d{1,2})?(?:-(?:dev|BETA\d*|RC\d*))?$/i', $this->version)) {
118 | throw new \RuntimeException(sprintf(
119 | "The Symfony version can be a branch number (e.g. 2.8), a full version\n".
120 | "number (e.g. 3.1.4), a special word ('latest' or 'lts') and a unstable\n".
121 | "version number (e.g. 3.2.0-rc1) but '%s' was given.", $this->version
122 | ));
123 | }
124 |
125 | // Get the full list of Symfony versions to check if it's installable
126 | $client = $this->getGuzzleClient();
127 | $symfonyVersions = $client->get('https://symfony.com/versions.json')->json();
128 | if (empty($symfonyVersions)) {
129 | throw new \RuntimeException(
130 | "There was a problem while downloading the list of Symfony versions from\n".
131 | "symfony.com. Check that you are online and the following URL is accessible:\n\n".
132 | 'https://symfony.com/versions.json'
133 | );
134 | }
135 |
136 | // if a branch number is used, transform it into a real version number
137 | if (preg_match('/^[2-9]\.\d$/', $this->version)) {
138 | if (!isset($symfonyVersions[$this->version])) {
139 | throw new \RuntimeException(sprintf(
140 | "The selected branch (%s) does not exist, or is not maintained.\n".
141 | "To solve this issue, install Symfony with the latest stable release:\n\n".
142 | '%s %s %s', $this->version, $_SERVER['PHP_SELF'], $this->getName(), $this->projectDir
143 | ));
144 | }
145 |
146 | $this->version = $symfonyVersions[$this->version];
147 | }
148 |
149 | // if a special version name is used, transform it into a real version number
150 | if (in_array($this->version, array('latest', 'lts'))) {
151 | $this->version = $symfonyVersions[$this->version];
152 | }
153 |
154 | // versions are case-sensitive in the download server (3.1.0-rc1 must be 3.1.0-RC1)
155 | if ($isUnstableVersion = preg_match('/^.*\-(BETA|RC)\d*$/i', $this->version)) {
156 | $this->version = strtoupper($this->version);
157 | }
158 |
159 | $isNonInstallable = in_array($this->version, $symfonyVersions['non_installable']);
160 | $isInstallable = in_array($this->version, $symfonyVersions['installable']);
161 |
162 | // installable and non-installable versions are explicitly declared in the
163 | // list of versions; there is an edge-case: unstable versions are not listed
164 | // and they are generally installable (e.g. 3.1.0-RC1)
165 | if ($isNonInstallable || (!$isInstallable && !$isUnstableVersion)) {
166 | throw new \RuntimeException(sprintf(
167 | "The selected version (%s) cannot be installed because it is not compatible\n".
168 | "with this installer or because it hasn't been published as a package yet.\n".
169 | "To solve this issue install Symfony manually executing the following command:\n\n".
170 | 'composer create-project symfony/framework-standard-edition %s %s',
171 | $this->version, $this->projectDir, $this->version
172 | ));
173 | }
174 |
175 | // check that the system has the PHP version required by the Symfony version to be installed
176 | if (version_compare($this->version, '3.0.0', '>=') && version_compare(PHP_VERSION, '5.5.9', '<')) {
177 | throw new \RuntimeException(sprintf(
178 | "The selected version (%s) cannot be installed because it requires\n".
179 | "PHP 5.5.9 or higher and your system has PHP %s installed.\n",
180 | $this->version, PHP_VERSION
181 | ));
182 | }
183 |
184 | // check that the Symfony version to be installed is not 4.x, which is incompatible with this installer
185 | if (version_compare($this->version, '4.0.0', '>=')) {
186 | throw new \RuntimeException(sprintf(
187 | "The Symfony Installer is not compatible with Symfony 4.x or newer versions.\n".
188 | "Run this other command to install Symfony using Composer instead:\n\n".
189 | 'composer create-project symfony/skeleton %s',
190 | $this->projectName
191 | ));
192 | }
193 |
194 | if ($isUnstableVersion) {
195 | $this->output->writeln("\n WARNING > You are downloading an unstable Symfony version.");
196 | }
197 |
198 | return $this;
199 | }
200 |
201 | /**
202 | * Removes all the temporary files and directories created to
203 | * download the project and removes Symfony-related files that don't make
204 | * sense in a proprietary project.
205 | *
206 | * @return $this
207 | */
208 | protected function cleanUp()
209 | {
210 | $this->fs->remove(dirname($this->downloadedFilePath));
211 |
212 | try {
213 | $licenseFile = array($this->projectDir.'/LICENSE');
214 | $upgradeFiles = glob($this->projectDir.'/UPGRADE*.md');
215 | $changelogFiles = glob($this->projectDir.'/CHANGELOG*.md');
216 |
217 | $filesToRemove = array_merge($licenseFile, $upgradeFiles, $changelogFiles);
218 | $this->fs->remove($filesToRemove);
219 | } catch (\Exception $e) {
220 | // don't throw an exception in case any of the Symfony-related files cannot
221 | // be removed, because this is just an enhancement, not something mandatory
222 | // for the project
223 | }
224 |
225 | return $this;
226 | }
227 |
228 | /**
229 | * It displays the message with the result of installing Symfony
230 | * and provides some pointers to the user.
231 | *
232 | * @return $this
233 | */
234 | protected function displayInstallationResult()
235 | {
236 | if (empty($this->requirementsErrors)) {
237 | $this->output->writeln(sprintf(
238 | " %s Symfony %s was successfully installed. Now you can:\n",
239 | defined('PHP_WINDOWS_VERSION_BUILD') ? 'OK' : '✔',
240 | $this->getInstalledSymfonyVersion()
241 | ));
242 | } else {
243 | $this->output->writeln(sprintf(
244 | " %s Symfony %s was successfully installed but your system doesn't meet its\n".
245 | " technical requirements! Fix the following issues before executing\n".
246 | " your Symfony application:\n",
247 | defined('PHP_WINDOWS_VERSION_BUILD') ? 'FAILED' : '✕',
248 | $this->getInstalledSymfonyVersion()
249 | ));
250 |
251 | foreach ($this->requirementsErrors as $helpText) {
252 | $this->output->writeln(' * '.$helpText);
253 | }
254 |
255 | $checkFile = $this->isSymfony3() ? 'bin/symfony_requirements' : 'app/check.php';
256 |
257 | $this->output->writeln(sprintf(
258 | " After fixing these issues, re-check Symfony requirements executing this command:\n\n".
259 | " php %s/%s\n\n".
260 | " Then, you can:\n",
261 | $this->projectName, $checkFile
262 | ));
263 | }
264 |
265 | if ('.' !== $this->projectDir) {
266 | $this->output->writeln(sprintf(
267 | " * Change your current directory to %s\n", $this->projectDir
268 | ));
269 | }
270 |
271 | $consoleDir = ($this->isSymfony3() ? 'bin' : 'app');
272 | $serverRunCommand = version_compare($this->version, '2.6.0', '>=') && extension_loaded('pcntl') ? 'server:start' : 'server:run';
273 |
274 | $this->output->writeln(sprintf(
275 | " * Configure your application in app/config/parameters.yml file.\n\n".
276 | " * Run your application:\n".
277 | " 1. Execute the php %s/console %s command.\n".
278 | " 2. Browse to the http://localhost:8000 URL.\n\n".
279 | " * Read the documentation at https://symfony.com/doc\n",
280 | $consoleDir, $serverRunCommand
281 | ));
282 |
283 | return $this;
284 | }
285 |
286 | /**
287 | * Dump a basic README.md file.
288 | *
289 | * @return $this
290 | */
291 | protected function dumpReadmeFile()
292 | {
293 | $readmeContents = sprintf("%s\n%s\n\nA Symfony project created on %s.\n", $this->projectName, str_repeat('=', strlen($this->projectName)), date('F j, Y, g:i a'));
294 | try {
295 | $this->fs->dumpFile($this->projectDir.'/README.md', $readmeContents);
296 | } catch (\Exception $e) {
297 | // don't throw an exception in case the file could not be created,
298 | // because this is just an enhancement, not something mandatory
299 | // for the project
300 | }
301 |
302 | return $this;
303 | }
304 |
305 | /**
306 | * Updates the Symfony parameters.yml file to replace default configuration
307 | * values with better generated values.
308 | *
309 | * @return $this
310 | */
311 | protected function updateParameters()
312 | {
313 | $filename = $this->projectDir.'/app/config/parameters.yml';
314 |
315 | if (!is_writable($filename)) {
316 | if ($this->output->isVerbose()) {
317 | $this->output->writeln(sprintf(
318 | " [WARNING] The value of the secret configuration option cannot be updated because\n".
319 | " the %s file is not writable.\n",
320 | $filename
321 | ));
322 | }
323 |
324 | return $this;
325 | }
326 |
327 | $ret = str_replace('ThisTokenIsNotSoSecretChangeIt', $this->generateRandomSecret(), file_get_contents($filename));
328 | file_put_contents($filename, $ret);
329 |
330 | return $this;
331 | }
332 |
333 | /**
334 | * Updates the composer.json file to provide better values for some of the
335 | * default configuration values.
336 | *
337 | * @return $this
338 | */
339 | protected function updateComposerConfig()
340 | {
341 | parent::updateComposerConfig();
342 | $this->composerManager->updateProjectConfig(array(
343 | 'name' => $this->composerManager->createPackageName($this->projectName),
344 | 'license' => 'proprietary',
345 | 'description' => null,
346 | 'extra' => array('branch-alias' => null),
347 | ));
348 |
349 | return $this;
350 | }
351 |
352 | /**
353 | * {@inheritdoc}
354 | */
355 | protected function getDownloadedApplicationType()
356 | {
357 | return 'Symfony';
358 | }
359 |
360 | /**
361 | * {@inheritdoc}
362 | */
363 | protected function getRemoteFileUrl()
364 | {
365 | return 'https://symfony.com/download?v=Symfony_Standard_Vendors_'.$this->version;
366 | }
367 | }
368 |
--------------------------------------------------------------------------------
/src/Symfony/Installer/SelfUpdateCommand.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Installer;
13 |
14 | use Symfony\Component\Console\Input\InputInterface;
15 | use Symfony\Component\Console\Input\InputOption;
16 | use Symfony\Component\Console\Output\OutputInterface;
17 | use Symfony\Component\Filesystem\Exception\IOException;
18 |
19 | /**
20 | * This command is inspired by the self-update command included
21 | * in the PHP-CS-Fixer library.
22 | *
23 | * @link https://github.com/fabpot/PHP-CS-Fixer/blob/master/Symfony/CS/Console/Command/SelfUpdateCommand.php.
24 | *
25 | * @author Igor Wiedler
26 | * @author Stephane PY
27 | * @author Grégoire Pineau
28 | */
29 | class SelfUpdateCommand extends DownloadCommand
30 | {
31 | /**
32 | * @var string The temp dir
33 | */
34 | private $tempDir;
35 |
36 | /**
37 | * @var string The URL where the latest installer version can be downloaded
38 | */
39 | private $remoteInstallerFile;
40 |
41 | /**
42 | * @var string The filepath of the installer currently installed in the local machine
43 | */
44 | private $currentInstallerFile;
45 |
46 | /**
47 | * @var string The filepath of the new installer downloaded to replace the current installer
48 | */
49 | private $newInstallerFile;
50 |
51 | /**
52 | * @var string The filepath of the backup of the current installer in case a rollback is performed
53 | */
54 | private $currentInstallerBackupFile;
55 |
56 | /**
57 | * @var bool Flag which indicates that, in case of a rollback, it's safe to restore the installer backup because
58 | * it corresponds to the most recent version
59 | */
60 | private $restorePreviousInstaller;
61 |
62 | /**
63 | * {@inheritdoc}
64 | */
65 | protected function configure()
66 | {
67 | $this
68 | ->setName('self-update')
69 | ->setAliases(array('selfupdate'))
70 | ->addOption('force-update', 'f', InputOption::VALUE_NONE, 'It updates the installer to the latest available version without checking if it\'s older or newer than the locally installed version.')
71 | ->setDescription('Update the Symfony Installer to the latest version.')
72 | ->setHelp('The %command.name% command updates the installer to the latest available version.')
73 | ;
74 | }
75 |
76 | /**
77 | * The self-update command is only available when using the installer via the PHAR file.
78 | *
79 | * @return bool Whether the command is enabled
80 | */
81 | public function isEnabled()
82 | {
83 | return 'phar://' === substr(__DIR__, 0, 7);
84 | }
85 |
86 | /**
87 | * {@inheritdoc}
88 | */
89 | protected function initialize(InputInterface $input, OutputInterface $output)
90 | {
91 | parent::initialize($input, $output);
92 | $this->remoteInstallerFile = 'https://symfony.com/installer';
93 | $this->currentInstallerFile = realpath($_SERVER['argv'][0]) ?: $_SERVER['argv'][0];
94 | $this->tempDir = sys_get_temp_dir();
95 | $this->currentInstallerBackupFile = basename($this->currentInstallerFile, '.phar').'-backup.phar';
96 | $this->newInstallerFile = $this->tempDir.'/'.basename($this->currentInstallerFile, '.phar').'-temp.phar';
97 | $this->restorePreviousInstaller = false;
98 | }
99 |
100 | /**
101 | * {@inheritdoc}
102 | */
103 | protected function execute(InputInterface $input, OutputInterface $output)
104 | {
105 | $forceUpdate = true === $input->getOption('force-update');
106 | if (!$forceUpdate && $this->isInstallerUpdated()) {
107 | $this->output->writeln(sprintf('// Symfony Installer is already updated to the latest version (%s).', $this->latestInstallerVersion));
108 |
109 | return;
110 | }
111 |
112 | $this->output->writeln(sprintf('// updating Symfony Installer to %s version', $this->latestInstallerVersion));
113 |
114 | try {
115 | $this
116 | ->downloadNewVersion()
117 | ->checkNewVersionIsValid()
118 | ->backupCurrentVersion()
119 | ->replaceCurrentVersionbyNewVersion()
120 | ->cleanUp()
121 | ;
122 | } catch (IOException $e) {
123 | if ($this->output->isVeryVerbose()) {
124 | $this->output->writeln($e->getMessage());
125 | }
126 |
127 | throw new \RuntimeException(sprintf(
128 | "The installer couldn't be updated, probably because of a permissions issue.\n".
129 | "Try to execute the command again with super user privileges:\n".
130 | " sudo %s\n",
131 | $this->getExecutedCommand()
132 | ));
133 | } catch (\Exception $e) {
134 | $this->rollback();
135 |
136 | if ($this->output->isVeryVerbose()) {
137 | $this->output->writeln($e->getMessage());
138 | }
139 |
140 | return 1;
141 | }
142 | }
143 |
144 | /**
145 | * Downloads the new version of the Symfony installer.
146 | *
147 | * @return $this
148 | */
149 | private function downloadNewVersion()
150 | {
151 | // check for permissions in local filesystem before start downloading files
152 | if (!is_writable($this->currentInstallerFile)) {
153 | throw new IOException('Symfony Installer update failed: the "'.$this->currentInstallerFile.'" file could not be written');
154 | }
155 |
156 | if (!is_writable($this->tempDir)) {
157 | throw new IOException('Symfony Installer update failed: the "'.$this->tempDir.'" directory used to download files temporarily could not be written');
158 | }
159 |
160 | if (false === $newInstaller = $this->getUrlContents($this->remoteInstallerFile)) {
161 | throw new \RuntimeException('The new version of the Symfony Installer couldn\'t be downloaded from the server.');
162 | }
163 |
164 | $newInstallerPermissions = $this->currentInstallerFile ? fileperms($this->currentInstallerFile) : 0777 & ~umask();
165 | $this->fs->dumpFile($this->newInstallerFile, $newInstaller, $newInstallerPermissions);
166 |
167 | return $this;
168 | }
169 |
170 | /**
171 | * Checks if the new version is valid.
172 | *
173 | * @return $this
174 | */
175 | private function checkNewVersionIsValid()
176 | {
177 | // creating a Phar instance for an existing file is not allowed
178 | // when the Phar extension is in readonly mode
179 | if (!ini_get('phar.readonly')) {
180 | // test the phar validity
181 | $phar = new \Phar($this->newInstallerFile);
182 |
183 | // free the variable to unlock the file
184 | unset($phar);
185 | }
186 |
187 | return $this;
188 | }
189 |
190 | /**
191 | * Does a backup of the current version of the Symfony installer.
192 | *
193 | * @return $this
194 | */
195 | private function backupCurrentVersion()
196 | {
197 | $this->fs->copy($this->currentInstallerFile, $this->currentInstallerBackupFile, true);
198 | $this->restorePreviousInstaller = true;
199 |
200 | return $this;
201 | }
202 |
203 | /**
204 | * Replaces the currenct version of the Symfony installer with the new one.
205 | *
206 | * @return $this
207 | */
208 | private function replaceCurrentVersionbyNewVersion()
209 | {
210 | $this->fs->copy($this->newInstallerFile, $this->currentInstallerFile, true);
211 |
212 | return $this;
213 | }
214 |
215 | /**
216 | * Removes the temporary used files.
217 | */
218 | private function cleanUp()
219 | {
220 | $this->fs->remove(array($this->currentInstallerBackupFile, $this->newInstallerFile));
221 | }
222 |
223 | /**
224 | * Restores the previously installed version of the Symfony installer.
225 | */
226 | private function rollback()
227 | {
228 | $this->output->writeln(array(
229 | '',
230 | 'There was an error while updating the installer.',
231 | 'The previous Symfony Installer version has been restored.',
232 | '',
233 | ));
234 |
235 | $this->fs->remove($this->newInstallerFile);
236 |
237 | if ($this->restorePreviousInstaller) {
238 | $this->fs->copy($this->currentInstallerBackupFile, $this->currentInstallerFile, true);
239 | }
240 | }
241 |
242 | /**
243 | * {@inheritdoc}
244 | */
245 | protected function getDownloadedApplicationType()
246 | {
247 | return 'Symfony Installer';
248 | }
249 |
250 | /**
251 | * {@inheritdoc}
252 | */
253 | protected function getRemoteFileUrl()
254 | {
255 | return 'https://symfony.com/installer';
256 | }
257 | }
258 |
--------------------------------------------------------------------------------
/symfony:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 | \n\n",
11 | PHP_VERSION
12 | ));
13 |
14 | exit(1);
15 | }
16 |
17 | if (extension_loaded('suhosin')) {
18 | file_put_contents('php://stderr',
19 | "Symfony Installer is not compatible with the 'suhosin' PHP extension.\n".
20 | "Disable that extension before running the installer.\n\n".
21 | "Alternatively, install Symfony manually executing the following command:\n\n".
22 | "composer create-project symfony/framework-standard-edition \n\n"
23 | );
24 |
25 | exit(1);
26 | }
27 |
28 | require file_exists(__DIR__.'/vendor/autoload.php')
29 | ? __DIR__.'/vendor/autoload.php'
30 | : __DIR__.'/../../autoload.php';
31 |
32 | $appVersion = '1.5.12-DEV';
33 |
34 | // Windows uses Path instead of PATH
35 | if (!isset($_SERVER['PATH']) && isset($_SERVER['Path'])) {
36 | $_SERVER['PATH'] = $_SERVER['Path'];
37 | }
38 |
39 | $app = new Symfony\Installer\Application('Symfony Installer', $appVersion);
40 | $app->add(new Symfony\Installer\AboutCommand($appVersion));
41 | $app->add(new Symfony\Installer\NewCommand());
42 | $app->add(new Symfony\Installer\DemoCommand());
43 | $app->add(new Symfony\Installer\SelfUpdateCommand());
44 |
45 | $app->setDefaultCommand('about');
46 |
47 | $app->run();
48 |
--------------------------------------------------------------------------------
/tests/Symfony/Installer/Tests/IntegrationTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Installer\Tests;
13 |
14 | use Symfony\Component\Console\Command\Command;
15 | use Symfony\Component\Process\Process;
16 | use Symfony\Component\Filesystem\Filesystem;
17 | use Symfony\Component\Process\Exception\ProcessFailedException;
18 | use Symfony\Component\Process\ProcessUtils;
19 |
20 | class IntegrationTest extends \PHPUnit_Framework_TestCase
21 | {
22 | /**
23 | * @var string The root directory
24 | */
25 | private $rootDir;
26 |
27 | /**
28 | * @var Filesystem The Filesystem component
29 | */
30 | private $fs;
31 |
32 | public function setUp()
33 | {
34 | $this->rootDir = realpath(__DIR__.'/../../../../');
35 | $this->fs = new Filesystem();
36 |
37 | if (!$this->fs->exists($this->rootDir.'/symfony.phar')) {
38 | throw new \RuntimeException(sprintf("Before running the tests, make sure that the Symfony Installer is available as a 'symfony.phar' file in the '%s' directory.", $this->rootDir));
39 | }
40 | }
41 |
42 | public function testDemoApplicationInstallation()
43 | {
44 | if (PHP_VERSION_ID < 50500) {
45 | $this->markTestSkipped('Symfony 3 requires PHP 5.5.9 or higher.');
46 | }
47 |
48 | $projectDir = sprintf('%s/my_test_project', sys_get_temp_dir());
49 | $this->fs->remove($projectDir);
50 |
51 | $output = $this->runCommand(sprintf('php symfony.phar demo %s', ProcessUtils::escapeArgument($projectDir)));
52 | $this->assertContains('Downloading the Symfony Demo Application', $output);
53 | $this->assertContains('Symfony Demo Application was successfully installed.', $output);
54 |
55 | $composerConfig = json_decode(file_get_contents($projectDir.'/composer.json'), true);
56 | $this->assertArrayNotHasKey('platform', $composerConfig['config'], 'The composer.json file does not define any platform configuration.');
57 | }
58 |
59 | /**
60 | * @dataProvider provideSymfonyInstallationData
61 | */
62 | public function testSymfonyInstallation($versionToInstall, $messageRegexp, $versionRegexp, $requiredPhpVersion)
63 | {
64 | if (version_compare(PHP_VERSION, $requiredPhpVersion, '<')) {
65 | $this->markTestSkipped(sprintf('This test requires PHP %s or higher.', $requiredPhpVersion));
66 | }
67 |
68 | $projectDir = sprintf('%s/my_test_project', sys_get_temp_dir());
69 | $this->fs->remove($projectDir);
70 |
71 | $output = $this->runCommand(sprintf('php symfony.phar new %s %s', ProcessUtils::escapeArgument($projectDir), $versionToInstall));
72 | $this->assertContains('Downloading Symfony...', $output);
73 | $this->assertRegExp($messageRegexp, $output);
74 |
75 | if (file_exists($projectDir.'/app/console')) {
76 | $output = $this->runCommand('php app/console --version', $projectDir);
77 | } else {
78 | $output = $this->runCommand('php bin/console --version', $projectDir);
79 | }
80 |
81 | $this->assertRegExp($versionRegexp, $output);
82 |
83 | $composerConfig = json_decode(file_get_contents($projectDir.'/composer.json'), true);
84 | $this->assertArrayNotHasKey(
85 | isset($composerConfig['config']) ? 'platform' : 'config',
86 | isset($composerConfig['config']) ? $composerConfig['config'] : $composerConfig,
87 | 'The composer.json file does not define any platform configuration.'
88 | );
89 | }
90 |
91 | /**
92 | * @expectedException \RuntimeException
93 | * @expectedExceptionMessageRegExp /.+The selected version \(3.0.0\) cannot be installed because it requires.+PHP 5.5.9 or higher and your system has PHP 5.4.* installed.+/s
94 | */
95 | public function testSymfonyRequiresNewerPhpVersion()
96 | {
97 | if (PHP_VERSION_ID >= 50500) {
98 | $this->markTestSkipped('This test requires PHP 5.4 or lower.');
99 | }
100 |
101 | $this->runCommand(sprintf('php %s/symfony.phar new my_test_project 3.0.0', $this->rootDir));
102 | }
103 |
104 | /**
105 | * @expectedException \RuntimeException
106 | * @expectedExceptionMessageRegExp /.+The Symfony Installer is not compatible with Symfony 4\.x or newer versions.*Run this other command to install Symfony using Composer instead:.*composer create-project symfony\/skeleton .+/s
107 | */
108 | public function testUseComposerToInstallSymfony4()
109 | {
110 | if (PHP_VERSION_ID < 50500) {
111 | $this->markTestSkipped('This test requires PHP 5.5 or newer.');
112 | }
113 |
114 | $this->runCommand(sprintf('php %s/symfony.phar new my_test_project', $this->rootDir));
115 | }
116 |
117 | public function testSymfonyInstallationInCurrentDirectory()
118 | {
119 | $projectDir = sprintf('%s/my_test_project', sys_get_temp_dir());
120 | $this->fs->remove($projectDir);
121 | $this->fs->mkdir($projectDir);
122 |
123 | $output = $this->runCommand(sprintf('php %s/symfony.phar new . 2.7.5', $this->rootDir), $projectDir);
124 | $this->assertContains('Downloading Symfony...', $output);
125 |
126 | $output = $this->runCommand('php app/console --version', $projectDir);
127 | $this->assertContains('Symfony version 2.7.5 - app/dev/debug', $output);
128 | }
129 |
130 | public function testSymfonyDemoInstallationWithNewCommand()
131 | {
132 | if (PHP_VERSION_ID < 50500) {
133 | $this->markTestSkipped('This test requires PHP 5.5 or higher.');
134 | }
135 |
136 | $output = $this->runCommand(sprintf('php %s/symfony.phar new demo 3.4', $this->rootDir));
137 | $this->assertContains("If you want to download the Symfony Demo app, execute 'symfony demo' instead of 'symfony new demo'", $output);
138 | $this->fs->remove('demo');
139 | }
140 |
141 | /**
142 | * Runs the given string as a command and returns the resulting output.
143 | * The CWD is set to the root project directory to simplify command paths.
144 | *
145 | * @param string $command The name of the command to execute
146 | * @param null|string $workingDirectory The working directory
147 | *
148 | * @return string The output of the command
149 | *
150 | * @throws ProcessFailedException If the command execution is not successful
151 | */
152 | private function runCommand($command, $workingDirectory = null)
153 | {
154 | $process = new Process($command);
155 | $process->setWorkingDirectory($workingDirectory ?: $this->rootDir);
156 | $process->mustRun();
157 |
158 | return $process->getOutput();
159 | }
160 |
161 | /**
162 | * Provides Symfony installation data.
163 | *
164 | * @return array
165 | */
166 | public function provideSymfonyInstallationData()
167 | {
168 | return array(
169 | array(
170 | '3.0',
171 | '/.*Symfony 3\.0\.\d+ was successfully installed.*/',
172 | '/Symfony version 3\.0\.\d+(-DEV)? - app\/dev\/debug/',
173 | '5.5.9',
174 | ),
175 |
176 | array(
177 | 'lts',
178 | '/.*Symfony 3\.4\.\d+ was successfully installed.*/',
179 | '/Symfony 3\.4\.\d+ \(kernel: app, env: dev, debug: true\)/',
180 | '5.5.9',
181 | ),
182 |
183 | array(
184 | '2.3',
185 | '/.*Symfony 2\.3\.\d+ was successfully installed.*/',
186 | '/Symfony version 2\.3\.\d+ - app\/dev\/debug/',
187 | '5.3.9',
188 | ),
189 |
190 | array(
191 | '2.5.6',
192 | '/.*Symfony 2\.5\.6 was successfully installed.*/',
193 | '/Symfony version 2\.5\.6 - app\/dev\/debug/',
194 | '5.3.9',
195 | ),
196 |
197 | array(
198 | '2.7.0-BETA1',
199 | '/.*Symfony 2\.7\.0\-BETA1 was successfully installed.*/',
200 | '/Symfony version 2\.7\.0\-BETA1 - app\/dev\/debug/',
201 | '5.3.9',
202 | ),
203 |
204 | array(
205 | '3.0.0-BETA1',
206 | '/.*Symfony dev\-master was successfully installed.*/',
207 | '/Symfony version 3\.0\.0\-BETA1 - app\/dev\/debug/',
208 | '5.5.9',
209 | ),
210 | );
211 | }
212 | }
213 |
--------------------------------------------------------------------------------
/tests/Symfony/Installer/Tests/Manager/ComposerManagerTest.php:
--------------------------------------------------------------------------------
1 | setAccessible(true);
17 |
18 | $fixedName = $method->invoke($composerManager, $originalName);
19 | $this->assertSame($expectedName, $fixedName);
20 | }
21 |
22 | public function getProjectNames()
23 | {
24 | return [
25 | ['foo/bar', 'foo/bar'],
26 | ['áèî/øū', 'aei/ou'],
27 | ['çñß/łŵž', 'cns/lwz'],
28 | ['foo#bar\foo?bar=foo!bar{foo]bar', 'foobarfoobarfoobarfoobar'],
29 | ['FOO/bar', 'foo/bar'],
30 | ];
31 | }
32 | }
33 |
--------------------------------------------------------------------------------