PageRenderTime 46ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Composer/ScriptHandler.php

https://gitlab.com/ineszribi/SmartBookStoreWeb
PHP | 566 lines | 316 code | 81 blank | 169 comment | 43 complexity | 8c70f16eecf35079d5dddceb0e44cca5 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Sensio\Bundle\DistributionBundle\Composer;
  11. use Symfony\Component\ClassLoader\ClassCollectionLoader;
  12. use Symfony\Component\Filesystem\Filesystem;
  13. use Symfony\Component\Process\Process;
  14. use Symfony\Component\Process\PhpExecutableFinder;
  15. use Composer\Script\CommandEvent;
  16. /**
  17. * @author Jordi Boggiano <j.boggiano@seld.be>
  18. */
  19. class ScriptHandler
  20. {
  21. /**
  22. * Composer variables are declared static so that an event could update
  23. * a composer.json and set new options, making them immediately available
  24. * to forthcoming listeners.
  25. */
  26. private static $options = array(
  27. 'symfony-app-dir' => 'app',
  28. 'symfony-web-dir' => 'web',
  29. 'symfony-assets-install' => 'hard',
  30. 'symfony-cache-warmup' => false,
  31. );
  32. /**
  33. * Asks if the new directory structure should be used, installs the structure if needed.
  34. *
  35. * @param CommandEvent $event
  36. */
  37. public static function defineDirectoryStructure(CommandEvent $event)
  38. {
  39. $options = self::getOptions($event);
  40. if (!getenv('SENSIOLABS_ENABLE_NEW_DIRECTORY_STRUCTURE') || !$event->getIO()->askConfirmation('Would you like to use Symfony 3 directory structure? [y/N] ', false)) {
  41. return;
  42. }
  43. $rootDir = getcwd();
  44. $appDir = $options['symfony-app-dir'];
  45. $webDir = $options['symfony-web-dir'];
  46. $binDir = self::$options['symfony-bin-dir'] = 'bin';
  47. $varDir = self::$options['symfony-var-dir'] = 'var';
  48. static::updateDirectoryStructure($event, $rootDir, $appDir, $binDir, $varDir, $webDir);
  49. }
  50. /**
  51. * Builds the bootstrap file.
  52. *
  53. * The bootstrap file contains PHP file that are always needed by the application.
  54. * It speeds up the application bootstrapping.
  55. *
  56. * @param $event CommandEvent A instance
  57. */
  58. public static function buildBootstrap(CommandEvent $event)
  59. {
  60. $options = self::getOptions($event);
  61. $bootstrapDir = $autoloadDir = $options['symfony-app-dir'];
  62. if (self::useNewDirectoryStructure($options)) {
  63. $bootstrapDir = $options['symfony-var-dir'];
  64. if (!self::hasDirectory($event, 'symfony-var-dir', $bootstrapDir, 'build bootstrap file')) {
  65. return;
  66. }
  67. }
  68. if (!self::hasDirectory($event, 'symfony-app-dir', $autoloadDir, 'build bootstrap file')) {
  69. return;
  70. }
  71. static::executeBuildBootstrap($event, $bootstrapDir, $autoloadDir, $options['process-timeout']);
  72. }
  73. protected static function hasDirectory(CommandEvent $event, $configName, $path, $actionName)
  74. {
  75. if (!is_dir($path)) {
  76. $event->getIO()->write(sprintf('The %s (%s) specified in composer.json was not found in %s, can not %s.', $configName, $path, getcwd(), $actionName));
  77. return false;
  78. }
  79. return true;
  80. }
  81. /**
  82. * Clears the Symfony cache.
  83. *
  84. * @param $event CommandEvent A instance
  85. */
  86. public static function clearCache(CommandEvent $event)
  87. {
  88. $options = self::getOptions($event);
  89. $consoleDir = self::getConsoleDir($event, 'clear the cache');
  90. if (null === $consoleDir) {
  91. return;
  92. }
  93. $warmup = '';
  94. if (!$options['symfony-cache-warmup']) {
  95. $warmup = ' --no-warmup';
  96. }
  97. static::executeCommand($event, $consoleDir, 'cache:clear'.$warmup, $options['process-timeout']);
  98. }
  99. /**
  100. * Installs the assets under the web root directory.
  101. *
  102. * For better interoperability, assets are copied instead of symlinked by default.
  103. *
  104. * Even if symlinks work on Windows, this is only true on Windows Vista and later,
  105. * but then, only when running the console with admin rights or when disabling the
  106. * strict user permission checks (which can be done on Windows 7 but not on Windows
  107. * Vista).
  108. *
  109. * @param $event CommandEvent A instance
  110. */
  111. public static function installAssets(CommandEvent $event)
  112. {
  113. $options = self::getOptions($event);
  114. $consoleDir = self::getConsoleDir($event, 'install assets');
  115. if (null === $consoleDir) {
  116. return;
  117. }
  118. $webDir = $options['symfony-web-dir'];
  119. $symlink = '';
  120. if ($options['symfony-assets-install'] == 'symlink') {
  121. $symlink = '--symlink ';
  122. } elseif ($options['symfony-assets-install'] == 'relative') {
  123. $symlink = '--symlink --relative ';
  124. }
  125. if (!self::hasDirectory($event, 'symfony-web-dir', $webDir, 'install assets')) {
  126. return;
  127. }
  128. static::executeCommand($event, $consoleDir, 'assets:install '.$symlink.escapeshellarg($webDir));
  129. }
  130. /**
  131. * Updated the requirements file.
  132. *
  133. * @param $event CommandEvent A instance
  134. */
  135. public static function installRequirementsFile(CommandEvent $event)
  136. {
  137. $options = self::getOptions($event);
  138. $appDir = $options['symfony-app-dir'];
  139. $fs = new Filesystem();
  140. $newDirectoryStructure = self::useNewDirectoryStructure($options);
  141. if (!$newDirectoryStructure) {
  142. if (!self::hasDirectory($event, 'symfony-app-dir', $appDir, 'install the requirements files')) {
  143. return;
  144. }
  145. $fs->copy(__DIR__.'/../Resources/skeleton/app/SymfonyRequirements.php', $appDir.'/SymfonyRequirements.php', true);
  146. $fs->copy(__DIR__.'/../Resources/skeleton/app/check.php', $appDir.'/check.php', true);
  147. } else {
  148. $binDir = $options['symfony-bin-dir'];
  149. $varDir = $options['symfony-var-dir'];
  150. if (!self::hasDirectory($event, 'symfony-var-dir', $varDir, 'install the requirements files')) {
  151. return;
  152. }
  153. if (!self::hasDirectory($event, 'symfony-bin-dir', $binDir, 'install the requirements files')) {
  154. return;
  155. }
  156. $fs->copy(__DIR__.'/../Resources/skeleton/app/SymfonyRequirements.php', $varDir.'/SymfonyRequirements.php', true);
  157. $fs->copy(__DIR__.'/../Resources/skeleton/app/check.php', $binDir.'/symfony_requirements', true);
  158. $fs->remove(array($appDir.'/check.php', $appDir.'/SymfonyRequirements.php', true));
  159. $fs->dumpFile($binDir.'/symfony_requirements', '#!/usr/bin/env php'.PHP_EOL.str_replace(".'/SymfonyRequirements.php'", ".'/".$fs->makePathRelative($varDir, $binDir)."SymfonyRequirements.php'", file_get_contents($binDir.'/symfony_requirements')), 0755);
  160. }
  161. $webDir = $options['symfony-web-dir'];
  162. // if the user has already removed the config.php file, do nothing
  163. // as the file must be removed for production use
  164. if ($fs->exists($webDir.'/config.php')) {
  165. if (!$newDirectoryStructure) {
  166. $fs->copy(__DIR__.'/../Resources/skeleton/web/config.php', $webDir.'/config.php', true);
  167. } else {
  168. $fs->dumpFile($webDir.'/config.php', str_replace('/../app/SymfonyRequirements.php', '/'.$fs->makePathRelative($varDir, $webDir).'SymfonyRequirements.php', file_get_contents(__DIR__.'/../Resources/skeleton/web/config.php')));
  169. }
  170. }
  171. }
  172. public static function removeSymfonyStandardFiles(CommandEvent $event)
  173. {
  174. $options = self::getOptions($event);
  175. $appDir = $options['symfony-app-dir'];
  176. if (!is_dir($appDir)) {
  177. return;
  178. }
  179. if (!is_dir($appDir.'/SymfonyStandard')) {
  180. return;
  181. }
  182. $fs = new Filesystem();
  183. $fs->remove($appDir.'/SymfonyStandard');
  184. }
  185. public static function installAcmeDemoBundle(CommandEvent $event)
  186. {
  187. $rootDir = getcwd();
  188. $options = self::getOptions($event);
  189. if (file_exists($rootDir.'/src/Acme/DemoBundle')) {
  190. return;
  191. }
  192. if (!getenv('SENSIOLABS_FORCE_ACME_DEMO')) {
  193. if (!$event->getIO()->askConfirmation('Would you like to install Acme demo bundle? [y/N] ', false)) {
  194. return;
  195. }
  196. }
  197. $event->getIO()->write('Installing the Acme demo bundle.');
  198. $appDir = $options['symfony-app-dir'];
  199. $kernelFile = $appDir.'/AppKernel.php';
  200. $fs = new Filesystem();
  201. $fs->mirror(__DIR__.'/../Resources/skeleton/acme-demo-bundle', $rootDir.'/src', null, array('override' => true));
  202. $ref = '$bundles[] = new Symfony\Bundle\WebProfilerBundle\WebProfilerBundle();';
  203. $bundleDeclaration = "\$bundles[] = new Acme\\DemoBundle\\AcmeDemoBundle();";
  204. $content = file_get_contents($kernelFile);
  205. if (false === strpos($content, $bundleDeclaration)) {
  206. $updatedContent = str_replace($ref, $bundleDeclaration."\n ".$ref, $content);
  207. if ($content === $updatedContent) {
  208. throw new \RuntimeException('Unable to patch %s.', $kernelFile);
  209. }
  210. $fs->dumpFile($kernelFile, $updatedContent);
  211. }
  212. self::patchAcmeDemoBundleConfiguration($appDir, $fs);
  213. }
  214. private static function patchAcmeDemoBundleConfiguration($appDir, Filesystem $fs)
  215. {
  216. $routingFile = $appDir.'/config/routing_dev.yml';
  217. $securityFile = $appDir.'/config/security.yml';
  218. $routingData = file_get_contents($routingFile).<<<EOF
  219. # AcmeDemoBundle routes (to be removed)
  220. _acme_demo:
  221. resource: "@AcmeDemoBundle/Resources/config/routing.yml"
  222. EOF;
  223. $fs->dumpFile($routingFile, $routingData);
  224. $securityData = <<<EOF
  225. # you can read more about security in the related section of the documentation
  226. # http://symfony.com/doc/current/book/security.html
  227. security:
  228. # http://symfony.com/doc/current/book/security.html#encoding-the-user-s-password
  229. encoders:
  230. Symfony\Component\Security\Core\User\User: plaintext
  231. # http://symfony.com/doc/current/book/security.html#hierarchical-roles
  232. role_hierarchy:
  233. ROLE_ADMIN: ROLE_USER
  234. ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
  235. # http://symfony.com/doc/current/book/security.html#where-do-users-come-from-user-providers
  236. providers:
  237. in_memory:
  238. memory:
  239. users:
  240. user: { password: userpass, roles: [ 'ROLE_USER' ] }
  241. admin: { password: adminpass, roles: [ 'ROLE_ADMIN' ] }
  242. # the main part of the security, where you can set up firewalls
  243. # for specific sections of your app
  244. firewalls:
  245. # disables authentication for assets and the profiler, adapt it according to your needs
  246. dev:
  247. pattern: ^/(_(profiler|wdt)|css|images|js)/
  248. security: false
  249. # the login page has to be accessible for everybody
  250. demo_login:
  251. pattern: ^/demo/secured/login$
  252. security: false
  253. # secures part of the application
  254. demo_secured_area:
  255. pattern: ^/demo/secured/
  256. # it's important to notice that in this case _demo_security_check and _demo_login
  257. # are route names and that they are specified in the AcmeDemoBundle
  258. form_login:
  259. check_path: _demo_security_check
  260. login_path: _demo_login
  261. logout:
  262. path: _demo_logout
  263. target: _demo
  264. #anonymous: ~
  265. #http_basic:
  266. # realm: "Secured Demo Area"
  267. # with these settings you can restrict or allow access for different parts
  268. # of your application based on roles, ip, host or methods
  269. # http://symfony.com/doc/current/cookbook/security/access_control.html
  270. access_control:
  271. #- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY, requires_channel: https }
  272. EOF;
  273. $fs->dumpFile($securityFile, $securityData);
  274. }
  275. public static function doBuildBootstrap($bootstrapDir, $autoloadDir = null, $useNewDirectoryStructure = false)
  276. {
  277. $file = $bootstrapDir.'/bootstrap.php.cache';
  278. if (file_exists($file)) {
  279. unlink($file);
  280. }
  281. $classes = array(
  282. 'Symfony\\Component\\HttpFoundation\\ParameterBag',
  283. 'Symfony\\Component\\HttpFoundation\\HeaderBag',
  284. 'Symfony\\Component\\HttpFoundation\\FileBag',
  285. 'Symfony\\Component\\HttpFoundation\\ServerBag',
  286. 'Symfony\\Component\\HttpFoundation\\Request',
  287. 'Symfony\\Component\\HttpFoundation\\Response',
  288. 'Symfony\\Component\\HttpFoundation\\ResponseHeaderBag',
  289. 'Symfony\\Component\\DependencyInjection\\ContainerAwareInterface',
  290. // Cannot be included because annotations will parse the big compiled class file
  291. //'Symfony\\Component\\DependencyInjection\\ContainerAware',
  292. 'Symfony\\Component\\DependencyInjection\\Container',
  293. 'Symfony\\Component\\HttpKernel\\Kernel',
  294. 'Symfony\\Component\\ClassLoader\\ClassCollectionLoader',
  295. 'Symfony\\Component\\ClassLoader\\ApcClassLoader',
  296. 'Symfony\\Component\\HttpKernel\\Bundle\\Bundle',
  297. 'Symfony\\Component\\Config\\ConfigCache',
  298. // cannot be included as commands are discovered based on the path to this class via Reflection
  299. //'Symfony\\Bundle\\FrameworkBundle\\FrameworkBundle',
  300. );
  301. // introspect the autoloader to get the right file
  302. // we cannot use class_exist() here as it would load the class
  303. // which won't be included into the cache then.
  304. // we know that composer autoloader is first (see bin/build_bootstrap.php)
  305. $autoloaders = spl_autoload_functions();
  306. if (is_array($autoloaders[0]) && method_exists($autoloaders[0][0], 'findFile') && $autoloaders[0][0]->findFile('Symfony\\Bundle\\FrameworkBundle\\HttpKernel')) {
  307. $classes[] = 'Symfony\\Bundle\\FrameworkBundle\\HttpKernel';
  308. } else {
  309. $classes[] = 'Symfony\\Component\\HttpKernel\\DependencyInjection\\ContainerAwareHttpKernel';
  310. }
  311. ClassCollectionLoader::load($classes, dirname($file), basename($file, '.php.cache'), false, false, '.php.cache');
  312. $fs = new Filesystem();
  313. $bootstrapContent = substr(file_get_contents($file), 5);
  314. if ($useNewDirectoryStructure) {
  315. $cacheDir = $fs->makePathRelative($bootstrapDir, $autoloadDir);
  316. $bootstrapContent = str_replace(array("return \$this->rootDir.'/logs", "return \$this->rootDir.'/cache"), array("return \$this->rootDir.'/".$cacheDir."logs", "return \$this->rootDir.'/".$cacheDir."cache"), $bootstrapContent);
  317. }
  318. if ($autoloadDir) {
  319. $fs = new Filesystem();
  320. $autoloadDir = $fs->makePathRelative($autoloadDir, $bootstrapDir);
  321. }
  322. file_put_contents($file, sprintf("<?php
  323. namespace { \$loader = require_once __DIR__.'/".$autoloadDir."autoload.php'; }
  324. %s
  325. namespace { return \$loader; }
  326. ", $bootstrapContent));
  327. }
  328. protected static function executeCommand(CommandEvent $event, $consoleDir, $cmd, $timeout = 300)
  329. {
  330. $php = escapeshellarg(self::getPhp(false));
  331. $phpArgs = implode(' ', array_map('escapeshellarg', self::getPhpArguments()));
  332. $console = escapeshellarg($consoleDir.'/console');
  333. if ($event->getIO()->isDecorated()) {
  334. $console .= ' --ansi';
  335. }
  336. $process = new Process($php.($phpArgs ? ' '.$phpArgs : '').' '.$console.' '.$cmd, null, null, null, $timeout);
  337. $process->run(function ($type, $buffer) use ($event) { $event->getIO()->write($buffer, false); });
  338. if (!$process->isSuccessful()) {
  339. throw new \RuntimeException(sprintf('An error occurred when executing the "%s" command.', escapeshellarg($cmd)));
  340. }
  341. }
  342. protected static function executeBuildBootstrap(CommandEvent $event, $bootstrapDir, $autoloadDir, $timeout = 300)
  343. {
  344. $php = escapeshellarg(self::getPhp(false));
  345. $phpArgs = implode(' ', array_map('escapeshellarg', self::getPhpArguments()));
  346. $cmd = escapeshellarg(__DIR__.'/../Resources/bin/build_bootstrap.php');
  347. $bootstrapDir = escapeshellarg($bootstrapDir);
  348. $autoloadDir = escapeshellarg($autoloadDir);
  349. $useNewDirectoryStructure = '';
  350. if (self::useNewDirectoryStructure(self::getOptions($event))) {
  351. $useNewDirectoryStructure = escapeshellarg('--use-new-directory-structure');
  352. }
  353. $process = new Process($php.($phpArgs ? ' '.$phpArgs : '').' '.$cmd.' '.$bootstrapDir.' '.$autoloadDir.' '.$useNewDirectoryStructure, getcwd(), null, null, $timeout);
  354. $process->run(function ($type, $buffer) use ($event) { $event->getIO()->write($buffer, false); });
  355. if (!$process->isSuccessful()) {
  356. throw new \RuntimeException('An error occurred when generating the bootstrap file.');
  357. }
  358. }
  359. protected static function updateDirectoryStructure(CommandEvent $event, $rootDir, $appDir, $binDir, $varDir, $webDir)
  360. {
  361. $event->getIO()->write('Updating Symfony directory structure...');
  362. $fs = new Filesystem();
  363. $fs->mkdir(array($binDir, $varDir));
  364. foreach (array(
  365. $appDir.'/console' => $binDir.'/console',
  366. $appDir.'/phpunit.xml.dist' => $rootDir.'/phpunit.xml.dist',
  367. ) as $source => $target) {
  368. $fs->rename($source, $target, true);
  369. }
  370. foreach (array('/logs', '/cache') as $dir) {
  371. $fs->rename($appDir.$dir, $varDir.$dir);
  372. }
  373. $gitignore = <<<EOF
  374. /web/bundles/
  375. /app/config/parameters.yml
  376. /var/bootstrap.php.cache
  377. /var/SymfonyRequirements.php
  378. /var/cache/*
  379. /var/logs/*
  380. !var/cache/.gitkeep
  381. !var/logs/.gitkeep
  382. /build/
  383. /vendor/
  384. /bin/*
  385. !bin/console
  386. !bin/symfony_requirements
  387. /composer.phar
  388. EOF;
  389. $phpunitKernelBefore = <<<EOF
  390. <!--
  391. <php>
  392. <server name="KERNEL_DIR" value="/path/to/your/app/" />
  393. </php>
  394. -->
  395. EOF;
  396. $phpunitKernelAfter = <<<EOF
  397. <php>
  398. <server name="KERNEL_DIR" value="$appDir/" />
  399. </php>
  400. EOF;
  401. $phpunit = str_replace(array('<directory>../src', '"bootstrap.php.cache"', $phpunitKernelBefore), array('<directory>src', '"'.$varDir.'/bootstrap.php.cache"', $phpunitKernelAfter), file_get_contents($rootDir.'/phpunit.xml.dist'));
  402. $composer = str_replace("\"symfony-app-dir\": \"app\",", "\"symfony-app-dir\": \"app\",\n \"symfony-bin-dir\": \"bin\",\n \"symfony-var-dir\": \"var\",", file_get_contents($rootDir.'/composer.json'));
  403. $fs->dumpFile($webDir.'/app.php', str_replace($appDir.'/bootstrap.php.cache', $varDir.'/bootstrap.php.cache', file_get_contents($webDir.'/app.php')));
  404. $fs->dumpFile($webDir.'/app_dev.php', str_replace($appDir.'/bootstrap.php.cache', $varDir.'/bootstrap.php.cache', file_get_contents($webDir.'/app_dev.php')));
  405. $fs->dumpFile($binDir.'/console', str_replace(array(".'/bootstrap.php.cache'", ".'/AppKernel.php'"), array(".'/".$fs->makePathRelative($varDir, $binDir)."bootstrap.php.cache'", ".'/".$fs->makePathRelative($appDir, $binDir)."AppKernel.php'"), file_get_contents($binDir.'/console')));
  406. $fs->dumpFile($rootDir.'/phpunit.xml.dist', $phpunit);
  407. $fs->dumpFile($rootDir.'/composer.json', $composer);
  408. $fs->dumpFile($rootDir.'/.gitignore', $gitignore);
  409. $fs->chmod($binDir.'/console', 0755);
  410. }
  411. protected static function getOptions(CommandEvent $event)
  412. {
  413. $options = array_merge(self::$options, $event->getComposer()->getPackage()->getExtra());
  414. $options['symfony-assets-install'] = getenv('SYMFONY_ASSETS_INSTALL') ?: $options['symfony-assets-install'];
  415. $options['process-timeout'] = $event->getComposer()->getConfig()->get('process-timeout');
  416. return $options;
  417. }
  418. protected static function getPhp($includeArgs = true)
  419. {
  420. $phpFinder = new PhpExecutableFinder();
  421. if (!$phpPath = $phpFinder->find($includeArgs)) {
  422. throw new \RuntimeException('The php executable could not be found, add it to your PATH environment variable and try again');
  423. }
  424. return $phpPath;
  425. }
  426. protected static function getPhpArguments()
  427. {
  428. $arguments = array();
  429. $phpFinder = new PhpExecutableFinder();
  430. if (method_exists($phpFinder, 'findArguments')) {
  431. $arguments = $phpFinder->findArguments();
  432. }
  433. if (false !== $ini = php_ini_loaded_file()) {
  434. $arguments[] = '--php-ini='.$ini;
  435. }
  436. return $arguments;
  437. }
  438. /**
  439. * Returns a relative path to the directory that contains the `console` command.
  440. *
  441. * @param CommandEvent $event The command event.
  442. * @param string $actionName The name of the action
  443. *
  444. * @return string|null The path to the console directory, null if not found.
  445. */
  446. protected static function getConsoleDir(CommandEvent $event, $actionName)
  447. {
  448. $options = self::getOptions($event);
  449. if (self::useNewDirectoryStructure($options)) {
  450. if (!self::hasDirectory($event, 'symfony-bin-dir', $options['symfony-bin-dir'], $actionName)) {
  451. return;
  452. }
  453. return $options['symfony-bin-dir'];
  454. }
  455. if (!self::hasDirectory($event, 'symfony-app-dir', $options['symfony-app-dir'], 'execute command')) {
  456. return;
  457. }
  458. return $options['symfony-app-dir'];
  459. }
  460. /**
  461. * Returns true if the new directory structure is used.
  462. *
  463. * @param array $options Composer options
  464. *
  465. * @return bool
  466. */
  467. protected static function useNewDirectoryStructure(array $options)
  468. {
  469. return isset($options['symfony-var-dir']) && is_dir($options['symfony-var-dir']);
  470. }
  471. }