4 * This file is part of Composer.
6 * (c) Nils Adermann <naderman@naderman.de>
7 * Jordi Boggiano <j.boggiano@seld.be>
9 * For the full copyright and license information, please view
10 * the license that is located at the bottom of this file.
13 // Avoid APC causing random fatal errors per https://github.com/composer/composer/issues/264
14 if (extension_loaded('apc') && ini_get('apc.enable_cli') && ini_get('apc.cache_by_default')) {
15 if (version_compare(phpversion('apc'), '3.0.12', '>=')) {
16 ini_set('apc.cache_by_default', 0);
18 fwrite(STDERR, 'Warning: APC <= 3.0.12 may cause fatal errors when running composer commands.'.PHP_EOL);
19 fwrite(STDERR, 'Update APC, or set apc.enable_cli or apc.cache_by_default to 0 in your php.ini.'.PHP_EOL);
23 Phar::mapPhar('composer.phar');
24 define('COMPOSER_DEV_WARNING_TIME', 1423113606);
25 require 'phar://composer.phar/bin/composer';
27 __HALT_COMPILER(); ?>
\r
28 jr
\0\0_
\ 1\0\0\11\0\0\0\ 1\0\r\0\0\0composer.phar
\0\0\0\0\11\0\0\0src/bootstrap.phpÅ
\ 1\0\0\86p«TÅ
\ 1\0\0¨¯2
\90¶
\ 1\0\0\0\0\0\0\1d\0\0\0src/Composer/IO/ConsoleIO.phpâ
\ f\0\0\86p«Tâ
\ f\0\0\ 4Ú N¶
\ 1\0\0\0\0\0\0\1a\0\0\0src/Composer/IO/NullIO.php%
\ 3\0\0\86p«T%
\ 3\0\0~`
\9f\ 4¶
\ 1\0\0\0\0\0\0\1f\0\0\0src/Composer/IO/IOInterface.phpö
\ 3\0\0\86p«Tö
\ 3\0\0Gj¯:¶
\ 1\0\0\0\0\0\0\1a\0\0\0src/Composer/IO/BaseIO.php
\ 6\ 5\0\0\86p«T
\ 6\ 5\0\0#
\ e°.¶
\ 1\0\0\0\0\0\0\1c\0\0\0src/Composer/IO/BufferIO.php+
\ 4\0\0\86p«T+
\ 4\0\0]
\v\12í¶
\ 1\0\0\0\0\0\0)
\0\0\0src/Composer/Command/RunScriptCommand.phpg
30 \0\0|̲D¶
\ 1\0\0\0\0\0\0(
\0\0\0src/Composer/Command/DiagnoseCommand.phpÕ,
\0\0\86p«TÕ,
\0\0SÛD
\r¶
\ 1\0\0\0\0\0\0'
\0\0\0src/Composer/Command/ArchiveCommand.phpà
\10\0\0\86p«Tà
\10\0\0\8c-zG¶
\ 1\0\0\0\0\0\0*
\0\0\0src/Composer/Command/ClearCacheCommand.phpB
\ 5\0\0\86p«TB
\ 5\0\0´
\13Oã¶
\ 1\0\0\0\0\0\0 \0\0\0src/Composer/Command/Command.php
\89\ 6\0\0\86p«T
\89\ 6\0\0µ
\fvJ¶
\ 1\0\0\0\0\0\0-
\0\0\0src/Composer/Command/CreateProjectCommand.php+1
\0\0\86p«T+1
\0\0³
\90~(¶
\ 1\0\0\0\0\0\0%
\0\0\0src/Composer/Command/AboutCommand.php¶
\ 2\0\0\86p«T¶
\ 2\0\0\89²E
\8f¶
\ 1\0\0\0\0\0\0+
\0\0\0src/Composer/Command/ScriptAliasCommand.php
\8d\ 5\0\0\86p«T
\8d\ 5\0\0¢ò9J¶
\ 1\0\0\0\0\0\0$
\0\0\0src/Composer/Command/ShowCommand.php~/
\0\0\86p«T~/
\0\0\83\8fJ¶
\ 1\0\0\0\0\0\0&
\0\0\0src/Composer/Command/UpdateCommand.phpK
\15\0\0\86p«TK
\15\0\0x
\v\91\1d¶
\ 1\0\0\0\0\0\0&
\0\0\0src/Composer/Command/ConfigCommand.phpõ0
\0\0\86p«Tõ0
\0\0ÈBÂk¶
\ 1\0\0\0\0\0\0'
\0\0\0src/Composer/Command/InstallCommand.php:
\12\0\0\86p«T:
\12\0\0]aI
\94¶
\ 1\0\0\0\0\0\0(
\0\0\0src/Composer/Command/ValidateCommand.phpq
\0\0\86p«Tq
\0\0>Ù:
\99¶
\ 1\0\0\0\0\0\0'
\0\0\0src/Composer/Command/DependsCommand.phpu
32 \0\0¢
\84°
\99¶
\ 1\0\0\0\0\0\0&
\0\0\0src/Composer/Command/SearchCommand.php]
\0\0\86p«T]
\0\0\8bÔke¶
\ 1\0\0\0\0\0\0*
\0\0\0src/Composer/Command/SelfUpdateCommand.php«
\19\0\0\86p«T«
\19\0\0Nºý'¶
\ 1\0\0\0\0\0\0(
\0\0\0src/Composer/Command/LicensesCommand.php&
\ e\0\0\86p«T&
\ e\0\0¾D橶
\ 1\0\0\0\0\0\0$
\0\0\0src/Composer/Command/HomeCommand.php2
\f\0\0\86p«T2
\f\0\0yT
\1e'¶
\ 1\0\0\0\0\0\0'
\0\0\0src/Composer/Command/RequireCommand.php§
\17\0\0\86p«T§
\17\0\0©ÿwÿ¶
\ 1\0\0\0\0\0\0,
\0\0\0src/Composer/Command/DumpAutoloadCommand.phpÁ
\ 6\0\0\86p«TÁ
\ 6\0\0\807µh¶
\ 1\0\0\0\0\0\0&
\0\0\0src/Composer/Command/GlobalCommand.php
\a\0\0\86p«T
\a\0\0Sµ
\16ƶ
\ 1\0\0\0\0\0\0&
\0\0\0src/Composer/Command/RemoveCommand.phpv
\ e\0\0\86p«Tv
\ e\0\0B «É¶
\ 1\0\0\0\0\0\0,
\0\0\0src/Composer/Command/Helper/DialogHelper.php
\9e\ 1\0\0\86p«T
\9e\ 1\0\0\95\82\8c&¶
\ 1\0\0\0\0\0\0&
\0\0\0src/Composer/Command/StatusCommand.phpN
\0\0\86p«TN
\0\0\1d\97ö$¶
\ 1\0\0\0\0\0\0$
\0\0\0src/Composer/Command/InitCommand.php¥6
\0\0\86p«T¥6
\0\0szÌ@¶
\ 1\0\0\0\0\0\0)
\0\0\0src/Composer/Downloader/VcsDownloader.phpÀ
\11\0\0\86p«TÀ
\11\0\0bûíL¶
\ 1\0\0\0\0\0\0)
\0\0\0src/Composer/Downloader/RarDownloader.phpß
\a\0\0\86p«Tß
\a\0\0\15¾¼
\7f¶
\ 1\0\0\0\0\0\0*
\0\0\0src/Composer/Downloader/FileDownloader.php7
\15\0\0\86p«T7
\15\0\0¼À
\90þ¶
\ 1\0\0\0\0\0\0)
\0\0\0src/Composer/Downloader/SvnDownloader.php9
\ f\0\0\86p«T9
\ f\0\0]Òë
\16¶
\ 1\0\0\0\0\0\00
\0\0\0src/Composer/Downloader/PearPackageExtractor.phpa
\e\0\0\86p«Ta
\e\0\0@ÔØ#¶
\ 1\0\0\0\0\0\0+
\0\0\0src/Composer/Downloader/DownloadManager.php
\87\11\0\0\86p«T
\87\11\0\0\7f=
33 @¶
\ 1\0\0\0\0\0\0/
\0\0\0src/Composer/Downloader/DownloaderInterface.phpÊ
\ 1\0\0\86p«TÊ
\ 1\0\0gs!l¶
\ 1\0\0\0\0\0\0.
\0\0\0src/Composer/Downloader/TransportException.php
\96\ 1\0\0\86p«T
\96\ 1\0\0h"Br¶
\ 1\0\0\0\0\0\0*
\0\0\0src/Composer/Downloader/PharDownloader.phpå
\0\0\0\86p«Tå
\0\0\0ÞÉ
\1fç¶
\ 1\0\0\0\0\0\0)
\0\0\0src/Composer/Downloader/TarDownloader.phpã
\0\0\0\86p«Tã
\0\0\0Í
\92X?¶
\ 1\0\0\0\0\0\0(
\0\0\0src/Composer/Downloader/HgDownloader.phpY
\b\0\0\86p«TY
\b\0\0\8bS¡Ù¶
\ 1\0\0\0\0\0\01
\0\0\0src/Composer/Downloader/ChangeReportInterface.phpÌ
\0\0\0\86p«TÌ
\0\0\0¯à¨¿¶
\ 1\0\0\0\0\0\0-
\0\0\0src/Composer/Downloader/ArchiveDownloader.php
\91\r\0\0\86p«T
\91\r\0\0H
\1c\9cö¶
\ 1\0\0\0\0\0\0/
\0\0\0src/Composer/Downloader/FilesystemException.php
\ f\ 1\0\0\86p«T
\ f\ 1\0\0]T½
\88¶
\ 1\0\0\0\0\0\0.
\0\0\0src/Composer/Downloader/PerforceDownloader.phpn
\a\0\0\86p«Tn
\a\0\0E¹:¾¶
\ 1\0\0\0\0\0\0)
\0\0\0src/Composer/Downloader/ZipDownloader.phpC
\v\0\0\86p«TC
\v\0\0\ 2]^+¶
\ 1\0\0\0\0\0\0*
\0\0\0src/Composer/Downloader/GzipDownloader.phpÈ
\ 5\0\0\86p«TÈ
\ 5\0\0äßж
\ 1\0\0\0\0\0\0)
\0\0\0src/Composer/Downloader/GitDownloader.php/#
\0\0\86p«T/#
\0\0\rM(L¶
\ 1\0\0\0\0\0\06
\0\0\0src/Composer/Repository/InvalidRepositoryException.phpn
\0\0\0\86p«Tn
\0\0\0à
\93ë
\98¶
\ 1\0\0\0\0\0\0+
\0\0\0src/Composer/Repository/ArrayRepository.php
\15\f\0\0\86p«T
\15\f\0\0´¯Þ/¶
\ 1\0\0\0\0\0\00
\0\0\0src/Composer/Repository/FilesystemRepository.phpÀ
\ 4\0\0\86p«TÀ
\ 4\0\0&xb£¶
\ 1\0\0\0\0\0\07
\0\0\0src/Composer/Repository/WritableRepositoryInterface.php
\89\ 1\0\0\86p«T
\89\ 1\0\0\91/sï¶
\ 1\0\0\0\0\0\0*
\0\0\0src/Composer/Repository/PearRepository.php¡
\15\0\0\86p«T¡
\15\0\0O¬¶r¶
\ 1\0\0\0\0\0\0-
\0\0\0src/Composer/Repository/RepositoryManager.php³
\a\0\0\86p«T³
\a\0\033¸ï¶
\ 1\0\0\0\0\0\03
\0\0\0src/Composer/Repository/WritableArrayRepository.php
\ f\ 3\0\0\86p«T
\ f\ 3\0\0¾G
\17*¶
\ 1\0\0\0\0\0\0,
\0\0\0src/Composer/Repository/Vcs/GitHubDriver.php^'
\0\0\86p«T^'
\0\0k\º<¶
\ 1\0\0\0\0\0\02
\0\0\0src/Composer/Repository/Vcs/GitBitbucketDriver.phpå
\f\0\0\86p«Tå
\f\0\0ìQ,L¶
\ 1\0\0\0\0\0\0)
\0\0\0src/Composer/Repository/Vcs/GitDriver.phpg
\15\0\0\86p«Tg
\15\0\0×,»Ê¶
\ 1\0\0\0\0\0\0.
\0\0\0src/Composer/Repository/Vcs/PerforceDriver.php!
35 \0\0\8d\80Ùk¶
\ 1\0\0\0\0\0\0)
\0\0\0src/Composer/Repository/Vcs/SvnDriver.php²
\19\0\0\86p«T²
\19\0\0ËÂ W¶
\ 1\0\0\0\0\0\02
\0\0\0src/Composer/Repository/Vcs/VcsDriverInterface.php
\89\ 2\0\0\86p«T
\89\ 2\0\0pO㤶
\ 1\0\0\0\0\0\0(
\0\0\0src/Composer/Repository/Vcs/HgDriver.phpÛ
\12\0\0\86p«TÛ
\12\0\0~Ï
\1eh¶
\ 1\0\0\0\0\0\0)
\0\0\0src/Composer/Repository/Vcs/VcsDriver.phpÑ
\ 5\0\0\86p«TÑ
\ 5\0\0å%
\ 6R¶
\ 1\0\0\0\0\0\01
\0\0\0src/Composer/Repository/Vcs/HgBitbucketDriver.phpí
\r\0\0\86p«Tí
\r\0\0Ü8)׶
\ 1\0\0\0\0\0\04
\0\0\0src/Composer/Repository/InstalledArrayRepository.php£
\0\0\0\86p«T£
\0\0\0/ö~>¶
\ 1\0\0\0\0\0\07
\0\0\0src/Composer/Repository/RepositorySecurityException.phpo
\0\0\0\86p«To
\0\0\0pÕ«ª¶
\ 1\0\0\0\0\0\0)
\0\0\0src/Composer/Repository/VcsRepository.phpá
\1c\0\0\86p«Tá
\1c\0\0Xë
\17à¶
\ 1\0\0\0\0\0\0.
\0\0\0src/Composer/Repository/PlatformRepository.php
\9d\ e\0\0\86p«T
\9d\ e\0\0µáÇض
\ 1\0\0\0\0\0\09
\0\0\0src/Composer/Repository/InstalledFilesystemRepository.php£
\0\0\0\86p«T£
\0\0\0V
36 \95_¶
\ 1\0\0\0\0\0\0/
\0\0\0src/Composer/Repository/CompositeRepository.php
\b \0\0\86p«T
\b \0\04ú
\ 3E¶
\ 1\0\0\0\0\0\08
\0\0\0src/Composer/Repository/InstalledRepositoryInterface.php
\87\0\0\0\86p«T
\87\0\0\0\18£9p¶
\ 1\0\0\0\0\0\0.
\0\0\0src/Composer/Repository/ComposerRepository.phpa>
\0\0\86p«Ta>
\0\0\1c_
\93ï¶
\ 1\0\0\0\0\0\0/
\0\0\0src/Composer/Repository/Pear/DependencyInfo.phpq
\ 1\0\0\86p«Tq
\ 1\0\0fºTò¶
\ 1\0\0\0\0\0\08
\0\0\0src/Composer/Repository/Pear/PackageDependencyParser.php%
\16\0\0\86p«T%
\16\0\0\ 6j?
\93¶
\ 1\0\0\0\0\0\0,
\0\0\0src/Composer/Repository/Pear/ChannelInfo.phpÄ
\ 1\0\0\86p«TÄ
\ 1\0\0:T*ɶ
\ 1\0\0\0\0\0\0.
\0\0\0src/Composer/Repository/Pear/ChannelReader.phpn
\ 6\0\0\86p«Tn
\ 6\0\0\1c\9a8
\15¶
\ 1\0\0\0\0\0\0,
\0\0\0src/Composer/Repository/Pear/PackageInfo.php°
\ 3\0\0\86p«T°
\ 3\0\0\9f\r¸
\f¶
\ 1\0\0\0\0\0\05
\0\0\0src/Composer/Repository/Pear/DependencyConstraint.phpq
\ 2\0\0\86p«Tq
\ 2\0\09
\ e\17=¶
\ 1\0\0\0\0\0\04
\0\0\0src/Composer/Repository/Pear/ChannelRest11Reader.php&
\0\0\86p«T&
\0\0òUb
\b¶
\ 1\0\0\0\0\0\0,
\0\0\0src/Composer/Repository/Pear/ReleaseInfo.php
\92\ 1\0\0\86p«T
\92\ 1\0\0o
\93\8aö
\ 1\0\0\0\0\0\02
\0\0\0src/Composer/Repository/Pear/BaseChannelReader.php6
\ 5\0\0\86p«T6
\ 5\0\0.fi!¶
\ 1\0\0\0\0\0\04
\0\0\0src/Composer/Repository/Pear/ChannelRest10Reader.phpÁ
\0\0\86p«TÁ
\0\0\ 4O
\80ë¶
\ 1\0\0\0\0\0\0/
\0\0\0src/Composer/Repository/RepositoryInterface.phpÔ
\ 1\0\0\86p«TÔ
\ 1\0\0ò
\90\9fɶ
\ 1\0\0\0\0\0\0.
\0\0\0src/Composer/Repository/ArtifactRepository.phpá
38 \0\0xQoP¶
\ 1\0\0\0\0\0\0-
\0\0\0src/Composer/Repository/PackageRepository.phpG
\ 3\0\0\86p«TG
\ 3\0\0í
\ 4:k¶
\ 1\0\0\0\0\0\0(
\0\0\0src/Composer/Package/CompletePackage.phpÿ
\ 6\0\0\86p«Tÿ
\ 6\0\0o+ã ¶
\ 1\0\0\0\0\0\0+
\0\0\0src/Composer/Package/Dumper/ArrayDumper.phpì
\v\0\0\86p«Tì
\v\0\0ª
\96\7fæ¶
\ 1\0\0\0\0\0\07
\0\0\0src/Composer/Package/Loader/InvalidPackageException.phpE
\ 2\0\0\86p«TE
\ 2\0\0xb
\13¾¶
\ 1\0\0\0\0\0\0*
\0\0\0src/Composer/Package/Loader/JsonLoader.phpù
\ 1\0\0\86p«Tù
\ 1\0\0!~
\88{¶
\ 1\0\0\0\0\0\0/
\0\0\0src/Composer/Package/Loader/LoaderInterface.php²
\0\0\0\86p«T²
\0\0\0¦}úζ
\ 1\0\0\0\0\0\0+
\0\0\0src/Composer/Package/Loader/ArrayLoader.phpð
\19\0\0\86p«Tð
\19\0\0\9f\1cak¶
\ 1\0\0\0\0\0\05
\0\0\0src/Composer/Package/Loader/ValidatingArrayLoader.php
\1f.
\0\0\86p«T
\1f.
\0\0n
\ 4{Z¶
\ 1\0\0\0\0\0\01
\0\0\0src/Composer/Package/Loader/RootPackageLoader.phpN!
\0\0\86p«TN!
\0\0\91-^˦
\ 1\0\0\0\0\0\0\1f\0\0\0src/Composer/Package/Locker.php¢
\1c\0\0\86p«T¢
\1c\0\0\8bÝ:â¶
\ 1\0\0\0\0\0\0)
\0\0\0src/Composer/Package/PackageInterface.php_
\a\0\0\86p«T_
\a\0\0æ
\88¹
\82¶
\ 1\0\0\0\0\0\0$
\0\0\0src/Composer/Package/BasePackage.phpM
\v\0\0\86p«TM
\v\0\0·
\v%þ¶
\ 1\0\0\0\0\0\00
\0\0\0src/Composer/Package/Version/VersionSelector.phpü
\b\0\0\86p«Tü
\b\0\0í
\aM¦
\ 1\0\0\0\0\0\0.
\0\0\0src/Composer/Package/Version/VersionParser.phpK-
\0\0\86p«TK-
\0\0·Ò
\9ai¶
\ 1\0\0\0\0\0\01
\0\0\0src/Composer/Package/CompletePackageInterface.phpõ
\ 1\0\0\86p«Tõ
\ 1\0\0¦Ê
\81ò¶
\ 1\0\0\0\0\0\0-
\0\0\0src/Composer/Package/RootPackageInterface.php´
\ 1\0\0\86p«T´
\ 1\0\0êqKж
\ 1\0\0\0\0\0\0$
\0\0\0src/Composer/Package/RootPackage.phpn
\ 4\0\0\86p«Tn
\ 4\0\0áACO¶
\ 1\0\0\0\0\0\03
\0\0\0src/Composer/Package/Archiver/ArchiverInterface.phpï
\0\0\0\86p«Tï
\0\0\0\a<ʸ¶
\ 1\0\0\0\0\0\07
\0\0\0src/Composer/Package/Archiver/ComposerExcludeFilter.php
\1f\ 1\0\0\86p«T
\1f\ 1\0\0\8bSZ0¶
\ 1\0\0\0\0\0\0.
\0\0\0src/Composer/Package/Archiver/PharArchiver.php[
\ 3\0\0\86p«T[
\ 3\0\0Ê5Íø¶
\ 1\0\0\0\0\0\03
\0\0\0src/Composer/Package/Archiver/BaseExcludeFilter.php
\91\ 6\0\0\86p«T
\91\ 6\0\0\11\ 4Mù¶
\ 1\0\0\0\0\0\02
\0\0\0src/Composer/Package/Archiver/GitExcludeFilter.phpw
\ 3\0\0\86p«Tw
\ 3\0\0LgUȦ
\ 1\0\0\0\0\0\07
\0\0\0src/Composer/Package/Archiver/ArchivableFilesFinder.php¿
\ 4\0\0\86p«T¿
\ 4\0\0\8ecEl¶
\ 1\0\0\0\0\0\00
\0\0\0src/Composer/Package/Archiver/ArchiveManager.php'
\f\0\0\86p«T'
\f\0\0é
\e\89ô¶
\ 1\0\0\0\0\0\01
\0\0\0src/Composer/Package/Archiver/HgExcludeFilter.php
\13\ 5\0\0\86p«T
\13\ 5\0\0~
\94\ e¸¶
\ 1\0\0\0\0\0\0)
\0\0\0src/Composer/Package/RootAliasPackage.phpÞ
\ 3\0\0\86p«TÞ
\ 3\0\0Õ
\12>
\ 5¶
\ 1\0\0\0\0\0\0 \0\0\0src/Composer/Package/Package.phpÄ
\1a\0\0\86p«TÄ
\1a\0\0\84ô
\13\11¶
\ 1\0\0\0\0\0\0%
\0\0\0src/Composer/Package/AliasPackage.phpW
\16\0\0\86p«TW
\16\0\0T/YÁ¶
\ 1\0\0\0\0\0\0\1d\0\0\0src/Composer/Package/Link.php*
\ 5\0\0\86p«T*
\ 5\0\0\1d_
\92\85¶
\ 1\0\0\0\0\0\07
\0\0\0src/Composer/Package/LinkConstraint/EmptyConstraint.phpê
\ 1\0\0\86p«Tê
\ 1\0\0\0ì
\e¾¶
\ 1\0\0\0\0\0\07
\0\0\0src/Composer/Package/LinkConstraint/MultiConstraint.phpg
\ 4\0\0\86p«Tg
\ 4\0\0s
\153
\ 6¶
\ 1\0\0\0\0\0\09
\0\0\0src/Composer/Package/LinkConstraint/VersionConstraint.phpÉ
\b\0\0\86p«TÉ
\b\0\0y?³Ø¶
\ 1\0\0\0\0\0\0:
\0\0\0src/Composer/Package/LinkConstraint/SpecificConstraint.phpp
\ 2\0\0\86p«Tp
\ 2\0\0_
\84\88Y¶
\ 1\0\0\0\0\0\0?
\0\0\0src/Composer/Package/LinkConstraint/LinkConstraintInterface.php
\15\ 1\0\0\86p«T
\15\ 1\0\0åþ
\87¢¶
\ 1\0\0\0\0\0\0\16\0\0\0src/Composer/Cache.php
\11\10\0\0\86p«T
\11\10\0\0§û
\99Ǧ
\ 1\0\0\0\0\0\03
\0\0\0src/Composer/DependencyResolver/PolicyInterface.php
\91\ 1\0\0\86p«T
\91\ 1\0\0B
\18\9f¶¶
\ 1\0\0\0\0\0\0+
\0\0\0src/Composer/DependencyResolver/RuleSet.php%
40 \0\09z
\ e¶
\ 1\0\0\0\0\0\06
\0\0\0src/Composer/DependencyResolver/SolverBugException.php
\98\ 1\0\0\86p«T
\98\ 1\0\0\7f"qN¶
\ 1\0\0\0\0\0\01
\0\0\0src/Composer/DependencyResolver/DefaultPolicy.php
\e\17\0\0\86p«T
\e\17\0\0\89\16&
\97¶
\ 1\0\0\0\0\0\0-
\0\0\0src/Composer/DependencyResolver/Decisions.phpQ
\ f\0\0\86p«TQ
\ f\0\0?
\98¬$¶
\ 1\0\0\0\0\0\01
\0\0\0src/Composer/DependencyResolver/RuleWatchNode.phpç
\ 3\0\0\86p«Tç
\ 3\0\0\97Þ
\12ȶ
\ 1\0\0\0\0\0\0;
\0\0\0src/Composer/DependencyResolver/SolverProblemsException.php%
\ 4\0\0\86p«T%
\ 4\0\0T
\1aíP¶
\ 1\0\0\0\0\0\0/
\0\0\0src/Composer/DependencyResolver/Transaction.phpÔ
\13\0\0\86p«TÔ
\13\0\0 3ô
\e¶
\ 1\0\0\0\0\0\0@
\0\0\0src/Composer/DependencyResolver/Operation/UninstallOperation.phpI
\ 2\0\0\86p«TI
\ 2\0\0FûÂɶ
\ 1\0\0\0\0\0\0=
\0\0\0src/Composer/DependencyResolver/Operation/UpdateOperation.phph
\ 3\0\0\86p«Th
\ 3\0\0öSÕ]¶
\ 1\0\0\0\0\0\0I
\0\0\0src/Composer/DependencyResolver/Operation/MarkAliasInstalledOperation.phpÐ
\ 2\0\0\86p«TÐ
\ 2\0\0xUZa¶
\ 1\0\0\0\0\0\0>
\0\0\0src/Composer/DependencyResolver/Operation/InstallOperation.phpC
\ 2\0\0\86p«TC
\ 2\0\0´\õ*¶
\ 1\0\0\0\0\0\0=
\0\0\0src/Composer/DependencyResolver/Operation/SolverOperation.phpë
\ 1\0\0\86p«Të
\ 1\0\0ħÝ
\94¶
\ 1\0\0\0\0\0\0K
\0\0\0src/Composer/DependencyResolver/Operation/MarkAliasUninstalledOperation.phpÖ
\ 2\0\0\86p«TÖ
\ 2\0\0_iÇ«¶
\ 1\0\0\0\0\0\0@
\0\0\0src/Composer/DependencyResolver/Operation/OperationInterface.phpÓ
\0\0\0\86p«TÓ
\0\0\0Ùâ&ä¶
\ 1\0\0\0\0\0\0(
\0\0\0src/Composer/DependencyResolver/Pool.php
\85!
\0\0\86p«T
\85!
\0\0~8ð6¶
\ 1\0\0\0\0\0\0(
\0\0\0src/Composer/DependencyResolver/Rule.php¡
\14\0\0\86p«T¡
\14\0\0z#¢Û¶
\ 1\0\0\0\0\0\04
\0\0\0src/Composer/DependencyResolver/RuleSetGenerator.php]
\e\0\0\86p«T]
\e\0\04
\93Ñ9¶
\ 1\0\0\0\0\0\0/
\0\0\0src/Composer/DependencyResolver/DebugSolver.php
\89\ 6\0\0\86p«T
\89\ 6\0\0£Ò
\85¶
\ 1\0\0\0\0\0\03
\0\0\0src/Composer/DependencyResolver/RuleSetIterator.php
\14\ 6\0\0\86p«T
\14\ 6\0\0}õÇù¶
\ 1\0\0\0\0\0\02
\0\0\0src/Composer/DependencyResolver/RuleWatchChain.phpi
\ 1\0\0\86p«Ti
\ 1\0\0hï
\9a,¶
\ 1\0\0\0\0\0\0*
\0\0\0src/Composer/DependencyResolver/Solver.phpè6
\0\0\86p«Tè6
\0\0|£w0¶
\ 1\0\0\0\0\0\0+
\0\0\0src/Composer/DependencyResolver/Request.phpÌ
\ 4\0\0\86p«TÌ
\ 4\0\0\99¦òä¶
\ 1\0\0\0\0\0\0+
\0\0\0src/Composer/DependencyResolver/Problem.php
\1c\12\0\0\86p«T
\1c\12\0\0õüùÀ¶
\ 1\0\0\0\0\0\02
\0\0\0src/Composer/DependencyResolver/RuleWatchGraph.phpÜ
\ 6\0\0\86p«TÜ
\ 6\0\0\89\ 3\ f8¶
\ 1\0\0\0\0\0\0-
\0\0\0src/Composer/Config/ConfigSourceInterface.php®
\ 1\0\0\86p«T®
\ 1\0\06J[ª¶
\ 1\0\0\0\0\0\0(
\0\0\0src/Composer/Config/JsonConfigSource.php}
\f\0\0\86p«T}
\f\0\0\ fèÇ ¶
\ 1\0\0\0\0\0\0$
\0\0\0src/Composer/Plugin/PluginEvents.php¤
\0\0\0\86p«T¤
\0\0\00ïÞX¶
\ 1\0\0\0\0\0\0$
\0\0\0src/Composer/Plugin/CommandEvent.phpâ
\ 2\0\0\86p«Tâ
\ 2\0\0³ÆÇW¶
\ 1\0\0\0\0\0\0,
\0\0\0src/Composer/Plugin/PreFileDownloadEvent.php`
\ 2\0\0\86p«T`
\ 2\0\0\09-ζ
\ 1\0\0\0\0\0\0'
\0\0\0src/Composer/Plugin/PluginInterface.phpô
\0\0\0\86p«Tô
\0\0\0\f1
\89%¶
\ 1\0\0\0\0\0\0%
\0\0\0src/Composer/Plugin/PluginManager.php
\16\17\0\0\86p«T
\16\17\0\0²$VU¶
\ 1\0\0\0\0\0\0\18\0\0\0src/Composer/Factory.phpÔ,
\0\0\86p«TÔ,
\0\08ÈÍõ¶
\ 1\0\0\0\0\0\0 \0\0\0src/Composer/Util/Filesystem.php4&
\0\0\86p«T4&
\0\0Rl
\97\87¶
\ 1\0\0\0\0\0\0\1c\0\0\0src/Composer/Util/GitHub.php*
\11\0\0\86p«T*
\11\0\0³
\8f?ȶ
\ 1\0\0\0\0\0\0$
\0\0\0src/Composer/Util/ComposerMirror.php±
\ 4\0\0\86p«T±
\ 4\0\0½øض
\ 1\0\0\0\0\0\0\1e\0\0\0src/Composer/Util/Perforce.php
\b3
\0\0\86p«T
\b3
\0\0\82=e.¶
\ 1\0\0\0\0\0\0%
\0\0\0src/Composer/Util/ProcessExecutor.phpé
\ 6\0\0\86p«Té
\ 6\0\0{kßã¶
\ 1\0\0\0\0\0\0\19\0\0\0src/Composer/Util/Git.phpM
\16\0\0\86p«TM
\16\0\0L¢äã¶
\ 1\0\0\0\0\0\0&
\0\0\0src/Composer/Util/RemoteFilesystem.phpA%
\0\0\86p«TA%
\0\0\10?
\v¨¶
\ 1\0\0\0\0\0\0*
\0\0\0src/Composer/Util/StreamContextFactory.phpâ
\f\0\0\86p«Tâ
\f\0\0\eÐ
\vB¶
\ 1\0\0\0\0\0\0%
\0\0\0src/Composer/Util/ConfigValidator.php2
\ e\0\0\86p«T2
\ e\0\0ö
\137.¶
\ 1\0\0\0\0\0\0"
\0\0\0src/Composer/Util/ErrorHandler.php
\14\ 2\0\0\86p«T
\14\ 2\0\0´@
\85æ¶
\ 1\0\0\0\0\0\0 \0\0\0src/Composer/Util/AuthHelper.phpÌ
\ 3\0\0\86p«TÌ
\ 3\0\0\9c¼
\8d˦
\ 1\0\0\0\0\0\0+
\0\0\0src/Composer/Util/SpdxLicenseIdentifier.php6
42 \0\0Ä6»o¶
\ 1\0\0\0\0\0\0$
\0\0\0src/Composer/Util/NoProxyPattern.php¾
\ 6\0\0\86p«T¾
\ 6\0\0Z+°m¶
\ 1\0\0\0\0\0\0\19\0\0\0src/Composer/Util/Svn.php
\17\11\0\0\86p«T
\17\11\0\0^îÕ
\12¶
\ 1\0\0\0\0\0\0\19\0\0\0src/Composer/Composer.php)
\0\0\86p«T)
\0\0O¸
\1a:¶
\ 1\0\0\0\0\0\0%
\0\0\0src/Composer/Json/JsonManipulator.php
\12%
\0\0\86p«T
\12%
\0\0\17¬¥ô¶
\ 1\0\0\0\0\0\0\1e\0\0\0src/Composer/Json/JsonFile.phpy
\10\0\0\86p«Ty
\10\0\0¼è
\7fî¶
\ 1\0\0\0\0\0\0#
\0\0\0src/Composer/Json/JsonFormatter.php
\a\ 6\0\0\86p«T
\a\ 6\0\0c
\96]Y¶
\ 1\0\0\0\0\0\0-
\0\0\0src/Composer/Json/JsonValidationException.php]
\ 1\0\0\86p«T]
\ 1\0\0\10\91#
\97¶
\ 1\0\0\0\0\0\0\17\0\0\0src/Composer/Config.php
\9a\17\0\0\86p«T
\9a\17\0\0¡Ú
\90¥¶
\ 1\0\0\0\0\0\09
\0\0\0src/Composer/EventDispatcher/EventSubscriberInterface.php©
\0\0\0\86p«T©
\0\0\0h·
\ 10¶
\ 1\0\0\0\0\0\0&
\0\0\0src/Composer/EventDispatcher/Event.php
\ 2\0\0\86p«T
\ 2\0\0±
\99jï¶
\ 1\0\0\0\0\0\00
\0\0\0src/Composer/EventDispatcher/EventDispatcher.php
\93\19\0\0\86p«T
\93\19\0\0ê®Ñ
\93¶
\ 1\0\0\0\0\0\0\1d\0\0\0src/Composer/Script/Event.phpµ
\ 2\0\0\86p«Tµ
\ 2\0\0lt¦M¶
\ 1\0\0\0\0\0\0$
\0\0\0src/Composer/Script/ScriptEvents.phpH
\ 4\0\0\86p«TH
\ 4\0\0¯ðܲ¶
\ 1\0\0\0\0\0\0$
\0\0\0src/Composer/Script/CommandEvent.phpW
\0\0\0\86p«TW
\0\0\0£VZt¶
\ 1\0\0\0\0\0\0$
\0\0\0src/Composer/Script/PackageEvent.php÷
\ 1\0\0\86p«T÷
\ 1\0\0a
\16± ¶
\ 1\0\0\0\0\0\0)
\0\0\0src/Composer/Installer/InstallerEvent.php
\97\ 5\0\0\86p«T
\97\ 5\0\0üa
\18\1f¶
\ 1\0\0\0\0\0\0(
\0\0\0src/Composer/Installer/NoopInstaller.php+
\ 5\0\0\86p«T+
\ 5\0\0À·M}¶
\ 1\0\0\0\0\0\0/
\0\0\0src/Composer/Installer/MetapackageInstaller.php
\9c\ 4\0\0\86p«T
\9c\ 4\0\0Æ
\12Å!¶
\ 1\0\0\0\0\0\0(
\0\0\0src/Composer/Installer/PearInstaller.phpQ
\11\0\0\86p«TQ
\11\0\0@W
\89\8a¶
\ 1\0\0\0\0\0\0+
\0\0\0src/Composer/Installer/ProjectInstaller.php
\1d\ 6\0\0\86p«T
\1d\ 6\0\0*0@P¶
\ 1\0\0\0\0\0\0+
\0\0\0src/Composer/Installer/LibraryInstaller.phpy
\1c\0\0\86p«Ty
\1c\0\0
43 M
\r°¶
\ 1\0\0\0\0\0\0.
\0\0\0src/Composer/Installer/InstallationManager.php@
\14\0\0\86p«T@
\14\0\0\9bö«
\ 3¶
\ 1\0\0\0\0\0\0*
\0\0\0src/Composer/Installer/InstallerEvents.phpÞ
\0\0\0\86p«TÞ
\0\0\0ì
\9f@G¶
\ 1\0\0\0\0\0\0*
\0\0\0src/Composer/Installer/PluginInstaller.phpJ
\ 6\0\0\86p«TJ
\ 6\0\0«
\10èV¶
\ 1\0\0\0\0\0\0-
\0\0\0src/Composer/Installer/InstallerInterface.phpÅ
\ 2\0\0\86p«TÅ
\ 2\0\0HS
\93¡¶
\ 1\0\0\0\0\0\0$
\0\0\0src/Composer/Console/Application.phpü
\0\0\86p«Tü
\0\0Çd
\13Û¶
\ 1\0\0\0\0\0\0,
\0\0\0src/Composer/Console/HtmlOutputFormatter.phpÐ
\ 5\0\0\86p«TÐ
\ 5\0\0ÝF×ê¶
\ 1\0\0\0\0\0\0+
\0\0\0src/Composer/Autoload/AutoloadGenerator.phpyC
\0\0\86p«TyC
\0\0Ln¿g¶
\ 1\0\0\0\0\0\0+
\0\0\0src/Composer/Autoload/ClassMapGenerator.phpE
\ e\0\0\86p«TE
\ e\0\0Dd
\85j¶
\ 1\0\0\0\0\0\0\1a\0\0\0src/Composer/Installer.phpÕr
\0\0\86p«TÕr
\0\0\9a\89_µ¶
\ 1\0\0\0\0\0\0%
\0\0\0src/Composer/Autoload/ClassLoader.phpç-
\0\0\86p«Tç-
\0\0\ 5ï0Ú¶
\ 1\0\0\0\0\0\0\18\0\0\0res/spdx-identifier.jsonD
\10\0\0\86p«TD
\10\0\0*Oiò¶
\ 1\0\0\0\0\0\0\18\0\0\0res/composer-schema.json_O
\0\0\86p«T_O
\0\0\87}ÖT¶
\ 1\0\0\0\0\0\0\1f\0\0\0src/Composer/IO/hiddeninput.exe
\0$
\0\0\86p«T
\0$
\0\0\95\8d¥v¶
\ 1\0\0\0\0\0\0?
\0\0\0vendor/symfony/process/Symfony/Component/Process/PhpProcess.php
\ f\ 3\0\0\86p«T
\ f\ 3\0\08ZÔ·¶
\ 1\0\0\0\0\0\0E
\0\0\0vendor/symfony/process/Symfony/Component/Process/ExecutableFinder.php
\8e\ 4\0\0\86p«T
\8e\ 4\0\0ëËXʶ
\ 1\0\0\0\0\0\0<
\0\0\0vendor/symfony/process/Symfony/Component/Process/Process.phpuN
\0\0\86p«TuN
\0\0@íÃ
\81¶
\ 1\0\0\0\0\0\0C
\0\0\0vendor/symfony/process/Symfony/Component/Process/ProcessBuilder.php
\ 3\v\0\0\86p«T
\ 3\v\0\0\848<²¶
\ 1\0\0\0\0\0\0A
\0\0\0vendor/symfony/process/Symfony/Component/Process/ProcessUtils.php
\94\ 5\0\0\86p«T
\94\ 5\0\00]ä/¶
\ 1\0\0\0\0\0\0W
\0\0\0vendor/symfony/process/Symfony/Component/Process/Exception/ProcessTimedOutException.php
\1f\ 4\0\0\86p«T
\1f\ 4\0\0. Ãá¶
\ 1\0\0\0\0\0\0Q
\0\0\0vendor/symfony/process/Symfony/Component/Process/Exception/ExceptionInterface.phpf
\0\0\0\86p«Tf
\0\0\0]ö>T¶
\ 1\0\0\0\0\0\0O
\0\0\0vendor/symfony/process/Symfony/Component/Process/Exception/RuntimeException.php
\98\0\0\0\86p«T
\98\0\0\0¢
\eØ:¶
\ 1\0\0\0\0\0\0M
\0\0\0vendor/symfony/process/Symfony/Component/Process/Exception/LogicException.php
\94\0\0\0\86p«T
\94\0\0\0 ³ãñ¶
\ 1\0\0\0\0\0\0U
\0\0\0vendor/symfony/process/Symfony/Component/Process/Exception/ProcessFailedException.php<
\ 3\0\0\86p«T<
\ 3\0\0"wÛn¶
\ 1\0\0\0\0\0\0W
\0\0\0vendor/symfony/process/Symfony/Component/Process/Exception/InvalidArgumentException.php¨
\0\0\0\86p«T¨
\0\0\0ÐÀ+_¶
\ 1\0\0\0\0\0\0H
\0\0\0vendor/symfony/process/Symfony/Component/Process/PhpExecutableFinder.php
\1c\ 4\0\0\86p«T
\1c\ 4\0\0¦Fж
\ 1\0\0\0\0\0\0I
\0\0\0vendor/symfony/process/Symfony/Component/Process/Pipes/PipesInterface.phpD
\ 1\0\0\86p«TD
\ 1\0\0vØ
\ 1\0\0\0\0\0\0H
\0\0\0vendor/symfony/process/Symfony/Component/Process/Pipes/AbstractPipes.php
\0\ 3\0\0\86p«T
\0\ 3\0\0\98|¥¾¶
\ 1\0\0\0\0\0\0G
\0\0\0vendor/symfony/process/Symfony/Component/Process/Pipes/WindowsPipes.phpÄ
\ e\0\0\86p«TÄ
\ e\0\0{침¶
\ 1\0\0\0\0\0\0D
\0\0\0vendor/symfony/process/Symfony/Component/Process/Pipes/UnixPipes.php¬
\v\0\0\86p«T¬
\v\0\0i
\14੶
\ 1\0\0\0\0\0\0H
\0\0\0vendor/symfony/console/Symfony/Component/Console/Command/HelpCommand.php1
\a\0\0\86p«T1
\a\0\0ý
\1d\80\94¶
\ 1\0\0\0\0\0\0D
\0\0\0vendor/symfony/console/Symfony/Component/Console/Command/Command.phpë
\e\0\0\86p«Të
\e\0\06è
\ eض
\ 1\0\0\0\0\0\0H
\0\0\0vendor/symfony/console/Symfony/Component/Console/Command/ListCommand.php³
\a\0\0\86p«T³
\a\0\0V
\ 3
\ 1\0\0\0\0\0\0M
\0\0\0vendor/symfony/console/Symfony/Component/Console/Tester/ApplicationTester.phpÔ
\ 5\0\0\86p«TÔ
\ 5\0\0¬ì¤d¶
\ 1\0\0\0\0\0\0I
\0\0\0vendor/symfony/console/Symfony/Component/Console/Tester/CommandTester.php
\90\ 6\0\0\86p«T
\90\ 6\0\0H»â`¶
\ 1\0\0\0\0\0\0X
\0\0\0vendor/symfony/console/Symfony/Component/Console/Formatter/OutputFormatterStyleStack.php
\b\ 5\0\0\86p«T
\b\ 5\0\0y
\bI'¶
\ 1\0\0\0\0\0\0S
\0\0\0vendor/symfony/console/Symfony/Component/Console/Formatter/OutputFormatterStyle.phpÅ
\ f\0\0\86p«TÅ
\ f\0\0ÂÚ³
\15¶
\ 1\0\0\0\0\0\0\
\0\0\0vendor/symfony/console/Symfony/Component/Console/Formatter/OutputFormatterStyleInterface.php
\8e\ 1\0\0\86p«T
\8e\ 1\0\0öëÄ=¶
\ 1\0\0\0\0\0\0N
\0\0\0vendor/symfony/console/Symfony/Component/Console/Formatter/OutputFormatter.php
\88\f\0\0\86p«T
\88\f\0\0+ÂF5¶
\ 1\0\0\0\0\0\0W
\0\0\0vendor/symfony/console/Symfony/Component/Console/Formatter/OutputFormatterInterface.php
\98\ 1\0\0\86p«T
\98\ 1\0\03l~´¶
\ 1\0\0\0\0\0\0@
\0\0\0vendor/symfony/console/Symfony/Component/Console/Application.php{Q
\0\0\86p«T{Q
\0\0W
\1f\võ¶
\ 1\0\0\0\0\0\0H
\0\0\0vendor/symfony/console/Symfony/Component/Console/Input/InputArgument.php
\9e\ 5\0\0\86p«T
\9e\ 5\0\0K]ìi¶
\ 1\0\0\0\0\0\0@
\0\0\0vendor/symfony/console/Symfony/Component/Console/Input/Input.php
\12
45 \0\0ÇýT
\ 5¶
\ 1\0\0\0\0\0\0F
\0\0\0vendor/symfony/console/Symfony/Component/Console/Input/StringInput.php
\8b\ 5\0\0\86p«T
\8b\ 5\0\0\86uný¶
\ 1\0\0\0\0\0\0F
\0\0\0vendor/symfony/console/Symfony/Component/Console/Input/InputOption.php«
\v\0\0\86p«T«
\v\0\0ê
\86®½¶
\ 1\0\0\0\0\0\0D
\0\0\0vendor/symfony/console/Symfony/Component/Console/Input/ArgvInput.phpÄ
\13\0\0\86p«TÄ
\13\0\0ÌSGd¶
\ 1\0\0\0\0\0\0I
\0\0\0vendor/symfony/console/Symfony/Component/Console/Input/InputInterface.php
\ 3\0\0\86p«T
\ 3\0\09
\94øǶ
\ 1\0\0\0\0\0\0E
\0\0\0vendor/symfony/console/Symfony/Component/Console/Input/ArrayInput.phpõ
\0\0\86p«Tõ
\0\0É×
\81\99¶
\ 1\0\0\0\0\0\0N
\0\0\0vendor/symfony/console/Symfony/Component/Console/Input/InputAwareInterface.php
\9a\0\0\0\86p«T
\9a\0\0\0\87jT
\9f¶
\ 1\0\0\0\0\0\0J
\0\0\0vendor/symfony/console/Symfony/Component/Console/Input/InputDefinition.php
\v\17\0\0\86p«T
\v\17\0\0\98£JM¶
\ 1\0\0\0\0\0\0:
\0\0\0vendor/symfony/console/Symfony/Component/Console/Shell.php.
\ f\0\0\86p«T.
\ f\0\0þÉ
\8e\87¶
\ 1\0\0\0\0\0\0L
\0\0\0vendor/symfony/console/Symfony/Component/Console/Question/ChoiceQuestion.phpZ
\a\0\0\86p«TZ
\a\0\0C8
\17c¶
\ 1\0\0\0\0\0\0R
\0\0\0vendor/symfony/console/Symfony/Component/Console/Question/ConfirmationQuestion.php@
\ 2\0\0\86p«T@
\ 2\0\0Y
\93B;¶
\ 1\0\0\0\0\0\0F
\0\0\0vendor/symfony/console/Symfony/Component/Console/Question/Question.php®
\b\0\0\86p«T®
\b\0\0.e8Ö¶
\ 1\0\0\0\0\0\0B
\0\0\0vendor/symfony/console/Symfony/Component/Console/Output/Output.php§
\b\0\0\86p«T§
\b\0\0\1cÚ _¶
\ 1\0\0\0\0\0\0F
\0\0\0vendor/symfony/console/Symfony/Component/Console/Output/NullOutput.php¿
\ 3\0\0\86p«T¿
\ 3\0\0`5E˶
\ 1\0\0\0\0\0\0R
\0\0\0vendor/symfony/console/Symfony/Component/Console/Output/ConsoleOutputInterface.phpå
\0\0\0\86p«Tå
\0\0\0rNô
\0¶
\ 1\0\0\0\0\0\0H
\0\0\0vendor/symfony/console/Symfony/Component/Console/Output/StreamOutput.php¢
\ 4\0\0\86p«T¢
\ 4\0\0\1a\ 6ü0¶
\ 1\0\0\0\0\0\0J
\0\0\0vendor/symfony/console/Symfony/Component/Console/Output/BufferedOutput.php_
\ 1\0\0\86p«T_
\ 1\0\0ûBÍ·¶
\ 1\0\0\0\0\0\0K
\0\0\0vendor/symfony/console/Symfony/Component/Console/Output/OutputInterface.phpI
\ 3\0\0\86p«TI
\ 3\0\0ÈâãB¶
\ 1\0\0\0\0\0\0I
\0\0\0vendor/symfony/console/Symfony/Component/Console/Output/ConsoleOutput.php
\14\ 5\0\0\86p«T
\14\ 5\0\0jå¬
\ 6¶
\ 1\0\0\0\0\0\0I
\0\0\0vendor/symfony/console/Symfony/Component/Console/Logger/ConsoleLogger.php9
\0\0\86p«T9
\0\0ã
\ 3\99ç¶
\ 1\0\0\0\0\0\0S
\0\0\0vendor/symfony/console/Symfony/Component/Console/Descriptor/DescriptorInterface.phpü
\0\0\0\86p«Tü
\0\0\0±Q
\aµ¶
\ 1\0\0\0\0\0\0N
\0\0\0vendor/symfony/console/Symfony/Component/Console/Descriptor/TextDescriptor.php4
\1a\0\0\86p«T4
\1a\0\0Ô±
\96G¶
\ 1\0\0\0\0\0\0M
\0\0\0vendor/symfony/console/Symfony/Component/Console/Descriptor/XmlDescriptor.php
\9e\1c\0\0\86p«T
\9e\1c\0\0Ò
\91¦j¶
\ 1\0\0\0\0\0\0R
\0\0\0vendor/symfony/console/Symfony/Component/Console/Descriptor/MarkdownDescriptor.php
\16\ e\0\0\86p«T
\16\ e\0\0ìC/
\83¶
\ 1\0\0\0\0\0\0V
\0\0\0vendor/symfony/console/Symfony/Component/Console/Descriptor/ApplicationDescription.php÷
\a\0\0\86p«T÷
\a\0\0)Ißð¶
\ 1\0\0\0\0\0\0J
\0\0\0vendor/symfony/console/Symfony/Component/Console/Descriptor/Descriptor.phpZ
\a\0\0\86p«TZ
\a\0\0v;
\83ö¶
\ 1\0\0\0\0\0\0N
\0\0\0vendor/symfony/console/Symfony/Component/Console/Descriptor/JsonDescriptor.php0
\r\0\0\86p«T0
\r\0\0\vFÄ`¶
\ 1\0\0\0\0\0\0K
\0\0\0vendor/symfony/console/Symfony/Component/Console/Helper/HelperInterface.phpï
\0\0\0\86p«Tï
\0\0\0=e
\e\f¶
\ 1\0\0\0\0\0\0G
\0\0\0vendor/symfony/console/Symfony/Component/Console/Helper/TableHelper.phpß
47 \0\0\85kzζ
\ 1\0\0\0\0\0\0A
\0\0\0vendor/symfony/console/Symfony/Component/Console/Helper/Table.php
\ 6\17\0\0\86p«T
\ 6\17\0\0µlk
\98¶
\ 1\0\0\0\0\0\0G
\0\0\0vendor/symfony/console/Symfony/Component/Console/Helper/ProgressBar.php
\15$
\0\0\86p«T
\15$
\0\02
\8c5J¶
\ 1\0\0\0\0\0\0L
\0\0\0vendor/symfony/console/Symfony/Component/Console/Helper/DescriptorHelper.php9
\ 5\0\0\86p«T9
\ 5\0\0ûùäð¶
\ 1\0\0\0\0\0\0K
\0\0\0vendor/symfony/console/Symfony/Component/Console/Helper/FormatterHelper.php
\1e\ 4\0\0\86p«T
\1e\ 4\0\0\9eI
\82\81¶
\ 1\0\0\0\0\0\0P
\0\0\0vendor/symfony/console/Symfony/Component/Console/Helper/DebugFormatterHelper.phpm
\b\0\0\86p«Tm
\b\0\0ò¯s>¶
\ 1\0\0\0\0\0\0I
\0\0\0vendor/symfony/console/Symfony/Component/Console/Helper/ProcessHelper.phpâ
\b\0\0\86p«Tâ
\b\0\0|
\7f̼¶
\ 1\0\0\0\0\0\0E
\0\0\0vendor/symfony/console/Symfony/Component/Console/Helper/HelperSet.php/
\ 4\0\0\86p«T/
\ 4\0\0âw
\0d¶
\ 1\0\0\0\0\0\0H
\0\0\0vendor/symfony/console/Symfony/Component/Console/Helper/DialogHelper.phpÓ
\e\0\0\86p«TÓ
\e\0\0jWð
\90¶
\ 1\0\0\0\0\0\0J
\0\0\0vendor/symfony/console/Symfony/Component/Console/Helper/QuestionHelper.phpp
\18\0\0\86p«Tp
\18\0\0¿7(ݶ
\ 1\0\0\0\0\0\0J
\0\0\0vendor/symfony/console/Symfony/Component/Console/Helper/TableSeparator.php[
\0\0\0\86p«T[
\0\0\0LV
\16¡¶
\ 1\0\0\0\0\0\0B
\0\0\0vendor/symfony/console/Symfony/Component/Console/Helper/Helper.phpß
\ 6\0\0\86p«Tß
\ 6\0\0o¾
\ 1ã¶
\ 1\0\0\0\0\0\0J
\0\0\0vendor/symfony/console/Symfony/Component/Console/Helper/ProgressHelper.php
\83\19\0\0\86p«T
\83\19\0\0ê
\ 5ëä¶
\ 1\0\0\0\0\0\0L
\0\0\0vendor/symfony/console/Symfony/Component/Console/Helper/InputAwareHelper.phpc
\ 1\0\0\86p«Tc
\ 1\0\0ñø
\90|¶
\ 1\0\0\0\0\0\0F
\0\0\0vendor/symfony/console/Symfony/Component/Console/Helper/TableStyle.phpÕ
\b\0\0\86p«TÕ
\b\0\0æ"ðù¶
\ 1\0\0\0\0\0\0G
\0\0\0vendor/symfony/console/Symfony/Component/Console/Event/ConsoleEvent.phpÅ
\ 2\0\0\86p«TÅ
\ 2\0\0ÒxÛ\¶
\ 1\0\0\0\0\0\0P
\0\0\0vendor/symfony/console/Symfony/Component/Console/Event/ConsoleTerminateEvent.phpz
\ 2\0\0\86p«Tz
\ 2\0\0³,îL¶
\ 1\0\0\0\0\0\0P
\0\0\0vendor/symfony/console/Symfony/Component/Console/Event/ConsoleExceptionEvent.php
\12\ 3\0\0\86p«T
\12\ 3\0\0á
\162é¶
\ 1\0\0\0\0\0\0N
\0\0\0vendor/symfony/console/Symfony/Component/Console/Event/ConsoleCommandEvent.php²
\ 1\0\0\86p«T²
\ 1\0\0Zk
\892¶
\ 1\0\0\0\0\0\0B
\0\0\0vendor/symfony/console/Symfony/Component/Console/ConsoleEvents.phpï
\0\0\0\86p«Tï
\0\0\0\rÕH¸¶
\ 1\0\0\0\0\0\09
\0\0\0vendor/symfony/finder/Symfony/Component/Finder/Finder.phpÍ
\0\0\86p«TÍ
\0\0ÿÛ¹1¶
\ 1\0\0\0\0\0\0@
\0\0\0vendor/symfony/finder/Symfony/Component/Finder/Shell/Command.php©
49 \0\0V
\82\84j¶
\ 1\0\0\0\0\0\0>
\0\0\0vendor/symfony/finder/Symfony/Component/Finder/Shell/Shell.phpé
\ 3\0\0\86p«Té
\ 3\0\0¿ëÛ
\95¶
\ 1\0\0\0\0\0\0C
\0\0\0vendor/symfony/finder/Symfony/Component/Finder/Expression/Regex.php
\81\ e\0\0\86p«T
\81\ e\0\0S7Pæ¶
\ 1\0\0\0\0\0\0B
\0\0\0vendor/symfony/finder/Symfony/Component/Finder/Expression/Glob.php¡
\a\0\0\86p«T¡
\a\0\0 V¿¶
\ 1\0\0\0\0\0\0H
\0\0\0vendor/symfony/finder/Symfony/Component/Finder/Expression/Expression.php}
\ 5\0\0\86p«T}
\ 5\0\0/·cð¶
\ 1\0\0\0\0\0\0L
\0\0\0vendor/symfony/finder/Symfony/Component/Finder/Expression/ValueInterface.php;
\ 1\0\0\86p«T;
\ 1\0\0\vîãÓ¶
\ 1\0\0\0\0\0\0K
\0\0\0vendor/symfony/finder/Symfony/Component/Finder/Adapter/AdapterInterface.php¯
\ 3\0\0\86p«T¯
\ 3\0\0\8b\béȶ
\ 1\0\0\0\0\0\0I
\0\0\0vendor/symfony/finder/Symfony/Component/Finder/Adapter/BsdFindAdapter.php{
\ 6\0\0\86p«T{
\ 6\0\0Q,D2¶
\ 1\0\0\0\0\0\0I
\0\0\0vendor/symfony/finder/Symfony/Component/Finder/Adapter/GnuFindAdapter.php^
\ 6\0\0\86p«T^
\ 6\0\0ßz
\98r¶
\ 1\0\0\0\0\0\0J
\0\0\0vendor/symfony/finder/Symfony/Component/Finder/Adapter/AbstractAdapter.php¤
51 \0\0¢)z9¶
\ 1\0\0\0\0\0\0N
\0\0\0vendor/symfony/finder/Symfony/Component/Finder/Adapter/AbstractFindAdapter.phpÛ
\18\0\0\86p«TÛ
\18\0\0SõT´¶
\ 1\0\0\0\0\0\0E
\0\0\0vendor/symfony/finder/Symfony/Component/Finder/Adapter/PhpAdapter.php+
\a\0\0\86p«T+
\a\0\0&
\98îÒ¶
\ 1\0\0\0\0\0\07
\0\0\0vendor/symfony/finder/Symfony/Component/Finder/Glob.php
\r\ 5\0\0\86p«T
\r\ 5\0\0z
\9dø
52 ¶
\ 1\0\0\0\0\0\0T
\0\0\0vendor/symfony/finder/Symfony/Component/Finder/Iterator/DepthRangeFilterIterator.phpð
\ 1\0\0\86p«Tð
\ 1\0\0ß0
\99\ 4¶
\ 1\0\0\0\0\0\0L
\0\0\0vendor/symfony/finder/Symfony/Component/Finder/Iterator/SortableIterator.phpÞ
\ 5\0\0\86p«TÞ
\ 5\0\0ö³
\ 6%¶
\ 1\0\0\0\0\0\0U
\0\0\0vendor/symfony/finder/Symfony/Component/Finder/Iterator/FilecontentFilterIterator.php#
\ 3\0\0\86p«T#
\ 3\0\0Ú_VǶ
\ 1\0\0\0\0\0\0M
\0\0\0vendor/symfony/finder/Symfony/Component/Finder/Iterator/FilePathsIterator.php
\8d\ 5\0\0\86p«T
\8d\ 5\0\0ýòäQ¶
\ 1\0\0\0\0\0\0S
\0\0\0vendor/symfony/finder/Symfony/Component/Finder/Iterator/SizeRangeFilterIterator.phpg
\ 2\0\0\86p«Tg
\ 2\0\0!Ô
\97é¶
\ 1\0\0\0\0\0\0Z
\0\0\0vendor/symfony/finder/Symfony/Component/Finder/Iterator/ExcludeDirectoryFilterIterator.php
\94\ 2\0\0\86p«T
\94\ 2\0\0"ÖóÁ¶
\ 1\0\0\0\0\0\0J
\0\0\0vendor/symfony/finder/Symfony/Component/Finder/Iterator/FilterIterator.php
\86\ 2\0\0\86p«T
\86\ 2\0\00£¾Ô¶
\ 1\0\0\0\0\0\0V
\0\0\0vendor/symfony/finder/Symfony/Component/Finder/Iterator/MultiplePcreFilterIterator.phpØ
\ 2\0\0\86p«TØ
\ 2\0\0\ 4Òù
\93¶
\ 1\0\0\0\0\0\0P
\0\0\0vendor/symfony/finder/Symfony/Component/Finder/Iterator/CustomFilterIterator.php]
\ 2\0\0\86p«T]
\ 2\0\0tà±µ¶
\ 1\0\0\0\0\0\0R
\0\0\0vendor/symfony/finder/Symfony/Component/Finder/Iterator/FilenameFilterIterator.php
\87\ 2\0\0\86p«T
\87\ 2\0\0F
\92\v¶
\ 1\0\0\0\0\0\0S
\0\0\0vendor/symfony/finder/Symfony/Component/Finder/Iterator/DateRangeFilterIterator.phpz
\ 2\0\0\86p«Tz
\ 2\0\0\7f}
\17¢¶
\ 1\0\0\0\0\0\0R
\0\0\0vendor/symfony/finder/Symfony/Component/Finder/Iterator/FileTypeFilterIterator.php\
\ 2\0\0\86p«T\
\ 2\0\0p
\91'
\98¶
\ 1\0\0\0\0\0\0V
\0\0\0vendor/symfony/finder/Symfony/Component/Finder/Iterator/RecursiveDirectoryIterator.phpY
\ 6\0\0\86p«TY
\ 6\0\0êÓÊܶ
\ 1\0\0\0\0\0\0N
\0\0\0vendor/symfony/finder/Symfony/Component/Finder/Iterator/PathFilterIterator.phpÀ
\ 2\0\0\86p«TÀ
\ 2\0\0Í"dÕ¶
\ 1\0\0\0\0\0\0L
\0\0\0vendor/symfony/finder/Symfony/Component/Finder/Comparator/DateComparator.php%
\ 3\0\0\86p«T%
\ 3\0\0L¿EǶ
\ 1\0\0\0\0\0\0N
\0\0\0vendor/symfony/finder/Symfony/Component/Finder/Comparator/NumberComparator.phpy
\ 3\0\0\86p«Ty
\ 3\0\0"`
\14Û¶
\ 1\0\0\0\0\0\0H
\0\0\0vendor/symfony/finder/Symfony/Component/Finder/Comparator/Comparator.php
\8c\ 3\0\0\86p«T
\8c\ 3\0\0\16wþT¶
\ 1\0\0\0\0\0\0R
\0\0\0vendor/symfony/finder/Symfony/Component/Finder/Exception/AccessDeniedException.php
\84\0\0\0\86p«T
\84\0\0\0½¾s
\9c¶
\ 1\0\0\0\0\0\0O
\0\0\0vendor/symfony/finder/Symfony/Component/Finder/Exception/ExceptionInterface.php
\84\0\0\0\86p«T
\84\0\0\0\1cGz-¶
\ 1\0\0\0\0\0\0Z
\0\0\0vendor/symfony/finder/Symfony/Component/Finder/Exception/OperationNotPermitedException.php
\8a\0\0\0\86p«T
\8a\0\0\0U
\1288¶
\ 1\0\0\0\0\0\0T
\0\0\0vendor/symfony/finder/Symfony/Component/Finder/Exception/AdapterFailureException.php
\16\ 2\0\0\86p«T
\16\ 2\0\0m
\8c_,¶
\ 1\0\0\0\0\0\0Y
\0\0\0vendor/symfony/finder/Symfony/Component/Finder/Exception/ShellCommandFailureException.php$
\ 2\0\0\86p«T$
\ 2\0\0C
\94sÓ¶
\ 1\0\0\0\0\0\0>
\0\0\0vendor/symfony/finder/Symfony/Component/Finder/SplFileInfo.phpû
\ 2\0\0\86p«Tû
\ 2\0\0\91\ 5\866¶
\ 1\0\0\0\0\0\04
\0\0\0vendor/seld/jsonlint/src/Seld/JsonLint/Undefined.php>
\0\0\0\86p«T>
\0\0\0ÿq
\9f\9f¶
\ 1\0\0\0\0\0\05
\0\0\0vendor/seld/jsonlint/src/Seld/JsonLint/JsonParser.php)1
\0\0\86p«T)1
\0\0?5R3¶
\ 1\0\0\0\0\0\00
\0\0\0vendor/seld/jsonlint/src/Seld/JsonLint/Lexer.php
\ 4\ f\0\0\86p«T
\ 4\ f\0\0ÒÅ~
\97¶
\ 1\0\0\0\0\0\0;
\0\0\0vendor/seld/jsonlint/src/Seld/JsonLint/ParsingException.php
\1e\ 1\0\0\86p«T
\1e\ 1\0\0\89²
\10ñ¶
\ 1\0\0\0\0\0\0?
\0\0\0vendor/justinrainbow/json-schema/src/JsonSchema/RefResolver.php|
54 \0\0\99&
\ri¶
\ 1\0\0\0\0\0\0I
\0\0\0vendor/justinrainbow/json-schema/src/JsonSchema/Constraints/Undefined.phpl
\17\0\0\86p«Tl
\17\0\0\83\rÎ
\89¶
\ 1\0\0\0\0\0\0D
\0\0\0vendor/justinrainbow/json-schema/src/JsonSchema/Constraints/Type.phph
\b\0\0\86p«Th
\b\0\0õ
\1cd϶
\ 1\0\0\0\0\0\0F
\0\0\0vendor/justinrainbow/json-schema/src/JsonSchema/Constraints/Schema.php
\16\ 2\0\0\86p«T
\16\ 2\0\0]
\12ÜÙ¶
\ 1\0\0\0\0\0\0F
\0\0\0vendor/justinrainbow/json-schema/src/JsonSchema/Constraints/Number.php«
\b\0\0\86p«T«
\b\0\0\9bçI)¶
\ 1\0\0\0\0\0\0F
\0\0\0vendor/justinrainbow/json-schema/src/JsonSchema/Constraints/Object.php¾
56 \0\0|Ò\F¶
\ 1\0\0\0\0\0\0S
\0\0\0vendor/justinrainbow/json-schema/src/JsonSchema/Constraints/ConstraintInterface.phpN
\ 1\0\0\86p«TN
\ 1\0\0øÆMy¶
\ 1\0\0\0\0\0\0J
\0\0\0vendor/justinrainbow/json-schema/src/JsonSchema/Constraints/Constraint.php9
\ e\0\0\86p«T9
\ e\0\0éð°#¶
\ 1\0\0\0\0\0\0J
\0\0\0vendor/justinrainbow/json-schema/src/JsonSchema/Constraints/Collection.phpý
\b\0\0\86p«Tý
\b\0\0Ç
\f;ÿ¶
\ 1\0\0\0\0\0\0D
\0\0\0vendor/justinrainbow/json-schema/src/JsonSchema/Constraints/Enum.phpè
\ 1\0\0\86p«Tè
\ 1\0\0ê
\10vĶ
\ 1\0\0\0\0\0\0F
\0\0\0vendor/justinrainbow/json-schema/src/JsonSchema/Constraints/Format.phpí
\r\0\0\86p«Tí
\r\0\0á
\94\18\r¶
\ 1\0\0\0\0\0\0F
\0\0\0vendor/justinrainbow/json-schema/src/JsonSchema/Constraints/String.php
\9f\ 3\0\0\86p«T
\9f\ 3\0\0\8d\88på¶
\ 1\0\0\0\0\0\0R
\0\0\0vendor/justinrainbow/json-schema/src/JsonSchema/Exception/UriResolverException.phpj
\0\0\0\86p«Tj
\0\0\0SÓdz¶
\ 1\0\0\0\0\0\0W
\0\0\0vendor/justinrainbow/json-schema/src/JsonSchema/Exception/ResourceNotFoundException.phpo
\0\0\0\86p«To
\0\0\0Æ$"Ŷ
\ 1\0\0\0\0\0\0]
\0\0\0vendor/justinrainbow/json-schema/src/JsonSchema/Exception/InvalidSchemaMediaTypeException.phpv
\0\0\0\86p«Tv
\0\0\0\ 2\8aCÓ¶
\ 1\0\0\0\0\0\0W
\0\0\0vendor/justinrainbow/json-schema/src/JsonSchema/Exception/InvalidSourceUriException.phpw
\0\0\0\86p«Tw
\0\0\0N-ò[¶
\ 1\0\0\0\0\0\0S
\0\0\0vendor/justinrainbow/json-schema/src/JsonSchema/Exception/JsonDecodingException.phpÞ
\ 2\0\0\86p«TÞ
\ 2\0\0\86¾©
\91¶
\ 1\0\0\0\0\0\0V
\0\0\0vendor/justinrainbow/json-schema/src/JsonSchema/Exception/InvalidArgumentException.phpv
\0\0\0\86p«Tv
\0\0\0¬ «"¶
\ 1\0\0\0\0\0\0C
\0\0\0vendor/justinrainbow/json-schema/src/JsonSchema/Uri/UriResolver.php®
\0\0\86p«T®
\0\0àP¾¡¶
\ 1\0\0\0\0\0\0D
\0\0\0vendor/justinrainbow/json-schema/src/JsonSchema/Uri/UriRetriever.php
\96\13\0\0\86p«T
\96\13\0\0ªÿ¨Ü¶
\ 1\0\0\0\0\0\0T
\0\0\0vendor/justinrainbow/json-schema/src/JsonSchema/Uri/Retrievers/AbstractRetriever.phpÜ
\0\0\0\86p«TÜ
\0\0\0\e]j
\1c¶
\ 1\0\0\0\0\0\0R
\0\0\0vendor/justinrainbow/json-schema/src/JsonSchema/Uri/Retrievers/FileGetContents.phpÑ
\ 4\0\0\86p«TÑ
\ 4\0\0æ
\87¨
\12¶
\ 1\0\0\0\0\0\0X
\0\0\0vendor/justinrainbow/json-schema/src/JsonSchema/Uri/Retrievers/UriRetrieverInterface.php©
\0\0\0\86p«T©
\0\0\0\ 6\ 3CO¶
\ 1\0\0\0\0\0\0G
\0\0\0vendor/justinrainbow/json-schema/src/JsonSchema/Uri/Retrievers/Curl.phpt
\ 4\0\0\86p«Tt
\ 4\0\0I·ý
\0¶
\ 1\0\0\0\0\0\0R
\0\0\0vendor/justinrainbow/json-schema/src/JsonSchema/Uri/Retrievers/PredefinedArray.php^
\ 2\0\0\86p«T^
\ 2\0\0"ß6o¶
\ 1\0\0\0\0\0\0=
\0\0\0vendor/justinrainbow/json-schema/src/JsonSchema/Validator.phps
\ 2\0\0\86p«Ts
\ 2\0\0ô
\84\7f=¶
\ 1\0\0\0\0\0\0\13\0\0\0vendor/autoload.php
\91\0\0\0\86p«T
\91\0\0\0ï4
\9f\8d¶
\ 1\0\0\0\0\0\0'
\0\0\0vendor/composer/autoload_namespaces.php±
\ 1\0\0\86p«T±
\ 1\0\0[®
\13ã¶
\ 1\0\0\0\0\0\0!
\0\0\0vendor/composer/autoload_psr4.php²
\0\0\0\86p«T²
\0\0\0Ô
\81¨Ð¶
\ 1\0\0\0\0\0\0%
\0\0\0vendor/composer/autoload_classmap.phpd
\0\0\0\86p«Td
\0\0\0Z¡¦H¶
\ 1\0\0\0\0\0\0!
\0\0\0vendor/composer/autoload_real.phpK
\ 4\0\0\86p«TK
\ 4\0\0J1·
\12¶
\ 1\0\0\0\0\0\0!
\0\0\0vendor/composer/include_paths.php
\9f\ 1\0\0\86p«T
\9f\ 1\0\0§Åᢶ
\ 1\0\0\0\0\0\0\1f\0\0\0vendor/composer/ClassLoader.php
\97\13\0\0\86p«T
\97\13\0\0B§
\860¶
\ 1\0\0\0\0\0\0\f\0\0\0bin/composern
\ 4\0\0\86p«Tn
\ 4\0\0\1f\96\85W¶
\ 1\0\0\0\0\0\0\a\0\0\0LICENSE3
\ 4\0\0\86p«T3
\ 4\0\0\v\812
\v¶
\ 1\0\0\0\0\0\0<?php
68 function includeIfExists($file)
70 return file_exists($file) ? include $file : false;
73 if ((!$loader = includeIfExists(__DIR__.'/../vendor/autoload.php')) && (!$loader = includeIfExists(__DIR__.'/../../../autoload.php'))) {
74 echo 'You must set up the project dependencies, run the following commands:'.PHP_EOL.
75 'curl -sS https://getcomposer.org/installer | php'.PHP_EOL.
76 'php composer.phar install'.PHP_EOL;
93 namespace Composer\IO;
95 use Symfony\Component\Console\Input\InputInterface;
96 use Symfony\Component\Console\Output\OutputInterface;
97 use Symfony\Component\Console\Helper\HelperSet;
98 use Symfony\Component\Process\ExecutableFinder;
106 class ConsoleIO extends BaseIO
110 protected $helperSet;
111 protected $lastMessage;
121 public function __construct(InputInterface $input, OutputInterface $output, HelperSet $helperSet)
123 $this->input = $input;
124 $this->output = $output;
125 $this->helperSet = $helperSet;
128 public function enableDebugging($startTime)
130 $this->startTime = $startTime;
136 public function isInteractive()
138 return $this->input->isInteractive();
144 public function isDecorated()
146 return $this->output->isDecorated();
152 public function isVerbose()
154 return $this->output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE;
160 public function isVeryVerbose()
162 return $this->output->getVerbosity() >= 3;
168 public function isDebug()
170 return $this->output->getVerbosity() >= 4;
176 public function write($messages, $newline = true)
178 if (null !== $this->startTime) {
179 $messages = (array) $messages;
180 $messages[0] = sprintf(
182 memory_get_usage() / 1024 / 1024,
183 microtime(true) - $this->startTime,
187 $this->output->write($messages, $newline);
188 $this->lastMessage = join($newline ? "\n" : '', (array) $messages);
194 public function overwrite($messages, $newline = true, $size = null)
196 if (!$this->output->isDecorated()) {
201 return $this->write($messages, count($messages) === 1 || $newline);
205 $messages = join($newline ? "\n" : '', (array) $messages);
210 $size = strlen(strip_tags($this->lastMessage));
213 $this->write(str_repeat("\x08", $size), false);
216 $this->write($messages, false);
218 $fill = $size - strlen(strip_tags($messages));
221 $this->write(str_repeat(' ', $fill), false);
223 $this->write(str_repeat("\x08", $fill), false);
229 $this->lastMessage = $messages;
235 public function ask($question, $default = null)
237 return $this->helperSet->get('dialog')->ask($this->output, $question, $default);
243 public function askConfirmation($question, $default = true)
245 return $this->helperSet->get('dialog')->askConfirmation($this->output, $question, $default);
251 public function askAndValidate($question, $validator, $attempts = false, $default = null)
253 return $this->helperSet->get('dialog')->askAndValidate($this->output, $question, $validator, $attempts, $default);
259 public function askAndHideAnswer($question)
262 if (defined('PHP_WINDOWS_VERSION_BUILD')) {
263 $finder = new ExecutableFinder();
266 if ($finder->find('bash') && $finder->find('stty')) {
267 $this->write($question, false);
268 $value = rtrim(shell_exec('bash -c "stty -echo; read -n0 discard; read -r mypassword; stty echo; echo $mypassword"'));
275 $exe = __DIR__.'\\hiddeninput.exe';
278 if ('phar:' === substr(__FILE__, 0, 5)) {
279 $tmpExe = sys_get_temp_dir().'/hiddeninput.exe';
283 $source = fopen(__DIR__.'\\hiddeninput.exe', 'r');
284 $target = fopen($tmpExe, 'w+');
285 stream_copy_to_stream($source, $target);
288 unset($source, $target);
293 $this->write($question, false);
294 $value = rtrim(shell_exec($exe));
298 if (isset($tmpExe)) {
305 if (file_exists('/usr/bin/env')) {
307 $test = "/usr/bin/env %s -c 'echo OK' 2> /dev/null";
308 foreach (array('bash', 'zsh', 'ksh', 'csh') as $sh) {
309 if ('OK' === rtrim(shell_exec(sprintf($test, $sh)))) {
315 $this->write($question, false);
316 $readCmd = ($shell === 'csh') ? 'set mypassword = $<' : 'read -r mypassword';
317 $command = sprintf("/usr/bin/env %s -c 'stty -echo; %s; stty echo; echo \$mypassword'", $shell, $readCmd);
318 $value = rtrim(shell_exec($command));
326 return $this->ask($question);
341 namespace Composer\IO;
348 class NullIO extends BaseIO
353 public function isInteractive()
361 public function isVerbose()
369 public function isVeryVerbose()
377 public function isDebug()
385 public function isDecorated()
393 public function write($messages, $newline = true)
400 public function overwrite($messages, $newline = true, $size = 80)
407 public function ask($question, $default = null)
415 public function askConfirmation($question, $default = true)
423 public function askAndValidate($question, $validator, $attempts = false, $default = null)
431 public function askAndHideAnswer($question)
448 namespace Composer\IO;
457 interface IOInterface
464 public function isInteractive();
471 public function isVerbose();
478 public function isVeryVerbose();
485 public function isDebug();
492 public function isDecorated();
500 public function write($messages, $newline = true);
509 public function overwrite($messages, $newline = true, $size = null);
521 public function ask($question, $default = null);
533 public function askConfirmation($question, $default = true);
551 public function askAndValidate($question, $validator, $attempts = false, $default = null);
560 public function askAndHideAnswer($question);
567 public function getAuthentications();
576 public function hasAuthentication($repositoryName);
585 public function getAuthentication($repositoryName);
594 public function setAuthentication($repositoryName, $username, $password = null);
601 public function loadConfiguration(Config $config);
615 namespace Composer\IO;
619 abstract class BaseIO implements IOInterface
621 protected $authentications = array();
626 public function getAuthentications()
628 return $this->authentications;
634 public function hasAuthentication($repositoryName)
636 return isset($this->authentications[$repositoryName]);
642 public function getAuthentication($repositoryName)
644 if (isset($this->authentications[$repositoryName])) {
645 return $this->authentications[$repositoryName];
648 return array('username' => null, 'password' => null);
654 public function setAuthentication($repositoryName, $username, $password = null)
656 $this->authentications[$repositoryName] = array('username' => $username, 'password' => $password);
662 public function loadConfiguration(Config $config)
665 if ($tokens = $config->get('github-oauth')) {
666 foreach ($tokens as $domain => $token) {
667 if (!preg_match('{^[a-z0-9]+$}', $token)) {
668 throw new \UnexpectedValueException('Your github oauth token for '.$domain.' contains invalid characters: "'.$token.'"');
670 $this->setAuthentication($domain, $token, 'x-oauth-basic');
675 if ($creds = $config->get('http-basic')) {
676 foreach ($creds as $domain => $cred) {
677 $this->setAuthentication($domain, $cred['username'], $cred['password']);
694 namespace Composer\IO;
696 use Symfony\Component\Console\Output\StreamOutput;
697 use Symfony\Component\Console\Formatter\OutputFormatterInterface;
698 use Symfony\Component\Console\Input\StringInput;
699 use Symfony\Component\Console\Helper\HelperSet;
704 class BufferIO extends ConsoleIO
711 public function __construct($input = '', $verbosity = null, OutputFormatterInterface $formatter = null)
713 $input = new StringInput($input);
714 $input->setInteractive(false);
716 $output = new StreamOutput(fopen('php://memory', 'rw'), $verbosity === null ? StreamOutput::VERBOSITY_NORMAL : $verbosity, !empty($formatter), $formatter);
718 parent::__construct($input, $output, new HelperSet(array()));
721 public function getOutput()
723 fseek($this->output->getStream(), 0);
725 $output = stream_get_contents($this->output->getStream());
727 $output = preg_replace_callback("{(?<=^|\n|\x08)(.+?)(\x08+)}", function ($matches) {
728 $pre = strip_tags($matches[1]);
730 if (strlen($pre) === strlen($matches[2])) {
735 return rtrim($matches[1])."\n";
753 namespace Composer\Command;
755 use Composer\Script\CommandEvent;
756 use Composer\Script\ScriptEvents;
757 use Symfony\Component\Console\Input\InputInterface;
758 use Symfony\Component\Console\Input\InputOption;
759 use Symfony\Component\Console\Input\InputArgument;
760 use Symfony\Component\Console\Output\OutputInterface;
765 class RunScriptCommand extends Command
770 protected $commandEvents = array(
771 ScriptEvents::PRE_INSTALL_CMD,
772 ScriptEvents::POST_INSTALL_CMD,
773 ScriptEvents::PRE_UPDATE_CMD,
774 ScriptEvents::POST_UPDATE_CMD,
775 ScriptEvents::PRE_STATUS_CMD,
776 ScriptEvents::POST_STATUS_CMD,
777 ScriptEvents::POST_ROOT_PACKAGE_INSTALL,
778 ScriptEvents::POST_CREATE_PROJECT_CMD
784 protected $scriptEvents = array(
785 ScriptEvents::PRE_ARCHIVE_CMD,
786 ScriptEvents::POST_ARCHIVE_CMD,
787 ScriptEvents::PRE_AUTOLOAD_DUMP,
788 ScriptEvents::POST_AUTOLOAD_DUMP
791 protected function configure()
794 ->setName('run-script')
795 ->setDescription('Run the scripts defined in composer.json.')
796 ->setDefinition(array(
797 new InputArgument('script', InputArgument::REQUIRED, 'Script name to run.'),
798 new InputArgument('args', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, ''),
799 new InputOption('dev', null, InputOption::VALUE_NONE, 'Sets the dev mode.'),
800 new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables the dev mode.'),
803 The <info>run-script</info> command runs scripts defined in composer.json:
805 <info>php composer.phar run-script post-update-cmd</info>
811 protected function execute(InputInterface $input, OutputInterface $output)
813 $script = $input->getArgument('script');
814 if (!in_array($script, $this->commandEvents) && !in_array($script, $this->scriptEvents)) {
815 if (defined('Composer\Script\ScriptEvents::'.str_replace('-', '_', strtoupper($script)))) {
816 throw new \InvalidArgumentException(sprintf('Script "%s" cannot be run with this command', $script));
820 $composer = $this->getComposer();
821 $hasListeners = $composer->getEventDispatcher()->hasEventListeners(new CommandEvent($script, $composer, $this->getIO()));
822 if (!$hasListeners) {
823 throw new \InvalidArgumentException(sprintf('Script "%s" is not defined in this package', $script));
827 $binDir = $composer->getConfig()->get('bin-dir');
828 if (is_dir($binDir)) {
829 putenv('PATH='.realpath($binDir).PATH_SEPARATOR.getenv('PATH'));
832 $args = $input->getArgument('args');
834 if (in_array($script, $this->commandEvents)) {
835 return $composer->getEventDispatcher()->dispatchCommandEvent($script, $input->getOption('dev') || !$input->getOption('no-dev'), $args);
838 return $composer->getEventDispatcher()->dispatchScript($script, $input->getOption('dev') || !$input->getOption('no-dev'), $args);
853 namespace Composer\Command;
855 use Composer\Composer;
856 use Composer\Factory;
857 use Composer\Downloader\TransportException;
858 use Composer\Plugin\CommandEvent;
859 use Composer\Plugin\PluginEvents;
860 use Composer\Util\ConfigValidator;
861 use Composer\Util\ProcessExecutor;
862 use Composer\Util\RemoteFilesystem;
863 use Composer\Util\StreamContextFactory;
864 use Symfony\Component\Console\Input\InputInterface;
865 use Symfony\Component\Console\Output\OutputInterface;
870 class DiagnoseCommand extends Command
874 protected $failures = 0;
876 protected function configure()
879 ->setName('diagnose')
880 ->setDescription('Diagnoses the system to identify common errors.')
882 The <info>diagnose</info> command checks common errors to help debugging problems.
889 protected function execute(InputInterface $input, OutputInterface $output)
891 $composer = $this->getComposer(false);
893 $commandEvent = new CommandEvent(PluginEvents::COMMAND, 'diagnose', $input, $output);
894 $composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
896 $output->write('Checking composer.json: ');
897 $this->outputResult($output, $this->checkComposerSchema());
901 $config = $composer->getConfig();
903 $config = Factory::createConfig();
906 $this->rfs = new RemoteFilesystem($this->getIO(), $config);
907 $this->process = new ProcessExecutor($this->getIO());
909 $output->write('Checking platform settings: ');
910 $this->outputResult($output, $this->checkPlatform());
912 $output->write('Checking git settings: ');
913 $this->outputResult($output, $this->checkGit());
915 $output->write('Checking http connectivity: ');
916 $this->outputResult($output, $this->checkHttp());
918 $opts = stream_context_get_options(StreamContextFactory::getContext('http://example.org'));
919 if (!empty($opts['http']['proxy'])) {
920 $output->write('Checking HTTP proxy: ');
921 $this->outputResult($output, $this->checkHttpProxy());
922 $output->write('Checking HTTP proxy support for request_fulluri: ');
923 $this->outputResult($output, $this->checkHttpProxyFullUriRequestParam());
924 $output->write('Checking HTTPS proxy support for request_fulluri: ');
925 $this->outputResult($output, $this->checkHttpsProxyFullUriRequestParam());
928 if ($oauth = $config->get('github-oauth')) {
929 foreach ($oauth as $domain => $token) {
930 $output->write('Checking '.$domain.' oauth access: ');
931 $this->outputResult($output, $this->checkGithubOauth($domain, $token));
935 $output->write('Checking disk free space: ');
936 $this->outputResult($output, $this->checkDiskSpace($config));
938 $output->write('Checking composer version: ');
939 $this->outputResult($output, $this->checkVersion());
941 return $this->failures;
944 private function checkComposerSchema()
946 $validator = new ConfigValidator($this->getIO());
947 list($errors, $publishErrors, $warnings) = $validator->validate(Factory::getComposerFile());
949 if ($errors || $publishErrors || $warnings) {
951 'error' => array_merge($errors, $publishErrors),
952 'warning' => $warnings,
956 foreach ($messages as $style => $msgs) {
957 foreach ($msgs as $msg) {
958 $output .= '<' . $style . '>' . $msg . '</' . $style . '>' . PHP_EOL;
962 return rtrim($output);
968 private function checkGit()
970 $this->process->execute('git config color.ui', $output);
971 if (strtolower(trim($output)) === 'always') {
972 return '<warning>Your git color.ui setting is set to always, this is known to create issues. Use "git config --global color.ui true" to set it correctly.</warning>';
978 private function checkHttp()
980 $protocol = extension_loaded('openssl') ? 'https' : 'http';
982 $json = $this->rfs->getContents('packagist.org', $protocol . '://packagist.org/packages.json', false);
983 } catch (\Exception $e) {
990 private function checkHttpProxy()
992 $protocol = extension_loaded('openssl') ? 'https' : 'http';
994 $json = json_decode($this->rfs->getContents('packagist.org', $protocol . '://packagist.org/packages.json', false), true);
995 $hash = reset($json['provider-includes']);
996 $hash = $hash['sha256'];
997 $path = str_replace('%hash%', $hash, key($json['provider-includes']));
998 $provider = $this->rfs->getContents('packagist.org', $protocol . '://packagist.org/'.$path, false);
1000 if (hash('sha256', $provider) !== $hash) {
1001 return 'It seems that your proxy is modifying http traffic on the fly';
1003 } catch (\Exception $e) {
1017 private function checkHttpProxyFullUriRequestParam()
1019 $url = 'http://packagist.org/packages.json';
1021 $this->rfs->getContents('packagist.org', $url, false);
1022 } catch (TransportException $e) {
1024 $this->rfs->getContents('packagist.org', $url, false, array('http' => array('request_fulluri' => false)));
1025 } catch (TransportException $e) {
1026 return 'Unable to assess the situation, maybe packagist.org is down ('.$e->getMessage().')';
1029 return 'It seems there is a problem with your proxy server, try setting the "HTTP_PROXY_REQUEST_FULLURI" and "HTTPS_PROXY_REQUEST_FULLURI" environment variables to "false"';
1042 private function checkHttpsProxyFullUriRequestParam()
1044 if (!extension_loaded('openssl')) {
1045 return 'You need the openssl extension installed for this check';
1048 $url = 'https://api.github.com/repos/Seldaek/jsonlint/zipball/1.0.0';
1050 $rfcResult = $this->rfs->getContents('github.com', $url, false);
1051 } catch (TransportException $e) {
1053 $this->rfs->getContents('github.com', $url, false, array('http' => array('request_fulluri' => false)));
1054 } catch (TransportException $e) {
1055 return 'Unable to assess the situation, maybe github is down ('.$e->getMessage().')';
1058 return 'It seems there is a problem with your proxy server, try setting the "HTTPS_PROXY_REQUEST_FULLURI" environment variable to "false"';
1064 private function checkGithubOauth($domain, $token)
1066 $this->getIO()->setAuthentication($domain, $token, 'x-oauth-basic');
1068 $url = $domain === 'github.com' ? 'https://api.'.$domain.'/user/repos' : 'https://'.$domain.'/api/v3/user/repos';
1070 return $this->rfs->getContents($domain, $url, false) ? true : 'Unexpected error';
1071 } catch (\Exception $e) {
1072 if ($e instanceof TransportException && $e->getCode() === 401) {
1073 return '<warning>The oauth token for '.$domain.' seems invalid, run "composer config --global --unset github-oauth.'.$domain.'" to remove it</warning>';
1080 private function checkDiskSpace($config)
1082 $minSpaceFree = 1024*1024;
1083 if ((($df = @disk_free_space($dir = $config->get('home'))) !== false && $df < $minSpaceFree)
1084 || (($df = @disk_free_space($dir = $config->get('vendor-dir'))) !== false && $df < $minSpaceFree)
1086 return '<error>The disk hosting '.$dir.' is full</error>';
1092 private function checkVersion()
1094 $protocol = extension_loaded('openssl') ? 'https' : 'http';
1095 $latest = trim($this->rfs->getContents('getcomposer.org', $protocol . '://getcomposer.org/version', false));
1097 if (Composer::VERSION !== $latest && Composer::VERSION !== '@package_version@') {
1098 return '<warning>You are not running the latest version</warning>';
1104 private function outputResult(OutputInterface $output, $result)
1106 if (true === $result) {
1107 $output->writeln('<info>OK</info>');
1110 $output->writeln('<error>FAIL</error>');
1111 if ($result instanceof \Exception) {
1112 $output->writeln('['.get_class($result).'] '.$result->getMessage());
1113 } elseif ($result) {
1114 $output->writeln($result);
1119 private function checkPlatform()
1122 $out = function ($msg, $style) use (&$output) {
1123 $output .= '<'.$style.'>'.$msg.'</'.$style.'>';
1128 $warnings = array();
1130 $iniPath = php_ini_loaded_file();
1131 $displayIniMessage = false;
1133 $iniMessage = PHP_EOL.PHP_EOL.'The php.ini used by your command-line PHP is: ' . $iniPath;
1135 $iniMessage = PHP_EOL.PHP_EOL.'A php.ini file does not exist. You will have to create one.';
1137 $iniMessage .= PHP_EOL.'If you can not modify the ini file, you can also run `php -d option=value` to modify ini values on the fly. You can use -d multiple times.';
1139 if (!ini_get('allow_url_fopen')) {
1140 $errors['allow_url_fopen'] = true;
1143 if (version_compare(PHP_VERSION, '5.3.2', '<')) {
1144 $errors['php'] = PHP_VERSION;
1147 if (!isset($errors['php']) && version_compare(PHP_VERSION, '5.3.4', '<')) {
1148 $warnings['php'] = PHP_VERSION;
1151 if (!extension_loaded('openssl')) {
1152 $warnings['openssl'] = true;
1155 if (!defined('HHVM_VERSION') && !extension_loaded('apcu') && ini_get('apc.enable_cli')) {
1156 $warnings['apc_cli'] = true;
1159 if (ini_get('xdebug.profiler_enabled')) {
1160 $warnings['xdebug_profile'] = true;
1161 } elseif (extension_loaded('xdebug')) {
1162 $warnings['xdebug_loaded'] = true;
1166 phpinfo(INFO_GENERAL);
1167 $phpinfo = ob_get_clean();
1168 if (preg_match('{Configure Command(?: *</td><td class="v">| *=> *)(.*?)(?:</td>|$)}m', $phpinfo, $match)) {
1169 $configure = $match[1];
1171 if (false !== strpos($configure, '--enable-sigchild')) {
1172 $warnings['sigchild'] = true;
1175 if (false !== strpos($configure, '--with-curlwrappers')) {
1176 $warnings['curlwrappers'] = true;
1180 if (!empty($errors)) {
1181 foreach ($errors as $error => $current) {
1184 $text = PHP_EOL."Your PHP ({$current}) is too old, you must upgrade to PHP 5.3.2 or higher.";
1187 case 'allow_url_fopen':
1188 $text = PHP_EOL."The allow_url_fopen setting is incorrect.".PHP_EOL;
1189 $text .= "Add the following to the end of your `php.ini`:".PHP_EOL;
1190 $text .= " allow_url_fopen = On";
1191 $displayIniMessage = true;
1194 $out($text, 'error');
1200 if (!empty($warnings)) {
1201 foreach ($warnings as $warning => $current) {
1204 $text = PHP_EOL."The apc.enable_cli setting is incorrect.".PHP_EOL;
1205 $text .= "Add the following to the end of your `php.ini`:".PHP_EOL;
1206 $text .= " apc.enable_cli = Off";
1207 $displayIniMessage = true;
1211 $text = PHP_EOL."PHP was compiled with --enable-sigchild which can cause issues on some platforms.".PHP_EOL;
1212 $text .= "Recompile it without this flag if possible, see also:".PHP_EOL;
1213 $text .= " https://bugs.php.net/bug.php?id=22999";
1216 case 'curlwrappers':
1217 $text = PHP_EOL."PHP was compiled with --with-curlwrappers which will cause issues with HTTP authentication and GitHub.".PHP_EOL;
1218 $text .= "Recompile it without this flag if possible";
1222 $text = PHP_EOL."The openssl extension is missing, which will reduce the security and stability of Composer.".PHP_EOL;
1223 $text .= "If possible you should enable it or recompile php with --with-openssl";
1227 $text = PHP_EOL."Your PHP ({$current}) is quite old, upgrading to PHP 5.3.4 or higher is recommended.".PHP_EOL;
1228 $text .= "Composer works with 5.3.2+ for most people, but there might be edge case issues.";
1231 case 'xdebug_loaded':
1232 $text = PHP_EOL."The xdebug extension is loaded, this can slow down Composer a little.".PHP_EOL;
1233 $text .= "Disabling it when using Composer is recommended, but should not cause issues beyond slowness.";
1236 case 'xdebug_profile':
1237 $text = PHP_EOL."The xdebug.profiler_enabled setting is enabled, this can slow down Composer a lot.".PHP_EOL;
1238 $text .= "Add the following to the end of your `php.ini` to disable it:".PHP_EOL;
1239 $text .= " xdebug.profiler_enabled = 0";
1240 $displayIniMessage = true;
1243 $out($text, 'warning');
1247 if ($displayIniMessage) {
1248 $out($iniMessage, 'warning');
1251 return !$warnings && !$errors ? true : $output;
1266 namespace Composer\Command;
1268 use Composer\Factory;
1269 use Composer\IO\IOInterface;
1270 use Composer\DependencyResolver\Pool;
1271 use Composer\Repository\CompositeRepository;
1272 use Composer\Script\ScriptEvents;
1273 use Composer\Plugin\CommandEvent;
1274 use Composer\Plugin\PluginEvents;
1275 use Composer\Package\Version\VersionParser;
1277 use Symfony\Component\Console\Input\InputArgument;
1278 use Symfony\Component\Console\Input\InputInterface;
1279 use Symfony\Component\Console\Input\InputOption;
1280 use Symfony\Component\Console\Output\OutputInterface;
1287 class ArchiveCommand extends Command
1289 protected function configure()
1292 ->setName('archive')
1293 ->setDescription('Create an archive of this composer package')
1294 ->setDefinition(array(
1295 new InputArgument('package', InputArgument::OPTIONAL, 'The package to archive instead of the current project'),
1296 new InputArgument('version', InputArgument::OPTIONAL, 'A version constraint to find the package to archive'),
1297 new InputOption('format', 'f', InputOption::VALUE_REQUIRED, 'Format of the resulting archive: tar or zip', 'tar'),
1298 new InputOption('dir', false, InputOption::VALUE_REQUIRED, 'Write the archive to this directory', '.'),
1301 The <info>archive</info> command creates an archive of the specified format
1302 containing the files and directories of the Composer project or the specified
1303 package in the specified version and writes it to the specified directory.
1305 <info>php composer.phar archive [--format=zip] [--dir=/foo] [package [version]]</info>
1312 protected function execute(InputInterface $input, OutputInterface $output)
1314 $composer = $this->getComposer(false);
1316 $commandEvent = new CommandEvent(PluginEvents::COMMAND, 'archive', $input, $output);
1317 $composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
1318 $composer->getEventDispatcher()->dispatchScript(ScriptEvents::PRE_ARCHIVE_CMD);
1321 $returnCode = $this->archive(
1323 $input->getArgument('package'),
1324 $input->getArgument('version'),
1325 $input->getOption('format'),
1326 $input->getOption('dir')
1329 if (0 === $returnCode && $composer) {
1330 $composer->getEventDispatcher()->dispatchScript(ScriptEvents::POST_ARCHIVE_CMD);
1336 protected function archive(IOInterface $io, $packageName = null, $version = null, $format = 'tar', $dest = '.')
1338 $config = Factory::createConfig();
1339 $factory = new Factory;
1340 $downloadManager = $factory->createDownloadManager($io, $config);
1341 $archiveManager = $factory->createArchiveManager($config, $downloadManager);
1344 $package = $this->selectPackage($io, $packageName, $version);
1350 $package = $this->getComposer()->getPackage();
1353 $io->write('<info>Creating the archive.</info>');
1354 $archiveManager->archive($package, $format, $dest);
1359 protected function selectPackage(IOInterface $io, $packageName, $version = null)
1361 $io->write('<info>Searching for the specified package.</info>');
1363 if ($composer = $this->getComposer(false)) {
1364 $localRepo = $composer->getRepositoryManager()->getLocalRepository();
1365 $repos = new CompositeRepository(array_merge(array($localRepo), $composer->getRepositoryManager()->getRepositories()));
1367 $defaultRepos = Factory::createDefaultRepositories($this->getIO());
1368 $io->write('No composer.json found in the current directory, searching packages from ' . implode(', ', array_keys($defaultRepos)));
1369 $repos = new CompositeRepository($defaultRepos);
1373 $pool->addRepository($repos);
1375 $parser = new VersionParser();
1376 $constraint = ($version) ? $parser->parseConstraints($version) : null;
1377 $packages = $pool->whatProvides($packageName, $constraint, true);
1379 if (count($packages) > 1) {
1380 $package = reset($packages);
1381 $io->write('<info>Found multiple matches, selected '.$package->getPrettyString().'.</info>');
1382 $io->write('Alternatives were '.implode(', ', array_map(function ($p) { return $p->getPrettyString(); }, $packages)).'.');
1383 $io->write('<comment>Please use a more specific constraint to pick a different package.</comment>');
1384 } elseif ($packages) {
1385 $package = reset($packages);
1386 $io->write('<info>Found an exact match '.$package->getPrettyString().'.</info>');
1388 $io->write('<error>Could not find a package matching '.$packageName.'.</error>');
1408 namespace Composer\Command;
1411 use Composer\Factory;
1412 use Symfony\Component\Console\Input\InputInterface;
1413 use Symfony\Component\Console\Output\OutputInterface;
1418 class ClearCacheCommand extends Command
1420 protected function configure()
1423 ->setName('clear-cache')
1424 ->setAliases(array('clearcache'))
1425 ->setDescription('Clears composer\'s internal package cache.')
1427 The <info>clear-cache</info> deletes all cached packages from composer's
1434 protected function execute(InputInterface $input, OutputInterface $output)
1436 $config = Factory::createConfig();
1437 $io = $this->getIO();
1439 $cachePaths = array(
1440 'cache-dir' => $config->get('cache-dir'),
1441 'cache-files-dir' => $config->get('cache-files-dir'),
1442 'cache-repo-dir' => $config->get('cache-repo-dir'),
1443 'cache-vcs-dir' => $config->get('cache-vcs-dir'),
1446 foreach ($cachePaths as $key => $cachePath) {
1447 $cachePath = realpath($cachePath);
1449 $io->write("<info>Cache directory does not exist ($key): $cachePath</info>");
1453 $cache = new Cache($io, $cachePath);
1454 if (!$cache->isEnabled()) {
1455 $io->write("<info>Cache is not enabled ($key): $cachePath</info>");
1460 $io->write("<info>Clearing cache ($key): $cachePath</info>");
1464 $io->write('<info>All caches cleared.</info>');
1479 namespace Composer\Command;
1481 use Composer\Composer;
1482 use Composer\Console\Application;
1483 use Composer\IO\IOInterface;
1484 use Composer\IO\NullIO;
1485 use Symfony\Component\Console\Input\InputInterface;
1486 use Symfony\Component\Console\Output\OutputInterface;
1487 use Symfony\Component\Console\Command\Command as BaseCommand;
1495 abstract class Command extends BaseCommand
1513 public function getComposer($required = true, $disablePlugins = false)
1515 if (null === $this->composer) {
1516 $application = $this->getApplication();
1517 if ($application instanceof Application) {
1519 $this->composer = $application->getComposer($required, $disablePlugins);
1520 } elseif ($required) {
1521 throw new \RuntimeException(
1522 'Could not create a Composer\Composer instance, you must inject '.
1523 'one if this command is not used with a Composer\Console\Application instance'
1528 return $this->composer;
1534 public function setComposer(Composer $composer)
1536 $this->composer = $composer;
1542 public function resetComposer()
1544 $this->composer = null;
1545 $this->getApplication()->resetComposer();
1551 public function getIO()
1553 if (null === $this->io) {
1554 $application = $this->getApplication();
1555 if ($application instanceof Application) {
1557 $this->io = $application->getIO();
1559 $this->io = new NullIO();
1569 public function setIO(IOInterface $io)
1577 protected function initialize(InputInterface $input, OutputInterface $output)
1579 if (true === $input->hasParameterOption(array('--no-ansi')) && $input->hasOption('no-progress')) {
1580 $input->setOption('no-progress', true);
1583 parent::initialize($input, $output);
1598 namespace Composer\Command;
1600 use Composer\Config;
1601 use Composer\Factory;
1602 use Composer\Installer;
1603 use Composer\Installer\ProjectInstaller;
1604 use Composer\Installer\InstallationManager;
1605 use Composer\IO\IOInterface;
1606 use Composer\Package\BasePackage;
1607 use Composer\DependencyResolver\Pool;
1608 use Composer\DependencyResolver\Operation\InstallOperation;
1609 use Composer\Package\Version\VersionSelector;
1610 use Composer\Repository\ComposerRepository;
1611 use Composer\Repository\CompositeRepository;
1612 use Composer\Repository\FilesystemRepository;
1613 use Composer\Repository\InstalledFilesystemRepository;
1614 use Composer\Script\ScriptEvents;
1615 use Symfony\Component\Console\Input\InputArgument;
1616 use Symfony\Component\Console\Input\InputInterface;
1617 use Symfony\Component\Console\Input\InputOption;
1618 use Symfony\Component\Console\Output\OutputInterface;
1619 use Symfony\Component\Finder\Finder;
1620 use Composer\Json\JsonFile;
1621 use Composer\Config\JsonConfigSource;
1622 use Composer\Util\Filesystem;
1623 use Composer\Util\RemoteFilesystem;
1624 use Composer\Package\Version\VersionParser;
1634 class CreateProjectCommand extends Command
1636 protected function configure()
1639 ->setName('create-project')
1640 ->setDescription('Create new project from a package into given directory.')
1641 ->setDefinition(array(
1642 new InputArgument('package', InputArgument::OPTIONAL, 'Package name to be installed'),
1643 new InputArgument('directory', InputArgument::OPTIONAL, 'Directory where the files should be created'),
1644 new InputArgument('version', InputArgument::OPTIONAL, 'Version, will default to latest'),
1645 new InputOption('stability', 's', InputOption::VALUE_REQUIRED, 'Minimum-stability allowed (unless a version is specified).'),
1646 new InputOption('prefer-source', null, InputOption::VALUE_NONE, 'Forces installation from package sources when possible, including VCS information.'),
1647 new InputOption('prefer-dist', null, InputOption::VALUE_NONE, 'Forces installation from package dist even for dev versions.'),
1648 new InputOption('repository-url', null, InputOption::VALUE_REQUIRED, 'Pick a different repository url to look for the package.'),
1649 new InputOption('dev', null, InputOption::VALUE_NONE, 'Enables installation of require-dev packages (enabled by default, only present for BC).'),
1650 new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables installation of require-dev packages.'),
1651 new InputOption('no-plugins', null, InputOption::VALUE_NONE, 'Whether to disable plugins.'),
1652 new InputOption('no-custom-installers', null, InputOption::VALUE_NONE, 'DEPRECATED: Use no-plugins instead.'),
1653 new InputOption('no-scripts', null, InputOption::VALUE_NONE, 'Whether to prevent execution of all defined scripts in the root package.'),
1654 new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'),
1655 new InputOption('keep-vcs', null, InputOption::VALUE_NONE, 'Whether to prevent deletion vcs folder.'),
1656 new InputOption('no-install', null, InputOption::VALUE_NONE, 'Whether to skip installation of the package dependencies.'),
1657 new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'),
1660 The <info>create-project</info> command creates a new project from a given
1661 package into a new directory. If executed without params and in a directory
1662 with a composer.json file it installs the packages for the current project.
1664 You can use this command to bootstrap new projects or setup a clean
1665 version-controlled installation for developers of your project.
1667 <info>php composer.phar create-project vendor/project target-directory [version]</info>
1669 You can also specify the version with the package name using = or : as separator.
1671 To install unstable packages, either specify the version you want, or use the
1672 --stability=dev (where dev can be one of RC, beta, alpha or dev).
1674 To setup a developer workable version you should create the project using the source
1675 controlled code by appending the <info>'--prefer-source'</info> flag.
1677 To install a package from another repository than the default one you
1678 can pass the <info>'--repository-url=http://myrepository.org'</info> flag.
1685 protected function execute(InputInterface $input, OutputInterface $output)
1687 $config = Factory::createConfig();
1689 $preferSource = false;
1690 $preferDist = false;
1691 $this->updatePreferredOptions($config, $input, $preferSource, $preferDist);
1693 if ($input->getOption('no-custom-installers')) {
1694 $output->writeln('<warning>You are using the deprecated option "no-custom-installers". Use "no-plugins" instead.</warning>');
1695 $input->setOption('no-plugins', true);
1698 return $this->installProject(
1701 $input->getArgument('package'),
1702 $input->getArgument('directory'),
1703 $input->getArgument('version'),
1704 $input->getOption('stability'),
1707 !$input->getOption('no-dev'),
1708 $input->getOption('repository-url'),
1709 $input->getOption('no-plugins'),
1710 $input->getOption('no-scripts'),
1711 $input->getOption('keep-vcs'),
1712 $input->getOption('no-progress'),
1713 $input->getOption('no-install'),
1714 $input->getOption('ignore-platform-reqs'),
1719 public function installProject(IOInterface $io, Config $config, $packageName, $directory = null, $packageVersion = null, $stability = 'stable', $preferSource = false, $preferDist = false, $installDevPackages = false, $repositoryUrl = null, $disablePlugins = false, $noScripts = false, $keepVcs = false, $noProgress = false, $noInstall = false, $ignorePlatformReqs = false, InputInterface $input)
1724 $io->loadConfiguration($config);
1726 if ($packageName !== null) {
1727 $installedFromVcs = $this->installRootPackage($io, $config, $packageName, $directory, $packageVersion, $stability, $preferSource, $preferDist, $installDevPackages, $repositoryUrl, $disablePlugins, $noScripts, $keepVcs, $noProgress);
1729 $installedFromVcs = false;
1732 $composer = Factory::create($io, null, $disablePlugins);
1733 $fs = new Filesystem();
1735 if ($noScripts === false) {
1737 $composer->getEventDispatcher()->dispatchCommandEvent(ScriptEvents::POST_ROOT_PACKAGE_INSTALL, $installDevPackages);
1740 $rootPackageConfig = $composer->getConfig();
1741 $this->updatePreferredOptions($rootPackageConfig, $input, $preferSource, $preferDist);
1744 if ($noInstall === false) {
1745 $installer = Installer::create($io, $composer);
1746 $installer->setPreferSource($preferSource)
1747 ->setPreferDist($preferDist)
1748 ->setDevMode($installDevPackages)
1749 ->setRunScripts(!$noScripts)
1750 ->setIgnorePlatformRequirements($ignorePlatformReqs);
1752 if ($disablePlugins) {
1753 $installer->disablePlugins();
1756 $status = $installer->run();
1757 if (0 !== $status) {
1762 $hasVcs = $installedFromVcs;
1763 if (!$keepVcs && $installedFromVcs
1765 !$io->isInteractive()
1766 || $io->askConfirmation('<info>Do you want to remove the existing VCS (.git, .svn..) history?</info> [<comment>Y,n</comment>]? ', true)
1769 $finder = new Finder();
1770 $finder->depth(0)->directories()->in(getcwd())->ignoreVCS(false)->ignoreDotFiles(false);
1771 foreach (array('.svn', '_svn', 'CVS', '_darcs', '.arch-params', '.monotone', '.bzr', '.git', '.hg') as $vcsName) {
1772 $finder->name($vcsName);
1776 $dirs = iterator_to_array($finder);
1778 foreach ($dirs as $dir) {
1779 if (!$fs->removeDirectory($dir)) {
1780 throw new \RuntimeException('Could not remove '.$dir);
1783 } catch (\Exception $e) {
1784 $io->write('<error>An error occurred while removing the VCS metadata: '.$e->getMessage().'</error>');
1792 $package = $composer->getPackage();
1793 $configSource = new JsonConfigSource(new JsonFile('composer.json'));
1794 foreach (BasePackage::$supportedLinkTypes as $type => $meta) {
1795 foreach ($package->{'get'.$meta['method']}() as $link) {
1796 if ($link->getPrettyConstraint() === 'self.version') {
1797 $configSource->addLink($type, $link->getTarget(), $package->getPrettyVersion());
1803 if ($noScripts === false) {
1805 $composer->getEventDispatcher()->dispatchCommandEvent(ScriptEvents::POST_CREATE_PROJECT_CMD, $installDevPackages);
1809 $vendorComposerDir = $composer->getConfig()->get('vendor-dir').'/composer';
1810 if (is_dir($vendorComposerDir) && $fs->isDirEmpty($vendorComposerDir)) {
1811 @rmdir($vendorComposerDir);
1812 $vendorDir = $composer->getConfig()->get('vendor-dir');
1813 if (is_dir($vendorDir) && $fs->isDirEmpty($vendorDir)) {
1821 protected function installRootPackage(IOInterface $io, Config $config, $packageName, $directory = null, $packageVersion = null, $stability = 'stable', $preferSource = false, $preferDist = false, $installDevPackages = false, $repositoryUrl = null, $disablePlugins = false, $noScripts = false, $keepVcs = false, $noProgress = false)
1823 if (null === $repositoryUrl) {
1824 $sourceRepo = new CompositeRepository(Factory::createDefaultRepositories($io, $config));
1825 } elseif ("json" === pathinfo($repositoryUrl, PATHINFO_EXTENSION) && file_exists($repositoryUrl)) {
1826 $json = new JsonFile($repositoryUrl, new RemoteFilesystem($io, $config));
1827 $data = $json->read();
1828 if (!empty($data['packages']) || !empty($data['includes']) || !empty($data['provider-includes'])) {
1829 $sourceRepo = new ComposerRepository(array('url' => 'file://' . strtr(realpath($repositoryUrl), '\\', '/')), $io, $config);
1831 $sourceRepo = new FilesystemRepository($json);
1833 } elseif (0 === strpos($repositoryUrl, 'http')) {
1834 $sourceRepo = new ComposerRepository(array('url' => $repositoryUrl), $io, $config);
1836 throw new \InvalidArgumentException("Invalid repository url given. Has to be a .json file or an http url.");
1839 $parser = new VersionParser();
1840 $requirements = $parser->parseNameVersionPairs(array($packageName));
1841 $name = strtolower($requirements[0]['name']);
1842 if (!$packageVersion && isset($requirements[0]['version'])) {
1843 $packageVersion = $requirements[0]['version'];
1846 if (null === $stability) {
1847 if (preg_match('{^[^,\s]*?@('.implode('|', array_keys(BasePackage::$stabilities)).')$}i', $packageVersion, $match)) {
1848 $stability = $match[1];
1850 $stability = VersionParser::parseStability($packageVersion);
1854 $stability = VersionParser::normalizeStability($stability);
1856 if (!isset(BasePackage::$stabilities[$stability])) {
1857 throw new \InvalidArgumentException('Invalid stability provided ('.$stability.'), must be one of: '.implode(', ', array_keys(BasePackage::$stabilities)));
1860 $pool = new Pool($stability);
1861 $pool->addRepository($sourceRepo);
1864 $versionSelector = new VersionSelector($pool);
1865 $package = $versionSelector->findBestCandidate($name, $packageVersion);
1868 throw new \InvalidArgumentException("Could not find package $name" . ($packageVersion ? " with version $packageVersion." : " with stability $stability."));
1871 if (null === $directory) {
1872 $parts = explode("/", $name, 2);
1873 $directory = getcwd() . DIRECTORY_SEPARATOR . array_pop($parts);
1876 $io->write('<info>Installing ' . $package->getName() . ' (' . VersionParser::formatVersion($package, false) . ')</info>');
1878 if ($disablePlugins) {
1879 $io->write('<info>Plugins have been disabled.</info>');
1882 if (0 === strpos($package->getPrettyVersion(), 'dev-') && in_array($package->getSourceType(), array('git', 'hg'))) {
1883 $package->setSourceReference(substr($package->getPrettyVersion(), 4));
1886 $dm = $this->createDownloadManager($io, $config);
1887 $dm->setPreferSource($preferSource)
1888 ->setPreferDist($preferDist)
1889 ->setOutputProgress(!$noProgress);
1891 $projectInstaller = new ProjectInstaller($directory, $dm);
1892 $im = $this->createInstallationManager();
1893 $im->addInstaller($projectInstaller);
1894 $im->install(new InstalledFilesystemRepository(new JsonFile('php://memory')), new InstallOperation($package));
1895 $im->notifyInstalls();
1897 $installedFromVcs = 'source' === $package->getInstallationSource();
1899 $io->write('<info>Created project in ' . $directory . '</info>');
1902 putenv('COMPOSER_ROOT_VERSION='.$package->getPrettyVersion());
1904 return $installedFromVcs;
1907 protected function createDownloadManager(IOInterface $io, Config $config)
1909 $factory = new Factory();
1911 return $factory->createDownloadManager($io, $config);
1914 protected function createInstallationManager()
1916 return new InstallationManager();
1926 protected function updatePreferredOptions(Config $config, InputInterface $input, &$preferSource, &$preferDist)
1928 switch ($config->get('preferred-install')) {
1930 $preferSource = true;
1931 $preferDist = false;
1934 $preferSource = false;
1943 if ($input->getOption('prefer-source') || $input->getOption('prefer-dist')) {
1944 $preferSource = $input->getOption('prefer-source');
1945 $preferDist = $input->getOption('prefer-dist');
1961 namespace Composer\Command;
1963 use Symfony\Component\Console\Input\InputInterface;
1964 use Symfony\Component\Console\Output\OutputInterface;
1969 class AboutCommand extends Command
1971 protected function configure()
1975 ->setDescription('Short information about Composer')
1977 <info>php composer.phar about</info>
1983 protected function execute(InputInterface $input, OutputInterface $output)
1985 $output->writeln(<<<EOT
1986 <info>Composer - Package Management for PHP</info>
1987 <comment>Composer is a dependency manager tracking local dependencies of your projects and libraries.
1988 See http://getcomposer.org/ for more information.</comment>
2005 namespace Composer\Command;
2007 use Symfony\Component\Console\Input\InputInterface;
2008 use Symfony\Component\Console\Input\InputOption;
2009 use Symfony\Component\Console\Input\InputArgument;
2010 use Symfony\Component\Console\Output\OutputInterface;
2015 class ScriptAliasCommand extends Command
2019 public function __construct($script)
2021 $this->script = $script;
2023 parent::__construct();
2026 protected function configure()
2029 ->setName($this->script)
2030 ->setDescription('Run the '.$this->script.' script as defined in composer.json.')
2031 ->setDefinition(array(
2032 new InputOption('dev', null, InputOption::VALUE_NONE, 'Sets the dev mode.'),
2033 new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables the dev mode.'),
2034 new InputArgument('args', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, ''),
2037 The <info>run-script</info> command runs scripts defined in composer.json:
2039 <info>php composer.phar run-script post-update-cmd</info>
2045 protected function execute(InputInterface $input, OutputInterface $output)
2047 $composer = $this->getComposer();
2050 $binDir = $composer->getConfig()->get('bin-dir');
2051 if (is_dir($binDir)) {
2052 putenv('PATH='.realpath($binDir).PATH_SEPARATOR.getenv('PATH'));
2055 $args = $input->getArguments();
2057 return $composer->getEventDispatcher()->dispatchScript($this->script, $input->getOption('dev') || !$input->getOption('no-dev'), $args['args']);
2072 namespace Composer\Command;
2074 use Composer\DependencyResolver\Pool;
2075 use Composer\DependencyResolver\DefaultPolicy;
2076 use Composer\Factory;
2077 use Composer\Package\CompletePackageInterface;
2078 use Composer\Package\Version\VersionParser;
2079 use Composer\Plugin\CommandEvent;
2080 use Composer\Plugin\PluginEvents;
2081 use Symfony\Component\Console\Input\InputInterface;
2082 use Symfony\Component\Console\Input\InputArgument;
2083 use Symfony\Component\Console\Input\InputOption;
2084 use Symfony\Component\Console\Output\OutputInterface;
2085 use Composer\Repository\ArrayRepository;
2086 use Composer\Repository\CompositeRepository;
2087 use Composer\Repository\ComposerRepository;
2088 use Composer\Repository\PlatformRepository;
2089 use Composer\Repository\RepositoryInterface;
2095 class ShowCommand extends Command
2097 protected $versionParser;
2099 protected function configure()
2103 ->setDescription('Show information about packages')
2104 ->setDefinition(array(
2105 new InputArgument('package', InputArgument::OPTIONAL, 'Package to inspect'),
2106 new InputArgument('version', InputArgument::OPTIONAL, 'Version or version constraint to inspect'),
2107 new InputOption('installed', 'i', InputOption::VALUE_NONE, 'List installed packages only'),
2108 new InputOption('platform', 'p', InputOption::VALUE_NONE, 'List platform packages only'),
2109 new InputOption('available', 'a', InputOption::VALUE_NONE, 'List available packages only'),
2110 new InputOption('self', 's', InputOption::VALUE_NONE, 'Show the root package information'),
2111 new InputOption('name-only', 'N', InputOption::VALUE_NONE, 'List package names only'),
2112 new InputOption('path', 'P', InputOption::VALUE_NONE, 'Show package paths'),
2115 The show command displays detailed information about a package, or
2116 lists all packages available.
2123 protected function execute(InputInterface $input, OutputInterface $output)
2125 $this->versionParser = new VersionParser;
2128 $platformRepo = new PlatformRepository;
2130 $composer = $this->getComposer(false);
2131 if ($input->getOption('self')) {
2132 $package = $this->getComposer()->getPackage();
2133 $repos = $installedRepo = new ArrayRepository(array($package));
2134 } elseif ($input->getOption('platform')) {
2135 $repos = $installedRepo = $platformRepo;
2136 } elseif ($input->getOption('installed')) {
2137 $repos = $installedRepo = $this->getComposer()->getRepositoryManager()->getLocalRepository();
2138 } elseif ($input->getOption('available')) {
2139 $installedRepo = $platformRepo;
2141 $repos = new CompositeRepository($composer->getRepositoryManager()->getRepositories());
2143 $defaultRepos = Factory::createDefaultRepositories($this->getIO());
2144 $repos = new CompositeRepository($defaultRepos);
2145 $output->writeln('No composer.json found in the current directory, showing available packages from ' . implode(', ', array_keys($defaultRepos)));
2147 } elseif ($composer) {
2148 $localRepo = $composer->getRepositoryManager()->getLocalRepository();
2149 $installedRepo = new CompositeRepository(array($localRepo, $platformRepo));
2150 $repos = new CompositeRepository(array_merge(array($installedRepo), $composer->getRepositoryManager()->getRepositories()));
2152 $defaultRepos = Factory::createDefaultRepositories($this->getIO());
2153 $output->writeln('No composer.json found in the current directory, showing available packages from ' . implode(', ', array_keys($defaultRepos)));
2154 $installedRepo = $platformRepo;
2155 $repos = new CompositeRepository(array_merge(array($installedRepo), $defaultRepos));
2159 $commandEvent = new CommandEvent(PluginEvents::COMMAND, 'show', $input, $output);
2160 $composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
2164 if ($input->getArgument('package') || !empty($package)) {
2165 $versions = array();
2166 if (empty($package)) {
2167 list($package, $versions) = $this->getPackage($installedRepo, $repos, $input->getArgument('package'), $input->getArgument('version'));
2170 throw new \InvalidArgumentException('Package '.$input->getArgument('package').' not found');
2173 $versions = array($package->getPrettyVersion() => $package->getVersion());
2176 $this->printMeta($input, $output, $package, $versions, $installedRepo, $repos);
2177 $this->printLinks($input, $output, $package, 'requires');
2178 $this->printLinks($input, $output, $package, 'devRequires', 'requires (dev)');
2179 if ($package->getSuggests()) {
2180 $output->writeln("\n<info>suggests</info>");
2181 foreach ($package->getSuggests() as $suggested => $reason) {
2182 $output->writeln($suggested . ' <comment>' . $reason . '</comment>');
2185 $this->printLinks($input, $output, $package, 'provides');
2186 $this->printLinks($input, $output, $package, 'conflicts');
2187 $this->printLinks($input, $output, $package, 'replaces');
2193 $packages = array();
2195 if ($repos instanceof CompositeRepository) {
2196 $repos = $repos->getRepositories();
2197 } elseif (!is_array($repos)) {
2198 $repos = array($repos);
2201 foreach ($repos as $repo) {
2202 if ($repo === $platformRepo) {
2203 $type = '<info>platform</info>:';
2205 $repo === $installedRepo
2206 || ($installedRepo instanceof CompositeRepository && in_array($repo, $installedRepo->getRepositories(), true))
2208 $type = '<info>installed</info>:';
2210 $type = '<comment>available</comment>:';
2212 if ($repo instanceof ComposerRepository && $repo->hasProviders()) {
2213 foreach ($repo->getProviderNames() as $name) {
2214 $packages[$type][$name] = $name;
2217 foreach ($repo->getPackages() as $package) {
2218 if (!isset($packages[$type][$package->getName()])
2219 || !is_object($packages[$type][$package->getName()])
2220 || version_compare($packages[$type][$package->getName()]->getVersion(), $package->getVersion(), '<')
2222 $packages[$type][$package->getName()] = $package;
2228 $tree = !$input->getOption('platform') && !$input->getOption('installed') && !$input->getOption('available');
2229 $indent = $tree ? ' ' : '';
2230 foreach (array('<info>platform</info>:' => true, '<comment>available</comment>:' => false, '<info>installed</info>:' => true) as $type => $showVersion) {
2231 if (isset($packages[$type])) {
2233 $output->writeln($type);
2235 ksort($packages[$type]);
2237 $nameLength = $versionLength = 0;
2238 foreach ($packages[$type] as $package) {
2239 if (is_object($package)) {
2240 $nameLength = max($nameLength, strlen($package->getPrettyName()));
2241 $versionLength = max($versionLength, strlen($this->versionParser->formatVersion($package)));
2243 $nameLength = max($nameLength, $package);
2246 list($width) = $this->getApplication()->getTerminalDimensions();
2247 if (null === $width) {
2250 $width = PHP_INT_MAX;
2252 if (defined('PHP_WINDOWS_VERSION_BUILD')) {
2256 $writePath = !$input->getOption('name-only') && $input->getOption('path');
2257 $writeVersion = !$input->getOption('name-only') && !$input->getOption('path') && $showVersion && ($nameLength + $versionLength + 3 <= $width);
2258 $writeDescription = !$input->getOption('name-only') && !$input->getOption('path') && ($nameLength + ($showVersion ? $versionLength : 0) + 24 <= $width);
2259 foreach ($packages[$type] as $package) {
2260 if (is_object($package)) {
2261 $output->write($indent . str_pad($package->getPrettyName(), $nameLength, ' '), false);
2263 if ($writeVersion) {
2264 $output->write(' ' . str_pad($this->versionParser->formatVersion($package), $versionLength, ' '), false);
2267 if ($writeDescription) {
2268 $description = strtok($package->getDescription(), "\r\n");
2269 $remaining = $width - $nameLength - $versionLength - 4;
2270 if (strlen($description) > $remaining) {
2271 $description = substr($description, 0, $remaining - 3) . '...';
2273 $output->write(' ' . $description);
2277 $path = strtok(realpath($composer->getInstallationManager()->getInstallPath($package)), "\r\n");
2278 $output->write(' ' . $path);
2281 $output->write($indent . $package);
2283 $output->writeln('');
2286 $output->writeln('');
2302 protected function getPackage(RepositoryInterface $installedRepo, RepositoryInterface $repos, $name, $version = null)
2304 $name = strtolower($name);
2307 $constraint = $this->versionParser->parseConstraints($version);
2310 $policy = new DefaultPolicy();
2311 $pool = new Pool('dev');
2312 $pool->addRepository($repos);
2314 $matchedPackage = null;
2315 $versions = array();
2316 $matches = $pool->whatProvides($name, $constraint);
2317 foreach ($matches as $index => $package) {
2319 if ($package->getName() !== $name) {
2320 unset($matches[$index]);
2325 if (null === $version && $installedRepo->hasPackage($package)) {
2326 $matchedPackage = $package;
2329 $versions[$package->getPrettyVersion()] = $package->getVersion();
2330 $matches[$index] = $package->getId();
2334 if (!$matchedPackage && $matches && $prefered = $policy->selectPreferedPackages($pool, array(), $matches)) {
2335 $matchedPackage = $pool->literalToPackage($prefered[0]);
2338 return array($matchedPackage, $versions);
2344 protected function printMeta(InputInterface $input, OutputInterface $output, CompletePackageInterface $package, array $versions, RepositoryInterface $installedRepo, RepositoryInterface $repos)
2346 $output->writeln('<info>name</info> : ' . $package->getPrettyName());
2347 $output->writeln('<info>descrip.</info> : ' . $package->getDescription());
2348 $output->writeln('<info>keywords</info> : ' . join(', ', $package->getKeywords() ?: array()));
2349 $this->printVersions($input, $output, $package, $versions, $installedRepo, $repos);
2350 $output->writeln('<info>type</info> : ' . $package->getType());
2351 $output->writeln('<info>license</info> : ' . implode(', ', $package->getLicense()));
2352 $output->writeln('<info>source</info> : ' . sprintf('[%s] <comment>%s</comment> %s', $package->getSourceType(), $package->getSourceUrl(), $package->getSourceReference()));
2353 $output->writeln('<info>dist</info> : ' . sprintf('[%s] <comment>%s</comment> %s', $package->getDistType(), $package->getDistUrl(), $package->getDistReference()));
2354 $output->writeln('<info>names</info> : ' . implode(', ', $package->getNames()));
2356 if ($package->isAbandoned()) {
2357 $replacement = ($package->getReplacementPackage() !== null)
2358 ? ' The author suggests using the ' . $package->getReplacementPackage(). ' package instead.'
2362 sprintf('<error>Attention: This package is abandoned and no longer maintained.%s</error>', $replacement)
2366 if ($package->getSupport()) {
2367 $output->writeln("\n<info>support</info>");
2368 foreach ($package->getSupport() as $type => $value) {
2369 $output->writeln('<comment>' . $type . '</comment> : '.$value);
2373 if ($package->getAutoload()) {
2374 $output->writeln("\n<info>autoload</info>");
2375 foreach ($package->getAutoload() as $type => $autoloads) {
2376 $output->writeln('<comment>' . $type . '</comment>');
2378 if ($type === 'psr-0') {
2379 foreach ($autoloads as $name => $path) {
2380 $output->writeln(($name ?: '*') . ' => ' . (is_array($path) ? implode(', ', $path) : ($path ?: '.')));
2382 } elseif ($type === 'psr-4') {
2383 foreach ($autoloads as $name => $path) {
2384 $output->writeln(($name ?: '*') . ' => ' . (is_array($path) ? implode(', ', $path) : ($path ?: '.')));
2386 } elseif ($type === 'classmap') {
2387 $output->writeln(implode(', ', $autoloads));
2390 if ($package->getIncludePaths()) {
2391 $output->writeln('<comment>include-path</comment>');
2392 $output->writeln(implode(', ', $package->getIncludePaths()));
2400 protected function printVersions(InputInterface $input, OutputInterface $output, CompletePackageInterface $package, array $versions, RepositoryInterface $installedRepo, RepositoryInterface $repos)
2402 uasort($versions, 'version_compare');
2403 $versions = array_keys(array_reverse($versions));
2406 if ($installedRepo->hasPackage($package)) {
2407 $installedVersion = $package->getPrettyVersion();
2408 $key = array_search($installedVersion, $versions);
2409 if (false !== $key) {
2410 $versions[$key] = '<info>* ' . $installedVersion . '</info>';
2414 $versions = implode(', ', $versions);
2416 $output->writeln('<info>versions</info> : ' . $versions);
2428 protected function printLinks(InputInterface $input, OutputInterface $output, CompletePackageInterface $package, $linkType, $title = null)
2430 $title = $title ?: $linkType;
2431 if ($links = $package->{'get'.ucfirst($linkType)}()) {
2432 $output->writeln("\n<info>" . $title . "</info>");
2434 foreach ($links as $link) {
2435 $output->writeln($link->getTarget() . ' <comment>' . $link->getPrettyConstraint() . '</comment>');
2452 namespace Composer\Command;
2454 use Composer\Installer;
2455 use Composer\Plugin\CommandEvent;
2456 use Composer\Plugin\PluginEvents;
2457 use Symfony\Component\Console\Input\InputInterface;
2458 use Symfony\Component\Console\Input\InputOption;
2459 use Symfony\Component\Console\Input\InputArgument;
2460 use Symfony\Component\Console\Output\OutputInterface;
2466 class UpdateCommand extends Command
2468 protected function configure()
2472 ->setDescription('Updates your dependencies to the latest version according to composer.json, and updates the composer.lock file.')
2473 ->setDefinition(array(
2474 new InputArgument('packages', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, 'Packages that should be updated, if not provided all packages are.'),
2475 new InputOption('prefer-source', null, InputOption::VALUE_NONE, 'Forces installation from package sources when possible, including VCS information.'),
2476 new InputOption('prefer-dist', null, InputOption::VALUE_NONE, 'Forces installation from package dist even for dev versions.'),
2477 new InputOption('dry-run', null, InputOption::VALUE_NONE, 'Outputs the operations but will not execute anything (implicitly enables --verbose).'),
2478 new InputOption('dev', null, InputOption::VALUE_NONE, 'Enables installation of require-dev packages (enabled by default, only present for BC).'),
2479 new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables installation of require-dev packages.'),
2480 new InputOption('lock', null, InputOption::VALUE_NONE, 'Only updates the lock file hash to suppress warning about the lock file being out of date.'),
2481 new InputOption('no-plugins', null, InputOption::VALUE_NONE, 'Disables all plugins.'),
2482 new InputOption('no-custom-installers', null, InputOption::VALUE_NONE, 'DEPRECATED: Use no-plugins instead.'),
2483 new InputOption('no-autoloader', null, InputOption::VALUE_NONE, 'Skips autoloader generation'),
2484 new InputOption('no-scripts', null, InputOption::VALUE_NONE, 'Skips the execution of all scripts defined in composer.json file.'),
2485 new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'),
2486 new InputOption('with-dependencies', null, InputOption::VALUE_NONE, 'Add also all dependencies of whitelisted packages to the whitelist.'),
2487 new InputOption('verbose', 'v|vv|vvv', InputOption::VALUE_NONE, 'Shows more details including new commits pulled in when updating packages.'),
2488 new InputOption('optimize-autoloader', 'o', InputOption::VALUE_NONE, 'Optimize autoloader during autoloader dump.'),
2489 new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'),
2490 new InputOption('prefer-stable', null, InputOption::VALUE_NONE, 'Prefer stable versions of dependencies.'),
2491 new InputOption('prefer-lowest', null, InputOption::VALUE_NONE, 'Prefer lowest versions of dependencies.'),
2494 The <info>update</info> command reads the composer.json file from the
2495 current directory, processes it, and updates, removes or installs all the
2498 <info>php composer.phar update</info>
2500 To limit the update operation to a few packages, you can list the package(s)
2501 you want to update as such:
2503 <info>php composer.phar update vendor/package1 foo/mypackage [...]</info>
2505 You may also use an asterisk (*) pattern to limit the update operation to package(s)
2506 from a specific vendor:
2508 <info>php composer.phar update vendor/package1 foo/* [...]</info>
2514 protected function execute(InputInterface $input, OutputInterface $output)
2516 if ($input->getOption('no-custom-installers')) {
2517 $output->writeln('<warning>You are using the deprecated option "no-custom-installers". Use "no-plugins" instead.</warning>');
2518 $input->setOption('no-plugins', true);
2521 $composer = $this->getComposer(true, $input->getOption('no-plugins'));
2522 $composer->getDownloadManager()->setOutputProgress(!$input->getOption('no-progress'));
2523 $io = $this->getIO();
2525 $commandEvent = new CommandEvent(PluginEvents::COMMAND, 'update', $input, $output);
2526 $composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
2528 $install = Installer::create($io, $composer);
2530 $preferSource = false;
2531 $preferDist = false;
2533 $config = $composer->getConfig();
2535 switch ($config->get('preferred-install')) {
2537 $preferSource = true;
2547 if ($input->getOption('prefer-source') || $input->getOption('prefer-dist')) {
2548 $preferSource = $input->getOption('prefer-source');
2549 $preferDist = $input->getOption('prefer-dist');
2552 $optimize = $input->getOption('optimize-autoloader') || $config->get('optimize-autoloader');
2555 ->setDryRun($input->getOption('dry-run'))
2556 ->setVerbose($input->getOption('verbose'))
2557 ->setPreferSource($preferSource)
2558 ->setPreferDist($preferDist)
2559 ->setDevMode(!$input->getOption('no-dev'))
2560 ->setDumpAutoloader(!$input->getOption('no-autoloader'))
2561 ->setRunScripts(!$input->getOption('no-scripts'))
2562 ->setOptimizeAutoloader($optimize)
2564 ->setUpdateWhitelist($input->getOption('lock') ? array('lock') : $input->getArgument('packages'))
2565 ->setWhitelistDependencies($input->getOption('with-dependencies'))
2566 ->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs'))
2567 ->setPreferStable($input->getOption('prefer-stable'))
2568 ->setPreferLowest($input->getOption('prefer-lowest'))
2571 if ($input->getOption('no-plugins')) {
2572 $install->disablePlugins();
2575 return $install->run();
2590 namespace Composer\Command;
2592 use Symfony\Component\Console\Input\InputInterface;
2593 use Symfony\Component\Console\Input\InputArgument;
2594 use Symfony\Component\Console\Input\InputOption;
2595 use Symfony\Component\Console\Output\OutputInterface;
2596 use Composer\Config;
2597 use Composer\Config\JsonConfigSource;
2598 use Composer\Factory;
2599 use Composer\Json\JsonFile;
2605 class ConfigCommand extends Command
2615 protected $configFile;
2620 protected $configSource;
2625 protected function configure()
2629 ->setDescription('Set config options')
2630 ->setDefinition(array(
2631 new InputOption('global', 'g', InputOption::VALUE_NONE, 'Apply command to the global config file'),
2632 new InputOption('editor', 'e', InputOption::VALUE_NONE, 'Open editor'),
2633 new InputOption('auth', 'a', InputOption::VALUE_NONE, 'Affect auth config file (only used for --editor)'),
2634 new InputOption('unset', null, InputOption::VALUE_NONE, 'Unset the given setting-key'),
2635 new InputOption('list', 'l', InputOption::VALUE_NONE, 'List configuration settings'),
2636 new InputOption('file', 'f', InputOption::VALUE_REQUIRED, 'If you want to choose a different composer.json or config.json', 'composer.json'),
2637 new InputOption('absolute', null, InputOption::VALUE_NONE, 'Returns absolute paths when fetching *-dir config values instead of relative'),
2638 new InputArgument('setting-key', null, 'Setting key'),
2639 new InputArgument('setting-value', InputArgument::IS_ARRAY, 'Setting value'),
2642 This command allows you to edit some basic composer settings in either the
2643 local composer.json file or the global config.json file.
2645 To edit the global config.json file:
2647 <comment>%command.full_name% --global</comment>
2649 To add a repository:
2651 <comment>%command.full_name% repositories.foo vcs http://bar.com</comment>
2653 You can add a repository to the global config.json file by passing in the
2654 <info>--global</info> option.
2656 To edit the file in an external editor:
2658 <comment>%command.full_name% --editor</comment>
2660 To choose your editor you can set the "EDITOR" env variable.
2662 To get a list of configuration values in the file:
2664 <comment>%command.full_name% --list</comment>
2666 You can always pass more than one option. As an example, if you want to edit the
2667 global config.json file.
2669 <comment>%command.full_name% --editor --global</comment>
2678 protected function initialize(InputInterface $input, OutputInterface $output)
2680 parent::initialize($input, $output);
2682 if ($input->getOption('global') && 'composer.json' !== $input->getOption('file')) {
2683 throw new \RuntimeException('--file and --global can not be combined');
2686 $this->config = Factory::createConfig($this->getIO());
2690 $configFile = $input->getOption('global')
2691 ? ($this->config->get('home') . '/config.json')
2692 : $input->getOption('file');
2694 $this->configFile = new JsonFile($configFile);
2695 $this->configSource = new JsonConfigSource($this->configFile);
2697 $authConfigFile = $input->getOption('global')
2698 ? ($this->config->get('home') . '/auth.json')
2699 : dirname(realpath($input->getOption('file'))) . '/auth.json';
2701 $this->authConfigFile = new JsonFile($authConfigFile);
2702 $this->authConfigSource = new JsonConfigSource($this->authConfigFile, true);
2705 if ($input->getOption('global') && !$this->configFile->exists()) {
2706 touch($this->configFile->getPath());
2707 $this->configFile->write(array('config' => new \ArrayObject));
2708 @chmod($this->configFile->getPath(), 0600);
2710 if ($input->getOption('global') && !$this->authConfigFile->exists()) {
2711 touch($this->authConfigFile->getPath());
2712 $this->authConfigFile->write(array('http-basic' => new \ArrayObject, 'github-oauth' => new \ArrayObject));
2713 @chmod($this->authConfigFile->getPath(), 0600);
2716 if (!$this->configFile->exists()) {
2717 throw new \RuntimeException('No composer.json found in the current directory');
2724 protected function execute(InputInterface $input, OutputInterface $output)
2727 if ($input->getOption('editor')) {
2728 $editor = escapeshellcmd(getenv('EDITOR'));
2730 if (defined('PHP_WINDOWS_VERSION_BUILD')) {
2731 $editor = 'notepad';
2733 foreach (array('vim', 'vi', 'nano', 'pico', 'ed') as $candidate) {
2734 if (exec('which '.$candidate)) {
2735 $editor = $candidate;
2742 $file = $input->getOption('auth') ? $this->authConfigFile->getPath() : $this->configFile->getPath();
2743 system($editor . ' ' . $file . (defined('PHP_WINDOWS_VERSION_BUILD') ? '' : ' > `tty`'));
2748 if (!$input->getOption('global')) {
2749 $this->config->merge($this->configFile->read());
2750 $this->config->merge(array('config' => $this->authConfigFile->exists() ? $this->authConfigFile->read() : array()));
2754 if ($input->getOption('list')) {
2755 $this->listConfiguration($this->config->all(), $this->config->raw(), $output);
2760 $settingKey = $input->getArgument('setting-key');
2766 if (array() !== $input->getArgument('setting-value') && $input->getOption('unset')) {
2767 throw new \RuntimeException('You can not combine a setting value with --unset');
2771 if (array() === $input->getArgument('setting-value') && !$input->getOption('unset')) {
2772 $data = $this->config->all();
2773 if (preg_match('/^repos?(?:itories)?(?:\.(.+))?/', $settingKey, $matches)) {
2774 if (empty($matches[1])) {
2775 $value = isset($data['repositories']) ? $data['repositories'] : array();
2777 if (!isset($data['repositories'][$matches[1]])) {
2778 throw new \InvalidArgumentException('There is no '.$matches[1].' repository defined');
2781 $value = $data['repositories'][$matches[1]];
2783 } elseif (strpos($settingKey, '.')) {
2784 $bits = explode('.', $settingKey);
2785 $data = $data['config'];
2786 foreach ($bits as $bit) {
2787 if (isset($data[$bit])) {
2788 $data = $data[$bit];
2789 } elseif (isset($data[implode('.', $bits)])) {
2791 $data = $data[implode('.', $bits)];
2794 throw new \RuntimeException($settingKey.' is not defined');
2800 } elseif (isset($data['config'][$settingKey])) {
2801 $value = $this->config->get($settingKey, $input->getOption('absolute') ? 0 : Config::RELATIVE_PATHS);
2803 throw new \RuntimeException($settingKey.' is not defined');
2806 if (is_array($value)) {
2807 $value = json_encode($value);
2810 $output->writeln($value);
2815 $values = $input->getArgument('setting-value');
2818 if (preg_match('/^repos?(?:itories)?\.(.+)/', $settingKey, $matches)) {
2819 if ($input->getOption('unset')) {
2820 return $this->configSource->removeRepository($matches[1]);
2823 if (2 !== count($values)) {
2824 throw new \RuntimeException('You must pass the type and a url. Example: php composer.phar config repositories.foo vcs http://bar.com');
2827 return $this->configSource->addRepository($matches[1], array(
2828 'type' => $values[0],
2829 'url' => $values[1],
2834 if (preg_match('/^(github-oauth|http-basic)\.(.+)/', $settingKey, $matches)) {
2835 if ($input->getOption('unset')) {
2836 $this->authConfigSource->removeConfigSetting($matches[1].'.'.$matches[2]);
2837 $this->configSource->removeConfigSetting($matches[1].'.'.$matches[2]);
2842 if ($matches[1] === 'github-oauth') {
2843 if (1 !== count($values)) {
2844 throw new \RuntimeException('Too many arguments, expected only one token');
2846 $this->configSource->removeConfigSetting($matches[1].'.'.$matches[2]);
2847 $this->authConfigSource->addConfigSetting($matches[1].'.'.$matches[2], $values[0]);
2848 } elseif ($matches[1] === 'http-basic') {
2849 if (2 !== count($values)) {
2850 throw new \RuntimeException('Expected two arguments (username, password), got '.count($values));
2852 $this->configSource->removeConfigSetting($matches[1].'.'.$matches[2]);
2853 $this->authConfigSource->addConfigSetting($matches[1].'.'.$matches[2], array('username' => $values[0], 'password' => $values[1]));
2859 $booleanValidator = function ($val) { return in_array($val, array('true', 'false', '1', '0'), true); };
2860 $booleanNormalizer = function ($val) { return $val !== 'false' && (bool) $val; };
2863 $uniqueConfigValues = array(
2864 'process-timeout' => array('is_numeric', 'intval'),
2865 'use-include-path' => array($booleanValidator, $booleanNormalizer),
2866 'preferred-install' => array(
2867 function ($val) { return in_array($val, array('auto', 'source', 'dist'), true); },
2868 function ($val) { return $val; }
2870 'store-auths' => array(
2871 function ($val) { return in_array($val, array('true', 'false', 'prompt'), true); },
2873 if ('prompt' === $val) {
2877 return $val !== 'false' && (bool) $val;
2880 'notify-on-install' => array($booleanValidator, $booleanNormalizer),
2881 'vendor-dir' => array('is_string', function ($val) { return $val; }),
2882 'bin-dir' => array('is_string', function ($val) { return $val; }),
2883 'cache-dir' => array('is_string', function ($val) { return $val; }),
2884 'cache-files-dir' => array('is_string', function ($val) { return $val; }),
2885 'cache-repo-dir' => array('is_string', function ($val) { return $val; }),
2886 'cache-vcs-dir' => array('is_string', function ($val) { return $val; }),
2887 'cache-ttl' => array('is_numeric', 'intval'),
2888 'cache-files-ttl' => array('is_numeric', 'intval'),
2889 'cache-files-maxsize' => array(
2890 function ($val) { return preg_match('/^\s*([0-9.]+)\s*(?:([kmg])(?:i?b)?)?\s*$/i', $val) > 0; },
2891 function ($val) { return $val; }
2893 'discard-changes' => array(
2894 function ($val) { return in_array($val, array('stash', 'true', 'false', '1', '0'), true); },
2896 if ('stash' === $val) {
2900 return $val !== 'false' && (bool) $val;
2903 'autoloader-suffix' => array('is_string', function ($val) { return $val === 'null' ? null : $val; }),
2904 'optimize-autoloader' => array($booleanValidator, $booleanNormalizer),
2905 'prepend-autoloader' => array($booleanValidator, $booleanNormalizer),
2906 'github-expose-hostname' => array($booleanValidator, $booleanNormalizer),
2908 $multiConfigValues = array(
2909 'github-protocols' => array(
2911 if (!is_array($vals)) {
2912 return 'array expected';
2915 foreach ($vals as $val) {
2916 if (!in_array($val, array('git', 'https', 'ssh'))) {
2917 return 'valid protocols include: git, https, ssh';
2927 'github-domains' => array(
2929 if (!is_array($vals)) {
2930 return 'array expected';
2941 foreach ($uniqueConfigValues as $name => $callbacks) {
2942 if ($settingKey === $name) {
2943 if ($input->getOption('unset')) {
2944 return $this->configSource->removeConfigSetting($settingKey);
2947 list($validator, $normalizer) = $callbacks;
2948 if (1 !== count($values)) {
2949 throw new \RuntimeException('You can only pass one value. Example: php composer.phar config process-timeout 300');
2952 if (true !== $validation = $validator($values[0])) {
2953 throw new \RuntimeException(sprintf(
2954 '"%s" is an invalid value'.($validation ? ' ('.$validation.')' : ''),
2959 return $this->configSource->addConfigSetting($settingKey, $normalizer($values[0]));
2963 foreach ($multiConfigValues as $name => $callbacks) {
2964 if ($settingKey === $name) {
2965 if ($input->getOption('unset')) {
2966 return $this->configSource->removeConfigSetting($settingKey);
2969 list($validator, $normalizer) = $callbacks;
2970 if (true !== $validation = $validator($values)) {
2971 throw new \RuntimeException(sprintf(
2972 '%s is an invalid value'.($validation ? ' ('.$validation.')' : ''),
2973 json_encode($values)
2977 return $this->configSource->addConfigSetting($settingKey, $normalizer($values));
2981 throw new \InvalidArgumentException('Setting '.$settingKey.' does not exist or is not supported by this command');
2992 protected function listConfiguration(array $contents, array $rawContents, OutputInterface $output, $k = null)
2995 foreach ($contents as $key => $value) {
2996 if ($k === null && !in_array($key, array('config', 'repositories'))) {
3000 $rawVal = isset($rawContents[$key]) ? $rawContents[$key] : null;
3002 if (is_array($value) && (!is_numeric(key($value)) || ($key === 'repositories' && null === $k))) {
3003 $k .= preg_replace('{^config\.}', '', $key . '.');
3004 $this->listConfiguration($value, $rawVal, $output, $k);
3006 if (substr_count($k, '.') > 1) {
3007 $k = str_split($k, strrpos($k, '.', -2));
3016 if (is_array($value)) {
3017 $value = array_map(function ($val) {
3018 return is_array($val) ? json_encode($val) : $val;
3021 $value = '['.implode(', ', $value).']';
3024 if (is_bool($value)) {
3025 $value = var_export($value, true);
3028 if (is_string($rawVal) && $rawVal != $value) {
3029 $output->writeln('[<comment>' . $k . $key . '</comment>] <info>' . $rawVal . ' (' . $value . ')</info>');
3031 $output->writeln('[<comment>' . $k . $key . '</comment>] <info>' . $value . '</info>');
3048 namespace Composer\Command;
3050 use Composer\Installer;
3051 use Composer\Plugin\CommandEvent;
3052 use Composer\Plugin\PluginEvents;
3053 use Symfony\Component\Console\Input\InputInterface;
3054 use Symfony\Component\Console\Input\InputOption;
3055 use Symfony\Component\Console\Input\InputArgument;
3056 use Symfony\Component\Console\Output\OutputInterface;
3064 class InstallCommand extends Command
3066 protected function configure()
3069 ->setName('install')
3070 ->setDescription('Installs the project dependencies from the composer.lock file if present, or falls back on the composer.json.')
3071 ->setDefinition(array(
3072 new InputOption('prefer-source', null, InputOption::VALUE_NONE, 'Forces installation from package sources when possible, including VCS information.'),
3073 new InputOption('prefer-dist', null, InputOption::VALUE_NONE, 'Forces installation from package dist even for dev versions.'),
3074 new InputOption('dry-run', null, InputOption::VALUE_NONE, 'Outputs the operations but will not execute anything (implicitly enables --verbose).'),
3075 new InputOption('dev', null, InputOption::VALUE_NONE, 'Enables installation of require-dev packages (enabled by default, only present for BC).'),
3076 new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables installation of require-dev packages.'),
3077 new InputOption('no-plugins', null, InputOption::VALUE_NONE, 'Disables all plugins.'),
3078 new InputOption('no-custom-installers', null, InputOption::VALUE_NONE, 'DEPRECATED: Use no-plugins instead.'),
3079 new InputOption('no-autoloader', null, InputOption::VALUE_NONE, 'Skips autoloader generation'),
3080 new InputOption('no-scripts', null, InputOption::VALUE_NONE, 'Skips the execution of all scripts defined in composer.json file.'),
3081 new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'),
3082 new InputOption('verbose', 'v|vv|vvv', InputOption::VALUE_NONE, 'Shows more details including new commits pulled in when updating packages.'),
3083 new InputOption('optimize-autoloader', 'o', InputOption::VALUE_NONE, 'Optimize autoloader during autoloader dump'),
3084 new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'),
3085 new InputArgument('packages', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, 'Should not be provided, use composer require instead to add a given package to composer.json.'),
3088 The <info>install</info> command reads the composer.lock file from
3089 the current directory, processes it, and downloads and installs all the
3090 libraries and dependencies outlined in that file. If the file does not
3091 exist it will look for composer.json and do the same.
3093 <info>php composer.phar install</info>
3100 protected function execute(InputInterface $input, OutputInterface $output)
3102 if ($args = $input->getArgument('packages')) {
3103 $output->writeln('<error>Invalid argument '.implode(' ', $args).'. Use "composer require '.implode(' ', $args).'" instead to add packages to your composer.json.</error>');
3108 if ($input->getOption('no-custom-installers')) {
3109 $output->writeln('<warning>You are using the deprecated option "no-custom-installers". Use "no-plugins" instead.</warning>');
3110 $input->setOption('no-plugins', true);
3113 $composer = $this->getComposer(true, $input->getOption('no-plugins'));
3114 $composer->getDownloadManager()->setOutputProgress(!$input->getOption('no-progress'));
3115 $io = $this->getIO();
3117 $commandEvent = new CommandEvent(PluginEvents::COMMAND, 'install', $input, $output);
3118 $composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
3120 $install = Installer::create($io, $composer);
3122 $preferSource = false;
3123 $preferDist = false;
3125 $config = $composer->getConfig();
3127 switch ($config->get('preferred-install')) {
3129 $preferSource = true;
3139 if ($input->getOption('prefer-source') || $input->getOption('prefer-dist')) {
3140 $preferSource = $input->getOption('prefer-source');
3141 $preferDist = $input->getOption('prefer-dist');
3144 $optimize = $input->getOption('optimize-autoloader') || $config->get('optimize-autoloader');
3147 ->setDryRun($input->getOption('dry-run'))
3148 ->setVerbose($input->getOption('verbose'))
3149 ->setPreferSource($preferSource)
3150 ->setPreferDist($preferDist)
3151 ->setDevMode(!$input->getOption('no-dev'))
3152 ->setDumpAutoloader(!$input->getOption('no-autoloader'))
3153 ->setRunScripts(!$input->getOption('no-scripts'))
3154 ->setOptimizeAutoloader($optimize)
3155 ->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs'))
3158 if ($input->getOption('no-plugins')) {
3159 $install->disablePlugins();
3162 return $install->run();
3177 namespace Composer\Command;
3179 use Composer\Package\Loader\ValidatingArrayLoader;
3180 use Composer\Util\ConfigValidator;
3181 use Symfony\Component\Console\Input\InputArgument;
3182 use Symfony\Component\Console\Input\InputInterface;
3183 use Symfony\Component\Console\Input\InputOption;
3184 use Symfony\Component\Console\Output\OutputInterface;
3192 class ValidateCommand extends Command
3197 protected function configure()
3200 ->setName('validate')
3201 ->setDescription('Validates a composer.json')
3202 ->setDefinition(array(
3203 new InputOption('no-check-all', null, InputOption::VALUE_NONE, 'Do not make a complete validation'),
3204 new InputArgument('file', InputArgument::OPTIONAL, 'path to composer.json file', './composer.json')
3207 The validate command validates a given composer.json
3219 protected function execute(InputInterface $input, OutputInterface $output)
3221 $file = $input->getArgument('file');
3223 if (!file_exists($file)) {
3224 $output->writeln('<error>' . $file . ' not found.</error>');
3228 if (!is_readable($file)) {
3229 $output->writeln('<error>' . $file . ' is not readable.</error>');
3234 $validator = new ConfigValidator($this->getIO());
3235 $checkAll = $input->getOption('no-check-all') ? 0 : ValidatingArrayLoader::CHECK_ALL;
3236 list($errors, $publishErrors, $warnings) = $validator->validate($file, $checkAll);
3239 if (!$errors && !$publishErrors && !$warnings) {
3240 $output->writeln('<info>' . $file . ' is valid</info>');
3241 } elseif (!$errors && !$publishErrors) {
3242 $output->writeln('<info>' . $file . ' is valid, but with a few warnings</info>');
3243 $output->writeln('<warning>See http://getcomposer.org/doc/04-schema.md for details on the schema</warning>');
3244 } elseif (!$errors) {
3245 $output->writeln('<info>' . $file . ' is valid for simple usage with composer but has</info>');
3246 $output->writeln('<info>strict errors that make it unable to be published as a package:</info>');
3247 $output->writeln('<warning>See http://getcomposer.org/doc/04-schema.md for details on the schema</warning>');
3249 $output->writeln('<error>' . $file . ' is invalid, the following errors/warnings were found:</error>');
3253 'error' => array_merge($errors, $publishErrors),
3254 'warning' => $warnings,
3257 foreach ($messages as $style => $msgs) {
3258 foreach ($msgs as $msg) {
3259 $output->writeln('<' . $style . '>' . $msg . '</' . $style . '>');
3263 return $errors || $publishErrors ? 1 : 0;
3278 namespace Composer\Command;
3280 use Composer\DependencyResolver\Pool;
3281 use Composer\Plugin\CommandEvent;
3282 use Composer\Plugin\PluginEvents;
3283 use Symfony\Component\Console\Input\InputInterface;
3284 use Symfony\Component\Console\Input\InputArgument;
3285 use Symfony\Component\Console\Input\InputOption;
3286 use Symfony\Component\Console\Output\OutputInterface;
3292 class DependsCommand extends Command
3294 protected $linkTypes = array(
3295 'require' => array('requires', 'requires'),
3296 'require-dev' => array('devRequires', 'requires (dev)'),
3299 protected function configure()
3302 ->setName('depends')
3303 ->setDescription('Shows which packages depend on the given package')
3304 ->setDefinition(array(
3305 new InputArgument('package', InputArgument::REQUIRED, 'Package to inspect'),
3306 new InputOption('link-type', '', InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Link types to show (require, require-dev)', array_keys($this->linkTypes)),
3309 Displays detailed information about where a package is referenced.
3311 <info>php composer.phar depends composer/composer</info>
3318 protected function execute(InputInterface $input, OutputInterface $output)
3320 $composer = $this->getComposer();
3322 $commandEvent = new CommandEvent(PluginEvents::COMMAND, 'depends', $input, $output);
3323 $composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
3325 $repo = $composer->getRepositoryManager()->getLocalRepository();
3326 $needle = $input->getArgument('package');
3329 $pool->addRepository($repo);
3331 $packages = $pool->whatProvides($needle);
3332 if (empty($packages)) {
3333 throw new \InvalidArgumentException('Could not find package "'.$needle.'" in your project.');
3336 $linkTypes = $this->linkTypes;
3338 $types = array_map(function ($type) use ($linkTypes) {
3339 $type = rtrim($type, 's');
3340 if (!isset($linkTypes[$type])) {
3341 throw new \InvalidArgumentException('Unexpected link type: '.$type.', valid types: '.implode(', ', array_keys($linkTypes)));
3345 }, $input->getOption('link-type'));
3347 $messages = array();
3348 $outputPackages = array();
3349 foreach ($repo->getPackages() as $package) {
3350 foreach ($types as $type) {
3351 foreach ($package->{'get'.$linkTypes[$type][0]}() as $link) {
3352 if ($link->getTarget() === $needle) {
3353 if (!isset($outputPackages[$package->getName()])) {
3354 $messages[] = '<info>'.$package->getPrettyName() . '</info> ' . $linkTypes[$type][1] . ' ' . $needle .' (<info>' . $link->getPrettyConstraint() . '</info>)';
3355 $outputPackages[$package->getName()] = true;
3364 $output->writeln($messages);
3366 $output->writeln('<info>There is no installed package depending on "'.$needle.'".</info>');
3382 namespace Composer\Command;
3384 use Symfony\Component\Console\Input\InputInterface;
3385 use Symfony\Component\Console\Input\InputArgument;
3386 use Symfony\Component\Console\Input\InputOption;
3387 use Symfony\Component\Console\Output\OutputInterface;
3388 use Composer\Repository\CompositeRepository;
3389 use Composer\Repository\PlatformRepository;
3390 use Composer\Repository\RepositoryInterface;
3391 use Composer\Factory;
3392 use Composer\Plugin\CommandEvent;
3393 use Composer\Plugin\PluginEvents;
3398 class SearchCommand extends Command
3401 protected $lowMatches = array();
3404 protected $onlyName;
3406 protected function configure()
3410 ->setDescription('Search for packages')
3411 ->setDefinition(array(
3412 new InputOption('only-name', 'N', InputOption::VALUE_NONE, 'Search only in name'),
3413 new InputArgument('tokens', InputArgument::IS_ARRAY | InputArgument::REQUIRED, 'tokens to search for'),
3416 The search command searches for packages by its name
3417 <info>php composer.phar search symfony composer</info>
3424 protected function execute(InputInterface $input, OutputInterface $output)
3427 $platformRepo = new PlatformRepository;
3428 if ($composer = $this->getComposer(false)) {
3429 $localRepo = $composer->getRepositoryManager()->getLocalRepository();
3430 $installedRepo = new CompositeRepository(array($localRepo, $platformRepo));
3431 $repos = new CompositeRepository(array_merge(array($installedRepo), $composer->getRepositoryManager()->getRepositories()));
3433 $defaultRepos = Factory::createDefaultRepositories($this->getIO());
3434 $output->writeln('No composer.json found in the current directory, showing packages from ' . implode(', ', array_keys($defaultRepos)));
3435 $installedRepo = $platformRepo;
3436 $repos = new CompositeRepository(array_merge(array($installedRepo), $defaultRepos));
3440 $commandEvent = new CommandEvent(PluginEvents::COMMAND, 'search', $input, $output);
3441 $composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
3444 $onlyName = $input->getOption('only-name');
3446 $flags = $onlyName ? RepositoryInterface::SEARCH_NAME : RepositoryInterface::SEARCH_FULLTEXT;
3447 $results = $repos->search(implode(' ', $input->getArgument('tokens')), $flags);
3449 foreach ($results as $result) {
3450 $output->writeln($result['name'] . (isset($result['description']) ? ' '. $result['description'] : ''));
3466 namespace Composer\Command;
3468 use Composer\Composer;
3469 use Composer\Factory;
3470 use Composer\Util\Filesystem;
3471 use Composer\Util\RemoteFilesystem;
3472 use Composer\Downloader\FilesystemException;
3473 use Symfony\Component\Console\Input\InputInterface;
3474 use Symfony\Component\Console\Input\InputOption;
3475 use Symfony\Component\Console\Input\InputArgument;
3476 use Symfony\Component\Console\Output\OutputInterface;
3477 use Symfony\Component\Finder\Finder;
3484 class SelfUpdateCommand extends Command
3486 const HOMEPAGE = 'getcomposer.org';
3487 const OLD_INSTALL_EXT = '-old.phar';
3489 protected function configure()
3492 ->setName('self-update')
3493 ->setAliases(array('selfupdate'))
3494 ->setDescription('Updates composer.phar to the latest version.')
3495 ->setDefinition(array(
3496 new InputOption('rollback', 'r', InputOption::VALUE_NONE, 'Revert to an older installation of composer'),
3497 new InputOption('clean-backups', null, InputOption::VALUE_NONE, 'Delete old backups during an update. This makes the current version of composer the only backup available after the update'),
3498 new InputArgument('version', InputArgument::OPTIONAL, 'The version to update to'),
3499 new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'),
3502 The <info>self-update</info> command checks getcomposer.org for newer
3503 versions of composer and if found, installs the latest.
3505 <info>php composer.phar self-update</info>
3512 protected function execute(InputInterface $input, OutputInterface $output)
3514 $baseUrl = (extension_loaded('openssl') ? 'https' : 'http') . '://' . self::HOMEPAGE;
3515 $config = Factory::createConfig();
3516 $remoteFilesystem = new RemoteFilesystem($this->getIO(), $config);
3517 $cacheDir = $config->get('cache-dir');
3518 $rollbackDir = $config->get('home');
3519 $localFilename = realpath($_SERVER['argv'][0]) ?: $_SERVER['argv'][0];
3522 $tmpDir = is_writable(dirname($localFilename)) ? dirname($localFilename) : $cacheDir;
3525 if (!is_writable($tmpDir)) {
3526 throw new FilesystemException('Composer update failed: the "'.$tmpDir.'" directory used to download the temp file could not be written');
3528 if (!is_writable($localFilename)) {
3529 throw new FilesystemException('Composer update failed: the "'.$localFilename.'" file could not be written');
3532 if ($input->getOption('rollback')) {
3533 return $this->rollback($output, $rollbackDir, $localFilename);
3536 $latestVersion = trim($remoteFilesystem->getContents(self::HOMEPAGE, $baseUrl. '/version', false));
3537 $updateVersion = $input->getArgument('version') ?: $latestVersion;
3539 if (preg_match('{^[0-9a-f]{40}$}', $updateVersion) && $updateVersion !== $latestVersion) {
3540 $output->writeln('<error>You can not update to a specific SHA-1 as those phars are not available for download</error>');
3545 if (Composer::VERSION === $updateVersion) {
3546 $output->writeln('<info>You are already using composer version '.$updateVersion.'.</info>');
3551 $tempFilename = $tmpDir . '/' . basename($localFilename, '.phar').'-temp.phar';
3552 $backupFile = sprintf(
3555 strtr(Composer::RELEASE_DATE, ' :', '_-'),
3556 preg_replace('{^([0-9a-f]{7})[0-9a-f]{33}$}', '$1', Composer::VERSION),
3557 self::OLD_INSTALL_EXT
3560 $output->writeln(sprintf("Updating to version <info>%s</info>.", $updateVersion));
3561 $remoteFilename = $baseUrl . (preg_match('{^[0-9a-f]{40}$}', $updateVersion) ? '/composer.phar' : "/download/{$updateVersion}/composer.phar");
3562 $remoteFilesystem->copy(self::HOMEPAGE, $remoteFilename, $tempFilename, !$input->getOption('no-progress'));
3563 if (!file_exists($tempFilename)) {
3564 $output->writeln('<error>The download of the new composer version failed for an unexpected reason</error>');
3570 if ($input->getOption('clean-backups')) {
3571 $finder = $this->getOldInstallationFinder($rollbackDir);
3573 $fs = new Filesystem;
3574 foreach ($finder as $file) {
3575 $file = (string) $file;
3576 $output->writeln('<info>Removing: '.$file.'</info>');
3581 if ($err = $this->setLocalPhar($localFilename, $tempFilename, $backupFile)) {
3582 $output->writeln('<error>The file is corrupted ('.$err->getMessage().').</error>');
3583 $output->writeln('<error>Please re-run the self-update command to try again.</error>');
3588 if (file_exists($backupFile)) {
3589 $output->writeln('Use <info>composer self-update --rollback</info> to return to version '.Composer::VERSION);
3591 $output->writeln('<warning>A backup of the current version could not be written to '.$backupFile.', no rollback possible</warning>');
3595 protected function rollback(OutputInterface $output, $rollbackDir, $localFilename)
3597 $rollbackVersion = $this->getLastBackupVersion($rollbackDir);
3598 if (!$rollbackVersion) {
3599 throw new \UnexpectedValueException('Composer rollback failed: no installation to roll back to in "'.$rollbackDir.'"');
3602 if (!is_writable($rollbackDir)) {
3603 throw new FilesystemException('Composer rollback failed: the "'.$rollbackDir.'" dir could not be written to');
3606 $old = $rollbackDir . '/' . $rollbackVersion . self::OLD_INSTALL_EXT;
3608 if (!is_file($old)) {
3609 throw new FilesystemException('Composer rollback failed: "'.$old.'" could not be found');
3611 if (!is_readable($old)) {
3612 throw new FilesystemException('Composer rollback failed: "'.$old.'" could not be read');
3615 $oldFile = $rollbackDir . "/{$rollbackVersion}" . self::OLD_INSTALL_EXT;
3616 $output->writeln(sprintf("Rolling back to version <info>%s</info>.", $rollbackVersion));
3617 if ($err = $this->setLocalPhar($localFilename, $oldFile)) {
3618 $output->writeln('<error>The backup file was corrupted ('.$err->getMessage().') and has been removed.</error>');
3626 protected function setLocalPhar($localFilename, $newFilename, $backupTarget = null)
3629 @chmod($newFilename, 0777 & ~umask());
3630 if (!ini_get('phar.readonly')) {
3632 $phar = new \Phar($newFilename);
3638 if ($backupTarget && file_exists($localFilename)) {
3639 @copy($localFilename, $backupTarget);
3642 rename($newFilename, $localFilename);
3643 } catch (\Exception $e) {
3644 if ($backupTarget) {
3645 @unlink($newFilename);
3647 if (!$e instanceof \UnexpectedValueException && !$e instanceof \PharException) {
3655 protected function getLastBackupVersion($rollbackDir)
3657 $finder = $this->getOldInstallationFinder($rollbackDir);
3658 $finder->sortByName();
3659 $files = iterator_to_array($finder);
3661 if (count($files)) {
3662 return basename(end($files), self::OLD_INSTALL_EXT);
3668 protected function getOldInstallationFinder($rollbackDir)
3670 $finder = Finder::create()
3673 ->name('*' . self::OLD_INSTALL_EXT)
3691 namespace Composer\Command;
3693 use Composer\Json\JsonFile;
3694 use Composer\Package\Version\VersionParser;
3695 use Composer\Plugin\CommandEvent;
3696 use Composer\Plugin\PluginEvents;
3697 use Composer\Package\PackageInterface;
3698 use Composer\Repository\RepositoryInterface;
3699 use Symfony\Component\Console\Helper\TableHelper;
3700 use Symfony\Component\Console\Input\InputInterface;
3701 use Symfony\Component\Console\Input\InputOption;
3702 use Symfony\Component\Console\Output\OutputInterface;
3707 class LicensesCommand extends Command
3709 protected function configure()
3712 ->setName('licenses')
3713 ->setDescription('Show information about licenses of dependencies')
3714 ->setDefinition(array(
3715 new InputOption('format', 'f', InputOption::VALUE_REQUIRED, 'Format of the output: text or json', 'text'),
3716 new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables search in require-dev packages.'),
3719 The license command displays detailed information about the licenses of
3720 the installed dependencies.
3727 protected function execute(InputInterface $input, OutputInterface $output)
3729 $composer = $this->getComposer();
3731 $commandEvent = new CommandEvent(PluginEvents::COMMAND, 'licenses', $input, $output);
3732 $composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
3734 $root = $composer->getPackage();
3735 $repo = $composer->getRepositoryManager()->getLocalRepository();
3737 $versionParser = new VersionParser;
3739 if ($input->getOption('no-dev')) {
3740 $packages = $this->filterRequiredPackages($repo, $root);
3742 $packages = $this->appendPackages($repo->getPackages(), array());
3747 switch ($format = $input->getOption('format')) {
3749 $output->writeln('Name: <comment>'.$root->getPrettyName().'</comment>');
3750 $output->writeln('Version: <comment>'.$versionParser->formatVersion($root).'</comment>');
3751 $output->writeln('Licenses: <comment>'.(implode(', ', $root->getLicense()) ?: 'none').'</comment>');
3752 $output->writeln('Dependencies:');
3754 $table = $this->getHelperSet()->get('table');
3755 $table->setLayout(TableHelper::LAYOUT_BORDERLESS);
3756 $table->setHorizontalBorderChar('');
3757 foreach ($packages as $package) {
3758 $table->addRow(array(
3759 $package->getPrettyName(),
3760 $versionParser->formatVersion($package),
3761 implode(', ', $package->getLicense()) ?: 'none',
3764 $table->render($output);
3768 foreach ($packages as $package) {
3769 $dependencies[$package->getPrettyName()] = array(
3770 'version' => $versionParser->formatVersion($package),
3771 'license' => $package->getLicense(),
3775 $output->writeln(JsonFile::encode(array(
3776 'name' => $root->getPrettyName(),
3777 'version' => $versionParser->formatVersion($root),
3778 'license' => $root->getLicense(),
3779 'dependencies' => $dependencies,
3784 throw new \RuntimeException(sprintf('Unsupported format "%s". See help for supported formats.', $format));
3794 private function filterRequiredPackages(RepositoryInterface $repo, PackageInterface $package, $bucket = array())
3796 $requires = array_keys($package->getRequires());
3798 $packageListNames = array_keys($bucket);
3799 $packages = array_filter(
3800 $repo->getPackages(),
3801 function ($package) use ($requires, $packageListNames) {
3802 return in_array($package->getName(), $requires) && !in_array($package->getName(), $packageListNames);
3806 $bucket = $this->appendPackages($packages, $bucket);
3808 foreach ($packages as $package) {
3809 $bucket = $this->filterRequiredPackages($repo, $package, $bucket);
3822 public function appendPackages(array $packages, array $bucket)
3824 foreach ($packages as $package) {
3825 $bucket[$package->getName()] = $package;
3843 namespace Composer\Command;
3845 use Composer\DependencyResolver\Pool;
3846 use Composer\Factory;
3847 use Composer\Package\CompletePackageInterface;
3848 use Composer\Repository\CompositeRepository;
3849 use Composer\Repository\RepositoryInterface;
3850 use Composer\Util\ProcessExecutor;
3851 use Symfony\Component\Console\Input\InputArgument;
3852 use Symfony\Component\Console\Input\InputOption;
3853 use Symfony\Component\Console\Input\InputInterface;
3854 use Symfony\Component\Console\Output\OutputInterface;
3859 class HomeCommand extends Command
3864 protected function configure()
3868 ->setAliases(array('home'))
3869 ->setDescription('Opens the package\'s repository URL or homepage in your browser.')
3870 ->setDefinition(array(
3871 new InputArgument('packages', InputArgument::IS_ARRAY | InputArgument::REQUIRED, 'Package(s) to browse to.'),
3872 new InputOption('homepage', 'H', InputOption::VALUE_NONE, 'Open the homepage instead of the repository URL.'),
3875 The home command opens a package's repository URL or
3876 homepage in your default browser.
3878 To open the homepage by default, use -H or --homepage.
3886 protected function execute(InputInterface $input, OutputInterface $output)
3888 $repo = $this->initializeRepo();
3891 foreach ($input->getArgument('packages') as $packageName) {
3892 $package = $this->getPackage($repo, $packageName);
3894 if (!$package instanceof CompletePackageInterface) {
3896 $output->writeln('<warning>Package '.$packageName.' not found</warning>');
3901 $support = $package->getSupport();
3902 $url = isset($support['source']) ? $support['source'] : $package->getSourceUrl();
3903 if (!$url || $input->getOption('homepage')) {
3904 $url = $package->getHomepage();
3907 if (!filter_var($url, FILTER_VALIDATE_URL)) {
3909 $output->writeln('<warning>'.($input->getOption('homepage') ? 'Invalid or missing homepage' : 'Invalid or missing repository URL').' for '.$packageName.'</warning>');
3914 $this->openBrowser($url);
3927 protected function getPackage(RepositoryInterface $repos, $name)
3929 $name = strtolower($name);
3930 $pool = new Pool('dev');
3931 $pool->addRepository($repos);
3932 $matches = $pool->whatProvides($name);
3934 foreach ($matches as $index => $package) {
3936 if ($package->getName() !== $name) {
3937 unset($matches[$index]);
3950 private function openBrowser($url)
3952 $url = ProcessExecutor::escape($url);
3954 if (defined('PHP_WINDOWS_VERSION_MAJOR')) {
3955 return passthru('start "web" explorer "' . $url . '"');
3958 passthru('which xdg-open', $linux);
3959 passthru('which open', $osx);
3962 passthru('xdg-open ' . $url);
3963 } elseif (0 === $osx) {
3964 passthru('open ' . $url);
3966 $this->getIO()->write('no suitable browser opening command found, open yourself: ' . $url);
3975 private function initializeRepo()
3977 $composer = $this->getComposer(false);
3980 $repo = new CompositeRepository($composer->getRepositoryManager()->getRepositories());
3982 $defaultRepos = Factory::createDefaultRepositories($this->getIO());
3983 $repo = new CompositeRepository($defaultRepos);
4001 namespace Composer\Command;
4003 use Symfony\Component\Console\Input\InputInterface;
4004 use Symfony\Component\Console\Input\InputArgument;
4005 use Symfony\Component\Console\Input\InputOption;
4006 use Symfony\Component\Console\Output\OutputInterface;
4007 use Composer\Factory;
4008 use Composer\Installer;
4009 use Composer\Json\JsonFile;
4010 use Composer\Json\JsonManipulator;
4011 use Composer\Package\Version\VersionParser;
4012 use Composer\Plugin\CommandEvent;
4013 use Composer\Plugin\PluginEvents;
4014 use Composer\Repository\CompositeRepository;
4015 use Composer\Repository\PlatformRepository;
4021 class RequireCommand extends InitCommand
4023 protected function configure()
4026 ->setName('require')
4027 ->setDescription('Adds required packages to your composer.json and installs them')
4028 ->setDefinition(array(
4029 new InputArgument('packages', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, 'Required package with a version constraint, e.g. foo/bar:1.0.0 or foo/bar=1.0.0 or "foo/bar 1.0.0"'),
4030 new InputOption('dev', null, InputOption::VALUE_NONE, 'Add requirement to require-dev.'),
4031 new InputOption('prefer-source', null, InputOption::VALUE_NONE, 'Forces installation from package sources when possible, including VCS information.'),
4032 new InputOption('prefer-dist', null, InputOption::VALUE_NONE, 'Forces installation from package dist even for dev versions.'),
4033 new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'),
4034 new InputOption('no-update', null, InputOption::VALUE_NONE, 'Disables the automatic update of the dependencies.'),
4035 new InputOption('update-no-dev', null, InputOption::VALUE_NONE, 'Run the dependency update with the --no-dev option.'),
4036 new InputOption('update-with-dependencies', null, InputOption::VALUE_NONE, 'Allows inherited dependencies to be updated with explicit dependencies.'),
4037 new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'),
4038 new InputOption('sort-packages', null, InputOption::VALUE_NONE, 'Sorts packages when adding/updating a new dependency'),
4041 The require command adds required packages to your composer.json and installs them
4043 If you do not want to install the new dependencies immediately you can call it with --no-update
4050 protected function execute(InputInterface $input, OutputInterface $output)
4052 $file = Factory::getComposerFile();
4054 $newlyCreated = !file_exists($file);
4055 if (!file_exists($file) && !file_put_contents($file, "{\n}\n")) {
4056 $output->writeln('<error>'.$file.' could not be created.</error>');
4060 if (!is_readable($file)) {
4061 $output->writeln('<error>'.$file.' is not readable.</error>');
4065 if (!is_writable($file)) {
4066 $output->writeln('<error>'.$file.' is not writable.</error>');
4071 $json = new JsonFile($file);
4072 $composerDefinition = $json->read();
4073 $composerBackup = file_get_contents($json->getPath());
4075 $composer = $this->getComposer();
4076 $repos = $composer->getRepositoryManager()->getRepositories();
4078 $this->repos = new CompositeRepository(array_merge(
4079 array(new PlatformRepository),
4083 $requirements = $this->determineRequirements($input, $output, $input->getArgument('packages'));
4085 $requireKey = $input->getOption('dev') ? 'require-dev' : 'require';
4086 $removeKey = $input->getOption('dev') ? 'require' : 'require-dev';
4087 $baseRequirements = array_key_exists($requireKey, $composerDefinition) ? $composerDefinition[$requireKey] : array();
4088 $requirements = $this->formatRequirements($requirements);
4091 $versionParser = new VersionParser();
4092 foreach ($requirements as $constraint) {
4093 $versionParser->parseConstraints($constraint);
4096 $sortPackages = $input->getOption('sort-packages');
4098 if (!$this->updateFileCleanly($json, $baseRequirements, $requirements, $requireKey, $removeKey, $sortPackages)) {
4099 foreach ($requirements as $package => $version) {
4100 $baseRequirements[$package] = $version;
4102 if (isset($composerDefinition[$removeKey][$package])) {
4103 unset($composerDefinition[$removeKey][$package]);
4107 $composerDefinition[$requireKey] = $baseRequirements;
4108 $json->write($composerDefinition);
4111 $output->writeln('<info>'.$file.' has been '.($newlyCreated ? 'created' : 'updated').'</info>');
4113 if ($input->getOption('no-update')) {
4116 $updateDevMode = !$input->getOption('update-no-dev');
4119 $this->resetComposer();
4120 $composer = $this->getComposer();
4121 $composer->getDownloadManager()->setOutputProgress(!$input->getOption('no-progress'));
4122 $io = $this->getIO();
4124 $commandEvent = new CommandEvent(PluginEvents::COMMAND, 'require', $input, $output);
4125 $composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
4127 $install = Installer::create($io, $composer);
4130 ->setVerbose($input->getOption('verbose'))
4131 ->setPreferSource($input->getOption('prefer-source'))
4132 ->setPreferDist($input->getOption('prefer-dist'))
4133 ->setDevMode($updateDevMode)
4135 ->setUpdateWhitelist(array_keys($requirements))
4136 ->setWhitelistDependencies($input->getOption('update-with-dependencies'))
4137 ->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs'))
4140 $status = $install->run();
4141 if ($status !== 0) {
4142 if ($newlyCreated) {
4143 $output->writeln("\n".'<error>Installation failed, deleting '.$file.'.</error>');
4144 unlink($json->getPath());
4146 $output->writeln("\n".'<error>Installation failed, reverting '.$file.' to its original content.</error>');
4147 file_put_contents($json->getPath(), $composerBackup);
4154 private function updateFileCleanly($json, array $base, array $new, $requireKey, $removeKey, $sortPackages)
4156 $contents = file_get_contents($json->getPath());
4158 $manipulator = new JsonManipulator($contents);
4160 foreach ($new as $package => $constraint) {
4161 if (!$manipulator->addLink($requireKey, $package, $constraint, $sortPackages)) {
4164 if (!$manipulator->removeSubNode($removeKey, $package)) {
4169 file_put_contents($json->getPath(), $manipulator->getContents());
4174 protected function interact(InputInterface $input, OutputInterface $output)
4191 namespace Composer\Command;
4193 use Composer\Plugin\CommandEvent;
4194 use Composer\Plugin\PluginEvents;
4195 use Symfony\Component\Console\Input\InputInterface;
4196 use Symfony\Component\Console\Input\InputOption;
4197 use Symfony\Component\Console\Output\OutputInterface;
4202 class DumpAutoloadCommand extends Command
4204 protected function configure()
4207 ->setName('dump-autoload')
4208 ->setAliases(array('dumpautoload'))
4209 ->setDescription('Dumps the autoloader')
4210 ->setDefinition(array(
4211 new InputOption('optimize', 'o', InputOption::VALUE_NONE, 'Optimizes PSR0 and PSR4 packages to be loaded with classmaps too, good for production.'),
4212 new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables autoload-dev rules.'),
4215 <info>php composer.phar dump-autoload</info>
4221 protected function execute(InputInterface $input, OutputInterface $output)
4223 $composer = $this->getComposer();
4225 $commandEvent = new CommandEvent(PluginEvents::COMMAND, 'dump-autoload', $input, $output);
4226 $composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
4228 $installationManager = $composer->getInstallationManager();
4229 $localRepo = $composer->getRepositoryManager()->getLocalRepository();
4230 $package = $composer->getPackage();
4231 $config = $composer->getConfig();
4233 $optimize = $input->getOption('optimize') || $config->get('optimize-autoloader');
4236 $output->writeln('<info>Generating optimized autoload files</info>');
4238 $output->writeln('<info>Generating autoload files</info>');
4241 $generator = $composer->getAutoloadGenerator();
4242 $generator->setDevMode(!$input->getOption('no-dev'));
4243 $generator->dump($config, $localRepo, $package, $installationManager, 'composer', $optimize);
4258 namespace Composer\Command;
4260 use Composer\Factory;
4261 use Symfony\Component\Console\Input\InputInterface;
4262 use Symfony\Component\Console\Input\InputArgument;
4263 use Symfony\Component\Console\Input\StringInput;
4264 use Symfony\Component\Console\Output\OutputInterface;
4269 class GlobalCommand extends Command
4271 protected function configure()
4275 ->setDescription('Allows running commands in the global composer dir ($COMPOSER_HOME).')
4276 ->setDefinition(array(
4277 new InputArgument('command-name', InputArgument::REQUIRED, ''),
4278 new InputArgument('args', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, ''),
4281 Use this command as a wrapper to run other Composer commands
4282 within the global context of COMPOSER_HOME.
4284 You can use this to install CLI utilities globally, all you need
4285 is to add the COMPOSER_HOME/vendor/bin dir to your PATH env var.
4287 COMPOSER_HOME is c:\Users\<user>\AppData\Roaming\Composer on Windows
4288 and /home/<user>/.composer on unix systems.
4290 Note: This path may vary depending on customizations to bin-dir in
4291 composer.json or the environmental variable COMPOSER_BIN_DIR.
4298 public function run(InputInterface $input, OutputInterface $output)
4301 $tokens = preg_split('{\s+}', $input->__toString());
4303 foreach ($tokens as $token) {
4304 if ($token && $token[0] !== '-') {
4306 if (count($args) >= 2) {
4313 if (count($args) < 2) {
4314 return parent::run($input, $output);
4318 $config = Factory::createConfig();
4319 chdir($config->get('home'));
4320 $output->writeln('<info>Changed current directory to '.$config->get('home').'</info>');
4323 $input = new StringInput(preg_replace('{\bg(?:l(?:o(?:b(?:a(?:l)?)?)?)?)?\b}', '', $input->__toString(), 1));
4325 return $this->getApplication()->run($input, $output);
4340 namespace Composer\Command;
4342 use Composer\Config\JsonConfigSource;
4343 use Composer\Installer;
4344 use Composer\Plugin\CommandEvent;
4345 use Composer\Plugin\PluginEvents;
4346 use Composer\Json\JsonFile;
4347 use Composer\Factory;
4348 use Symfony\Component\Console\Input\InputInterface;
4349 use Symfony\Component\Console\Input\InputOption;
4350 use Symfony\Component\Console\Input\InputArgument;
4351 use Symfony\Component\Console\Output\OutputInterface;
4357 class RemoveCommand extends Command
4359 protected function configure()
4363 ->setDescription('Removes a package from the require or require-dev')
4364 ->setDefinition(array(
4365 new InputArgument('packages', InputArgument::IS_ARRAY, 'Packages that should be removed.'),
4366 new InputOption('dev', null, InputOption::VALUE_NONE, 'Removes a package from the require-dev section.'),
4367 new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'),
4368 new InputOption('no-update', null, InputOption::VALUE_NONE, 'Disables the automatic update of the dependencies.'),
4369 new InputOption('update-no-dev', null, InputOption::VALUE_NONE, 'Run the dependency update with the --no-dev option.'),
4370 new InputOption('update-with-dependencies', null, InputOption::VALUE_NONE, 'Allows inherited dependencies to be updated with explicit dependencies.'),
4371 new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'),
4374 The <info>remove</info> command removes a package from the current
4375 list of installed packages
4377 <info>php composer.phar remove</info>
4384 protected function execute(InputInterface $input, OutputInterface $output)
4386 $packages = $input->getArgument('packages');
4388 $file = Factory::getComposerFile();
4390 $jsonFile = new JsonFile($file);
4391 $composer = $jsonFile->read();
4392 $composerBackup = file_get_contents($jsonFile->getPath());
4394 $json = new JsonConfigSource($jsonFile);
4396 $type = $input->getOption('dev') ? 'require-dev' : 'require';
4397 $altType = !$input->getOption('dev') ? 'require-dev' : 'require';
4399 foreach ($packages as $package) {
4400 if (isset($composer[$type][$package])) {
4401 $json->removeLink($type, $package);
4402 } elseif (isset($composer[$altType][$package])) {
4403 $output->writeln('<warning>'.$package.' could not be found in '.$type.' but it is present in '.$altType.'</warning>');
4404 $dialog = $this->getHelperSet()->get('dialog');
4405 if ($this->getIO()->isInteractive()) {
4406 if ($dialog->askConfirmation($output, $dialog->getQuestion('Do you want to remove it from '.$altType, 'yes', '?'), true)) {
4407 $json->removeLink($altType, $package);
4411 $output->writeln('<warning>'.$package.' is not required in your composer.json and has not been removed</warning>');
4415 if ($input->getOption('no-update')) {
4420 $composer = $this->getComposer();
4421 $composer->getDownloadManager()->setOutputProgress(!$input->getOption('no-progress'));
4422 $io = $this->getIO();
4424 $commandEvent = new CommandEvent(PluginEvents::COMMAND, 'remove', $input, $output);
4425 $composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
4427 $install = Installer::create($io, $composer);
4429 $updateDevMode = !$input->getOption('update-no-dev');
4431 ->setVerbose($input->getOption('verbose'))
4432 ->setDevMode($updateDevMode)
4434 ->setUpdateWhitelist($packages)
4435 ->setWhitelistDependencies($input->getOption('update-with-dependencies'))
4436 ->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs'))
4439 $status = $install->run();
4440 if ($status !== 0) {
4441 $output->writeln("\n".'<error>Removal failed, reverting '.$file.' to its original content.</error>');
4442 file_put_contents($jsonFile->getPath(), $composerBackup);
4460 namespace Composer\Command\Helper;
4462 use Symfony\Component\Console\Helper\DialogHelper as BaseDialogHelper;
4464 class DialogHelper extends BaseDialogHelper
4477 public function getQuestion($question, $default = null, $sep = ':')
4479 return $default !== null ?
4480 sprintf('<info>%s</info> [<comment>%s</comment>]%s ', $question, $default, $sep) :
4481 sprintf('<info>%s</info>%s ', $question, $sep);
4496 namespace Composer\Command;
4498 use Symfony\Component\Console\Input\InputInterface;
4499 use Symfony\Component\Console\Input\InputOption;
4500 use Symfony\Component\Console\Output\OutputInterface;
4501 use Composer\Downloader\ChangeReportInterface;
4502 use Composer\Plugin\CommandEvent;
4503 use Composer\Plugin\PluginEvents;
4504 use Composer\Script\ScriptEvents;
4510 class StatusCommand extends Command
4512 protected function configure()
4516 ->setDescription('Show a list of locally modified packages')
4517 ->setDefinition(array(
4518 new InputOption('verbose', 'v|vv|vvv', InputOption::VALUE_NONE, 'Show modified files for each directory that contains changes.'),
4521 The status command displays a list of dependencies that have
4522 been modified locally.
4529 protected function execute(InputInterface $input, OutputInterface $output)
4532 $composer = $this->getComposer();
4534 $commandEvent = new CommandEvent(PluginEvents::COMMAND, 'status', $input, $output);
4535 $composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
4537 $installedRepo = $composer->getRepositoryManager()->getLocalRepository();
4539 $dm = $composer->getDownloadManager();
4540 $im = $composer->getInstallationManager();
4543 $composer->getEventDispatcher()->dispatchCommandEvent(ScriptEvents::PRE_STATUS_CMD, true);
4548 foreach ($installedRepo->getPackages() as $package) {
4549 $downloader = $dm->getDownloaderForInstalledPackage($package);
4551 if ($downloader instanceof ChangeReportInterface) {
4552 $targetDir = $im->getInstallPath($package);
4554 if ($changes = $downloader->getLocalChanges($package, $targetDir)) {
4555 $errors[$targetDir] = $changes;
4562 $output->writeln('<info>No local changes</info>');
4564 $output->writeln('<error>You have changes in the following dependencies:</error>');
4567 foreach ($errors as $path => $changes) {
4568 if ($input->getOption('verbose')) {
4569 $indentedChanges = implode("\n", array_map(function ($line) {
4570 return ' ' . ltrim($line);
4571 }, explode("\n", $changes)));
4572 $output->writeln('<info>'.$path.'</info>:');
4573 $output->writeln($indentedChanges);
4575 $output->writeln($path);
4579 if ($errors && !$input->getOption('verbose')) {
4580 $output->writeln('Use --verbose (-v) to see modified files');
4584 $composer->getEventDispatcher()->dispatchCommandEvent(ScriptEvents::POST_STATUS_CMD, true);
4586 return $errors ? 1 : 0;
4601 namespace Composer\Command;
4603 use Composer\DependencyResolver\Pool;
4604 use Composer\Json\JsonFile;
4605 use Composer\Factory;
4606 use Composer\Package\BasePackage;
4607 use Composer\Package\Version\VersionSelector;
4608 use Composer\Repository\CompositeRepository;
4609 use Composer\Repository\PlatformRepository;
4610 use Composer\Package\Version\VersionParser;
4611 use Composer\Util\ProcessExecutor;
4612 use Symfony\Component\Console\Input\InputInterface;
4613 use Symfony\Component\Console\Input\InputOption;
4614 use Symfony\Component\Console\Output\OutputInterface;
4615 use Symfony\Component\Process\Process;
4616 use Symfony\Component\Process\ExecutableFinder;
4622 class InitCommand extends Command
4629 public function parseAuthorString($author)
4631 if (preg_match('/^(?P<name>[- \.,\p{L}\'’]+) <(?P<email>.+?)>$/u', $author, $match)) {
4632 if ($this->isValidEmail($match['email'])) {
4634 'name' => trim($match['name']),
4635 'email' => $match['email']
4640 throw new \InvalidArgumentException(
4641 'Invalid author string. Must be in the format: '.
4642 'John Smith <john@example.com>'
4646 protected function configure()
4650 ->setDescription('Creates a basic composer.json file in current directory.')
4651 ->setDefinition(array(
4652 new InputOption('name', null, InputOption::VALUE_REQUIRED, 'Name of the package'),
4653 new InputOption('description', null, InputOption::VALUE_REQUIRED, 'Description of package'),
4654 new InputOption('author', null, InputOption::VALUE_REQUIRED, 'Author name of package'),
4656 new InputOption('homepage', null, InputOption::VALUE_REQUIRED, 'Homepage of package'),
4657 new InputOption('require', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Package to require with a version constraint, e.g. foo/bar:1.0.0 or foo/bar=1.0.0 or "foo/bar 1.0.0"'),
4658 new InputOption('require-dev', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Package to require for development with a version constraint, e.g. foo/bar:1.0.0 or foo/bar=1.0.0 or "foo/bar 1.0.0"'),
4659 new InputOption('stability', 's', InputOption::VALUE_REQUIRED, 'Minimum stability (empty or one of: '.implode(', ', array_keys(BasePackage::$stabilities)).')'),
4660 new InputOption('license', 'l', InputOption::VALUE_REQUIRED, 'License of package'),
4663 The <info>init</info> command creates a basic composer.json file
4664 in the current directory.
4666 <info>php composer.phar init</info>
4673 protected function execute(InputInterface $input, OutputInterface $output)
4675 $dialog = $this->getHelperSet()->get('dialog');
4677 $whitelist = array('name', 'description', 'author', 'homepage', 'require', 'require-dev', 'stability', 'license');
4679 $options = array_filter(array_intersect_key($input->getOptions(), array_flip($whitelist)));
4681 if (isset($options['author'])) {
4682 $options['authors'] = $this->formatAuthors($options['author']);
4683 unset($options['author']);
4686 if (isset($options['stability'])) {
4687 $options['minimum-stability'] = $options['stability'];
4688 unset($options['stability']);
4691 $options['require'] = isset($options['require']) ? $this->formatRequirements($options['require']) : new \stdClass;
4692 if (array() === $options['require']) {
4693 $options['require'] = new \stdClass;
4696 if (isset($options['require-dev'])) {
4697 $options['require-dev'] = $this->formatRequirements($options['require-dev']);
4698 if (array() === $options['require-dev']) {
4699 $options['require-dev'] = new \stdClass;
4703 $file = new JsonFile('composer.json');
4705 $json = $file->encode($options);
4707 if ($input->isInteractive()) {
4708 $output->writeln(array(
4713 if (!$dialog->askConfirmation($output, $dialog->getQuestion('Do you confirm generation', 'yes', '?'), true)) {
4714 $output->writeln('<error>Command aborted</error>');
4720 $file->write($options);
4722 if ($input->isInteractive() && is_dir('.git')) {
4723 $ignoreFile = realpath('.gitignore');
4725 if (false === $ignoreFile) {
4726 $ignoreFile = realpath('.') . '/.gitignore';
4729 if (!$this->hasVendorIgnore($ignoreFile)) {
4730 $question = 'Would you like the <info>vendor</info> directory added to your <info>.gitignore</info> [<comment>yes</comment>]?';
4732 if ($dialog->askConfirmation($output, $question, true)) {
4733 $this->addVendorIgnore($ignoreFile);
4739 protected function interact(InputInterface $input, OutputInterface $output)
4741 $git = $this->getGitConfig();
4743 $dialog = $this->getHelperSet()->get('dialog');
4744 $formatter = $this->getHelperSet()->get('formatter');
4745 $output->writeln(array(
4747 $formatter->formatBlock('Welcome to the Composer config generator', 'bg=blue;fg=white', true),
4752 $output->writeln(array(
4754 'This command will guide you through creating your composer.json config.',
4758 $cwd = realpath(".");
4760 if (!$name = $input->getOption('name')) {
4761 $name = basename($cwd);
4762 $name = preg_replace('{(?:([a-z])([A-Z])|([A-Z])([A-Z][a-z]))}', '\\1\\3-\\2\\4', $name);
4763 $name = strtolower($name);
4764 if (isset($git['github.user'])) {
4765 $name = $git['github.user'] . '/' . $name;
4766 } elseif (!empty($_SERVER['USERNAME'])) {
4767 $name = $_SERVER['USERNAME'] . '/' . $name;
4768 } elseif (get_current_user()) {
4769 $name = get_current_user() . '/' . $name;
4772 $name = $name . '/' . $name;
4775 if (!preg_match('{^[a-z0-9_.-]+/[a-z0-9_.-]+$}', $name)) {
4776 throw new \InvalidArgumentException(
4777 'The package name '.$name.' is invalid, it should be lowercase and have a vendor name, a forward slash, and a package name, matching: [a-z0-9_.-]+/[a-z0-9_.-]+'
4782 $name = $dialog->askAndValidate(
4784 $dialog->getQuestion('Package name (<vendor>/<name>)', $name),
4785 function ($value) use ($name) {
4786 if (null === $value) {
4790 if (!preg_match('{^[a-z0-9_.-]+/[a-z0-9_.-]+$}', $value)) {
4791 throw new \InvalidArgumentException(
4792 'The package name '.$value.' is invalid, it should be lowercase and have a vendor name, a forward slash, and a package name, matching: [a-z0-9_.-]+/[a-z0-9_.-]+'
4799 $input->setOption('name', $name);
4801 $description = $input->getOption('description') ?: false;
4802 $description = $dialog->ask(
4804 $dialog->getQuestion('Description', $description),
4807 $input->setOption('description', $description);
4809 if (null === $author = $input->getOption('author')) {
4810 if (isset($git['user.name']) && isset($git['user.email'])) {
4811 $author = sprintf('%s <%s>', $git['user.name'], $git['user.email']);
4816 $author = $dialog->askAndValidate(
4818 $dialog->getQuestion('Author', $author),
4819 function ($value) use ($self, $author) {
4820 $value = $value ?: $author;
4821 $author = $self->parseAuthorString($value);
4823 return sprintf('%s <%s>', $author['name'], $author['email']);
4826 $input->setOption('author', $author);
4828 $minimumStability = $input->getOption('stability') ?: '';
4829 $minimumStability = $dialog->askAndValidate(
4831 $dialog->getQuestion('Minimum Stability', $minimumStability),
4832 function ($value) use ($self, $minimumStability) {
4833 if (null === $value) {
4834 return $minimumStability;
4837 if (!isset(BasePackage::$stabilities[$value])) {
4838 throw new \InvalidArgumentException(
4839 'Invalid minimum stability "'.$value.'". Must be empty or one of: '.
4840 implode(', ', array_keys(BasePackage::$stabilities))
4847 $input->setOption('stability', $minimumStability);
4849 $license = $input->getOption('license') ?: false;
4850 $license = $dialog->ask(
4852 $dialog->getQuestion('License', $license),
4855 $input->setOption('license', $license);
4857 $output->writeln(array(
4859 'Define your dependencies.',
4863 $requirements = array();
4864 if ($dialog->askConfirmation($output, $dialog->getQuestion('Would you like to define your dependencies (require) interactively', 'yes', '?'), true)) {
4865 $requirements = $this->determineRequirements($input, $output, $input->getOption('require'));
4867 $input->setOption('require', $requirements);
4868 $devRequirements = array();
4869 if ($dialog->askConfirmation($output, $dialog->getQuestion('Would you like to define your dev dependencies (require-dev) interactively', 'yes', '?'), true)) {
4870 $devRequirements = $this->determineRequirements($input, $output, $input->getOption('require-dev'));
4872 $input->setOption('require-dev', $devRequirements);
4875 protected function findPackages($name)
4877 return $this->getRepos()->search($name);
4880 protected function getRepos()
4882 if (!$this->repos) {
4883 $this->repos = new CompositeRepository(array_merge(
4884 array(new PlatformRepository),
4885 Factory::createDefaultRepositories($this->getIO())
4889 return $this->repos;
4892 protected function determineRequirements(InputInterface $input, OutputInterface $output, $requires = array())
4894 $dialog = $this->getHelperSet()->get('dialog');
4895 $prompt = $dialog->getQuestion('Search for a package', false, ':');
4898 $requires = $this->normalizeRequirements($requires);
4901 foreach ($requires as $requirement) {
4902 if (!isset($requirement['version'])) {
4904 $version = $this->findBestVersionForPackage($input, $requirement['name']);
4905 $requirement['version'] = $version;
4907 $output->writeln(sprintf(
4908 'Using version <info>%s</info> for <info>%s</info>',
4909 $requirement['version'],
4910 $requirement['name']
4914 $result[] = $requirement['name'] . ' ' . $requirement['version'];
4920 while (null !== $package = $dialog->ask($output, $prompt)) {
4921 $matches = $this->findPackages($package);
4923 if (count($matches)) {
4926 foreach ($matches as $position => $foundPackage) {
4927 $choices[] = sprintf(' <info>%5s</info> %s', "[$position]", $foundPackage['name']);
4928 if ($foundPackage['name'] === $package) {
4936 $output->writeln(array(
4938 sprintf('Found <info>%s</info> packages matching <info>%s</info>', count($matches), $package),
4942 $output->writeln($choices);
4943 $output->writeln('');
4945 $validator = function ($selection) use ($matches) {
4946 if ('' === $selection) {
4950 if (!is_numeric($selection) && preg_match('{^\s*(\S+)\s+(\S.*)\s*$}', $selection, $matches)) {
4951 return $matches[1].' '.$matches[2];
4954 if (!isset($matches[(int) $selection])) {
4955 throw new \Exception('Not a valid selection');
4958 $package = $matches[(int) $selection];
4960 return $package['name'];
4963 $package = $dialog->askAndValidate($output, $dialog->getQuestion('Enter package # to add, or the complete package name if it is not listed', false, ':'), $validator, 3);
4967 if (false !== $package && false === strpos($package, ' ')) {
4968 $validator = function ($input) {
4969 $input = trim($input);
4971 return $input ?: false;
4974 $constraint = $dialog->askAndValidate(
4976 $dialog->getQuestion('Enter the version constraint to require (or leave blank to use the latest version)', false, ':'),
4980 if (false === $constraint) {
4981 $constraint = $this->findBestVersionForPackage($input, $package);
4983 $output->writeln(sprintf(
4984 'Using version <info>%s</info> for <info>%s</info>',
4990 $package .= ' '.$constraint;
4993 if (false !== $package) {
4994 $requires[] = $package;
5002 protected function formatAuthors($author)
5004 return array($this->parseAuthorString($author));
5007 protected function formatRequirements(array $requirements)
5009 $requires = array();
5010 $requirements = $this->normalizeRequirements($requirements);
5011 foreach ($requirements as $requirement) {
5012 $requires[$requirement['name']] = $requirement['version'];
5018 protected function getGitConfig()
5020 if (null !== $this->gitConfig) {
5021 return $this->gitConfig;
5024 $finder = new ExecutableFinder();
5025 $gitBin = $finder->find('git');
5027 $cmd = new Process(sprintf('%s config -l', ProcessExecutor::escape($gitBin)));
5030 if ($cmd->isSuccessful()) {
5031 $this->gitConfig = array();
5032 preg_match_all('{^([^=]+)=(.*)$}m', $cmd->getOutput(), $matches, PREG_SET_ORDER);
5033 foreach ($matches as $match) {
5034 $this->gitConfig[$match[1]] = $match[2];
5037 return $this->gitConfig;
5040 return $this->gitConfig = array();
5059 protected function hasVendorIgnore($ignoreFile, $vendor = 'vendor')
5061 if (!file_exists($ignoreFile)) {
5065 $pattern = sprintf('{^/?%s(/\*?)?$}', preg_quote($vendor));
5067 $lines = file($ignoreFile, FILE_IGNORE_NEW_LINES);
5068 foreach ($lines as $line) {
5069 if (preg_match($pattern, $line)) {
5077 protected function normalizeRequirements(array $requirements)
5079 $parser = new VersionParser();
5081 return $parser->parseNameVersionPairs($requirements);
5084 protected function addVendorIgnore($ignoreFile, $vendor = '/vendor/')
5087 if (file_exists($ignoreFile)) {
5088 $contents = file_get_contents($ignoreFile);
5090 if ("\n" !== substr($contents, 0, -1)) {
5095 file_put_contents($ignoreFile, $contents . $vendor. "\n");
5098 protected function isValidEmail($email)
5101 if (!function_exists('filter_var')) {
5106 if (version_compare(PHP_VERSION, '5.3.3', '<')) {
5110 return false !== filter_var($email, FILTER_VALIDATE_EMAIL);
5113 private function getPool(InputInterface $input)
5116 $this->pool = new Pool($this->getMinimumStability($input));
5117 $this->pool->addRepository($this->getRepos());
5123 private function getMinimumStability(InputInterface $input)
5125 if ($input->hasOption('stability')) {
5126 return $input->getOption('stability') ?: 'stable';
5129 $file = Factory::getComposerFile();
5130 if (is_file($file) && is_readable($file) && is_array($composer = json_decode(file_get_contents($file), true))) {
5131 if (!empty($composer['minimum-stability'])) {
5132 return $composer['minimum-stability'];
5149 private function findBestVersionForPackage(InputInterface $input, $name)
5152 $versionSelector = new VersionSelector($this->getPool($input));
5153 $package = $versionSelector->findBestCandidate($name);
5156 throw new \InvalidArgumentException(sprintf(
5157 'Could not find package %s at any version for your minimum-stability (%s). Check the package spelling or your minimum-stability',
5159 $this->getMinimumStability($input)
5163 return $versionSelector->findRecommendedRequireVersion($package);
5178 namespace Composer\Downloader;
5180 use Composer\Config;
5181 use Composer\Package\PackageInterface;
5182 use Composer\Package\Version\VersionParser;
5183 use Composer\Util\ProcessExecutor;
5184 use Composer\IO\IOInterface;
5185 use Composer\Util\Filesystem;
5190 abstract class VcsDownloader implements DownloaderInterface, ChangeReportInterface
5195 protected $filesystem;
5197 public function __construct(IOInterface $io, Config $config, ProcessExecutor $process = null, Filesystem $fs = null)
5200 $this->config = $config;
5201 $this->process = $process ?: new ProcessExecutor($io);
5202 $this->filesystem = $fs ?: new Filesystem;
5208 public function getInstallationSource()
5216 public function download(PackageInterface $package, $path)
5218 if (!$package->getSourceReference()) {
5219 throw new \InvalidArgumentException('Package '.$package->getPrettyName().' is missing reference information');
5222 $this->io->write(" - Installing <info>" . $package->getName() . "</info> (<comment>" . VersionParser::formatVersion($package) . "</comment>)");
5223 $this->filesystem->emptyDirectory($path);
5225 $urls = $package->getSourceUrls();
5226 while ($url = array_shift($urls)) {
5228 if (Filesystem::isLocalPath($url)) {
5229 $url = realpath($url);
5231 $this->doDownload($package, $path, $url);
5233 } catch (\Exception $e) {
5234 if ($this->io->isDebug()) {
5235 $this->io->write('Failed: ['.get_class($e).'] '.$e->getMessage());
5236 } elseif (count($urls)) {
5237 $this->io->write(' Failed, trying the next URL');
5239 if (!count($urls)) {
5245 $this->io->write('');
5251 public function update(PackageInterface $initial, PackageInterface $target, $path)
5253 if (!$target->getSourceReference()) {
5254 throw new \InvalidArgumentException('Package '.$target->getPrettyName().' is missing reference information');
5257 $name = $target->getName();
5258 if ($initial->getPrettyVersion() == $target->getPrettyVersion()) {
5259 if ($target->getSourceType() === 'svn') {
5260 $from = $initial->getSourceReference();
5261 $to = $target->getSourceReference();
5263 $from = substr($initial->getSourceReference(), 0, 7);
5264 $to = substr($target->getSourceReference(), 0, 7);
5266 $name .= ' '.$initial->getPrettyVersion();
5268 $from = VersionParser::formatVersion($initial);
5269 $to = VersionParser::formatVersion($target);
5272 $this->io->write(" - Updating <info>" . $name . "</info> (<comment>" . $from . "</comment> => <comment>" . $to . "</comment>)");
5274 $this->cleanChanges($initial, $path, true);
5275 $urls = $target->getSourceUrls();
5276 while ($url = array_shift($urls)) {
5278 if (Filesystem::isLocalPath($url)) {
5279 $url = realpath($url);
5281 $this->doUpdate($initial, $target, $path, $url);
5283 } catch (\Exception $e) {
5284 if ($this->io->isDebug()) {
5285 $this->io->write('Failed: ['.get_class($e).'] '.$e->getMessage());
5286 } elseif (count($urls)) {
5287 $this->io->write(' Failed, trying the next URL');
5290 $this->reapplyChanges($path);
5297 $this->reapplyChanges($path);
5300 if ($this->io->isVerbose()) {
5301 $message = 'Pulling in changes:';
5302 $logs = $this->getCommitLogs($initial->getSourceReference(), $target->getSourceReference(), $path);
5305 $message = 'Rolling back changes:';
5306 $logs = $this->getCommitLogs($target->getSourceReference(), $initial->getSourceReference(), $path);
5310 $logs = implode("\n", array_map(function ($line) {
5312 }, explode("\n", $logs)));
5314 $this->io->write(' '.$message);
5315 $this->io->write($logs);
5319 $this->io->write('');
5325 public function remove(PackageInterface $package, $path)
5327 $this->io->write(" - Removing <info>" . $package->getName() . "</info> (<comment>" . $package->getPrettyVersion() . "</comment>)");
5328 $this->cleanChanges($package, $path, false);
5329 if (!$this->filesystem->removeDirectory($path)) {
5330 throw new \RuntimeException('Could not completely delete '.$path.', aborting.');
5338 public function setOutputProgress($outputProgress)
5352 protected function cleanChanges(PackageInterface $package, $path, $update)
5355 if (null !== $this->getLocalChanges($package, $path)) {
5356 throw new \RuntimeException('Source directory ' . $path . ' has uncommitted changes.');
5366 protected function reapplyChanges($path)
5377 abstract protected function doDownload(PackageInterface $package, $path, $url);
5387 abstract protected function doUpdate(PackageInterface $initial, PackageInterface $target, $path, $url);
5397 abstract protected function getCommitLogs($fromReference, $toReference, $path);
5411 namespace Composer\Downloader;
5413 use Composer\Config;
5415 use Composer\EventDispatcher\EventDispatcher;
5416 use Composer\Util\ProcessExecutor;
5417 use Composer\IO\IOInterface;
5427 class RarDownloader extends ArchiveDownloader
5431 public function __construct(IOInterface $io, Config $config, EventDispatcher $eventDispatcher = null, Cache $cache = null, ProcessExecutor $process = null)
5433 $this->process = $process ?: new ProcessExecutor($io);
5434 parent::__construct($io, $config, $eventDispatcher, $cache);
5437 protected function extract($file, $path)
5439 $processError = null;
5442 if (!defined('PHP_WINDOWS_VERSION_BUILD')) {
5443 $command = 'unrar x ' . ProcessExecutor::escape($file) . ' ' . ProcessExecutor::escape($path) . ' && chmod -R u+w ' . ProcessExecutor::escape($path);
5445 if (0 === $this->process->execute($command, $ignoredOutput)) {
5449 $processError = 'Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput();
5452 if (!class_exists('RarArchive')) {
5454 $iniPath = php_ini_loaded_file();
5457 $iniMessage = 'The php.ini used by your command-line PHP is: ' . $iniPath;
5459 $iniMessage = 'A php.ini file does not exist. You will have to create one.';
5462 $error = "Could not decompress the archive, enable the PHP rar extension or install unrar.\n"
5463 . $iniMessage . "\n" . $processError;
5465 if (!defined('PHP_WINDOWS_VERSION_BUILD')) {
5466 $error = "Could not decompress the archive, enable the PHP rar extension.\n" . $iniMessage;
5469 throw new \RuntimeException($error);
5472 $rarArchive = RarArchive::open($file);
5474 if (false === $rarArchive) {
5475 throw new \UnexpectedValueException('Could not open RAR archive: ' . $file);
5478 $entries = $rarArchive->getEntries();
5480 if (false === $entries) {
5481 throw new \RuntimeException('Could not retrieve RAR archive entries');
5484 foreach ($entries as $entry) {
5485 if (false === $entry->extract($path)) {
5486 throw new \RuntimeException('Could not extract entry');
5490 $rarArchive->close();
5505 namespace Composer\Downloader;
5507 use Composer\Config;
5509 use Composer\IO\IOInterface;
5510 use Composer\Package\PackageInterface;
5511 use Composer\Package\Version\VersionParser;
5512 use Composer\Plugin\PluginEvents;
5513 use Composer\Plugin\PreFileDownloadEvent;
5514 use Composer\EventDispatcher\EventDispatcher;
5515 use Composer\Util\Filesystem;
5516 use Composer\Util\RemoteFilesystem;
5526 class FileDownloader implements DownloaderInterface
5531 protected $filesystem;
5533 protected $outputProgress = true;
5545 public function __construct(IOInterface $io, Config $config, EventDispatcher $eventDispatcher = null, Cache $cache = null, RemoteFilesystem $rfs = null, Filesystem $filesystem = null)
5548 $this->config = $config;
5549 $this->eventDispatcher = $eventDispatcher;
5550 $this->rfs = $rfs ?: new RemoteFilesystem($io, $config);
5551 $this->filesystem = $filesystem ?: new Filesystem();
5552 $this->cache = $cache;
5554 if ($this->cache && $this->cache->gcIsNecessary()) {
5555 $this->cache->gc($config->get('cache-files-ttl'), $config->get('cache-files-maxsize'));
5562 public function getInstallationSource()
5570 public function download(PackageInterface $package, $path)
5572 if (!$package->getDistUrl()) {
5573 throw new \InvalidArgumentException('The given package is missing url information');
5576 $this->io->write(" - Installing <info>" . $package->getName() . "</info> (<comment>" . VersionParser::formatVersion($package) . "</comment>)");
5578 $urls = $package->getDistUrls();
5579 while ($url = array_shift($urls)) {
5581 return $this->doDownload($package, $path, $url);
5582 } catch (\Exception $e) {
5583 if ($this->io->isDebug()) {
5584 $this->io->write('');
5585 $this->io->write('Failed: ['.get_class($e).'] '.$e->getMessage());
5586 } elseif (count($urls)) {
5587 $this->io->write('');
5588 $this->io->write(' Failed, trying the next URL');
5591 if (!count($urls)) {
5597 $this->io->write('');
5600 protected function doDownload(PackageInterface $package, $path, $url)
5602 $this->filesystem->emptyDirectory($path);
5604 $fileName = $this->getFileName($package, $path);
5606 $processedUrl = $this->processUrl($package, $url);
5607 $hostname = parse_url($processedUrl, PHP_URL_HOST);
5609 $preFileDownloadEvent = new PreFileDownloadEvent(PluginEvents::PRE_FILE_DOWNLOAD, $this->rfs, $processedUrl);
5610 if ($this->eventDispatcher) {
5611 $this->eventDispatcher->dispatch($preFileDownloadEvent->getName(), $preFileDownloadEvent);
5613 $rfs = $preFileDownloadEvent->getRemoteFilesystem();
5616 $checksum = $package->getDistSha1Checksum();
5617 $cacheKey = $this->getCacheKey($package);
5620 if (!$this->cache || ($checksum && $checksum !== $this->cache->sha1($cacheKey)) || !$this->cache->copyTo($cacheKey, $fileName)) {
5621 if (!$this->outputProgress) {
5622 $this->io->write(' Downloading');
5627 while ($retries--) {
5629 $rfs->copy($hostname, $processedUrl, $fileName, $this->outputProgress, $package->getTransportOptions());
5631 } catch (TransportException $e) {
5633 if ((0 !== $e->getCode() && !in_array($e->getCode(),array(500, 502, 503, 504))) || !$retries) {
5636 if ($this->io->isVerbose()) {
5637 $this->io->write(' Download failed, retrying...');
5644 $this->cache->copyFrom($cacheKey, $fileName);
5647 $this->io->write(' Loading from cache');
5650 if (!file_exists($fileName)) {
5651 throw new \UnexpectedValueException($url.' could not be saved to '.$fileName.', make sure the'
5652 .' directory is writable and you have internet connectivity');
5655 if ($checksum && hash_file('sha1', $fileName) !== $checksum) {
5656 throw new \UnexpectedValueException('The checksum verification of the file failed (downloaded from '.$url.')');
5658 } catch (\Exception $e) {
5660 $this->filesystem->removeDirectory($path);
5661 $this->clearCache($package, $path);
5671 public function setOutputProgress($outputProgress)
5673 $this->outputProgress = $outputProgress;
5678 protected function clearCache(PackageInterface $package, $path)
5681 $fileName = $this->getFileName($package, $path);
5682 $this->cache->remove($this->getCacheKey($package));
5689 public function update(PackageInterface $initial, PackageInterface $target, $path)
5691 $this->remove($initial, $path);
5692 $this->download($target, $path);
5698 public function remove(PackageInterface $package, $path)
5700 $this->io->write(" - Removing <info>" . $package->getName() . "</info> (<comment>" . VersionParser::formatVersion($package) . "</comment>)");
5701 if (!$this->filesystem->removeDirectory($path)) {
5702 throw new \RuntimeException('Could not completely delete '.$path.', aborting.');
5713 protected function getFileName(PackageInterface $package, $path)
5715 return $path.'/'.pathinfo(parse_url($package->getDistUrl(), PHP_URL_PATH), PATHINFO_BASENAME);
5727 protected function processUrl(PackageInterface $package, $url)
5729 if (!extension_loaded('openssl') && 0 === strpos($url, 'https:')) {
5730 throw new \RuntimeException('You must enable the openssl extension to download files via https');
5736 private function getCacheKey(PackageInterface $package)
5738 if (preg_match('{^[a-f0-9]{40}$}', $package->getDistReference())) {
5739 return $package->getName().'/'.$package->getDistReference().'.'.$package->getDistType();
5742 return $package->getName().'/'.$package->getVersion().'-'.$package->getDistReference().'.'.$package->getDistType();
5757 namespace Composer\Downloader;
5759 use Composer\Package\PackageInterface;
5760 use Composer\Util\Svn as SvnUtil;
5766 class SvnDownloader extends VcsDownloader
5771 public function doDownload(PackageInterface $package, $path, $url)
5773 SvnUtil::cleanEnv();
5774 $ref = $package->getSourceReference();
5776 $this->io->write(" Checking out ".$package->getSourceReference());
5777 $this->execute($url, "svn co", sprintf("%s/%s", $url, $ref), null, $path);
5783 public function doUpdate(PackageInterface $initial, PackageInterface $target, $path, $url)
5785 SvnUtil::cleanEnv();
5786 $ref = $target->getSourceReference();
5788 if (!is_dir($path.'/.svn')) {
5789 throw new \RuntimeException('The .svn directory is missing from '.$path.', see http://getcomposer.org/commit-deps for more information');
5793 if (0 === $this->process->execute('svn --version', $output)) {
5794 if (preg_match('{(\d+(?:\.\d+)+)}', $output, $match) && version_compare($match[1], '1.7.0', '>=')) {
5795 $flags .= ' --ignore-ancestry';
5799 $this->io->write(" Checking out " . $ref);
5800 $this->execute($url, "svn switch" . $flags, sprintf("%s/%s", $url, $ref), $path);
5806 public function getLocalChanges(PackageInterface $package, $path)
5808 if (!is_dir($path.'/.svn')) {
5812 $this->process->execute('svn status --ignore-externals', $output, $path);
5814 return preg_match('{^ *[^X ] +}m', $output) ? $output : null;
5829 protected function execute($baseUrl, $command, $url, $cwd = null, $path = null)
5831 $util = new SvnUtil($baseUrl, $this->io, $this->config);
5833 return $util->execute($command, $url, $cwd, $path, $this->io->isVerbose());
5834 } catch (\RuntimeException $e) {
5835 throw new \RuntimeException(
5836 'Package could not be downloaded, '.$e->getMessage()
5844 protected function cleanChanges(PackageInterface $package, $path, $update)
5846 if (!$changes = $this->getLocalChanges($package, $path)) {
5850 if (!$this->io->isInteractive()) {
5851 if (true === $this->config->get('discard-changes')) {
5852 return $this->discardChanges($path);
5855 return parent::cleanChanges($package, $path, $update);
5858 $changes = array_map(function ($elem) {
5860 }, preg_split('{\s*\r?\n\s*}', $changes));
5861 $this->io->write(' <error>The package has modified files:</error>');
5862 $this->io->write(array_slice($changes, 0, 10));
5863 if (count($changes) > 10) {
5864 $this->io->write(' <info>'.count($changes) - 10 . ' more files modified, choose "v" to view the full list</info>');
5868 switch ($this->io->ask(' <info>Discard changes [y,n,v,?]?</info> ', '?')) {
5870 $this->discardChanges($path);
5874 throw new \RuntimeException('Update aborted');
5877 $this->io->write($changes);
5882 $this->io->write(array(
5883 ' y - discard changes and apply the '.($update ? 'update' : 'uninstall'),
5884 ' n - abort the '.($update ? 'update' : 'uninstall').' and let you manually clean things up',
5885 ' v - view modified files',
5896 protected function getCommitLogs($fromReference, $toReference, $path)
5898 if (preg_match('{.*@(\d+)$}', $fromReference) && preg_match('{.*@(\d+)$}', $toReference) ) {
5900 $fromRevision = preg_replace('{.*@(\d+)$}', '$1', $fromReference);
5901 $toRevision = preg_replace('{.*@(\d+)$}', '$1', $toReference);
5903 $command = sprintf('svn log -r%s:%s --incremental', $fromRevision, $toRevision);
5905 if (0 !== $this->process->execute($command, $output, $path)) {
5906 throw new \RuntimeException(
5907 'Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput()
5911 $output = "Could not retrieve changes between $fromReference and $toReference due to missing revision information";
5917 protected function discardChanges($path)
5919 if (0 !== $this->process->execute('svn revert -R .', $output, $path)) {
5920 throw new \RuntimeException("Could not reset changes\n\n:".$this->process->getErrorOutput());
5936 namespace Composer\Downloader;
5938 use Composer\Util\Filesystem;
5949 class PearPackageExtractor
5951 private static $rolesWithoutPackageNamePrefix = array('php', 'script', 'www');
5953 private $filesystem;
5956 public function __construct($file)
5958 if (!is_file($file)) {
5959 throw new \UnexpectedValueException('PEAR package file is not found at '.$file);
5962 $this->filesystem = new Filesystem();
5963 $this->file = $file;
5976 public function extractTo($target, array $roles = array('php' => '/', 'script' => '/bin'), $vars = array())
5978 $extractionPath = $target.'/tarball';
5981 $archive = new \PharData($this->file);
5982 $archive->extractTo($extractionPath, null, true);
5984 if (!is_file($this->combine($extractionPath, '/package.xml'))) {
5985 throw new \RuntimeException('Invalid PEAR package. It must contain package.xml file.');
5988 $fileCopyActions = $this->buildCopyActions($extractionPath, $roles, $vars);
5989 $this->copyFiles($fileCopyActions, $extractionPath, $target, $roles, $vars);
5990 $this->filesystem->removeDirectory($extractionPath);
5991 } catch (\Exception $exception) {
5992 throw new \UnexpectedValueException(sprintf('Failed to extract PEAR package %s to %s. Reason: %s', $this->file, $target, $exception->getMessage()), 0, $exception);
6005 private function copyFiles($files, $source, $target, $roles, $vars)
6007 foreach ($files as $file) {
6008 $from = $this->combine($source, $file['from']);
6009 $to = $this->combine($target, $roles[$file['role']]);
6010 $to = $this->combine($to, $file['to']);
6011 $tasks = $file['tasks'];
6012 $this->copyFile($from, $to, $tasks, $vars);
6016 private function copyFile($from, $to, $tasks, $vars)
6018 if (!is_file($from)) {
6019 throw new \RuntimeException('Invalid PEAR package. package.xml defines file that is not located inside tarball.');
6022 $this->filesystem->ensureDirectoryExists(dirname($to));
6024 if (0 == count($tasks)) {
6025 $copied = copy($from, $to);
6027 $content = file_get_contents($from);
6028 $replacements = array();
6029 foreach ($tasks as $task) {
6030 $pattern = $task['from'];
6031 $varName = $task['to'];
6032 if (isset($vars[$varName])) {
6033 if ($varName === 'php_bin' && false === strpos($to, '.bat')) {
6034 $replacements[$pattern] = preg_replace('{\.bat$}', '', $vars[$varName]);
6036 $replacements[$pattern] = $vars[$varName];
6040 $content = strtr($content, $replacements);
6042 $copied = file_put_contents($to, $content);
6045 if (false === $copied) {
6046 throw new \RuntimeException(sprintf('Failed to copy %s to %s', $from, $to));
6060 private function buildCopyActions($source, array $roles, $vars)
6063 $package = simplexml_load_file($this->combine($source, 'package.xml'));
6064 if (false === $package) {
6065 throw new \RuntimeException('Package definition file is not valid.');
6068 $packageSchemaVersion = $package['version'];
6069 if ('1.0' == $packageSchemaVersion) {
6070 $children = $package->release->filelist->children();
6071 $packageName = (string) $package->name;
6072 $packageVersion = (string) $package->release->version;
6073 $sourceDir = $packageName . '-' . $packageVersion;
6074 $result = $this->buildSourceList10($children, $roles, $sourceDir, '', null, $packageName);
6075 } elseif ('2.0' == $packageSchemaVersion || '2.1' == $packageSchemaVersion) {
6076 $children = $package->contents->children();
6077 $packageName = (string) $package->name;
6078 $packageVersion = (string) $package->version->release;
6079 $sourceDir = $packageName . '-' . $packageVersion;
6080 $result = $this->buildSourceList20($children, $roles, $sourceDir, '', null, $packageName);
6082 $namespaces = $package->getNamespaces();
6083 $package->registerXPathNamespace('ns', $namespaces['']);
6084 $releaseNodes = $package->xpath('ns:phprelease');
6085 $this->applyRelease($result, $releaseNodes, $vars);
6087 throw new \RuntimeException('Unsupported schema version of package definition file.');
6093 private function applyRelease(&$actions, $releaseNodes, $vars)
6095 foreach ($releaseNodes as $releaseNode) {
6096 $requiredOs = $releaseNode->installconditions && $releaseNode->installconditions->os && $releaseNode->installconditions->os->name ? (string) $releaseNode->installconditions->os->name : '';
6097 if ($requiredOs && $vars['os'] != $requiredOs) {
6101 if ($releaseNode->filelist) {
6102 foreach ($releaseNode->filelist->children() as $action) {
6103 if ('install' == $action->getName()) {
6104 $name = (string) $action['name'];
6105 $as = (string) $action['as'];
6106 if (isset($actions[$name])) {
6107 $actions[$name]['to'] = $as;
6109 } elseif ('ignore' == $action->getName()) {
6110 $name = (string) $action['name'];
6111 unset($actions[$name]);
6121 private function buildSourceList10($children, $targetRoles, $source, $target, $role, $packageName)
6126 foreach ($children as $child) {
6128 if ($child->getName() == 'dir') {
6129 $dirSource = $this->combine($source, (string) $child['name']);
6130 $dirTarget = $child['baseinstalldir'] ?: $target;
6131 $dirRole = $child['role'] ?: $role;
6132 $dirFiles = $this->buildSourceList10($child->children(), $targetRoles, $dirSource, $dirTarget, $dirRole, $packageName);
6133 $result = array_merge($result, $dirFiles);
6134 } elseif ($child->getName() == 'file') {
6135 $fileRole = (string) $child['role'] ?: $role;
6136 if (isset($targetRoles[$fileRole])) {
6137 $fileName = (string) ($child['name'] ?: $child[0]);
6138 $fileSource = $this->combine($source, $fileName);
6139 $fileTarget = $this->combine((string) $child['baseinstalldir'] ?: $target, $fileName);
6140 if (!in_array($fileRole, self::$rolesWithoutPackageNamePrefix)) {
6141 $fileTarget = $packageName . '/' . $fileTarget;
6143 $result[(string) $child['name']] = array('from' => $fileSource, 'to' => $fileTarget, 'role' => $fileRole, 'tasks' => array());
6151 private function buildSourceList20($children, $targetRoles, $source, $target, $role, $packageName)
6156 foreach ($children as $child) {
6158 if ('dir' == $child->getName()) {
6159 $dirSource = $this->combine($source, $child['name']);
6160 $dirTarget = $child['baseinstalldir'] ?: $target;
6161 $dirRole = $child['role'] ?: $role;
6162 $dirFiles = $this->buildSourceList20($child->children(), $targetRoles, $dirSource, $dirTarget, $dirRole, $packageName);
6163 $result = array_merge($result, $dirFiles);
6164 } elseif ('file' == $child->getName()) {
6165 $fileRole = (string) $child['role'] ?: $role;
6166 if (isset($targetRoles[$fileRole])) {
6167 $fileSource = $this->combine($source, (string) $child['name']);
6168 $fileTarget = $this->combine((string) ($child['baseinstalldir'] ?: $target), (string) $child['name']);
6169 $fileTasks = array();
6170 foreach ($child->children('http://pear.php.net/dtd/tasks-1.0') as $taskNode) {
6171 if ('replace' == $taskNode->getName()) {
6172 $fileTasks[] = array('from' => (string) $taskNode->attributes()->from, 'to' => (string) $taskNode->attributes()->to);
6175 if (!in_array($fileRole, self::$rolesWithoutPackageNamePrefix)) {
6176 $fileTarget = $packageName . '/' . $fileTarget;
6178 $result[(string) $child['name']] = array('from' => $fileSource, 'to' => $fileTarget, 'role' => $fileRole, 'tasks' => $fileTasks);
6186 private function combine($left, $right)
6188 return rtrim($left, '/') . '/' . ltrim($right, '/');
6203 namespace Composer\Downloader;
6205 use Composer\Package\PackageInterface;
6206 use Composer\IO\IOInterface;
6207 use Composer\Util\Filesystem;
6214 class DownloadManager
6217 private $preferDist = false;
6218 private $preferSource = false;
6219 private $filesystem;
6220 private $downloaders = array();
6229 public function __construct(IOInterface $io, $preferSource = false, Filesystem $filesystem = null)
6232 $this->preferSource = $preferSource;
6233 $this->filesystem = $filesystem ?: new Filesystem();
6242 public function setPreferSource($preferSource)
6244 $this->preferSource = $preferSource;
6255 public function setPreferDist($preferDist)
6257 $this->preferDist = $preferDist;
6269 public function setOutputProgress($outputProgress)
6271 foreach ($this->downloaders as $downloader) {
6272 $downloader->setOutputProgress($outputProgress);
6285 public function setDownloader($type, DownloaderInterface $downloader)
6287 $type = strtolower($type);
6288 $this->downloaders[$type] = $downloader;
6301 public function getDownloader($type)
6303 $type = strtolower($type);
6304 if (!isset($this->downloaders[$type])) {
6305 throw new \InvalidArgumentException(sprintf('Unknown downloader type: %s. Available types: %s.', $type, implode(', ', array_keys($this->downloaders))));
6308 return $this->downloaders[$type];
6321 public function getDownloaderForInstalledPackage(PackageInterface $package)
6323 $installationSource = $package->getInstallationSource();
6325 if ('metapackage' === $package->getType()) {
6329 if ('dist' === $installationSource) {
6330 $downloader = $this->getDownloader($package->getDistType());
6331 } elseif ('source' === $installationSource) {
6332 $downloader = $this->getDownloader($package->getSourceType());
6334 throw new \InvalidArgumentException(
6335 'Package '.$package.' seems not been installed properly'
6339 if ($installationSource !== $downloader->getInstallationSource()) {
6340 throw new \LogicException(sprintf(
6341 'Downloader "%s" is a %s type downloader and can not be used to download %s',
6342 get_class($downloader), $downloader->getInstallationSource(), $installationSource
6359 public function download(PackageInterface $package, $targetDir, $preferSource = null)
6361 $preferSource = null !== $preferSource ? $preferSource : $this->preferSource;
6362 $sourceType = $package->getSourceType();
6363 $distType = $package->getDistType();
6367 $sources[] = 'source';
6370 $sources[] = 'dist';
6373 if (empty($sources)) {
6374 throw new \InvalidArgumentException('Package '.$package.' must have a source or dist specified');
6377 if ((!$package->isDev() || $this->preferDist) && !$preferSource) {
6378 $sources = array_reverse($sources);
6381 $this->filesystem->ensureDirectoryExists($targetDir);
6383 foreach ($sources as $i => $source) {
6385 $this->io->write(' <warning>Now trying to download from ' . $source . '</warning>');
6387 $package->setInstallationSource($source);
6389 $downloader = $this->getDownloaderForInstalledPackage($package);
6391 $downloader->download($package, $targetDir);
6394 } catch (\RuntimeException $e) {
6395 if ($i === count($sources) - 1) {
6400 ' <warning>Failed to download '.
6401 $package->getPrettyName().
6402 ' from ' . $source . ': '.
6403 $e->getMessage().'</warning>'
6418 public function update(PackageInterface $initial, PackageInterface $target, $targetDir)
6420 $downloader = $this->getDownloaderForInstalledPackage($initial);
6425 $installationSource = $initial->getInstallationSource();
6427 if ('dist' === $installationSource) {
6428 $initialType = $initial->getDistType();
6429 $targetType = $target->getDistType();
6431 $initialType = $initial->getSourceType();
6432 $targetType = $target->getSourceType();
6436 if ($target->isDev() && 'dist' === $installationSource) {
6437 $downloader->remove($initial, $targetDir);
6438 $this->download($target, $targetDir);
6443 if ($initialType === $targetType) {
6444 $target->setInstallationSource($installationSource);
6445 $downloader->update($initial, $target, $targetDir);
6447 $downloader->remove($initial, $targetDir);
6448 $this->download($target, $targetDir, 'source' === $installationSource);
6458 public function remove(PackageInterface $package, $targetDir)
6460 $downloader = $this->getDownloaderForInstalledPackage($package);
6462 $downloader->remove($package, $targetDir);
6478 namespace Composer\Downloader;
6480 use Composer\Package\PackageInterface;
6488 interface DownloaderInterface
6495 public function getInstallationSource();
6503 public function download(PackageInterface $package, $path);
6512 public function update(PackageInterface $initial, PackageInterface $target, $path);
6520 public function remove(PackageInterface $package, $path);
6528 public function setOutputProgress($outputProgress);
6542 namespace Composer\Downloader;
6547 class TransportException extends \RuntimeException
6550 protected $response;
6552 public function setHeaders($headers)
6554 $this->headers = $headers;
6557 public function getHeaders()
6559 return $this->headers;
6562 public function setResponse($response)
6564 $this->response = $response;
6567 public function getResponse()
6569 return $this->response;
6584 namespace Composer\Downloader;
6591 class PharDownloader extends ArchiveDownloader
6596 protected function extract($file, $path)
6599 $archive = new \Phar($file);
6600 $archive->extractTo($path, null, true);
6620 namespace Composer\Downloader;
6627 class TarDownloader extends ArchiveDownloader
6632 protected function extract($file, $path)
6635 $archive = new \PharData($file);
6636 $archive->extractTo($path, null, true);
6651 namespace Composer\Downloader;
6653 use Composer\Package\PackageInterface;
6654 use Composer\Util\ProcessExecutor;
6659 class HgDownloader extends VcsDownloader
6664 public function doDownload(PackageInterface $package, $path, $url)
6666 $url = ProcessExecutor::escape($url);
6667 $ref = ProcessExecutor::escape($package->getSourceReference());
6668 $this->io->write(" Cloning ".$package->getSourceReference());
6669 $command = sprintf('hg clone %s %s', $url, ProcessExecutor::escape($path));
6670 if (0 !== $this->process->execute($command, $ignoredOutput)) {
6671 throw new \RuntimeException('Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput());
6673 $command = sprintf('hg up %s', $ref);
6674 if (0 !== $this->process->execute($command, $ignoredOutput, realpath($path))) {
6675 throw new \RuntimeException('Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput());
6682 public function doUpdate(PackageInterface $initial, PackageInterface $target, $path, $url)
6684 $url = ProcessExecutor::escape($url);
6685 $ref = ProcessExecutor::escape($target->getSourceReference());
6686 $this->io->write(" Updating to ".$target->getSourceReference());
6688 if (!is_dir($path.'/.hg')) {
6689 throw new \RuntimeException('The .hg directory is missing from '.$path.', see http://getcomposer.org/commit-deps for more information');
6692 $command = sprintf('hg pull %s && hg up %s', $url, $ref);
6693 if (0 !== $this->process->execute($command, $ignoredOutput, realpath($path))) {
6694 throw new \RuntimeException('Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput());
6701 public function getLocalChanges(PackageInterface $package, $path)
6703 if (!is_dir($path.'/.hg')) {
6707 $this->process->execute('hg st', $output, realpath($path));
6709 return trim($output) ?: null;
6715 protected function getCommitLogs($fromReference, $toReference, $path)
6717 $command = sprintf('hg log -r %s:%s --style compact', $fromReference, $toReference);
6719 if (0 !== $this->process->execute($command, $output, realpath($path))) {
6720 throw new \RuntimeException('Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput());
6738 namespace Composer\Downloader;
6740 use Composer\Package\PackageInterface;
6747 interface ChangeReportInterface
6756 public function getLocalChanges(PackageInterface $package, $path);
6770 namespace Composer\Downloader;
6772 use Composer\Package\PackageInterface;
6773 use Symfony\Component\Finder\Finder;
6782 abstract class ArchiveDownloader extends FileDownloader
6787 public function download(PackageInterface $package, $path)
6789 $temporaryDir = $this->config->get('vendor-dir').'/composer/'.substr(md5(uniqid('', true)), 0, 8);
6791 while ($retries--) {
6792 $fileName = parent::download($package, $path);
6794 if ($this->io->isVerbose()) {
6795 $this->io->write(' Extracting archive');
6799 $this->filesystem->ensureDirectoryExists($temporaryDir);
6801 $this->extract($fileName, $temporaryDir);
6802 } catch (\Exception $e) {
6804 parent::clearCache($package, $path);
6808 $this->filesystem->unlink($fileName);
6810 $contentDir = $this->getFolderContent($temporaryDir);
6813 if (1 === count($contentDir) && is_dir(reset($contentDir))) {
6814 $contentDir = $this->getFolderContent((string) reset($contentDir));
6818 foreach ($contentDir as $file) {
6819 $file = (string) $file;
6820 $this->filesystem->rename($file, $path . '/' . basename($file));
6823 $this->filesystem->removeDirectory($temporaryDir);
6824 if ($this->filesystem->isDirEmpty($this->config->get('vendor-dir').'/composer/')) {
6825 $this->filesystem->removeDirectory($this->config->get('vendor-dir').'/composer/');
6827 if ($this->filesystem->isDirEmpty($this->config->get('vendor-dir'))) {
6828 $this->filesystem->removeDirectory($this->config->get('vendor-dir'));
6830 } catch (\Exception $e) {
6832 $this->filesystem->removeDirectory($path);
6833 $this->filesystem->removeDirectory($temporaryDir);
6836 if ($retries && $e instanceof \UnexpectedValueException && class_exists('ZipArchive') && $e->getCode() === \ZipArchive::ER_NOZIP) {
6837 $this->io->write(' Invalid zip file, retrying...');
6848 $this->io->write('');
6854 protected function getFileName(PackageInterface $package, $path)
6856 return rtrim($path.'/'.md5($path.spl_object_hash($package)).'.'.pathinfo(parse_url($package->getDistUrl(), PHP_URL_PATH), PATHINFO_EXTENSION), '.');
6862 protected function processUrl(PackageInterface $package, $url)
6864 if ($package->getDistReference() && strpos($url, 'github.com')) {
6865 if (preg_match('{^https?://(?:www\.)?github\.com/([^/]+)/([^/]+)/(zip|tar)ball/(.+)$}i', $url, $match)) {
6867 $url = 'https://api.github.com/repos/' . $match[1] . '/'. $match[2] . '/' . $match[3] . 'ball/' . $package->getDistReference();
6868 } elseif ($package->getDistReference() && preg_match('{^https?://(?:www\.)?github\.com/([^/]+)/([^/]+)/archive/.+\.(zip|tar)(?:\.gz)?$}i', $url, $match)) {
6870 $url = 'https://api.github.com/repos/' . $match[1] . '/'. $match[2] . '/' . $match[3] . 'ball/' . $package->getDistReference();
6871 } elseif ($package->getDistReference() && preg_match('{^https?://api\.github\.com/repos/([^/]+)/([^/]+)/(zip|tar)ball(?:/.+)?$}i', $url, $match)) {
6873 $url = 'https://api.github.com/repos/' . $match[1] . '/'. $match[2] . '/' . $match[3] . 'ball/' . $package->getDistReference();
6877 if (!extension_loaded('openssl') && (0 === strpos($url, 'https:') || 0 === strpos($url, 'http://github.com'))) {
6878 throw new \RuntimeException('You must enable the openssl extension to download files via https');
6881 return parent::processUrl($package, $url);
6892 abstract protected function extract($file, $path);
6900 private function getFolderContent($dir)
6902 $finder = Finder::create()
6904 ->ignoreDotFiles(false)
6908 return iterator_to_array($finder);
6923 namespace Composer\Downloader;
6930 class FilesystemException extends \Exception
6932 public function __construct($message = null, $code = null, \Exception $previous = null)
6934 parent::__construct("Filesystem exception: \n".$message, $code, $previous);
6949 namespace Composer\Downloader;
6951 use Composer\Package\PackageInterface;
6952 use Composer\Repository\VcsRepository;
6953 use Composer\Util\Perforce;
6958 class PerforceDownloader extends VcsDownloader
6960 protected $perforce;
6965 public function doDownload(PackageInterface $package, $path, $url)
6967 $ref = $package->getSourceReference();
6968 $label = $this->getLabelFromSourceReference($ref);
6970 $this->io->write(' Cloning ' . $ref);
6971 $this->initPerforce($package, $path, $url);
6972 $this->perforce->setStream($ref);
6973 $this->perforce->p4Login($this->io);
6974 $this->perforce->writeP4ClientSpec();
6975 $this->perforce->connectClient();
6976 $this->perforce->syncCodeBase($label);
6977 $this->perforce->cleanupClientSpec();
6980 private function getLabelFromSourceReference($ref)
6982 $pos = strpos($ref,'@');
6983 if (false !== $pos) {
6984 return substr($ref, $pos + 1);
6990 public function initPerforce($package, $path, $url)
6992 if (!empty($this->perforce)) {
6993 $this->perforce->initializePath($path);
6998 $repository = $package->getRepository();
7000 if ($repository instanceof VcsRepository) {
7001 $repoConfig = $this->getRepoConfig($repository);
7003 $this->perforce = Perforce::create($repoConfig, $url, $path, $this->process, $this->io);
7006 private function getRepoConfig(VcsRepository $repository)
7008 return $repository->getRepoConfig();
7014 public function doUpdate(PackageInterface $initial, PackageInterface $target, $path, $url)
7016 $this->doDownload($target, $path, $url);
7022 public function getLocalChanges(PackageInterface $package, $path)
7024 $this->io->write('Perforce driver does not check for local changes before overriding', true);
7032 protected function getCommitLogs($fromReference, $toReference, $path)
7034 $commitLogs = $this->perforce->getCommitLogs($fromReference, $toReference);
7039 public function setPerforce($perforce)
7041 $this->perforce = $perforce;
7056 namespace Composer\Downloader;
7058 use Composer\Config;
7060 use Composer\EventDispatcher\EventDispatcher;
7061 use Composer\Util\ProcessExecutor;
7062 use Composer\IO\IOInterface;
7068 class ZipDownloader extends ArchiveDownloader
7072 public function __construct(IOInterface $io, Config $config, EventDispatcher $eventDispatcher = null, Cache $cache = null, ProcessExecutor $process = null)
7074 $this->process = $process ?: new ProcessExecutor($io);
7075 parent::__construct($io, $config, $eventDispatcher, $cache);
7078 protected function extract($file, $path)
7080 $processError = null;
7083 if (!defined('PHP_WINDOWS_VERSION_BUILD')) {
7084 $command = 'unzip '.ProcessExecutor::escape($file).' -d '.ProcessExecutor::escape($path) . ' && chmod -R u+w ' . ProcessExecutor::escape($path);
7086 if (0 === $this->process->execute($command, $ignoredOutput)) {
7090 $processError = 'Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput();
7091 } catch (\Exception $e) {
7092 $processError = 'Failed to execute ' . $command . "\n\n" . $e->getMessage();
7096 if (!class_exists('ZipArchive')) {
7098 $iniPath = php_ini_loaded_file();
7101 $iniMessage = 'The php.ini used by your command-line PHP is: ' . $iniPath;
7103 $iniMessage = 'A php.ini file does not exist. You will have to create one.';
7106 $error = "Could not decompress the archive, enable the PHP zip extension or install unzip.\n"
7107 . $iniMessage . "\n" . $processError;
7109 if (!defined('PHP_WINDOWS_VERSION_BUILD')) {
7110 $error = "Could not decompress the archive, enable the PHP zip extension.\n" . $iniMessage;
7113 throw new \RuntimeException($error);
7116 $zipArchive = new ZipArchive();
7118 if (true !== ($retval = $zipArchive->open($file))) {
7119 throw new \UnexpectedValueException($this->getErrorMessage($retval, $file), $retval);
7122 if (true !== $zipArchive->extractTo($path)) {
7123 throw new \RuntimeException("There was an error extracting the ZIP file. Corrupt file?");
7126 $zipArchive->close();
7136 protected function getErrorMessage($retval, $file)
7139 case ZipArchive::ER_EXISTS:
7140 return sprintf("File '%s' already exists.", $file);
7141 case ZipArchive::ER_INCONS:
7142 return sprintf("Zip archive '%s' is inconsistent.", $file);
7143 case ZipArchive::ER_INVAL:
7144 return sprintf("Invalid argument (%s)", $file);
7145 case ZipArchive::ER_MEMORY:
7146 return sprintf("Malloc failure (%s)", $file);
7147 case ZipArchive::ER_NOENT:
7148 return sprintf("No such zip file: '%s'", $file);
7149 case ZipArchive::ER_NOZIP:
7150 return sprintf("'%s' is not a zip archive.", $file);
7151 case ZipArchive::ER_OPEN:
7152 return sprintf("Can't open zip file: %s", $file);
7153 case ZipArchive::ER_READ:
7154 return sprintf("Zip read error (%s)", $file);
7155 case ZipArchive::ER_SEEK:
7156 return sprintf("Zip seek error (%s)", $file);
7158 return sprintf("'%s' is not a valid zip archive, got error code: %s", $file, $retval);
7174 namespace Composer\Downloader;
7176 use Composer\Config;
7178 use Composer\EventDispatcher\EventDispatcher;
7179 use Composer\Package\PackageInterface;
7180 use Composer\Util\ProcessExecutor;
7181 use Composer\IO\IOInterface;
7188 class GzipDownloader extends ArchiveDownloader
7192 public function __construct(IOInterface $io, Config $config, EventDispatcher $eventDispatcher = null, Cache $cache = null, ProcessExecutor $process = null)
7194 $this->process = $process ?: new ProcessExecutor($io);
7195 parent::__construct($io, $config, $eventDispatcher, $cache);
7198 protected function extract($file, $path)
7200 $targetFilepath = $path . DIRECTORY_SEPARATOR . basename(substr($file, 0, -3));
7203 if (!defined('PHP_WINDOWS_VERSION_BUILD')) {
7204 $command = 'gzip -cd ' . ProcessExecutor::escape($file) . ' > ' . ProcessExecutor::escape($targetFilepath);
7206 if (0 === $this->process->execute($command, $ignoredOutput)) {
7210 $processError = 'Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput();
7211 throw new \RuntimeException($processError);
7215 $archiveFile = gzopen($file, 'rb');
7216 $targetFile = fopen($targetFilepath, 'wb');
7217 while ($string = gzread($archiveFile, 4096)) {
7218 fwrite($targetFile, $string, strlen($string));
7220 gzclose($archiveFile);
7221 fclose($targetFile);
7227 protected function getFileName(PackageInterface $package, $path)
7229 return $path.'/'.pathinfo(parse_url($package->getDistUrl(), PHP_URL_PATH), PATHINFO_BASENAME);
7244 namespace Composer\Downloader;
7246 use Composer\Package\PackageInterface;
7247 use Composer\Util\GitHub;
7248 use Composer\Util\Git as GitUtil;
7249 use Composer\Util\ProcessExecutor;
7250 use Composer\IO\IOInterface;
7251 use Composer\Util\Filesystem;
7252 use Composer\Config;
7257 class GitDownloader extends VcsDownloader
7259 private $hasStashedChanges = false;
7262 public function __construct(IOInterface $io, Config $config, ProcessExecutor $process = null, Filesystem $fs = null)
7264 parent::__construct($io, $config, $process, $fs);
7265 $this->gitUtil = new GitUtil($this->io, $this->config, $this->process, $this->filesystem);
7271 public function doDownload(PackageInterface $package, $path, $url)
7273 GitUtil::cleanEnv();
7274 $path = $this->normalizePath($path);
7276 $ref = $package->getSourceReference();
7277 $flag = defined('PHP_WINDOWS_VERSION_MAJOR') ? '/D ' : '';
7278 $command = 'git clone --no-checkout %s %s && cd '.$flag.'%2$s && git remote add composer %1$s && git fetch composer';
7279 $this->io->write(" Cloning ".$ref);
7281 $commandCallable = function ($url) use ($ref, $path, $command) {
7282 return sprintf($command, ProcessExecutor::escape($url), ProcessExecutor::escape($path), ProcessExecutor::escape($ref));
7285 $this->gitUtil->runCommand($commandCallable, $url, $path, true);
7286 if ($url !== $package->getSourceUrl()) {
7287 $url = $package->getSourceUrl();
7288 $this->process->execute(sprintf('git remote set-url origin %s', ProcessExecutor::escape($url)), $output, $path);
7290 $this->setPushUrl($path, $url);
7292 if ($newRef = $this->updateToCommit($path, $ref, $package->getPrettyVersion(), $package->getReleaseDate())) {
7293 if ($package->getDistReference() === $package->getSourceReference()) {
7294 $package->setDistReference($newRef);
7296 $package->setSourceReference($newRef);
7303 public function doUpdate(PackageInterface $initial, PackageInterface $target, $path, $url)
7305 GitUtil::cleanEnv();
7306 $path = $this->normalizePath($path);
7307 if (!is_dir($path.'/.git')) {
7308 throw new \RuntimeException('The .git directory is missing from '.$path.', see http://getcomposer.org/commit-deps for more information');
7311 $ref = $target->getSourceReference();
7312 $this->io->write(" Checking out ".$ref);
7313 $command = 'git remote set-url composer %s && git fetch composer && git fetch --tags composer';
7315 $commandCallable = function ($url) use ($command) {
7316 return sprintf($command, ProcessExecutor::escape ($url));
7319 $this->gitUtil->runCommand($commandCallable, $url, $path);
7320 if ($newRef = $this->updateToCommit($path, $ref, $target->getPrettyVersion(), $target->getReleaseDate())) {
7321 if ($target->getDistReference() === $target->getSourceReference()) {
7322 $target->setDistReference($newRef);
7324 $target->setSourceReference($newRef);
7331 public function getLocalChanges(PackageInterface $package, $path)
7333 GitUtil::cleanEnv();
7334 $path = $this->normalizePath($path);
7335 if (!is_dir($path.'/.git')) {
7339 $command = 'git status --porcelain --untracked-files=no';
7340 if (0 !== $this->process->execute($command, $output, $path)) {
7341 throw new \RuntimeException('Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput());
7344 return trim($output) ?: null;
7350 protected function cleanChanges(PackageInterface $package, $path, $update)
7352 GitUtil::cleanEnv();
7353 $path = $this->normalizePath($path);
7354 if (!$changes = $this->getLocalChanges($package, $path)) {
7358 if (!$this->io->isInteractive()) {
7359 $discardChanges = $this->config->get('discard-changes');
7360 if (true === $discardChanges) {
7361 return $this->discardChanges($path);
7363 if ('stash' === $discardChanges) {
7365 return parent::cleanChanges($package, $path, $update);
7368 return $this->stashChanges($path);
7371 return parent::cleanChanges($package, $path, $update);
7374 $changes = array_map(function ($elem) {
7376 }, preg_split('{\s*\r?\n\s*}', $changes));
7377 $this->io->write(' <error>The package has modified files:</error>');
7378 $this->io->write(array_slice($changes, 0, 10));
7379 if (count($changes) > 10) {
7380 $this->io->write(' <info>'.count($changes) - 10 . ' more files modified, choose "v" to view the full list</info>');
7384 switch ($this->io->ask(' <info>Discard changes [y,n,v,'.($update ? 's,' : '').'?]?</info> ', '?')) {
7386 $this->discardChanges($path);
7394 $this->stashChanges($path);
7398 throw new \RuntimeException('Update aborted');
7401 $this->io->write($changes);
7407 $this->io->write(array(
7408 ' y - discard changes and apply the '.($update ? 'update' : 'uninstall'),
7409 ' n - abort the '.($update ? 'update' : 'uninstall').' and let you manually clean things up',
7410 ' v - view modified files',
7413 $this->io->write(' s - stash changes and try to reapply them after the update');
7415 $this->io->write(' ? - print help');
7424 protected function reapplyChanges($path)
7426 $path = $this->normalizePath($path);
7427 if ($this->hasStashedChanges) {
7428 $this->hasStashedChanges = false;
7429 $this->io->write(' <info>Re-applying stashed changes</info>');
7430 if (0 !== $this->process->execute('git stash pop', $output, $path)) {
7431 throw new \RuntimeException("Failed to apply stashed changes:\n\n".$this->process->getErrorOutput());
7447 protected function updateToCommit($path, $reference, $branch, $date)
7449 $template = 'git checkout %s && git reset --hard %1$s';
7450 $branch = preg_replace('{(?:^dev-|(?:\.x)?-dev$)}i', '', $branch);
7453 if (0 === $this->process->execute('git branch -r', $output, $path)) {
7454 $branches = $output;
7458 $gitRef = $reference;
7459 if (!preg_match('{^[a-f0-9]{40}$}', $reference)
7461 && preg_match('{^\s+composer/'.preg_quote($reference).'$}m', $branches)
7463 $command = sprintf('git checkout -B %s %s && git reset --hard %2$s', ProcessExecutor::escape($branch), ProcessExecutor::escape('composer/'.$reference));
7464 if (0 === $this->process->execute($command, $output, $path)) {
7470 if (preg_match('{^[a-f0-9]{40}$}', $reference)) {
7472 if (!preg_match('{^\s+composer/'.preg_quote($branch).'$}m', $branches) && preg_match('{^\s+composer/v'.preg_quote($branch).'$}m', $branches)) {
7473 $branch = 'v' . $branch;
7476 $command = sprintf('git checkout %s', ProcessExecutor::escape($branch));
7477 $fallbackCommand = sprintf('git checkout -B %s %s', ProcessExecutor::escape($branch), ProcessExecutor::escape('composer/'.$branch));
7478 if (0 === $this->process->execute($command, $output, $path)
7479 || 0 === $this->process->execute($fallbackCommand, $output, $path)
7481 $command = sprintf('git reset --hard %s', ProcessExecutor::escape($reference));
7482 if (0 === $this->process->execute($command, $output, $path)) {
7488 $command = sprintf($template, ProcessExecutor::escape($gitRef));
7489 if (0 === $this->process->execute($command, $output, $path)) {
7494 if (false !== strpos($this->process->getErrorOutput(), $reference)) {
7495 $this->io->write(' <warning>'.$reference.' is gone (history was rewritten?)</warning>');
7498 throw new \RuntimeException('Failed to execute ' . GitUtil::sanitizeUrl($command) . "\n\n" . $this->process->getErrorOutput());
7501 protected function setPushUrl($path, $url)
7504 if (preg_match('{^(?:https?|git)://'.GitUtil::getGitHubDomainsRegex($this->config).'/([^/]+)/([^/]+?)(?:\.git)?$}', $url, $match)) {
7505 $protocols = $this->config->get('github-protocols');
7506 $pushUrl = 'git@'.$match[1].':'.$match[2].'/'.$match[3].'.git';
7507 if ($protocols[0] !== 'git') {
7508 $pushUrl = 'https://' . $match[1] . '/'.$match[2].'/'.$match[3].'.git';
7510 $cmd = sprintf('git remote set-url --push origin %s', ProcessExecutor::escape($pushUrl));
7511 $this->process->execute($cmd, $ignoredOutput, $path);
7518 protected function getCommitLogs($fromReference, $toReference, $path)
7520 $path = $this->normalizePath($path);
7521 $command = sprintf('git log %s..%s --pretty=format:"%%h - %%an: %%s"', $fromReference, $toReference);
7523 if (0 !== $this->process->execute($command, $output, $path)) {
7524 throw new \RuntimeException('Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput());
7534 protected function discardChanges($path)
7536 $path = $this->normalizePath($path);
7537 if (0 !== $this->process->execute('git reset --hard', $output, $path)) {
7538 throw new \RuntimeException("Could not reset changes\n\n:".$this->process->getErrorOutput());
7546 protected function stashChanges($path)
7548 $path = $this->normalizePath($path);
7549 if (0 !== $this->process->execute('git stash', $output, $path)) {
7550 throw new \RuntimeException("Could not stash changes\n\n:".$this->process->getErrorOutput());
7553 $this->hasStashedChanges = true;
7556 protected function normalizePath($path)
7558 if (defined('PHP_WINDOWS_VERSION_MAJOR') && strlen($path) > 0) {
7562 while (!is_dir($basePath) && $basePath !== '\\') {
7563 array_unshift($removed, basename($basePath));
7564 $basePath = dirname($basePath);
7567 if ($basePath === '\\') {
7571 $path = rtrim(realpath($basePath) . '/' . implode('/', $removed), '/');
7589 namespace Composer\Repository;
7596 class InvalidRepositoryException extends \Exception
7611 namespace Composer\Repository;
7613 use Composer\Package\AliasPackage;
7614 use Composer\Package\PackageInterface;
7615 use Composer\Package\CompletePackageInterface;
7616 use Composer\Package\Version\VersionParser;
7623 class ArrayRepository implements RepositoryInterface
7625 protected $packages;
7627 public function __construct(array $packages = array())
7629 foreach ($packages as $package) {
7630 $this->addPackage($package);
7637 public function findPackage($name, $version)
7640 $versionParser = new VersionParser();
7641 $version = $versionParser->normalize($version);
7642 $name = strtolower($name);
7644 foreach ($this->getPackages() as $package) {
7645 if ($name === $package->getName() && $version === $package->getVersion()) {
7654 public function findPackages($name, $version = null)
7657 $name = strtolower($name);
7660 if (null !== $version) {
7661 $versionParser = new VersionParser();
7662 $version = $versionParser->normalize($version);
7665 $packages = array();
7667 foreach ($this->getPackages() as $package) {
7668 if ($package->getName() === $name && (null === $version || $version === $package->getVersion())) {
7669 $packages[] = $package;
7679 public function search($query, $mode = 0)
7681 $regex = '{(?:'.implode('|', preg_split('{\s+}', $query)).')}i';
7684 foreach ($this->getPackages() as $package) {
7685 $name = $package->getName();
7686 if (isset($matches[$name])) {
7689 if (preg_match($regex, $name)
7690 || ($mode === self::SEARCH_FULLTEXT && $package instanceof CompletePackageInterface && preg_match($regex, implode(' ', (array) $package->getKeywords()) . ' ' . $package->getDescription()))
7692 $matches[$name] = array(
7693 'name' => $package->getPrettyName(),
7694 'description' => $package->getDescription(),
7699 return array_values($matches);
7705 public function hasPackage(PackageInterface $package)
7707 $packageId = $package->getUniqueName();
7709 foreach ($this->getPackages() as $repoPackage) {
7710 if ($packageId === $repoPackage->getUniqueName()) {
7723 public function addPackage(PackageInterface $package)
7725 if (null === $this->packages) {
7726 $this->initialize();
7728 $package->setRepository($this);
7729 $this->packages[] = $package;
7731 if ($package instanceof AliasPackage) {
7732 $aliasedPackage = $package->getAliasOf();
7733 if (null === $aliasedPackage->getRepository()) {
7734 $this->addPackage($aliasedPackage);
7739 protected function createAliasPackage(PackageInterface $package, $alias, $prettyAlias)
7741 return new AliasPackage($package instanceof AliasPackage ? $package->getAliasOf() : $package, $alias, $prettyAlias);
7749 public function removePackage(PackageInterface $package)
7751 $packageId = $package->getUniqueName();
7753 foreach ($this->getPackages() as $key => $repoPackage) {
7754 if ($packageId === $repoPackage->getUniqueName()) {
7755 array_splice($this->packages, $key, 1);
7765 public function getPackages()
7767 if (null === $this->packages) {
7768 $this->initialize();
7771 return $this->packages;
7779 public function count()
7781 return count($this->packages);
7787 protected function initialize()
7789 $this->packages = array();
7804 namespace Composer\Repository;
7806 use Composer\Json\JsonFile;
7807 use Composer\Package\Loader\ArrayLoader;
7808 use Composer\Package\Dumper\ArrayDumper;
7816 class FilesystemRepository extends WritableArrayRepository
7825 public function __construct(JsonFile $repositoryFile)
7827 $this->file = $repositoryFile;
7833 protected function initialize()
7835 parent::initialize();
7837 if (!$this->file->exists()) {
7842 $packages = $this->file->read();
7844 if (!is_array($packages)) {
7845 throw new \UnexpectedValueException('Could not parse package list from the repository');
7847 } catch (\Exception $e) {
7848 throw new InvalidRepositoryException('Invalid repository data in '.$this->file->getPath().', packages could not be loaded: ['.get_class($e).'] '.$e->getMessage());
7851 $loader = new ArrayLoader(null, true);
7852 foreach ($packages as $packageData) {
7853 $package = $loader->load($packageData);
7854 $this->addPackage($package);
7858 public function reload()
7860 $this->packages = null;
7861 $this->initialize();
7867 public function write()
7870 $dumper = new ArrayDumper();
7872 foreach ($this->getCanonicalPackages() as $package) {
7873 $data[] = $dumper->dump($package);
7876 $this->file->write($data);
7891 namespace Composer\Repository;
7893 use Composer\Package\PackageInterface;
7900 interface WritableRepositoryInterface extends RepositoryInterface
7905 public function write();
7912 public function addPackage(PackageInterface $package);
7919 public function removePackage(PackageInterface $package);
7926 public function getCanonicalPackages();
7931 public function reload();
7945 namespace Composer\Repository;
7947 use Composer\IO\IOInterface;
7948 use Composer\Package\Version\VersionParser;
7949 use Composer\Repository\Pear\ChannelReader;
7950 use Composer\Package\CompletePackage;
7951 use Composer\Repository\Pear\ChannelInfo;
7952 use Composer\EventDispatcher\EventDispatcher;
7953 use Composer\Package\Link;
7954 use Composer\Package\LinkConstraint\VersionConstraint;
7955 use Composer\Util\RemoteFilesystem;
7956 use Composer\Config;
7967 class PearRepository extends ArrayRepository
7972 private $versionParser;
7977 private $vendorAlias;
7979 public function __construct(array $repoConfig, IOInterface $io, Config $config, EventDispatcher $dispatcher = null, RemoteFilesystem $rfs = null)
7981 if (!preg_match('{^https?://}', $repoConfig['url'])) {
7982 $repoConfig['url'] = 'http://'.$repoConfig['url'];
7985 $urlBits = parse_url($repoConfig['url']);
7986 if (empty($urlBits['scheme']) || empty($urlBits['host'])) {
7987 throw new \UnexpectedValueException('Invalid url given for PEAR repository: '.$repoConfig['url']);
7990 $this->url = rtrim($repoConfig['url'], '/');
7992 $this->rfs = $rfs ?: new RemoteFilesystem($this->io, $config);
7993 $this->vendorAlias = isset($repoConfig['vendor-alias']) ? $repoConfig['vendor-alias'] : null;
7994 $this->versionParser = new VersionParser();
7997 protected function initialize()
7999 parent::initialize();
8001 $this->io->write('Initializing PEAR repository '.$this->url);
8003 $reader = new ChannelReader($this->rfs);
8005 $channelInfo = $reader->read($this->url);
8006 } catch (\Exception $e) {
8007 $this->io->write('<warning>PEAR repository from '.$this->url.' could not be loaded. '.$e->getMessage().'</warning>');
8011 $packages = $this->buildComposerPackages($channelInfo, $this->versionParser);
8012 foreach ($packages as $package) {
8013 $this->addPackage($package);
8024 private function buildComposerPackages(ChannelInfo $channelInfo, VersionParser $versionParser)
8027 foreach ($channelInfo->getPackages() as $packageDefinition) {
8028 foreach ($packageDefinition->getReleases() as $version => $releaseInfo) {
8030 $normalizedVersion = $versionParser->normalize($version);
8031 } catch (\UnexpectedValueException $e) {
8032 if ($this->io->isVerbose()) {
8033 $this->io->write('Could not load '.$packageDefinition->getPackageName().' '.$version.': '.$e->getMessage());
8038 $composerPackageName = $this->buildComposerPackageName($packageDefinition->getChannelName(), $packageDefinition->getPackageName());
8042 $urlBits = parse_url($this->url);
8043 $scheme = (isset($urlBits['scheme']) && 'https' === $urlBits['scheme'] && extension_loaded('openssl')) ? 'https' : 'http';
8044 $distUrl = "{$scheme}://{$packageDefinition->getChannelName()}/get/{$packageDefinition->getPackageName()}-{$version}.tgz";
8046 $requires = array();
8047 $suggests = array();
8048 $conflicts = array();
8049 $replaces = array();
8053 if ($channelInfo->getName() == $packageDefinition->getChannelName()) {
8054 $composerPackageAlias = $this->buildComposerPackageName($channelInfo->getAlias(), $packageDefinition->getPackageName());
8055 $aliasConstraint = new VersionConstraint('==', $normalizedVersion);
8056 $replaces[] = new Link($composerPackageName, $composerPackageAlias, $aliasConstraint, 'replaces', (string) $aliasConstraint);
8060 if (!empty($this->vendorAlias)
8061 && ($this->vendorAlias != 'pear-'.$channelInfo->getAlias() || $channelInfo->getName() != $packageDefinition->getChannelName())
8063 $composerPackageAlias = "{$this->vendorAlias}/{$packageDefinition->getPackageName()}";
8064 $aliasConstraint = new VersionConstraint('==', $normalizedVersion);
8065 $replaces[] = new Link($composerPackageName, $composerPackageAlias, $aliasConstraint, 'replaces', (string) $aliasConstraint);
8068 foreach ($releaseInfo->getDependencyInfo()->getRequires() as $dependencyConstraint) {
8069 $dependencyPackageName = $this->buildComposerPackageName($dependencyConstraint->getChannelName(), $dependencyConstraint->getPackageName());
8070 $constraint = $versionParser->parseConstraints($dependencyConstraint->getConstraint());
8071 $link = new Link($composerPackageName, $dependencyPackageName, $constraint, $dependencyConstraint->getType(), $dependencyConstraint->getConstraint());
8072 switch ($dependencyConstraint->getType()) {
8074 $requires[] = $link;
8077 $conflicts[] = $link;
8080 $replaces[] = $link;
8085 foreach ($releaseInfo->getDependencyInfo()->getOptionals() as $group => $dependencyConstraints) {
8086 foreach ($dependencyConstraints as $dependencyConstraint) {
8087 $dependencyPackageName = $this->buildComposerPackageName($dependencyConstraint->getChannelName(), $dependencyConstraint->getPackageName());
8088 $suggests[$group.'-'.$dependencyPackageName] = $dependencyConstraint->getConstraint();
8092 $package = new CompletePackage($composerPackageName, $normalizedVersion, $version);
8093 $package->setType('pear-library');
8094 $package->setDescription($packageDefinition->getDescription());
8095 $package->setLicense(array($packageDefinition->getLicense()));
8096 $package->setDistType('file');
8097 $package->setDistUrl($distUrl);
8098 $package->setAutoload(array('classmap' => array('')));
8099 $package->setIncludePaths(array('/'));
8100 $package->setRequires($requires);
8101 $package->setConflicts($conflicts);
8102 $package->setSuggests($suggests);
8103 $package->setReplaces($replaces);
8104 $result[] = $package;
8111 private function buildComposerPackageName($channelName, $packageName)
8113 if ('php' === $channelName) {
8116 if ('ext' === $channelName) {
8117 return "ext-{$packageName}";
8120 return "pear-{$channelName}/{$packageName}";
8135 namespace Composer\Repository;
8137 use Composer\IO\IOInterface;
8138 use Composer\Config;
8139 use Composer\EventDispatcher\EventDispatcher;
8148 class RepositoryManager
8150 private $localRepository;
8151 private $repositories = array();
8152 private $repositoryClasses = array();
8155 private $eventDispatcher;
8157 public function __construct(IOInterface $io, Config $config, EventDispatcher $eventDispatcher = null)
8160 $this->config = $config;
8161 $this->eventDispatcher = $eventDispatcher;
8172 public function findPackage($name, $version)
8174 foreach ($this->repositories as $repository) {
8175 if ($package = $repository->findPackage($name, $version)) {
8189 public function findPackages($name, $version)
8191 $packages = array();
8193 foreach ($this->repositories as $repository) {
8194 $packages = array_merge($packages, $repository->findPackages($name, $version));
8205 public function addRepository(RepositoryInterface $repository)
8207 $this->repositories[] = $repository;
8218 public function createRepository($type, $config)
8220 if (!isset($this->repositoryClasses[$type])) {
8221 throw new \InvalidArgumentException('Repository type is not registered: '.$type);
8224 $class = $this->repositoryClasses[$type];
8226 return new $class($config, $this->io, $this->config, $this->eventDispatcher);
8235 public function setRepositoryClass($type, $class)
8237 $this->repositoryClasses[$type] = $class;
8245 public function getRepositories()
8247 return $this->repositories;
8255 public function setLocalRepository(WritableRepositoryInterface $repository)
8257 $this->localRepository = $repository;
8265 public function getLocalRepository()
8267 return $this->localRepository;
8276 public function getLocalRepositories()
8278 trigger_error('This method is deprecated, use getLocalRepository instead since the getLocalDevRepository is now gone', E_USER_DEPRECATED);
8280 return array($this->localRepository);
8295 namespace Composer\Repository;
8297 use Composer\Package\AliasPackage;
8304 class WritableArrayRepository extends ArrayRepository implements WritableRepositoryInterface
8309 public function write()
8316 public function reload()
8323 public function getCanonicalPackages()
8325 $packages = $this->getPackages();
8328 $packagesByName = array();
8329 foreach ($packages as $package) {
8330 if (!isset($packagesByName[$package->getName()]) || $packagesByName[$package->getName()] instanceof AliasPackage) {
8331 $packagesByName[$package->getName()] = $package;
8335 $canonicalPackages = array();
8338 foreach ($packagesByName as $package) {
8339 while ($package instanceof AliasPackage) {
8340 $package = $package->getAliasOf();
8343 $canonicalPackages[] = $package;
8346 return $canonicalPackages;
8361 namespace Composer\Repository\Vcs;
8363 use Composer\Config;
8364 use Composer\Downloader\TransportException;
8365 use Composer\Json\JsonFile;
8367 use Composer\IO\IOInterface;
8368 use Composer\Util\GitHub;
8373 class GitHubDriver extends VcsDriver
8377 protected $repository;
8379 protected $branches;
8380 protected $rootIdentifier;
8381 protected $hasIssues;
8382 protected $infoCache = array();
8383 protected $isPrivate = false;
8390 protected $gitDriver;
8395 public function initialize()
8397 preg_match('#^(?:(?:https?|git)://([^/]+)/|git@([^:]+):)([^/]+)/(.+?)(?:\.git|/)?$#', $this->url, $match);
8398 $this->owner = $match[3];
8399 $this->repository = $match[4];
8400 $this->originUrl = !empty($match[1]) ? $match[1] : $match[2];
8401 $this->cache = new Cache($this->io, $this->config->get('cache-repo-dir').'/'.$this->originUrl.'/'.$this->owner.'/'.$this->repository);
8403 if (isset($this->repoConfig['no-api']) && $this->repoConfig['no-api']) {
8404 $this->setupGitDriver($this->url);
8409 $this->fetchRootIdentifier();
8412 public function getRepositoryUrl()
8414 return 'https://'.$this->originUrl.'/'.$this->owner.'/'.$this->repository;
8420 public function getRootIdentifier()
8422 if ($this->gitDriver) {
8423 return $this->gitDriver->getRootIdentifier();
8426 return $this->rootIdentifier;
8432 public function getUrl()
8434 if ($this->gitDriver) {
8435 return $this->gitDriver->getUrl();
8438 return 'https://' . $this->originUrl . '/'.$this->owner.'/'.$this->repository.'.git';
8444 protected function getApiUrl()
8446 if ('github.com' === $this->originUrl) {
8447 $apiUrl = 'api.github.com';
8449 $apiUrl = $this->originUrl . '/api/v3';
8452 return 'https://' . $apiUrl;
8458 public function getSource($identifier)
8460 if ($this->gitDriver) {
8461 return $this->gitDriver->getSource($identifier);
8463 if ($this->isPrivate) {
8466 $url = $this->generateSshUrl();
8468 $url = $this->getUrl();
8471 return array('type' => 'git', 'url' => $url, 'reference' => $identifier);
8477 public function getDist($identifier)
8479 $url = $this->getApiUrl() . '/repos/'.$this->owner.'/'.$this->repository.'/zipball/'.$identifier;
8481 return array('type' => 'zip', 'url' => $url, 'reference' => $identifier, 'shasum' => '');
8487 public function getComposerInformation($identifier)
8489 if ($this->gitDriver) {
8490 return $this->gitDriver->getComposerInformation($identifier);
8493 if (preg_match('{[a-f0-9]{40}}i', $identifier) && $res = $this->cache->read($identifier)) {
8494 $this->infoCache[$identifier] = JsonFile::parseJson($res);
8497 if (!isset($this->infoCache[$identifier])) {
8498 $notFoundRetries = 2;
8499 while ($notFoundRetries) {
8501 $resource = $this->getApiUrl() . '/repos/'.$this->owner.'/'.$this->repository.'/contents/composer.json?ref='.urlencode($identifier);
8502 $composer = JsonFile::parseJson($this->getContents($resource));
8503 if (empty($composer['content']) || $composer['encoding'] !== 'base64' || !($composer = base64_decode($composer['content']))) {
8504 throw new \RuntimeException('Could not retrieve composer.json from '.$resource);
8507 } catch (TransportException $e) {
8508 if (404 !== $e->getCode()) {
8520 $composer = JsonFile::parseJson($composer, $resource);
8522 if (empty($composer['time'])) {
8523 $resource = $this->getApiUrl() . '/repos/'.$this->owner.'/'.$this->repository.'/commits/'.urlencode($identifier);
8524 $commit = JsonFile::parseJson($this->getContents($resource), $resource);
8525 $composer['time'] = $commit['commit']['committer']['date'];
8527 if (!isset($composer['support']['source'])) {
8528 $label = array_search($identifier, $this->getTags()) ?: array_search($identifier, $this->getBranches()) ?: $identifier;
8529 $composer['support']['source'] = sprintf('https://%s/%s/%s/tree/%s', $this->originUrl, $this->owner, $this->repository, $label);
8531 if (!isset($composer['support']['issues']) && $this->hasIssues) {
8532 $composer['support']['issues'] = sprintf('https://%s/%s/%s/issues', $this->originUrl, $this->owner, $this->repository);
8536 if (preg_match('{[a-f0-9]{40}}i', $identifier)) {
8537 $this->cache->write($identifier, json_encode($composer));
8540 $this->infoCache[$identifier] = $composer;
8543 return $this->infoCache[$identifier];
8549 public function getTags()
8551 if ($this->gitDriver) {
8552 return $this->gitDriver->getTags();
8554 if (null === $this->tags) {
8555 $this->tags = array();
8556 $resource = $this->getApiUrl() . '/repos/'.$this->owner.'/'.$this->repository.'/tags?per_page=100';
8559 $tagsData = JsonFile::parseJson($this->getContents($resource), $resource);
8560 foreach ($tagsData as $tag) {
8561 $this->tags[$tag['name']] = $tag['commit']['sha'];
8564 $resource = $this->getNextPage();
8565 } while ($resource);
8574 public function getBranches()
8576 if ($this->gitDriver) {
8577 return $this->gitDriver->getBranches();
8579 if (null === $this->branches) {
8580 $this->branches = array();
8581 $resource = $this->getApiUrl() . '/repos/'.$this->owner.'/'.$this->repository.'/git/refs/heads?per_page=100';
8583 $branchBlacklist = array('gh-pages');
8586 $branchData = JsonFile::parseJson($this->getContents($resource), $resource);
8587 foreach ($branchData as $branch) {
8588 $name = substr($branch['ref'], 11);
8589 if (!in_array($name, $branchBlacklist)) {
8590 $this->branches[$name] = $branch['object']['sha'];
8594 $resource = $this->getNextPage();
8595 } while ($resource);
8598 return $this->branches;
8604 public static function supports(IOInterface $io, Config $config, $url, $deep = false)
8606 if (!preg_match('#^((?:https?|git)://([^/]+)/|git@([^:]+):)([^/]+)/(.+?)(?:\.git|/)?$#', $url, $matches)) {
8610 $originUrl = !empty($matches[2]) ? $matches[2] : $matches[3];
8611 if (!in_array($originUrl, $config->get('github-domains'))) {
8615 if (!extension_loaded('openssl')) {
8616 if ($io->isVerbose()) {
8617 $io->write('Skipping GitHub driver for '.$url.' because the OpenSSL PHP extension is missing.');
8631 protected function generateSshUrl()
8633 return 'git@' . $this->originUrl . ':'.$this->owner.'/'.$this->repository.'.git';
8639 protected function getContents($url, $fetchingRepoData = false)
8642 return parent::getContents($url);
8643 } catch (TransportException $e) {
8644 $gitHubUtil = new GitHub($this->io, $this->config, $this->process, $this->remoteFilesystem);
8646 switch ($e->getCode()) {
8650 if (!$fetchingRepoData) {
8654 if ($gitHubUtil->authorizeOAuth($this->originUrl)) {
8655 return parent::getContents($url);
8658 if (!$this->io->isInteractive()) {
8659 return $this->attemptCloneFallback();
8662 $gitHubUtil->authorizeOAuthInteractively($this->originUrl, 'Your GitHub credentials are required to fetch private repository metadata (<info>'.$this->url.'</info>)');
8664 return parent::getContents($url);
8667 if (!$this->io->hasAuthentication($this->originUrl) && $gitHubUtil->authorizeOAuth($this->originUrl)) {
8668 return parent::getContents($url);
8671 if (!$this->io->isInteractive() && $fetchingRepoData) {
8672 return $this->attemptCloneFallback();
8675 $rateLimited = false;
8676 foreach ($e->getHeaders() as $header) {
8677 if (preg_match('{^X-RateLimit-Remaining: *0$}i', trim($header))) {
8678 $rateLimited = true;
8682 if (!$this->io->hasAuthentication($this->originUrl)) {
8683 if (!$this->io->isInteractive()) {
8684 $this->io->write('<error>GitHub API limit exhausted. Failed to get metadata for the '.$this->url.' repository, try running in interactive mode so that you can enter your GitHub credentials to increase the API limit</error>');
8688 $gitHubUtil->authorizeOAuthInteractively($this->originUrl, 'API limit exhausted. Enter your GitHub credentials to get a larger API limit (<info>'.$this->url.'</info>)');
8690 return parent::getContents($url);
8694 $rateLimit = $this->getRateLimit($e->getHeaders());
8695 $this->io->write(sprintf(
8696 '<error>GitHub API limit (%d calls/hr) is exhausted. You are already authorized so you have to wait until %s before doing more requests</error>',
8697 $rateLimit['limit'],
8717 protected function getRateLimit(array $headers)
8724 foreach ($headers as $header) {
8725 $header = trim($header);
8726 if (false === strpos($header, 'X-RateLimit-')) {
8729 list($type, $value) = explode(':', $header, 2);
8731 case 'X-RateLimit-Limit':
8732 $rateLimit['limit'] = (int) trim($value);
8734 case 'X-RateLimit-Reset':
8735 $rateLimit['reset'] = date('Y-m-d H:i:s', (int) trim($value));
8748 protected function fetchRootIdentifier()
8750 $repoDataUrl = $this->getApiUrl() . '/repos/'.$this->owner.'/'.$this->repository;
8752 $repoData = JsonFile::parseJson($this->getContents($repoDataUrl, true), $repoDataUrl);
8753 if (null === $repoData && null !== $this->gitDriver) {
8757 $this->owner = $repoData['owner']['login'];
8758 $this->repository = $repoData['name'];
8760 $this->isPrivate = !empty($repoData['private']);
8761 if (isset($repoData['default_branch'])) {
8762 $this->rootIdentifier = $repoData['default_branch'];
8763 } elseif (isset($repoData['master_branch'])) {
8764 $this->rootIdentifier = $repoData['master_branch'];
8766 $this->rootIdentifier = 'master';
8768 $this->hasIssues = !empty($repoData['has_issues']);
8771 protected function attemptCloneFallback()
8773 $this->isPrivate = true;
8780 $this->setupGitDriver($this->generateSshUrl());
8783 } catch (\RuntimeException $e) {
8784 $this->gitDriver = null;
8786 $this->io->write('<error>Failed to clone the '.$this->generateSshUrl().' repository, try running in interactive mode so that you can enter your GitHub credentials</error>');
8791 protected function setupGitDriver($url)
8793 $this->gitDriver = new GitDriver(
8794 array('url' => $url),
8798 $this->remoteFilesystem
8800 $this->gitDriver->initialize();
8803 protected function getNextPage()
8805 $headers = $this->remoteFilesystem->getLastHeaders();
8806 foreach ($headers as $header) {
8807 if (substr($header, 0, 5) === 'Link:') {
8808 $links = explode(',', substr($header, 5));
8809 foreach ($links as $link) {
8810 if (preg_match('{<(.+?)>; *rel="next"}', $link, $match)) {
8830 namespace Composer\Repository\Vcs;
8832 use Composer\Config;
8833 use Composer\Json\JsonFile;
8834 use Composer\IO\IOInterface;
8839 class GitBitbucketDriver extends VcsDriver implements VcsDriverInterface
8842 protected $repository;
8844 protected $branches;
8845 protected $rootIdentifier;
8846 protected $infoCache = array();
8851 public function initialize()
8853 preg_match('#^https://bitbucket\.org/([^/]+)/(.+?)\.git$#', $this->url, $match);
8854 $this->owner = $match[1];
8855 $this->repository = $match[2];
8856 $this->originUrl = 'bitbucket.org';
8862 public function getRootIdentifier()
8864 if (null === $this->rootIdentifier) {
8865 $resource = $this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository;
8866 $repoData = JsonFile::parseJson($this->getContents($resource), $resource);
8867 $this->rootIdentifier = !empty($repoData['main_branch']) ? $repoData['main_branch'] : 'master';
8870 return $this->rootIdentifier;
8876 public function getUrl()
8884 public function getSource($identifier)
8886 return array('type' => 'git', 'url' => $this->getUrl(), 'reference' => $identifier);
8892 public function getDist($identifier)
8894 $url = $this->getScheme() . '://bitbucket.org/'.$this->owner.'/'.$this->repository.'/get/'.$identifier.'.zip';
8896 return array('type' => 'zip', 'url' => $url, 'reference' => $identifier, 'shasum' => '');
8902 public function getComposerInformation($identifier)
8904 if (!isset($this->infoCache[$identifier])) {
8905 $resource = $this->getScheme() . '://bitbucket.org/'.$this->owner.'/'.$this->repository.'/raw/'.$identifier.'/composer.json';
8906 $composer = $this->getContents($resource);
8911 $composer = JsonFile::parseJson($composer, $resource);
8913 if (empty($composer['time'])) {
8914 $resource = $this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/changesets/'.$identifier;
8915 $changeset = JsonFile::parseJson($this->getContents($resource), $resource);
8916 $composer['time'] = $changeset['timestamp'];
8918 $this->infoCache[$identifier] = $composer;
8921 return $this->infoCache[$identifier];
8927 public function getTags()
8929 if (null === $this->tags) {
8930 $resource = $this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/tags';
8931 $tagsData = JsonFile::parseJson($this->getContents($resource), $resource);
8932 $this->tags = array();
8933 foreach ($tagsData as $tag => $data) {
8934 $this->tags[$tag] = $data['raw_node'];
8944 public function getBranches()
8946 if (null === $this->branches) {
8947 $resource = $this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/branches';
8948 $branchData = JsonFile::parseJson($this->getContents($resource), $resource);
8949 $this->branches = array();
8950 foreach ($branchData as $branch => $data) {
8951 $this->branches[$branch] = $data['raw_node'];
8955 return $this->branches;
8961 public static function supports(IOInterface $io, Config $config, $url, $deep = false)
8963 if (!preg_match('#^https://bitbucket\.org/([^/]+)/(.+?)\.git$#', $url)) {
8967 if (!extension_loaded('openssl')) {
8968 if ($io->isVerbose()) {
8969 $io->write('Skipping Bitbucket git driver for '.$url.' because the OpenSSL PHP extension is missing.');
8990 namespace Composer\Repository\Vcs;
8992 use Composer\Json\JsonFile;
8993 use Composer\Util\ProcessExecutor;
8994 use Composer\Util\Filesystem;
8995 use Composer\Util\Git as GitUtil;
8996 use Composer\IO\IOInterface;
8998 use Composer\Config;
9003 class GitDriver extends VcsDriver
9007 protected $branches;
9008 protected $rootIdentifier;
9010 protected $infoCache = array();
9015 public function initialize()
9017 if (Filesystem::isLocalPath($this->url)) {
9018 $this->repoDir = $this->url;
9019 $cacheUrl = realpath($this->url);
9021 $this->repoDir = $this->config->get('cache-vcs-dir') . '/' . preg_replace('{[^a-z0-9.]}i', '-', $this->url) . '/';
9023 GitUtil::cleanEnv();
9025 $fs = new Filesystem();
9026 $fs->ensureDirectoryExists(dirname($this->repoDir));
9028 if (!is_writable(dirname($this->repoDir))) {
9029 throw new \RuntimeException('Can not clone '.$this->url.' to access package information. The "'.dirname($this->repoDir).'" directory is not writable by the current user.');
9032 if (preg_match('{^ssh://[^@]+@[^:]+:[^0-9]+}', $this->url)) {
9033 throw new \InvalidArgumentException('The source URL '.$this->url.' is invalid, ssh URLs should have a port number after ":".'."\n".'Use ssh://git@example.com:22/path or just git@example.com:path if you do not want to provide a password or custom port.');
9036 $gitUtil = new GitUtil($this->io, $this->config, $this->process, $fs);
9039 if (is_dir($this->repoDir) && 0 === $this->process->execute('git rev-parse --git-dir', $output, $this->repoDir) && trim($output) === '.') {
9041 $commandCallable = function ($url) {
9042 return sprintf('git remote set-url origin %s && git remote update --prune origin', ProcessExecutor::escape($url));
9044 $gitUtil->runCommand($commandCallable, $this->url, $this->repoDir);
9045 } catch (\Exception $e) {
9046 $this->io->write('<error>Failed to update '.$this->url.', package information from this repository may be outdated ('.$e->getMessage().')</error>');
9050 $fs->removeDirectory($this->repoDir);
9052 $repoDir = $this->repoDir;
9053 $commandCallable = function ($url) use ($repoDir) {
9054 return sprintf('git clone --mirror %s %s', ProcessExecutor::escape($url), ProcessExecutor::escape($repoDir));
9057 $gitUtil->runCommand($commandCallable, $this->url, $this->repoDir, true);
9060 $cacheUrl = $this->url;
9064 $this->getBranches();
9066 $this->cache = new Cache($this->io, $this->config->get('cache-repo-dir').'/'.preg_replace('{[^a-z0-9.]}i', '-', $cacheUrl));
9072 public function getRootIdentifier()
9074 if (null === $this->rootIdentifier) {
9075 $this->rootIdentifier = 'master';
9078 $this->process->execute('git branch --no-color', $output, $this->repoDir);
9079 $branches = $this->process->splitLines($output);
9080 if (!in_array('* master', $branches)) {
9081 foreach ($branches as $branch) {
9082 if ($branch && preg_match('{^\* +(\S+)}', $branch, $match)) {
9083 $this->rootIdentifier = $match[1];
9090 return $this->rootIdentifier;
9096 public function getUrl()
9104 public function getSource($identifier)
9106 return array('type' => 'git', 'url' => $this->getUrl(), 'reference' => $identifier);
9112 public function getDist($identifier)
9120 public function getComposerInformation($identifier)
9122 if (preg_match('{[a-f0-9]{40}}i', $identifier) && $res = $this->cache->read($identifier)) {
9123 $this->infoCache[$identifier] = JsonFile::parseJson($res);
9126 if (!isset($this->infoCache[$identifier])) {
9127 $resource = sprintf('%s:composer.json', ProcessExecutor::escape($identifier));
9128 $this->process->execute(sprintf('git show %s', $resource), $composer, $this->repoDir);
9130 if (!trim($composer)) {
9134 $composer = JsonFile::parseJson($composer, $resource);
9136 if (empty($composer['time'])) {
9137 $this->process->execute(sprintf('git log -1 --format=%%at %s', ProcessExecutor::escape($identifier)), $output, $this->repoDir);
9138 $date = new \DateTime('@'.trim($output), new \DateTimeZone('UTC'));
9139 $composer['time'] = $date->format('Y-m-d H:i:s');
9142 if (preg_match('{[a-f0-9]{40}}i', $identifier)) {
9143 $this->cache->write($identifier, json_encode($composer));
9146 $this->infoCache[$identifier] = $composer;
9149 return $this->infoCache[$identifier];
9155 public function getTags()
9157 if (null === $this->tags) {
9158 $this->tags = array();
9160 $this->process->execute('git show-ref --tags', $output, $this->repoDir);
9161 foreach ($output = $this->process->splitLines($output) as $tag) {
9162 if ($tag && preg_match('{^([a-f0-9]{40}) refs/tags/(\S+)$}', $tag, $match)) {
9163 $this->tags[$match[2]] = $match[1];
9174 public function getBranches()
9176 if (null === $this->branches) {
9177 $branches = array();
9179 $this->process->execute('git branch --no-color --no-abbrev -v', $output, $this->repoDir);
9180 foreach ($this->process->splitLines($output) as $branch) {
9181 if ($branch && !preg_match('{^ *[^/]+/HEAD }', $branch)) {
9182 if (preg_match('{^(?:\* )? *(\S+) *([a-f0-9]+)(?: .*)?$}', $branch, $match)) {
9183 $branches[$match[1]] = $match[2];
9188 $this->branches = $branches;
9191 return $this->branches;
9197 public static function supports(IOInterface $io, Config $config, $url, $deep = false)
9199 if (preg_match('#(^git://|\.git$|git(?:olite)?@|//git\.|//github.com/)#i', $url)) {
9204 if (Filesystem::isLocalPath($url)) {
9205 $url = Filesystem::getPlatformPath($url);
9206 if (!is_dir($url)) {
9210 $process = new ProcessExecutor($io);
9212 if ($process->execute('git tag', $output, $url) === 0) {
9237 namespace Composer\Repository\Vcs;
9239 use Composer\Config;
9240 use Composer\IO\IOInterface;
9241 use Composer\Util\ProcessExecutor;
9242 use Composer\Util\Perforce;
9247 class PerforceDriver extends VcsDriver
9251 protected $perforce;
9252 protected $composerInfo;
9253 protected $composerInfoIdentifier;
9258 public function initialize()
9260 $this->depot = $this->repoConfig['depot'];
9262 if (!empty($this->repoConfig['branch'])) {
9263 $this->branch = $this->repoConfig['branch'];
9266 $this->initPerforce($this->repoConfig);
9267 $this->perforce->p4Login($this->io);
9268 $this->perforce->checkStream($this->depot);
9270 $this->perforce->writeP4ClientSpec();
9271 $this->perforce->connectClient();
9276 private function initPerforce($repoConfig)
9278 if (!empty($this->perforce)) {
9282 $repoDir = $this->config->get('cache-vcs-dir') . '/' . $this->depot;
9283 $this->perforce = Perforce::create($repoConfig, $this->getUrl(), $repoDir, $this->process, $this->io);
9289 public function getComposerInformation($identifier)
9291 if (!empty($this->composerInfoIdentifier)) {
9292 if (strcmp($identifier, $this->composerInfoIdentifier) === 0) {
9293 return $this->composerInfo;
9296 $composer_info = $this->perforce->getComposerInformation($identifier);
9298 return $composer_info;
9304 public function getRootIdentifier()
9306 return $this->branch;
9312 public function getBranches()
9314 $branches = $this->perforce->getBranches();
9322 public function getTags()
9324 $tags = $this->perforce->getTags();
9332 public function getDist($identifier)
9340 public function getSource($identifier)
9343 'type' => 'perforce',
9344 'url' => $this->repoConfig['url'],
9345 'reference' => $identifier,
9346 'p4user' => $this->perforce->getUser()
9355 public function getUrl()
9363 public function hasComposerFile($identifier)
9365 $this->composerInfo = $this->perforce->getComposerInformation('//' . $this->depot . '/' . $identifier);
9366 $this->composerInfoIdentifier = $identifier;
9368 return !empty($this->composerInfo);
9374 public function getContents($url)
9382 public static function supports(IOInterface $io, Config $config, $url, $deep = false)
9384 if ($deep || preg_match('#\b(perforce|p4)\b#i', $url)) {
9385 return Perforce::checkServerExists($url, new ProcessExecutor($io));
9394 public function cleanup()
9396 $this->perforce->cleanupClientSpec();
9397 $this->perforce = null;
9400 public function getDepot()
9402 return $this->depot;
9405 public function getBranch()
9407 return $this->branch;
9422 namespace Composer\Repository\Vcs;
9425 use Composer\Config;
9426 use Composer\Json\JsonFile;
9427 use Composer\Util\ProcessExecutor;
9428 use Composer\Util\Filesystem;
9429 use Composer\Util\Svn as SvnUtil;
9430 use Composer\IO\IOInterface;
9431 use Composer\Downloader\TransportException;
9437 class SvnDriver extends VcsDriver
9445 protected $branches;
9446 protected $rootIdentifier;
9447 protected $infoCache = array();
9449 protected $trunkPath = 'trunk';
9450 protected $branchesPath = 'branches';
9451 protected $tagsPath = 'tags';
9452 protected $packagePath = '';
9453 protected $cacheCredentials = true;
9463 public function initialize()
9465 $this->url = $this->baseUrl = rtrim(self::normalizeUrl($this->url), '/');
9467 SvnUtil::cleanEnv();
9469 if (isset($this->repoConfig['trunk-path'])) {
9470 $this->trunkPath = $this->repoConfig['trunk-path'];
9472 if (isset($this->repoConfig['branches-path'])) {
9473 $this->branchesPath = $this->repoConfig['branches-path'];
9475 if (isset($this->repoConfig['tags-path'])) {
9476 $this->tagsPath = $this->repoConfig['tags-path'];
9478 if (array_key_exists('svn-cache-credentials', $this->repoConfig)) {
9479 $this->cacheCredentials = (bool) $this->repoConfig['svn-cache-credentials'];
9481 if (isset($this->repoConfig['package-path'])) {
9482 $this->packagePath = '/' . trim($this->repoConfig['package-path'], '/');
9485 if (false !== ($pos = strrpos($this->url, '/' . $this->trunkPath))) {
9486 $this->baseUrl = substr($this->url, 0, $pos);
9489 $this->cache = new Cache($this->io, $this->config->get('cache-repo-dir').'/'.preg_replace('{[^a-z0-9.]}i', '-', $this->baseUrl));
9491 $this->getBranches();
9498 public function getRootIdentifier()
9500 return $this->rootIdentifier ?: $this->trunkPath;
9506 public function getUrl()
9514 public function getSource($identifier)
9516 return array('type' => 'svn', 'url' => $this->baseUrl, 'reference' => $identifier);
9522 public function getDist($identifier)
9530 public function getComposerInformation($identifier)
9532 $identifier = '/' . trim($identifier, '/') . '/';
9534 if ($res = $this->cache->read($identifier.'.json')) {
9535 $this->infoCache[$identifier] = JsonFile::parseJson($res);
9538 if (!isset($this->infoCache[$identifier])) {
9539 preg_match('{^(.+?)(@\d+)?/$}', $identifier, $match);
9540 if (!empty($match[2])) {
9544 $path = $identifier;
9549 $resource = $path.'composer.json';
9550 $output = $this->execute('svn cat', $this->baseUrl . $resource . $rev);
9551 if (!trim($output)) {
9554 } catch (\RuntimeException $e) {
9555 throw new TransportException($e->getMessage());
9558 $composer = JsonFile::parseJson($output, $this->baseUrl . $resource . $rev);
9560 if (empty($composer['time'])) {
9561 $output = $this->execute('svn info', $this->baseUrl . $path . $rev);
9562 foreach ($this->process->splitLines($output) as $line) {
9563 if ($line && preg_match('{^Last Changed Date: ([^(]+)}', $line, $match)) {
9564 $date = new \DateTime($match[1], new \DateTimeZone('UTC'));
9565 $composer['time'] = $date->format('Y-m-d H:i:s');
9571 $this->cache->write($identifier.'.json', json_encode($composer));
9572 $this->infoCache[$identifier] = $composer;
9575 return $this->infoCache[$identifier];
9581 public function getTags()
9583 if (null === $this->tags) {
9584 $this->tags = array();
9586 if ($this->tagsPath !== false) {
9587 $output = $this->execute('svn ls --verbose', $this->baseUrl . '/' . $this->tagsPath);
9589 foreach ($this->process->splitLines($output) as $line) {
9590 $line = trim($line);
9591 if ($line && preg_match('{^\s*(\S+).*?(\S+)\s*$}', $line, $match)) {
9592 if (isset($match[1]) && isset($match[2]) && $match[2] !== './') {
9593 $this->tags[rtrim($match[2], '/')] = $this->buildIdentifier(
9594 '/' . $this->tagsPath . '/' . $match[2],
9610 public function getBranches()
9612 if (null === $this->branches) {
9613 $this->branches = array();
9615 if (false === $this->trunkPath) {
9616 $trunkParent = $this->baseUrl . '/';
9618 $trunkParent = $this->baseUrl . '/' . $this->trunkPath;
9621 $output = $this->execute('svn ls --verbose', $trunkParent);
9623 foreach ($this->process->splitLines($output) as $line) {
9624 $line = trim($line);
9625 if ($line && preg_match('{^\s*(\S+).*?(\S+)\s*$}', $line, $match)) {
9626 if (isset($match[1]) && isset($match[2]) && $match[2] === './') {
9627 $this->branches['trunk'] = $this->buildIdentifier(
9628 '/' . $this->trunkPath,
9631 $this->rootIdentifier = $this->branches['trunk'];
9639 if ($this->branchesPath !== false) {
9640 $output = $this->execute('svn ls --verbose', $this->baseUrl . '/' . $this->branchesPath);
9642 foreach ($this->process->splitLines(trim($output)) as $line) {
9643 $line = trim($line);
9644 if ($line && preg_match('{^\s*(\S+).*?(\S+)\s*$}', $line, $match)) {
9645 if (isset($match[1]) && isset($match[2]) && $match[2] !== './') {
9646 $this->branches[rtrim($match[2], '/')] = $this->buildIdentifier(
9647 '/' . $this->branchesPath . '/' . $match[2],
9657 return $this->branches;
9663 public static function supports(IOInterface $io, Config $config, $url, $deep = false)
9665 $url = self::normalizeUrl($url);
9666 if (preg_match('#(^svn://|^svn\+ssh://|svn\.)#i', $url)) {
9671 if (!$deep && !Filesystem::isLocalPath($url)) {
9675 $processExecutor = new ProcessExecutor();
9677 $exit = $processExecutor->execute(
9678 "svn info --non-interactive {$url}",
9687 if (false !== stripos($processExecutor->getErrorOutput(), 'authorization failed:')) {
9703 protected static function normalizeUrl($url)
9705 $fs = new Filesystem();
9706 if ($fs->isAbsolutePath($url)) {
9707 return 'file://' . strtr($url, '\\', '/');
9722 protected function execute($command, $url)
9724 if (null === $this->util) {
9725 $this->util = new SvnUtil($this->baseUrl, $this->io, $this->config, $this->process);
9726 $this->util->setCacheCredentials($this->cacheCredentials);
9730 return $this->util->execute($command, $url);
9731 } catch (\RuntimeException $e) {
9732 if (0 !== $this->process->execute('svn --version', $ignoredOutput)) {
9733 throw new \RuntimeException('Failed to load '.$this->url.', svn was not found, check that it is installed and in your PATH env.' . "\n\n" . $this->process->getErrorOutput());
9736 throw new \RuntimeException(
9737 'Repository '.$this->url.' could not be processed, '.$e->getMessage()
9750 protected function buildIdentifier($baseDir, $revision)
9752 return rtrim($baseDir, '/') . $this->packagePath . '/@' . $revision;
9767 namespace Composer\Repository\Vcs;
9769 use Composer\Config;
9770 use Composer\IO\IOInterface;
9775 interface VcsDriverInterface
9780 public function initialize();
9788 public function getComposerInformation($identifier);
9795 public function getRootIdentifier();
9802 public function getBranches();
9809 public function getTags();
9815 public function getDist($identifier);
9821 public function getSource($identifier);
9828 public function getUrl();
9837 public function hasComposerFile($identifier);
9843 public function cleanup();
9854 public static function supports(IOInterface $io, Config $config, $url, $deep = false);
9868 namespace Composer\Repository\Vcs;
9870 use Composer\Config;
9871 use Composer\Json\JsonFile;
9872 use Composer\Util\ProcessExecutor;
9873 use Composer\Util\Filesystem;
9874 use Composer\IO\IOInterface;
9879 class HgDriver extends VcsDriver
9882 protected $branches;
9883 protected $rootIdentifier;
9885 protected $infoCache = array();
9890 public function initialize()
9892 if (Filesystem::isLocalPath($this->url)) {
9893 $this->repoDir = $this->url;
9895 $cacheDir = $this->config->get('cache-vcs-dir');
9896 $this->repoDir = $cacheDir . '/' . preg_replace('{[^a-z0-9]}i', '-', $this->url) . '/';
9898 $fs = new Filesystem();
9899 $fs->ensureDirectoryExists($cacheDir);
9901 if (!is_writable(dirname($this->repoDir))) {
9902 throw new \RuntimeException('Can not clone '.$this->url.' to access package information. The "'.$cacheDir.'" directory is not writable by the current user.');
9906 if (is_dir($this->repoDir) && 0 === $this->process->execute('hg summary', $output, $this->repoDir)) {
9907 if (0 !== $this->process->execute('hg pull', $output, $this->repoDir)) {
9908 $this->io->write('<error>Failed to update '.$this->url.', package information from this repository may be outdated ('.$this->process->getErrorOutput().')</error>');
9912 $fs->removeDirectory($this->repoDir);
9914 if (0 !== $this->process->execute(sprintf('hg clone --noupdate %s %s', ProcessExecutor::escape($this->url), ProcessExecutor::escape($this->repoDir)), $output, $cacheDir)) {
9915 $output = $this->process->getErrorOutput();
9917 if (0 !== $this->process->execute('hg --version', $ignoredOutput)) {
9918 throw new \RuntimeException('Failed to clone '.$this->url.', hg was not found, check that it is installed and in your PATH env.' . "\n\n" . $this->process->getErrorOutput());
9921 throw new \RuntimeException('Failed to clone '.$this->url.', could not read packages from it' . "\n\n" .$output);
9927 $this->getBranches();
9933 public function getRootIdentifier()
9935 if (null === $this->rootIdentifier) {
9936 $this->process->execute(sprintf('hg tip --template "{node}"'), $output, $this->repoDir);
9937 $output = $this->process->splitLines($output);
9938 $this->rootIdentifier = $output[0];
9941 return $this->rootIdentifier;
9947 public function getUrl()
9955 public function getSource($identifier)
9957 return array('type' => 'hg', 'url' => $this->getUrl(), 'reference' => $identifier);
9963 public function getDist($identifier)
9971 public function getComposerInformation($identifier)
9973 if (!isset($this->infoCache[$identifier])) {
9974 $this->process->execute(sprintf('hg cat -r %s composer.json', ProcessExecutor::escape($identifier)), $composer, $this->repoDir);
9976 if (!trim($composer)) {
9980 $composer = JsonFile::parseJson($composer, $identifier);
9982 if (empty($composer['time'])) {
9983 $this->process->execute(sprintf('hg log --template "{date|rfc3339date}" -r %s', ProcessExecutor::escape($identifier)), $output, $this->repoDir);
9984 $date = new \DateTime(trim($output), new \DateTimeZone('UTC'));
9985 $composer['time'] = $date->format('Y-m-d H:i:s');
9987 $this->infoCache[$identifier] = $composer;
9990 return $this->infoCache[$identifier];
9996 public function getTags()
9998 if (null === $this->tags) {
10001 $this->process->execute('hg tags', $output, $this->repoDir);
10002 foreach ($this->process->splitLines($output) as $tag) {
10003 if ($tag && preg_match('(^([^\s]+)\s+\d+:(.*)$)', $tag, $match)) {
10004 $tags[$match[1]] = $match[2];
10007 unset($tags['tip']);
10009 $this->tags = $tags;
10012 return $this->tags;
10018 public function getBranches()
10020 if (null === $this->branches) {
10021 $branches = array();
10022 $bookmarks = array();
10024 $this->process->execute('hg branches', $output, $this->repoDir);
10025 foreach ($this->process->splitLines($output) as $branch) {
10026 if ($branch && preg_match('(^([^\s]+)\s+\d+:([a-f0-9]+))', $branch, $match)) {
10027 $branches[$match[1]] = $match[2];
10031 $this->process->execute('hg bookmarks', $output, $this->repoDir);
10032 foreach ($this->process->splitLines($output) as $branch) {
10033 if ($branch && preg_match('(^(?:[\s*]*)([^\s]+)\s+\d+:(.*)$)', $branch, $match)) {
10034 $bookmarks[$match[1]] = $match[2];
10039 $this->branches = array_merge($bookmarks, $branches);
10042 return $this->branches;
10048 public static function supports(IOInterface $io, Config $config, $url, $deep = false)
10050 if (preg_match('#(^(?:https?|ssh)://(?:[^@]@)?bitbucket.org|https://(?:.*?)\.kilnhg.com)#i', $url)) {
10055 if (Filesystem::isLocalPath($url)) {
10056 $url = Filesystem::getPlatformPath($url);
10057 if (!is_dir($url)) {
10061 $process = new ProcessExecutor();
10063 if ($process->execute('hg summary', $output, $url) === 0) {
10072 $processExecutor = new ProcessExecutor();
10073 $exit = $processExecutor->execute(sprintf('hg identify %s', ProcessExecutor::escape($url)), $ignored);
10075 return $exit === 0;
10090 namespace Composer\Repository\Vcs;
10092 use Composer\Downloader\TransportException;
10093 use Composer\Config;
10094 use Composer\IO\IOInterface;
10095 use Composer\Util\ProcessExecutor;
10096 use Composer\Util\RemoteFilesystem;
10097 use Composer\Util\Filesystem;
10104 abstract class VcsDriver implements VcsDriverInterface
10107 protected $originUrl;
10108 protected $repoConfig;
10111 protected $process;
10112 protected $remoteFilesystem;
10123 final public function __construct(array $repoConfig, IOInterface $io, Config $config, ProcessExecutor $process = null, RemoteFilesystem $remoteFilesystem = null)
10125 if (Filesystem::isLocalPath($repoConfig['url'])) {
10126 $repoConfig['url'] = Filesystem::getPlatformPath($repoConfig['url']);
10129 $this->url = $repoConfig['url'];
10130 $this->originUrl = $repoConfig['url'];
10131 $this->repoConfig = $repoConfig;
10133 $this->config = $config;
10134 $this->process = $process ?: new ProcessExecutor($io);
10135 $this->remoteFilesystem = $remoteFilesystem ?: new RemoteFilesystem($io, $config);
10141 public function hasComposerFile($identifier)
10144 return (bool) $this->getComposerInformation($identifier);
10145 } catch (TransportException $e) {
10158 protected function getScheme()
10160 if (extension_loaded('openssl')) {
10174 protected function getContents($url)
10176 return $this->remoteFilesystem->getContents($this->originUrl, $url, false);
10182 public function cleanup()
10199 namespace Composer\Repository\Vcs;
10201 use Composer\Config;
10202 use Composer\Json\JsonFile;
10203 use Composer\IO\IOInterface;
10208 class HgBitbucketDriver extends VcsDriver
10211 protected $repository;
10213 protected $branches;
10214 protected $rootIdentifier;
10215 protected $infoCache = array();
10220 public function initialize()
10222 preg_match('#^https://bitbucket\.org/([^/]+)/([^/]+)/?$#', $this->url, $match);
10223 $this->owner = $match[1];
10224 $this->repository = $match[2];
10225 $this->originUrl = 'bitbucket.org';
10231 public function getRootIdentifier()
10233 if (null === $this->rootIdentifier) {
10234 $resource = $this->getScheme() . '://bitbucket.org/api/1.0/repositories/'.$this->owner.'/'.$this->repository.'/tags';
10235 $repoData = JsonFile::parseJson($this->getContents($resource), $resource);
10236 if (array() === $repoData || !isset($repoData['tip'])) {
10237 throw new \RuntimeException($this->url.' does not appear to be a mercurial repository, use '.$this->url.'.git if this is a git bitbucket repository');
10239 $this->rootIdentifier = $repoData['tip']['raw_node'];
10242 return $this->rootIdentifier;
10248 public function getUrl()
10256 public function getSource($identifier)
10258 return array('type' => 'hg', 'url' => $this->getUrl(), 'reference' => $identifier);
10264 public function getDist($identifier)
10266 $url = $this->getScheme() . '://bitbucket.org/'.$this->owner.'/'.$this->repository.'/get/'.$identifier.'.zip';
10268 return array('type' => 'zip', 'url' => $url, 'reference' => $identifier, 'shasum' => '');
10274 public function getComposerInformation($identifier)
10276 if (!isset($this->infoCache[$identifier])) {
10277 $resource = $this->getScheme() . '://bitbucket.org/api/1.0/repositories/'.$this->owner.'/'.$this->repository.'/src/'.$identifier.'/composer.json';
10278 $repoData = JsonFile::parseJson($this->getContents($resource), $resource);
10285 if (!array_key_exists('data', $repoData)) {
10289 $composer = JsonFile::parseJson($repoData['data'], $resource);
10291 if (empty($composer['time'])) {
10292 $resource = $this->getScheme() . '://bitbucket.org/api/1.0/repositories/'.$this->owner.'/'.$this->repository.'/changesets/'.$identifier;
10293 $changeset = JsonFile::parseJson($this->getContents($resource), $resource);
10294 $composer['time'] = $changeset['timestamp'];
10296 $this->infoCache[$identifier] = $composer;
10299 return $this->infoCache[$identifier];
10305 public function getTags()
10307 if (null === $this->tags) {
10308 $resource = $this->getScheme() . '://bitbucket.org/api/1.0/repositories/'.$this->owner.'/'.$this->repository.'/tags';
10309 $tagsData = JsonFile::parseJson($this->getContents($resource), $resource);
10310 $this->tags = array();
10311 foreach ($tagsData as $tag => $data) {
10312 $this->tags[$tag] = $data['raw_node'];
10314 unset($this->tags['tip']);
10317 return $this->tags;
10323 public function getBranches()
10325 if (null === $this->branches) {
10326 $resource = $this->getScheme() . '://bitbucket.org/api/1.0/repositories/'.$this->owner.'/'.$this->repository.'/branches';
10327 $branchData = JsonFile::parseJson($this->getContents($resource), $resource);
10328 $this->branches = array();
10329 foreach ($branchData as $branch => $data) {
10330 $this->branches[$branch] = $data['raw_node'];
10334 return $this->branches;
10340 public static function supports(IOInterface $io, Config $config, $url, $deep = false)
10342 if (!preg_match('#^https://bitbucket\.org/([^/]+)/([^/]+)/?$#', $url)) {
10346 if (!extension_loaded('openssl')) {
10347 if ($io->isVerbose()) {
10348 $io->write('Skipping Bitbucket hg driver for '.$url.' because the OpenSSL PHP extension is missing.');
10369 namespace Composer\Repository;
10378 class InstalledArrayRepository extends WritableArrayRepository implements InstalledRepositoryInterface
10393 namespace Composer\Repository;
10400 class RepositorySecurityException extends \Exception
10415 namespace Composer\Repository;
10417 use Composer\Downloader\TransportException;
10418 use Composer\Repository\Vcs\VcsDriverInterface;
10419 use Composer\Package\Version\VersionParser;
10420 use Composer\Package\Loader\ArrayLoader;
10421 use Composer\Package\Loader\ValidatingArrayLoader;
10422 use Composer\Package\Loader\InvalidPackageException;
10423 use Composer\Package\Loader\LoaderInterface;
10424 use Composer\EventDispatcher\EventDispatcher;
10425 use Composer\IO\IOInterface;
10426 use Composer\Config;
10431 class VcsRepository extends ArrayRepository
10434 protected $packageName;
10435 protected $verbose;
10438 protected $versionParser;
10441 protected $repoConfig;
10442 protected $branchErrorOccurred = false;
10444 public function __construct(array $repoConfig, IOInterface $io, Config $config, EventDispatcher $dispatcher = null, array $drivers = null)
10446 $this->drivers = $drivers ?: array(
10447 'github' => 'Composer\Repository\Vcs\GitHubDriver',
10448 'git-bitbucket' => 'Composer\Repository\Vcs\GitBitbucketDriver',
10449 'git' => 'Composer\Repository\Vcs\GitDriver',
10450 'hg-bitbucket' => 'Composer\Repository\Vcs\HgBitbucketDriver',
10451 'hg' => 'Composer\Repository\Vcs\HgDriver',
10452 'perforce' => 'Composer\Repository\Vcs\PerforceDriver',
10454 'svn' => 'Composer\Repository\Vcs\SvnDriver',
10457 $this->url = $repoConfig['url'];
10459 $this->type = isset($repoConfig['type']) ? $repoConfig['type'] : 'vcs';
10460 $this->verbose = $io->isVerbose();
10461 $this->config = $config;
10462 $this->repoConfig = $repoConfig;
10465 public function getRepoConfig()
10467 return $this->repoConfig;
10470 public function setLoader(LoaderInterface $loader)
10472 $this->loader = $loader;
10475 public function getDriver()
10477 if (isset($this->drivers[$this->type])) {
10478 $class = $this->drivers[$this->type];
10479 $driver = new $class($this->repoConfig, $this->io, $this->config);
10480 $driver->initialize();
10485 foreach ($this->drivers as $driver) {
10486 if ($driver::supports($this->io, $this->config, $this->url)) {
10487 $driver = new $driver($this->repoConfig, $this->io, $this->config);
10488 $driver->initialize();
10494 foreach ($this->drivers as $driver) {
10495 if ($driver::supports($this->io, $this->config, $this->url, true)) {
10496 $driver = new $driver($this->repoConfig, $this->io, $this->config);
10497 $driver->initialize();
10504 public function hadInvalidBranches()
10506 return $this->branchErrorOccurred;
10509 protected function initialize()
10511 parent::initialize();
10513 $verbose = $this->verbose;
10515 $driver = $this->getDriver();
10517 throw new \InvalidArgumentException('No driver found to handle VCS repository '.$this->url);
10520 $this->versionParser = new VersionParser;
10521 if (!$this->loader) {
10522 $this->loader = new ArrayLoader($this->versionParser);
10526 if ($driver->hasComposerFile($driver->getRootIdentifier())) {
10527 $data = $driver->getComposerInformation($driver->getRootIdentifier());
10528 $this->packageName = !empty($data['name']) ? $data['name'] : null;
10530 } catch (\Exception $e) {
10532 $this->io->write('<error>Skipped parsing '.$driver->getRootIdentifier().', '.$e->getMessage().'</error>');
10536 foreach ($driver->getTags() as $tag => $identifier) {
10537 $msg = 'Reading composer.json of <info>' . ($this->packageName ?: $this->url) . '</info> (<comment>' . $tag . '</comment>)';
10539 $this->io->write($msg);
10541 $this->io->overwrite($msg, false);
10545 $tag = str_replace('release-', '', $tag);
10547 if (!$parsedTag = $this->validateTag($tag)) {
10549 $this->io->write('<warning>Skipped tag '.$tag.', invalid tag name</warning>');
10555 if (!$data = $driver->getComposerInformation($identifier)) {
10557 $this->io->write('<warning>Skipped tag '.$tag.', no composer file</warning>');
10563 if (isset($data['version'])) {
10564 $data['version_normalized'] = $this->versionParser->normalize($data['version']);
10567 $data['version'] = $tag;
10568 $data['version_normalized'] = $parsedTag;
10572 $data['version'] = preg_replace('{[.-]?dev$}i', '', $data['version']);
10573 $data['version_normalized'] = preg_replace('{(^dev-|[.-]?dev$)}i', '', $data['version_normalized']);
10576 if ($data['version_normalized'] !== $parsedTag) {
10578 $this->io->write('<warning>Skipped tag '.$tag.', tag ('.$parsedTag.') does not match version ('.$data['version_normalized'].') in composer.json</warning>');
10584 $this->io->write('Importing tag '.$tag.' ('.$data['version_normalized'].')');
10587 $this->addPackage($this->loader->load($this->preProcess($driver, $data, $identifier)));
10588 } catch (\Exception $e) {
10590 $this->io->write('<warning>Skipped tag '.$tag.', '.($e instanceof TransportException ? 'no composer file was found' : $e->getMessage()).'</warning>');
10597 $this->io->overwrite('', false);
10600 foreach ($driver->getBranches() as $branch => $identifier) {
10601 $msg = 'Reading composer.json of <info>' . ($this->packageName ?: $this->url) . '</info> (<comment>' . $branch . '</comment>)';
10603 $this->io->write($msg);
10605 $this->io->overwrite($msg, false);
10608 if (!$parsedBranch = $this->validateBranch($branch)) {
10610 $this->io->write('<warning>Skipped branch '.$branch.', invalid name</warning>');
10616 if (!$data = $driver->getComposerInformation($identifier)) {
10618 $this->io->write('<warning>Skipped branch '.$branch.', no composer file</warning>');
10624 $data['version'] = $branch;
10625 $data['version_normalized'] = $parsedBranch;
10628 if ('dev-' === substr($parsedBranch, 0, 4) || '9999999-dev' === $parsedBranch) {
10629 $data['version'] = 'dev-' . $data['version'];
10631 $data['version'] = preg_replace('{(\.9{7})+}', '.x', $parsedBranch);
10635 $this->io->write('Importing branch '.$branch.' ('.$data['version'].')');
10638 $packageData = $this->preProcess($driver, $data, $identifier);
10639 $package = $this->loader->load($packageData);
10640 if ($this->loader instanceof ValidatingArrayLoader && $this->loader->getWarnings()) {
10641 throw new InvalidPackageException($this->loader->getErrors(), $this->loader->getWarnings(), $packageData);
10643 $this->addPackage($package);
10644 } catch (TransportException $e) {
10646 $this->io->write('<warning>Skipped branch '.$branch.', no composer file was found</warning>');
10649 } catch (\Exception $e) {
10651 $this->io->write('');
10653 $this->branchErrorOccurred = true;
10654 $this->io->write('<error>Skipped branch '.$branch.', '.$e->getMessage().'</error>');
10655 $this->io->write('');
10659 $driver->cleanup();
10662 $this->io->overwrite('', false);
10665 if (!$this->getPackages()) {
10666 throw new InvalidRepositoryException('No valid composer.json was found in any branch or tag of '.$this->url.', could not load a package from it.');
10670 protected function preProcess(VcsDriverInterface $driver, array $data, $identifier)
10673 $data['name'] = $this->packageName ?: $data['name'];
10675 if (!isset($data['dist'])) {
10676 $data['dist'] = $driver->getDist($identifier);
10678 if (!isset($data['source'])) {
10679 $data['source'] = $driver->getSource($identifier);
10685 private function validateBranch($branch)
10688 return $this->versionParser->normalizeBranch($branch);
10689 } catch (\Exception $e) {
10695 private function validateTag($version)
10698 return $this->versionParser->normalize($version);
10699 } catch (\Exception $e) {
10717 namespace Composer\Repository;
10719 use Composer\Package\CompletePackage;
10720 use Composer\Package\Version\VersionParser;
10721 use Composer\Plugin\PluginInterface;
10726 class PlatformRepository extends ArrayRepository
10728 const PLATFORM_PACKAGE_REGEX = '{^(?:php(?:-64bit)?|hhvm|(?:ext|lib)-[^/]+)$}i';
10730 protected function initialize()
10732 parent::initialize();
10734 $versionParser = new VersionParser();
10736 $prettyVersion = PluginInterface::PLUGIN_API_VERSION;
10737 $version = $versionParser->normalize($prettyVersion);
10738 $composerPluginApi = new CompletePackage('composer-plugin-api', $version, $prettyVersion);
10739 $composerPluginApi->setDescription('The Composer Plugin API');
10740 parent::addPackage($composerPluginApi);
10743 $prettyVersion = PHP_VERSION;
10744 $version = $versionParser->normalize($prettyVersion);
10745 } catch (\UnexpectedValueException $e) {
10746 $prettyVersion = preg_replace('#^([^~+-]+).*$#', '$1', PHP_VERSION);
10747 $version = $versionParser->normalize($prettyVersion);
10750 $php = new CompletePackage('php', $version, $prettyVersion);
10751 $php->setDescription('The PHP interpreter');
10752 parent::addPackage($php);
10754 if (PHP_INT_SIZE === 8) {
10755 $php64 = new CompletePackage('php-64bit', $version, $prettyVersion);
10756 $php64->setDescription('The PHP interpreter (64bit)');
10757 parent::addPackage($php64);
10760 $loadedExtensions = get_loaded_extensions();
10763 foreach ($loadedExtensions as $name) {
10764 if (in_array($name, array('standard', 'Core'))) {
10768 $reflExt = new \ReflectionExtension($name);
10770 $prettyVersion = $reflExt->getVersion();
10771 $version = $versionParser->normalize($prettyVersion);
10772 } catch (\UnexpectedValueException $e) {
10773 $prettyVersion = '0';
10774 $version = $versionParser->normalize($prettyVersion);
10777 $packageName = $this->buildPackageName($name);
10778 $ext = new CompletePackage($packageName, $version, $prettyVersion);
10779 $ext->setDescription('The '.$name.' PHP extension');
10780 parent::addPackage($ext);
10786 foreach ($loadedExtensions as $name) {
10787 $prettyVersion = null;
10790 $curlVersion = curl_version();
10791 $prettyVersion = $curlVersion['version'];
10795 $prettyVersion = ICONV_VERSION;
10800 if (defined('INTL_ICU_VERSION')) {
10801 $prettyVersion = INTL_ICU_VERSION;
10803 $reflector = new \ReflectionExtension('intl');
10806 $reflector->info();
10807 $output = ob_get_clean();
10809 preg_match('/^ICU version => (.*)$/m', $output, $matches);
10810 $prettyVersion = $matches[1];
10816 $prettyVersion = LIBXML_DOTTED_VERSION;
10820 $prettyVersion = preg_replace_callback('{^(?:OpenSSL\s*)?([0-9.]+)([a-z]?).*}', function ($match) {
10821 return $match[1] . (empty($match[2]) ? '' : '.'.(ord($match[2]) - 96));
10822 }, OPENSSL_VERSION_TEXT);
10826 $prettyVersion = preg_replace('{^(\S+).*}', '$1', PCRE_VERSION);
10830 $prettyVersion = phpversion('uuid');
10834 $prettyVersion = LIBXSLT_DOTTED_VERSION;
10843 $version = $versionParser->normalize($prettyVersion);
10844 } catch (\UnexpectedValueException $e) {
10848 $lib = new CompletePackage('lib-'.$name, $version, $prettyVersion);
10849 $lib->setDescription('The '.$name.' PHP library');
10850 parent::addPackage($lib);
10853 if (defined('HHVM_VERSION')) {
10855 $prettyVersion = HHVM_VERSION;
10856 $version = $versionParser->normalize($prettyVersion);
10857 } catch (\UnexpectedValueException $e) {
10858 $prettyVersion = preg_replace('#^([^~+-]+).*$#', '$1', HHVM_VERSION);
10859 $version = $versionParser->normalize($prettyVersion);
10862 $hhvm = new CompletePackage('hhvm', $version, $prettyVersion);
10863 $hhvm->setDescription('The HHVM Runtime (64bit)');
10864 parent::addPackage($hhvm);
10868 private function buildPackageName($name)
10870 return 'ext-' . str_replace(' ', '-', $name);
10885 namespace Composer\Repository;
10892 class InstalledFilesystemRepository extends FilesystemRepository implements InstalledRepositoryInterface
10907 namespace Composer\Repository;
10909 use Composer\Package\PackageInterface;
10916 class CompositeRepository implements RepositoryInterface
10922 private $repositories;
10928 public function __construct(array $repositories)
10930 $this->repositories = array();
10931 foreach ($repositories as $repo) {
10932 $this->addRepository($repo);
10941 public function getRepositories()
10943 return $this->repositories;
10949 public function hasPackage(PackageInterface $package)
10951 foreach ($this->repositories as $repository) {
10953 if ($repository->hasPackage($package)) {
10964 public function findPackage($name, $version)
10966 foreach ($this->repositories as $repository) {
10968 $package = $repository->findPackage($name, $version);
10969 if (null !== $package) {
10980 public function findPackages($name, $version = null)
10982 $packages = array();
10983 foreach ($this->repositories as $repository) {
10985 $packages[] = $repository->findPackages($name, $version);
10988 return $packages ? call_user_func_array('array_merge', $packages) : array();
10994 public function search($query, $mode = 0)
10996 $matches = array();
10997 foreach ($this->repositories as $repository) {
10999 $matches[] = $repository->search($query, $mode);
11002 return $matches ? call_user_func_array('array_merge', $matches) : array();
11008 public function filterPackages($callback, $class = 'Composer\Package\Package')
11010 foreach ($this->repositories as $repository) {
11011 if (false === $repository->filterPackages($callback, $class)) {
11022 public function getPackages()
11024 $packages = array();
11025 foreach ($this->repositories as $repository) {
11027 $packages[] = $repository->getPackages();
11030 return $packages ? call_user_func_array('array_merge', $packages) : array();
11036 public function removePackage(PackageInterface $package)
11038 foreach ($this->repositories as $repository) {
11040 $repository->removePackage($package);
11047 public function count()
11050 foreach ($this->repositories as $repository) {
11052 $total += $repository->count();
11062 public function addRepository(RepositoryInterface $repository)
11064 if ($repository instanceof self) {
11065 foreach ($repository->getRepositories() as $repo) {
11066 $this->addRepository($repo);
11069 $this->repositories[] = $repository;
11085 namespace Composer\Repository;
11094 interface InstalledRepositoryInterface extends WritableRepositoryInterface
11109 namespace Composer\Repository;
11111 use Composer\Package\Loader\ArrayLoader;
11112 use Composer\Package\Package;
11113 use Composer\Package\PackageInterface;
11114 use Composer\Package\AliasPackage;
11115 use Composer\Package\Version\VersionParser;
11116 use Composer\DependencyResolver\Pool;
11117 use Composer\Json\JsonFile;
11118 use Composer\Cache;
11119 use Composer\Config;
11120 use Composer\IO\IOInterface;
11121 use Composer\Util\RemoteFilesystem;
11122 use Composer\Plugin\PluginEvents;
11123 use Composer\Plugin\PreFileDownloadEvent;
11124 use Composer\EventDispatcher\EventDispatcher;
11129 class ComposerRepository extends ArrayRepository
11132 protected $options;
11134 protected $baseUrl;
11138 protected $notifyUrl;
11139 protected $searchUrl;
11140 protected $hasProviders = false;
11141 protected $providersUrl;
11142 protected $lazyProvidersUrl;
11143 protected $providerListing;
11144 protected $providers = array();
11145 protected $providersByUid = array();
11147 protected $rootAliases;
11148 protected $allowSslDowngrade = false;
11149 protected $eventDispatcher;
11150 protected $sourceMirrors;
11151 protected $distMirrors;
11152 private $degradedMode = false;
11155 public function __construct(array $repoConfig, IOInterface $io, Config $config, EventDispatcher $eventDispatcher = null)
11157 if (!preg_match('{^[\w.]+\??://}', $repoConfig['url'])) {
11159 $repoConfig['url'] = 'http://'.$repoConfig['url'];
11161 $repoConfig['url'] = rtrim($repoConfig['url'], '/');
11163 if ('https?' === substr($repoConfig['url'], 0, 6)) {
11164 $repoConfig['url'] = (extension_loaded('openssl') ? 'https' : 'http') . substr($repoConfig['url'], 6);
11167 $urlBits = parse_url($repoConfig['url']);
11168 if ($urlBits === false || empty($urlBits['scheme'])) {
11169 throw new \UnexpectedValueException('Invalid url given for Composer repository: '.$repoConfig['url']);
11172 if (!isset($repoConfig['options'])) {
11173 $repoConfig['options'] = array();
11175 if (isset($repoConfig['allow_ssl_downgrade']) && true === $repoConfig['allow_ssl_downgrade']) {
11176 $this->allowSslDowngrade = true;
11179 $this->config = $config;
11180 $this->options = $repoConfig['options'];
11181 $this->url = $repoConfig['url'];
11182 $this->baseUrl = rtrim(preg_replace('{^(.*)(?:/[^/\\]+.json)?(?:[?#].*)?$}', '$1', $this->url), '/');
11184 $this->cache = new Cache($io, $config->get('cache-repo-dir').'/'.preg_replace('{[^a-z0-9.]}i', '-', $this->url), 'a-z0-9.$');
11185 $this->loader = new ArrayLoader();
11186 $this->rfs = new RemoteFilesystem($this->io, $this->config, $this->options);
11187 $this->eventDispatcher = $eventDispatcher;
11190 public function setRootAliases(array $rootAliases)
11192 $this->rootAliases = $rootAliases;
11198 public function findPackage($name, $version)
11200 if (!$this->hasProviders()) {
11201 return parent::findPackage($name, $version);
11204 $versionParser = new VersionParser();
11205 $version = $versionParser->normalize($version);
11206 $name = strtolower($name);
11208 foreach ($this->getProviderNames() as $providerName) {
11209 if ($name === $providerName) {
11210 $packages = $this->whatProvides(new Pool('dev'), $providerName);
11211 foreach ($packages as $package) {
11212 if ($name == $package->getName() && $version === $package->getVersion()) {
11223 public function findPackages($name, $version = null)
11225 if (!$this->hasProviders()) {
11226 return parent::findPackages($name, $version);
11229 $name = strtolower($name);
11232 if (null !== $version) {
11233 $versionParser = new VersionParser();
11234 $version = $versionParser->normalize($version);
11237 $packages = array();
11239 foreach ($this->getProviderNames() as $providerName) {
11240 if ($name === $providerName) {
11241 $packages = $this->whatProvides(new Pool('dev'), $providerName);
11242 foreach ($packages as $package) {
11243 if ($name == $package->getName() && (null === $version || $version === $package->getVersion())) {
11244 $packages[] = $package;
11253 public function getPackages()
11255 if ($this->hasProviders()) {
11256 throw new \LogicException('Composer repositories that have providers can not load the complete list of packages, use getProviderNames instead.');
11259 return parent::getPackages();
11265 public function search($query, $mode = 0)
11267 $this->loadRootServerFile();
11269 if ($this->searchUrl && $mode === self::SEARCH_FULLTEXT) {
11270 $url = str_replace('%query%', $query, $this->searchUrl);
11272 $hostname = parse_url($url, PHP_URL_HOST) ?: $url;
11273 $json = $this->rfs->getContents($hostname, $url, false);
11274 $results = JsonFile::parseJson($json, $url);
11276 return $results['results'];
11279 if ($this->hasProviders()) {
11280 $results = array();
11281 $regex = '{(?:'.implode('|', preg_split('{\s+}', $query)).')}i';
11283 foreach ($this->getProviderNames() as $name) {
11284 if (preg_match($regex, $name)) {
11285 $results[] = array('name' => $name);
11292 return parent::search($query, $mode);
11295 public function getProviderNames()
11297 $this->loadRootServerFile();
11299 if (null === $this->providerListing) {
11300 $this->loadProviderListings($this->loadRootServerFile());
11303 if ($this->providersUrl) {
11304 return array_keys($this->providerListing);
11308 $providers = array();
11309 foreach (array_keys($this->providerListing) as $provider) {
11310 $providers[] = substr($provider, 2, -5);
11316 protected function configurePackageTransportOptions(PackageInterface $package)
11318 foreach ($package->getDistUrls() as $url) {
11319 if (strpos($url, $this->baseUrl) === 0) {
11320 $package->setTransportOptions($this->options);
11327 public function hasProviders()
11329 $this->loadRootServerFile();
11331 return $this->hasProviders;
11334 public function resetPackageIds()
11336 foreach ($this->providersByUid as $package) {
11337 if ($package instanceof AliasPackage) {
11338 $package->getAliasOf()->setId(-1);
11340 $package->setId(-1);
11344 public function whatProvides(Pool $pool, $name)
11346 if (isset($this->providers[$name])) {
11347 return $this->providers[$name];
11351 if (preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $name) || '__root__' === $name) {
11355 if (null === $this->providerListing) {
11356 $this->loadProviderListings($this->loadRootServerFile());
11359 if ($this->lazyProvidersUrl && !isset($this->providerListing[$name])) {
11361 $url = str_replace('%package%', $name, $this->lazyProvidersUrl);
11363 } elseif ($this->providersUrl) {
11365 if (!isset($this->providerListing[$name])) {
11369 $hash = $this->providerListing[$name]['sha256'];
11370 $url = str_replace(array('%package%', '%hash%'), array($name, $hash), $this->providersUrl);
11371 $cacheKey = 'provider-'.strtr($name, '/', '$').'.json';
11374 $url = 'p/'.$name.'.json';
11377 if (!isset($this->providerListing[$url])) {
11380 $hash = $this->providerListing[$url]['sha256'];
11384 if ($cacheKey && $this->cache->sha256($cacheKey) === $hash) {
11385 $packages = json_decode($this->cache->read($cacheKey), true);
11387 $packages = $this->fetchFile($url, $cacheKey, $hash);
11390 $this->providers[$name] = array();
11391 foreach ($packages['packages'] as $versions) {
11392 foreach ($versions as $version) {
11394 if (isset($this->providersByUid[$version['uid']])) {
11396 if (!isset($this->providers[$name][$version['uid']])) {
11398 if ($this->providersByUid[$version['uid']] instanceof AliasPackage) {
11399 $this->providers[$name][$version['uid']] = $this->providersByUid[$version['uid']]->getAliasOf();
11400 $this->providers[$name][$version['uid'].'-alias'] = $this->providersByUid[$version['uid']];
11402 $this->providers[$name][$version['uid']] = $this->providersByUid[$version['uid']];
11405 if (isset($this->providersByUid[$version['uid'].'-root'])) {
11406 $this->providers[$name][$version['uid'].'-root'] = $this->providersByUid[$version['uid'].'-root'];
11410 if (!$pool->isPackageAcceptable(strtolower($version['name']), VersionParser::parseStability($version['version']))) {
11415 $package = $this->createPackage($version, 'Composer\Package\Package');
11416 $package->setRepository($this);
11418 if ($package instanceof AliasPackage) {
11419 $aliased = $package->getAliasOf();
11420 $aliased->setRepository($this);
11422 $this->providers[$name][$version['uid']] = $aliased;
11423 $this->providers[$name][$version['uid'].'-alias'] = $package;
11426 $this->providersByUid[$version['uid']] = $package;
11428 $this->providers[$name][$version['uid']] = $package;
11429 $this->providersByUid[$version['uid']] = $package;
11433 unset($rootAliasData);
11435 if (isset($this->rootAliases[$package->getName()][$package->getVersion()])) {
11436 $rootAliasData = $this->rootAliases[$package->getName()][$package->getVersion()];
11437 } elseif ($package instanceof AliasPackage && isset($this->rootAliases[$package->getName()][$package->getAliasOf()->getVersion()])) {
11438 $rootAliasData = $this->rootAliases[$package->getName()][$package->getAliasOf()->getVersion()];
11441 if (isset($rootAliasData)) {
11442 $alias = $this->createAliasPackage($package, $rootAliasData['alias_normalized'], $rootAliasData['alias']);
11443 $alias->setRepository($this);
11445 $this->providers[$name][$version['uid'].'-root'] = $alias;
11446 $this->providersByUid[$version['uid'].'-root'] = $alias;
11452 return $this->providers[$name];
11458 protected function initialize()
11460 parent::initialize();
11462 $repoData = $this->loadDataFromServer();
11464 foreach ($repoData as $package) {
11465 $this->addPackage($this->createPackage($package, 'Composer\Package\CompletePackage'));
11474 public function addPackage(PackageInterface $package)
11476 parent::addPackage($package);
11477 $this->configurePackageTransportOptions($package);
11480 protected function loadRootServerFile()
11482 if (null !== $this->rootData) {
11483 return $this->rootData;
11486 if (!extension_loaded('openssl') && 'https' === substr($this->url, 0, 5)) {
11487 throw new \RuntimeException('You must enable the openssl extension in your php.ini to load information from '.$this->url);
11490 $jsonUrlParts = parse_url($this->url);
11492 if (isset($jsonUrlParts['path']) && false !== strpos($jsonUrlParts['path'], '.json')) {
11493 $jsonUrl = $this->url;
11495 $jsonUrl = $this->url . '/packages.json';
11498 $data = $this->fetchFile($jsonUrl, 'packages.json');
11500 if (!empty($data['notify-batch'])) {
11501 $this->notifyUrl = $this->canonicalizeUrl($data['notify-batch']);
11502 } elseif (!empty($data['notify_batch'])) {
11504 $this->notifyUrl = $this->canonicalizeUrl($data['notify_batch']);
11505 } elseif (!empty($data['notify'])) {
11506 $this->notifyUrl = $this->canonicalizeUrl($data['notify']);
11509 if (!empty($data['search'])) {
11510 $this->searchUrl = $this->canonicalizeUrl($data['search']);
11513 if (!empty($data['mirrors'])) {
11514 foreach ($data['mirrors'] as $mirror) {
11515 if (!empty($mirror['git-url'])) {
11516 $this->sourceMirrors['git'][] = array('url' => $mirror['git-url'], 'preferred' => !empty($mirror['preferred']));
11518 if (!empty($mirror['hg-url'])) {
11519 $this->sourceMirrors['hg'][] = array('url' => $mirror['hg-url'], 'preferred' => !empty($mirror['preferred']));
11521 if (!empty($mirror['dist-url'])) {
11522 $this->distMirrors[] = array('url' => $mirror['dist-url'], 'preferred' => !empty($mirror['preferred']));
11527 if (!empty($data['warning'])) {
11528 $this->io->write('<warning>Warning from '.$this->url.': '.$data['warning'].'</warning>');
11531 if (!empty($data['providers-lazy-url'])) {
11532 $this->lazyProvidersUrl = $this->canonicalizeUrl($data['providers-lazy-url']);
11533 $this->hasProviders = true;
11536 if ($this->allowSslDowngrade) {
11537 $this->url = str_replace('https://', 'http://', $this->url);
11540 if (!empty($data['providers-url'])) {
11541 $this->providersUrl = $this->canonicalizeUrl($data['providers-url']);
11542 $this->hasProviders = true;
11545 if (!empty($data['providers']) || !empty($data['providers-includes'])) {
11546 $this->hasProviders = true;
11549 return $this->rootData = $data;
11552 protected function canonicalizeUrl($url)
11554 if ('/' === $url[0]) {
11555 return preg_replace('{(https?://[^/]+).*}i', '$1' . $url, $this->url);
11561 protected function loadDataFromServer()
11563 $data = $this->loadRootServerFile();
11565 return $this->loadIncludes($data);
11568 protected function loadProviderListings($data)
11570 if (isset($data['providers'])) {
11571 if (!is_array($this->providerListing)) {
11572 $this->providerListing = array();
11574 $this->providerListing = array_merge($this->providerListing, $data['providers']);
11577 if ($this->providersUrl && isset($data['provider-includes'])) {
11578 $includes = $data['provider-includes'];
11579 foreach ($includes as $include => $metadata) {
11580 $url = $this->baseUrl . '/' . str_replace('%hash%', $metadata['sha256'], $include);
11581 $cacheKey = str_replace(array('%hash%','$'), '', $include);
11582 if ($this->cache->sha256($cacheKey) === $metadata['sha256']) {
11583 $includedData = json_decode($this->cache->read($cacheKey), true);
11585 $includedData = $this->fetchFile($url, $cacheKey, $metadata['sha256']);
11588 $this->loadProviderListings($includedData);
11590 } elseif (isset($data['providers-includes'])) {
11592 $includes = $data['providers-includes'];
11593 foreach ($includes as $include => $metadata) {
11594 if ($this->cache->sha256($include) === $metadata['sha256']) {
11595 $includedData = json_decode($this->cache->read($include), true);
11597 $includedData = $this->fetchFile($include, null, $metadata['sha256']);
11600 $this->loadProviderListings($includedData);
11605 protected function loadIncludes($data)
11607 $packages = array();
11610 if (!isset($data['packages']) && !isset($data['includes'])) {
11611 foreach ($data as $pkg) {
11612 foreach ($pkg['versions'] as $metadata) {
11613 $packages[] = $metadata;
11620 if (isset($data['packages'])) {
11621 foreach ($data['packages'] as $package => $versions) {
11622 foreach ($versions as $version => $metadata) {
11623 $packages[] = $metadata;
11628 if (isset($data['includes'])) {
11629 foreach ($data['includes'] as $include => $metadata) {
11630 if ($this->cache->sha1($include) === $metadata['sha1']) {
11631 $includedData = json_decode($this->cache->read($include), true);
11633 $includedData = $this->fetchFile($include);
11635 $packages = array_merge($packages, $this->loadIncludes($includedData));
11642 protected function createPackage(array $data, $class)
11645 if (!isset($data['notification-url'])) {
11646 $data['notification-url'] = $this->notifyUrl;
11649 $package = $this->loader->load($data, 'Composer\Package\CompletePackage');
11650 if (isset($this->sourceMirrors[$package->getSourceType()])) {
11651 $package->setSourceMirrors($this->sourceMirrors[$package->getSourceType()]);
11653 $package->setDistMirrors($this->distMirrors);
11654 $this->configurePackageTransportOptions($package);
11657 } catch (\Exception $e) {
11658 throw new \RuntimeException('Could not load package '.(isset($data['name']) ? $data['name'] : json_encode($data)).' in '.$this->url.': ['.get_class($e).'] '.$e->getMessage(), 0, $e);
11662 protected function fetchFile($filename, $cacheKey = null, $sha256 = null)
11664 if (null === $cacheKey) {
11665 $cacheKey = $filename;
11666 $filename = $this->baseUrl.'/'.$filename;
11670 while ($retries--) {
11672 $preFileDownloadEvent = new PreFileDownloadEvent(PluginEvents::PRE_FILE_DOWNLOAD, $this->rfs, $filename);
11673 if ($this->eventDispatcher) {
11674 $this->eventDispatcher->dispatch($preFileDownloadEvent->getName(), $preFileDownloadEvent);
11677 $hostname = parse_url($filename, PHP_URL_HOST) ?: $filename;
11678 $json = $preFileDownloadEvent->getRemoteFilesystem()->getContents($hostname, $filename, false);
11679 if ($sha256 && $sha256 !== hash('sha256', $json)) {
11687 throw new RepositorySecurityException('The contents of '.$filename.' do not match its signature. This should indicate a man-in-the-middle attack. Try running composer again and report this if you think it is a mistake.');
11689 $data = JsonFile::parseJson($json, $filename);
11691 $this->cache->write($cacheKey, $json);
11695 } catch (\Exception $e) {
11701 if ($e instanceof RepositorySecurityException) {
11705 if ($cacheKey && ($contents = $this->cache->read($cacheKey))) {
11706 if (!$this->degradedMode) {
11707 $this->io->write('<warning>'.$e->getMessage().'</warning>');
11708 $this->io->write('<warning>'.$this->url.' could not be fully loaded, package information was loaded from the local cache and may be out of date</warning>');
11710 $this->degradedMode = true;
11711 $data = JsonFile::parseJson($contents, $this->cache->getRoot().$cacheKey);
11735 namespace Composer\Repository\Pear;
11742 class DependencyInfo
11745 private $optionals;
11751 public function __construct($requires, $optionals)
11753 $this->requires = $requires;
11754 $this->optionals = $optionals;
11760 public function getRequires()
11762 return $this->requires;
11768 public function getOptionals()
11770 return $this->optionals;
11785 namespace Composer\Repository\Pear;
11792 class PackageDependencyParser
11800 public function buildDependencyInfo($depArray)
11802 if (!is_array($depArray)) {
11803 return new DependencyInfo(array(), array());
11805 if (!$this->isHash($depArray)) {
11806 return new DependencyInfo($this->buildDependency10Info($depArray), array());
11809 return $this->buildDependency20Info($depArray);
11824 private function buildDependency10Info($depArray)
11826 static $dep10toOperatorMap = array('has' => '==', 'eq' => '==', 'ge' => '>=', 'gt' => '>', 'le' => '<=', 'lt' => '<', 'not' => '!=');
11830 foreach ($depArray as $depItem) {
11831 if (empty($depItem['rel']) || !array_key_exists($depItem['rel'], $dep10toOperatorMap)) {
11836 $depType = !empty($depItem['optional']) && 'yes' == $depItem['optional']
11839 $depType = 'not' == $depItem['rel']
11843 $depVersion = !empty($depItem['version']) ? $this->parseVersion($depItem['version']) : '*';
11846 $depVersionConstraint = ('has' == $depItem['rel'] || 'not' == $depItem['rel']) && '*' == $depVersion
11848 : $dep10toOperatorMap[$depItem['rel']] . $depVersion;
11850 switch ($depItem['type']) {
11852 $depChannelName = 'php';
11853 $depPackageName = '';
11856 $depChannelName = !empty($depItem['channel']) ? $depItem['channel'] : 'pear.php.net';
11857 $depPackageName = $depItem['name'];
11860 $depChannelName = 'ext';
11861 $depPackageName = $depItem['name'];
11865 $depChannelName = '';
11866 $depPackageName = '';
11869 $depChannelName = '';
11870 $depPackageName = '';
11874 if ('' != $depChannelName) {
11875 $result[] = new DependencyConstraint(
11877 $depVersionConstraint,
11893 private function buildDependency20Info($depArray)
11896 $optionals = array();
11897 $defaultOptionals = array();
11898 foreach ($depArray as $depType => $depTypeGroup) {
11899 if (!is_array($depTypeGroup)) {
11902 if ('required' == $depType || 'optional' == $depType) {
11903 foreach ($depTypeGroup as $depItemType => $depItem) {
11904 switch ($depItemType) {
11906 $result[] = new DependencyConstraint(
11908 $this->parse20VersionConstraint($depItem),
11914 $deps = $this->buildDepPackageConstraints($depItem, $depType);
11915 $result = array_merge($result, $deps);
11918 $deps = $this->buildDepExtensionConstraints($depItem, $depType);
11919 $result = array_merge($result, $deps);
11922 $deps = $this->buildDepPackageConstraints($depItem, 'replaces');
11923 $defaultOptionals += $deps;
11926 case 'pearinstaller':
11932 } elseif ('group' == $depType) {
11933 if ($this->isHash($depTypeGroup)) {
11934 $depTypeGroup = array($depTypeGroup);
11937 foreach ($depTypeGroup as $depItem) {
11938 $groupName = $depItem['attribs']['name'];
11939 if (!isset($optionals[$groupName])) {
11940 $optionals[$groupName] = array();
11943 if (isset($depItem['subpackage'])) {
11944 $optionals[$groupName] += $this->buildDepPackageConstraints($depItem['subpackage'], 'replaces');
11946 $result += $this->buildDepPackageConstraints($depItem['package'], 'optional');
11952 if (count($defaultOptionals) > 0) {
11953 $optionals['*'] = $defaultOptionals;
11956 return new DependencyInfo($result, $optionals);
11966 private function buildDepExtensionConstraints($depItem, $depType)
11968 if ($this->isHash($depItem)) {
11969 $depItem = array($depItem);
11973 foreach ($depItem as $subDepItem) {
11974 $depChannelName = 'ext';
11975 $depPackageName = $subDepItem['name'];
11976 $depVersionConstraint = $this->parse20VersionConstraint($subDepItem);
11978 $result[] = new DependencyConstraint(
11980 $depVersionConstraint,
11996 private function buildDepPackageConstraints($depItem, $depType)
11998 if ($this->isHash($depItem)) {
11999 $depItem = array($depItem);
12003 foreach ($depItem as $subDepItem) {
12004 $depChannelName = $subDepItem['channel'];
12005 $depPackageName = $subDepItem['name'];
12006 $depVersionConstraint = $this->parse20VersionConstraint($subDepItem);
12007 if (isset($subDepItem['conflicts'])) {
12008 $depType = 'conflicts';
12011 $result[] = new DependencyConstraint(
12013 $depVersionConstraint,
12028 private function parse20VersionConstraint(array $data)
12030 static $dep20toOperatorMap = array('has' => '==', 'min' => '>=', 'max' => '<=', 'exclude' => '!=');
12032 $versions = array();
12033 $values = array_intersect_key($data, $dep20toOperatorMap);
12034 if (0 == count($values)) {
12037 if (isset($values['min']) && isset($values['exclude']) && $data['min'] == $data['exclude']) {
12038 $versions[] = '>' . $this->parseVersion($values['min']);
12039 } elseif (isset($values['max']) && isset($values['exclude']) && $data['max'] == $data['exclude']) {
12040 $versions[] = '<' . $this->parseVersion($values['max']);
12042 foreach ($values as $op => $version) {
12043 if ('exclude' == $op && is_array($version)) {
12044 foreach ($version as $versionPart) {
12045 $versions[] = $dep20toOperatorMap[$op] . $this->parseVersion($versionPart);
12048 $versions[] = $dep20toOperatorMap[$op] . $this->parseVersion($version);
12053 return implode(',', $versions);
12062 private function parseVersion($version)
12064 if (preg_match('{^v?(\d{1,3})(\.\d+)?(\.\d+)?(\.\d+)?}i', $version, $matches)) {
12065 $version = $matches[1]
12066 .(!empty($matches[2]) ? $matches[2] : '.0')
12067 .(!empty($matches[3]) ? $matches[3] : '.0')
12068 .(!empty($matches[4]) ? $matches[4] : '.0');
12082 private function isHash(array $array)
12084 return !array_key_exists(1, $array) && !array_key_exists(0, $array);
12099 namespace Composer\Repository\Pear;
12117 public function __construct($name, $alias, array $packages)
12119 $this->name = $name;
12120 $this->alias = $alias;
12121 $this->packages = $packages;
12129 public function getName()
12131 return $this->name;
12139 public function getAlias()
12141 return $this->alias;
12149 public function getPackages()
12151 return $this->packages;
12166 namespace Composer\Repository\Pear;
12168 use Composer\Util\RemoteFilesystem;
12177 class ChannelReader extends BaseChannelReader
12180 private $readerMap;
12182 public function __construct(RemoteFilesystem $rfs)
12184 parent::__construct($rfs);
12186 $rest10reader = new ChannelRest10Reader($rfs);
12187 $rest11reader = new ChannelRest11Reader($rfs);
12189 $this->readerMap = array(
12190 'REST1.3' => $rest11reader,
12191 'REST1.2' => $rest11reader,
12192 'REST1.1' => $rest11reader,
12193 'REST1.0' => $rest10reader,
12204 public function read($url)
12206 $xml = $this->requestXml($url, "/channel.xml");
12208 $channelName = (string) $xml->name;
12209 $channelSummary = (string) $xml->summary;
12210 $channelAlias = (string) $xml->suggestedalias;
12212 $supportedVersions = array_keys($this->readerMap);
12213 $selectedRestVersion = $this->selectRestVersion($xml, $supportedVersions);
12214 if (!$selectedRestVersion) {
12215 throw new \UnexpectedValueException(sprintf('PEAR repository %s does not supports any of %s protocols.', $url, implode(', ', $supportedVersions)));
12218 $reader = $this->readerMap[$selectedRestVersion['version']];
12219 $packageDefinitions = $reader->read($selectedRestVersion['baseUrl']);
12221 return new ChannelInfo($channelName, $channelAlias, $packageDefinitions);
12231 private function selectRestVersion($channelXml, $supportedVersions)
12233 $channelXml->registerXPathNamespace('ns', self::CHANNEL_NS);
12235 foreach ($supportedVersions as $version) {
12236 $xpathTest = "ns:servers/ns:primary/ns:rest/ns:baseurl[@type='{$version}']";
12237 $testResult = $channelXml->xpath($xpathTest);
12238 if (count($testResult) > 0) {
12239 return array('version' => $version, 'baseUrl' => (string) $testResult[0]);
12258 namespace Composer\Repository\Pear;
12267 private $channelName;
12268 private $packageName;
12270 private $shortDescription;
12271 private $description;
12282 public function __construct($channelName, $packageName, $license, $shortDescription, $description, $releases)
12284 $this->channelName = $channelName;
12285 $this->packageName = $packageName;
12286 $this->license = $license;
12287 $this->shortDescription = $shortDescription;
12288 $this->description = $description;
12289 $this->releases = $releases;
12295 public function getChannelName()
12297 return $this->channelName;
12303 public function getPackageName()
12305 return $this->packageName;
12311 public function getDescription()
12313 return $this->description;
12319 public function getShortDescription()
12321 return $this->shortDescription;
12327 public function getLicense()
12329 return $this->license;
12335 public function getReleases()
12337 return $this->releases;
12352 namespace Composer\Repository\Pear;
12359 class DependencyConstraint
12362 private $constraint;
12363 private $channelName;
12364 private $packageName;
12372 public function __construct($type, $constraint, $channelName, $packageName)
12374 $this->type = $type;
12375 $this->constraint = $constraint;
12376 $this->channelName = $channelName;
12377 $this->packageName = $packageName;
12380 public function getChannelName()
12382 return $this->channelName;
12385 public function getConstraint()
12387 return $this->constraint;
12390 public function getPackageName()
12392 return $this->packageName;
12395 public function getType()
12397 return $this->type;
12412 namespace Composer\Repository\Pear;
12423 class ChannelRest11Reader extends BaseChannelReader
12425 private $dependencyReader;
12427 public function __construct($rfs)
12429 parent::__construct($rfs);
12431 $this->dependencyReader = new PackageDependencyParser();
12441 public function read($baseUrl)
12443 return $this->readChannelPackages($baseUrl);
12453 private function readChannelPackages($baseUrl)
12457 $xml = $this->requestXml($baseUrl, "/c/categories.xml");
12458 $xml->registerXPathNamespace('ns', self::ALL_CATEGORIES_NS);
12459 foreach ($xml->xpath('ns:c') as $node) {
12460 $categoryName = (string) $node;
12461 $categoryPackages = $this->readCategoryPackages($baseUrl, $categoryName);
12462 $result = array_merge($result, $categoryPackages);
12476 private function readCategoryPackages($baseUrl, $categoryName)
12480 $categoryPath = '/c/'.urlencode($categoryName).'/packagesinfo.xml';
12481 $xml = $this->requestXml($baseUrl, $categoryPath);
12482 $xml->registerXPathNamespace('ns', self::CATEGORY_PACKAGES_INFO_NS);
12483 foreach ($xml->xpath('ns:pi') as $node) {
12484 $packageInfo = $this->parsePackage($node);
12485 $result[] = $packageInfo;
12497 private function parsePackage($packageInfo)
12499 $packageInfo->registerXPathNamespace('ns', self::CATEGORY_PACKAGES_INFO_NS);
12500 $channelName = (string) $packageInfo->p->c;
12501 $packageName = (string) $packageInfo->p->n;
12502 $license = (string) $packageInfo->p->l;
12503 $shortDescription = (string) $packageInfo->p->s;
12504 $description = (string) $packageInfo->p->d;
12506 $dependencies = array();
12507 foreach ($packageInfo->xpath('ns:deps') as $node) {
12508 $dependencyVersion = (string) $node->v;
12509 $dependencyArray = unserialize((string) $node->d);
12511 $dependencyInfo = $this->dependencyReader->buildDependencyInfo($dependencyArray);
12513 $dependencies[$dependencyVersion] = $dependencyInfo;
12516 $releases = array();
12517 $releasesInfo = $packageInfo->xpath('ns:a/ns:r');
12518 if ($releasesInfo) {
12519 foreach ($releasesInfo as $node) {
12520 $releaseVersion = (string) $node->v;
12521 $releaseStability = (string) $node->s;
12522 $releases[$releaseVersion] = new ReleaseInfo(
12524 isset($dependencies[$releaseVersion]) ? $dependencies[$releaseVersion] : new DependencyInfo(array(), array())
12529 return new PackageInfo(
12551 namespace Composer\Repository\Pear;
12560 private $stability;
12561 private $dependencyInfo;
12567 public function __construct($stability, $dependencyInfo)
12569 $this->stability = $stability;
12570 $this->dependencyInfo = $dependencyInfo;
12576 public function getDependencyInfo()
12578 return $this->dependencyInfo;
12584 public function getStability()
12586 return $this->stability;
12601 namespace Composer\Repository\Pear;
12603 use Composer\Util\RemoteFilesystem;
12612 abstract class BaseChannelReader
12617 const CHANNEL_NS = 'http://pear.php.net/channel-1.0';
12618 const ALL_CATEGORIES_NS = 'http://pear.php.net/dtd/rest.allcategories';
12619 const CATEGORY_PACKAGES_INFO_NS = 'http://pear.php.net/dtd/rest.categorypackageinfo';
12620 const ALL_PACKAGES_NS = 'http://pear.php.net/dtd/rest.allpackages';
12621 const ALL_RELEASES_NS = 'http://pear.php.net/dtd/rest.allreleases';
12622 const PACKAGE_INFO_NS = 'http://pear.php.net/dtd/rest.package';
12627 protected function __construct(RemoteFilesystem $rfs)
12640 protected function requestContent($origin, $path)
12642 $url = rtrim($origin, '/') . '/' . ltrim($path, '/');
12643 $content = $this->rfs->getContents($origin, $url, false);
12645 throw new \UnexpectedValueException('The PEAR channel at ' . $url . ' did not respond.');
12659 protected function requestXml($origin, $path)
12662 $xml = simplexml_load_string($this->requestContent($origin, $path), "SimpleXMLElement", LIBXML_NOERROR);
12664 if (false == $xml) {
12665 $url = rtrim($origin, '/') . '/' . ltrim($path, '/');
12666 throw new \UnexpectedValueException(sprintf('The PEAR channel at ' . $origin . ' is broken. (Invalid XML at file `%s`)', $path));
12684 namespace Composer\Repository\Pear;
12686 use Composer\Downloader\TransportException;
12699 class ChannelRest10Reader extends BaseChannelReader
12701 private $dependencyReader;
12703 public function __construct($rfs)
12705 parent::__construct($rfs);
12707 $this->dependencyReader = new PackageDependencyParser();
12717 public function read($baseUrl)
12719 return $this->readPackages($baseUrl);
12729 private function readPackages($baseUrl)
12733 $xmlPath = '/p/packages.xml';
12734 $xml = $this->requestXml($baseUrl, $xmlPath);
12735 $xml->registerXPathNamespace('ns', self::ALL_PACKAGES_NS);
12736 foreach ($xml->xpath('ns:p') as $node) {
12737 $packageName = (string) $node;
12738 $packageInfo = $this->readPackage($baseUrl, $packageName);
12739 $result[] = $packageInfo;
12753 private function readPackage($baseUrl, $packageName)
12755 $xmlPath = '/p/' . strtolower($packageName) . '/info.xml';
12756 $xml = $this->requestXml($baseUrl, $xmlPath);
12757 $xml->registerXPathNamespace('ns', self::PACKAGE_INFO_NS);
12759 $channelName = (string) $xml->c;
12760 $packageName = (string) $xml->n;
12761 $license = (string) $xml->l;
12762 $shortDescription = (string) $xml->s;
12763 $description = (string) $xml->d;
12765 return new PackageInfo(
12771 $this->readPackageReleases($baseUrl, $packageName)
12784 private function readPackageReleases($baseUrl, $packageName)
12789 $xmlPath = '/r/' . strtolower($packageName) . '/allreleases.xml';
12790 $xml = $this->requestXml($baseUrl, $xmlPath);
12791 $xml->registerXPathNamespace('ns', self::ALL_RELEASES_NS);
12792 foreach ($xml->xpath('ns:r') as $node) {
12793 $releaseVersion = (string) $node->v;
12794 $releaseStability = (string) $node->s;
12797 $result[$releaseVersion] = new ReleaseInfo(
12799 $this->readPackageReleaseDependencies($baseUrl, $packageName, $releaseVersion)
12801 } catch (TransportException $exception) {
12802 if ($exception->getCode() != 404) {
12807 } catch (TransportException $exception) {
12808 if ($exception->getCode() != 404) {
12825 private function readPackageReleaseDependencies($baseUrl, $packageName, $version)
12827 $dependencyReader = new PackageDependencyParser();
12829 $depthPath = '/r/' . strtolower($packageName) . '/deps.' . $version . '.txt';
12830 $content = $this->requestContent($baseUrl, $depthPath);
12831 $dependencyArray = unserialize($content);
12832 $result = $dependencyReader->buildDependencyInfo($dependencyArray);
12849 namespace Composer\Repository;
12851 use Composer\Package\PackageInterface;
12860 interface RepositoryInterface extends \Countable
12862 const SEARCH_FULLTEXT = 0;
12863 const SEARCH_NAME = 1;
12872 public function hasPackage(PackageInterface $package);
12882 public function findPackage($name, $version);
12892 public function findPackages($name, $version = null);
12899 public function getPackages();
12908 public function search($query, $mode = 0);
12922 namespace Composer\Repository;
12924 use Composer\IO\IOInterface;
12925 use Composer\Json\JsonFile;
12926 use Composer\Package\Loader\ArrayLoader;
12931 class ArtifactRepository extends ArrayRepository
12938 public function __construct(array $repoConfig, IOInterface $io)
12940 if (!extension_loaded('zip')) {
12941 throw new \RuntimeException('The artifact repository requires PHP\'s zip extension');
12944 $this->loader = new ArrayLoader();
12945 $this->lookup = $repoConfig['url'];
12949 protected function initialize()
12951 parent::initialize();
12953 $this->scanDirectory($this->lookup);
12956 private function scanDirectory($path)
12960 $directory = new \RecursiveDirectoryIterator($path);
12961 $iterator = new \RecursiveIteratorIterator($directory);
12962 $regex = new \RegexIterator($iterator, '/^.+\.(zip|phar)$/i');
12963 foreach ($regex as $file) {
12965 if (!$file->isFile()) {
12969 $package = $this->getComposerInformation($file);
12971 if ($io->isVerbose()) {
12972 $io->write("File <comment>{$file->getBasename()}</comment> doesn't seem to hold a package");
12977 if ($io->isVerbose()) {
12978 $template = 'Found package <info>%s</info> (<comment>%s</comment>) in file <info>%s</info>';
12979 $io->write(sprintf($template, $package->getName(), $package->getPrettyVersion(), $file->getBasename()));
12982 $this->addPackage($package);
12993 private function locateFile(\ZipArchive $zip, $filename)
12995 $indexOfShortestMatch = false;
12996 $lengthOfShortestMatch = -1;
12998 for ($i = 0; $i < $zip->numFiles; $i++) {
12999 $stat = $zip->statIndex($i);
13000 if (strcmp(basename($stat['name']), $filename) === 0) {
13001 $directoryName = dirname($stat['name']);
13002 if ($directoryName == '.') {
13008 if (strpos($directoryName, '\\') !== false ||
13009 strpos($directoryName, '/') !== false) {
13014 $length = strlen($stat['name']);
13015 if ($indexOfShortestMatch == false || $length < $lengthOfShortestMatch) {
13017 $contents = $zip->getFromIndex($i);
13018 if ($contents !== false) {
13019 $indexOfShortestMatch = $i;
13020 $lengthOfShortestMatch = $length;
13026 return $indexOfShortestMatch;
13029 private function getComposerInformation(\SplFileInfo $file)
13031 $zip = new \ZipArchive();
13032 $zip->open($file->getPathname());
13034 if (0 == $zip->numFiles) {
13038 $foundFileIndex = $this->locateFile($zip, 'composer.json');
13039 if (false === $foundFileIndex) {
13043 $configurationFileName = $zip->getNameIndex($foundFileIndex);
13045 $composerFile = "zip://{$file->getPathname()}#$configurationFileName";
13046 $json = file_get_contents($composerFile);
13048 $package = JsonFile::parseJson($json, $composerFile);
13049 $package['dist'] = array(
13051 'url' => $file->getPathname(),
13052 'shasum' => sha1_file($file->getRealPath())
13055 $package = $this->loader->load($package);
13072 namespace Composer\Repository;
13074 use Composer\Package\Loader\ArrayLoader;
13075 use Composer\Package\Loader\ValidatingArrayLoader;
13082 class PackageRepository extends ArrayRepository
13091 public function __construct(array $config)
13093 $this->config = $config['package'];
13096 if (!is_numeric(key($this->config))) {
13097 $this->config = array($this->config);
13104 protected function initialize()
13106 parent::initialize();
13108 $loader = new ValidatingArrayLoader(new ArrayLoader, false);
13109 foreach ($this->config as $package) {
13111 $package = $loader->load($package);
13112 } catch (\Exception $e) {
13113 throw new InvalidRepositoryException('A repository of type "package" contains an invalid package definition: '.$e->getMessage()."\n\nInvalid package definition:\n".json_encode($package));
13116 $this->addPackage($package);
13132 namespace Composer\Package;
13139 class CompletePackage extends Package implements CompletePackageInterface
13141 protected $repositories;
13142 protected $license = array();
13143 protected $keywords;
13144 protected $authors;
13145 protected $description;
13146 protected $homepage;
13147 protected $scripts = array();
13148 protected $support = array();
13149 protected $abandoned = false;
13154 public function setScripts(array $scripts)
13156 $this->scripts = $scripts;
13162 public function getScripts()
13164 return $this->scripts;
13172 public function setRepositories($repositories)
13174 $this->repositories = $repositories;
13180 public function getRepositories()
13182 return $this->repositories;
13190 public function setLicense(array $license)
13192 $this->license = $license;
13198 public function getLicense()
13200 return $this->license;
13208 public function setKeywords(array $keywords)
13210 $this->keywords = $keywords;
13216 public function getKeywords()
13218 return $this->keywords;
13226 public function setAuthors(array $authors)
13228 $this->authors = $authors;
13234 public function getAuthors()
13236 return $this->authors;
13244 public function setDescription($description)
13246 $this->description = $description;
13252 public function getDescription()
13254 return $this->description;
13262 public function setHomepage($homepage)
13264 $this->homepage = $homepage;
13270 public function getHomepage()
13272 return $this->homepage;
13280 public function setSupport(array $support)
13282 $this->support = $support;
13288 public function getSupport()
13290 return $this->support;
13296 public function isAbandoned()
13298 return (boolean) $this->abandoned;
13304 public function setAbandoned($abandoned)
13306 $this->abandoned = $abandoned;
13314 public function getReplacementPackage()
13316 return is_string($this->abandoned) ? $this->abandoned : null;
13331 namespace Composer\Package\Dumper;
13333 use Composer\Package\BasePackage;
13334 use Composer\Package\PackageInterface;
13335 use Composer\Package\CompletePackageInterface;
13336 use Composer\Package\RootPackageInterface;
13344 public function dump(PackageInterface $package)
13347 'binaries' => 'bin',
13350 'installationSource' => 'installation-source',
13352 'devAutoload' => 'autoload-dev',
13353 'notificationUrl' => 'notification-url',
13354 'includePaths' => 'include-path',
13358 $data['name'] = $package->getPrettyName();
13359 $data['version'] = $package->getPrettyVersion();
13360 $data['version_normalized'] = $package->getVersion();
13362 if ($package->getTargetDir()) {
13363 $data['target-dir'] = $package->getTargetDir();
13366 if ($package->getSourceType()) {
13367 $data['source']['type'] = $package->getSourceType();
13368 $data['source']['url'] = $package->getSourceUrl();
13369 $data['source']['reference'] = $package->getSourceReference();
13370 if ($mirrors = $package->getSourceMirrors()) {
13371 $data['source']['mirrors'] = $mirrors;
13375 if ($package->getDistType()) {
13376 $data['dist']['type'] = $package->getDistType();
13377 $data['dist']['url'] = $package->getDistUrl();
13378 $data['dist']['reference'] = $package->getDistReference();
13379 $data['dist']['shasum'] = $package->getDistSha1Checksum();
13380 if ($mirrors = $package->getDistMirrors()) {
13381 $data['dist']['mirrors'] = $mirrors;
13385 if ($package->getArchiveExcludes()) {
13386 $data['archive']['exclude'] = $package->getArchiveExcludes();
13389 foreach (BasePackage::$supportedLinkTypes as $type => $opts) {
13390 if ($links = $package->{'get'.ucfirst($opts['method'])}()) {
13391 foreach ($links as $link) {
13392 $data[$type][$link->getTarget()] = $link->getPrettyConstraint();
13394 ksort($data[$type]);
13398 if ($packages = $package->getSuggests()) {
13400 $data['suggest'] = $packages;
13403 if ($package->getReleaseDate()) {
13404 $data['time'] = $package->getReleaseDate()->format('Y-m-d H:i:s');
13407 $data = $this->dumpValues($package, $keys, $data);
13409 if ($package instanceof CompletePackageInterface) {
13421 $data = $this->dumpValues($package, $keys, $data);
13423 if (isset($data['keywords']) && is_array($data['keywords'])) {
13424 sort($data['keywords']);
13427 if ($package->isAbandoned()) {
13428 $data['abandoned'] = $package->getReplacementPackage() ?: true;
13432 if ($package instanceof RootPackageInterface) {
13433 $minimumStability = $package->getMinimumStability();
13434 if ($minimumStability) {
13435 $data['minimum-stability'] = $minimumStability;
13439 if (count($package->getTransportOptions()) > 0) {
13440 $data['transport-options'] = $package->getTransportOptions();
13446 private function dumpValues(PackageInterface $package, array $keys, array $data)
13448 foreach ($keys as $method => $key) {
13449 if (is_numeric($method)) {
13453 $getter = 'get'.ucfirst($method);
13454 $value = $package->$getter();
13456 if (null !== $value && !(is_array($value) && 0 === count($value))) {
13457 $data[$key] = $value;
13476 namespace Composer\Package\Loader;
13481 class InvalidPackageException extends \Exception
13487 public function __construct(array $errors, array $warnings, array $data)
13489 $this->errors = $errors;
13490 $this->warnings = $warnings;
13491 $this->data = $data;
13492 parent::__construct("Invalid package information: \n".implode("\n", array_merge($errors, $warnings)));
13495 public function getData()
13497 return $this->data;
13500 public function getErrors()
13502 return $this->errors;
13505 public function getWarnings()
13507 return $this->warnings;
13522 namespace Composer\Package\Loader;
13524 use Composer\Json\JsonFile;
13533 public function __construct(LoaderInterface $loader)
13535 $this->loader = $loader;
13542 public function load($json)
13544 if ($json instanceof JsonFile) {
13545 $config = $json->read();
13546 } elseif (file_exists($json)) {
13547 $config = JsonFile::parseJson(file_get_contents($json), $json);
13548 } elseif (is_string($json)) {
13549 $config = JsonFile::parseJson($json);
13552 return $this->loader->load($config);
13567 namespace Composer\Package\Loader;
13574 interface LoaderInterface
13583 public function load(array $package, $class = 'Composer\Package\CompletePackage');
13597 namespace Composer\Package\Loader;
13599 use Composer\Package;
13600 use Composer\Package\AliasPackage;
13601 use Composer\Package\RootAliasPackage;
13602 use Composer\Package\RootPackageInterface;
13603 use Composer\Package\Version\VersionParser;
13609 class ArrayLoader implements LoaderInterface
13611 protected $versionParser;
13612 protected $loadOptions;
13614 public function __construct(VersionParser $parser = null, $loadOptions = false)
13617 $parser = new VersionParser;
13619 $this->versionParser = $parser;
13620 $this->loadOptions = $loadOptions;
13623 public function load(array $config, $class = 'Composer\Package\CompletePackage')
13625 if (!isset($config['name'])) {
13626 throw new \UnexpectedValueException('Unknown package has no name defined ('.json_encode($config).').');
13628 if (!isset($config['version'])) {
13629 throw new \UnexpectedValueException('Package '.$config['name'].' has no version defined.');
13633 if (isset($config['version_normalized'])) {
13634 $version = $config['version_normalized'];
13636 $version = $this->versionParser->normalize($config['version']);
13638 $package = new $class($config['name'], $version, $config['version']);
13639 $package->setType(isset($config['type']) ? strtolower($config['type']) : 'library');
13641 if (isset($config['target-dir'])) {
13642 $package->setTargetDir($config['target-dir']);
13645 if (isset($config['extra']) && is_array($config['extra'])) {
13646 $package->setExtra($config['extra']);
13649 if (isset($config['bin'])) {
13650 if (!is_array($config['bin'])) {
13651 throw new \UnexpectedValueException('Package '.$config['name'].'\'s bin key should be an array, '.gettype($config['bin']).' given.');
13653 foreach ($config['bin'] as $key => $bin) {
13654 $config['bin'][$key] = ltrim($bin, '/');
13656 $package->setBinaries($config['bin']);
13659 if (isset($config['installation-source'])) {
13660 $package->setInstallationSource($config['installation-source']);
13663 if (isset($config['source'])) {
13664 if (!isset($config['source']['type']) || !isset($config['source']['url']) || !isset($config['source']['reference'])) {
13665 throw new \UnexpectedValueException(sprintf(
13666 "Package %s's source key should be specified as {\"type\": ..., \"url\": ..., \"reference\": ...},\n%s given.",
13668 json_encode($config['source'])
13671 $package->setSourceType($config['source']['type']);
13672 $package->setSourceUrl($config['source']['url']);
13673 $package->setSourceReference($config['source']['reference']);
13674 if (isset($config['source']['mirrors'])) {
13675 $package->setSourceMirrors($config['source']['mirrors']);
13679 if (isset($config['dist'])) {
13680 if (!isset($config['dist']['type'])
13681 || !isset($config['dist']['url'])) {
13682 throw new \UnexpectedValueException(sprintf(
13683 "Package %s's dist key should be specified as ".
13684 "{\"type\": ..., \"url\": ..., \"reference\": ..., \"shasum\": ...},\n%s given.",
13686 json_encode($config['dist'])
13689 $package->setDistType($config['dist']['type']);
13690 $package->setDistUrl($config['dist']['url']);
13691 $package->setDistReference(isset($config['dist']['reference']) ? $config['dist']['reference'] : null);
13692 $package->setDistSha1Checksum(isset($config['dist']['shasum']) ? $config['dist']['shasum'] : null);
13693 if (isset($config['dist']['mirrors'])) {
13694 $package->setDistMirrors($config['dist']['mirrors']);
13698 foreach (Package\BasePackage::$supportedLinkTypes as $type => $opts) {
13699 if (isset($config[$type])) {
13700 $method = 'set'.ucfirst($opts['method']);
13701 $package->{$method}(
13702 $this->versionParser->parseLinks(
13703 $package->getName(),
13704 $package->getPrettyVersion(),
13705 $opts['description'],
13712 if (isset($config['suggest']) && is_array($config['suggest'])) {
13713 foreach ($config['suggest'] as $target => $reason) {
13714 if ('self.version' === trim($reason)) {
13715 $config['suggest'][$target] = $package->getPrettyVersion();
13718 $package->setSuggests($config['suggest']);
13721 if (isset($config['autoload'])) {
13722 $package->setAutoload($config['autoload']);
13725 if (isset($config['autoload-dev'])) {
13726 $package->setDevAutoload($config['autoload-dev']);
13729 if (isset($config['include-path'])) {
13730 $package->setIncludePaths($config['include-path']);
13733 if (!empty($config['time'])) {
13734 $time = ctype_digit($config['time']) ? '@'.$config['time'] : $config['time'];
13737 $date = new \DateTime($time, new \DateTimeZone('UTC'));
13738 $package->setReleaseDate($date);
13739 } catch (\Exception $e) {
13743 if (!empty($config['notification-url'])) {
13744 $package->setNotificationUrl($config['notification-url']);
13747 if (!empty($config['archive']['exclude'])) {
13748 $package->setArchiveExcludes($config['archive']['exclude']);
13751 if ($package instanceof Package\CompletePackageInterface) {
13752 if (isset($config['scripts']) && is_array($config['scripts'])) {
13753 foreach ($config['scripts'] as $event => $listeners) {
13754 $config['scripts'][$event] = (array) $listeners;
13756 $package->setScripts($config['scripts']);
13759 if (!empty($config['description']) && is_string($config['description'])) {
13760 $package->setDescription($config['description']);
13763 if (!empty($config['homepage']) && is_string($config['homepage'])) {
13764 $package->setHomepage($config['homepage']);
13767 if (!empty($config['keywords']) && is_array($config['keywords'])) {
13768 $package->setKeywords($config['keywords']);
13771 if (!empty($config['license'])) {
13772 $package->setLicense(is_array($config['license']) ? $config['license'] : array($config['license']));
13775 if (!empty($config['authors']) && is_array($config['authors'])) {
13776 $package->setAuthors($config['authors']);
13779 if (isset($config['support'])) {
13780 $package->setSupport($config['support']);
13783 if (isset($config['abandoned'])) {
13784 $package->setAbandoned($config['abandoned']);
13788 if ($aliasNormalized = $this->getBranchAlias($config)) {
13789 if ($package instanceof RootPackageInterface) {
13790 $package = new RootAliasPackage($package, $aliasNormalized, preg_replace('{(\.9{7})+}', '.x', $aliasNormalized));
13792 $package = new AliasPackage($package, $aliasNormalized, preg_replace('{(\.9{7})+}', '.x', $aliasNormalized));
13796 if ($this->loadOptions && isset($config['transport-options'])) {
13797 $package->setTransportOptions($config['transport-options']);
13809 public function getBranchAlias(array $config)
13811 if ('dev-' !== substr($config['version'], 0, 4)
13812 || !isset($config['extra']['branch-alias'])
13813 || !is_array($config['extra']['branch-alias'])
13818 foreach ($config['extra']['branch-alias'] as $sourceBranch => $targetBranch) {
13820 if ('-dev' !== substr($targetBranch, -4)) {
13825 $validatedTargetBranch = $this->versionParser->normalizeBranch(substr($targetBranch, 0, -4));
13826 if ('-dev' !== substr($validatedTargetBranch, -4)) {
13831 if (strtolower($config['version']) !== strtolower($sourceBranch)) {
13835 return $validatedTargetBranch;
13851 namespace Composer\Package\Loader;
13853 use Composer\Package;
13854 use Composer\Package\BasePackage;
13855 use Composer\Package\LinkConstraint\VersionConstraint;
13856 use Composer\Package\Version\VersionParser;
13857 use Composer\Repository\PlatformRepository;
13862 class ValidatingArrayLoader implements LoaderInterface
13864 const CHECK_ALL = 1;
13865 const CHECK_UNBOUND_CONSTRAINTS = 1;
13868 private $versionParser;
13872 private $strictName;
13875 public function __construct(LoaderInterface $loader, $strictName = true, VersionParser $parser = null, $flags = 0)
13877 $this->loader = $loader;
13878 $this->versionParser = $parser ?: new VersionParser();
13879 $this->strictName = $strictName;
13880 $this->flags = $flags;
13883 public function load(array $config, $class = 'Composer\Package\CompletePackage')
13885 $this->errors = array();
13886 $this->warnings = array();
13887 $this->config = $config;
13889 if ($this->strictName) {
13890 $this->validateRegex('name', '[A-Za-z0-9][A-Za-z0-9_.-]*/[A-Za-z0-9][A-Za-z0-9_.-]*', true);
13892 $this->validateString('name', true);
13895 if (!empty($this->config['version'])) {
13897 $this->versionParser->normalize($this->config['version']);
13898 } catch (\Exception $e) {
13899 $this->errors[] = 'version : invalid value ('.$this->config['version'].'): '.$e->getMessage();
13900 unset($this->config['version']);
13904 $this->validateRegex('type', '[A-Za-z0-9-]+');
13905 $this->validateString('target-dir');
13906 $this->validateArray('extra');
13907 $this->validateFlatArray('bin');
13908 $this->validateArray('scripts');
13909 $this->validateString('description');
13910 $this->validateUrl('homepage');
13911 $this->validateFlatArray('keywords', '[A-Za-z0-9 ._-]+');
13913 if (isset($this->config['license'])) {
13914 if (is_string($this->config['license'])) {
13915 $this->validateRegex('license', '[A-Za-z0-9+. ()-]+');
13917 $this->validateFlatArray('license', '[A-Za-z0-9+. ()-]+');
13921 $this->validateString('time');
13922 if (!empty($this->config['time'])) {
13924 $date = new \DateTime($this->config['time'], new \DateTimeZone('UTC'));
13925 } catch (\Exception $e) {
13926 $this->errors[] = 'time : invalid value ('.$this->config['time'].'): '.$e->getMessage();
13927 unset($this->config['time']);
13931 if ($this->validateArray('authors') && !empty($this->config['authors'])) {
13932 foreach ($this->config['authors'] as $key => $author) {
13933 if (!is_array($author)) {
13934 $this->errors[] = 'authors.'.$key.' : should be an array, '.gettype($author).' given';
13935 unset($this->config['authors'][$key]);
13938 foreach (array('homepage', 'email', 'name', 'role') as $authorData) {
13939 if (isset($author[$authorData]) && !is_string($author[$authorData])) {
13940 $this->errors[] = 'authors.'.$key.'.'.$authorData.' : invalid value, must be a string';
13941 unset($this->config['authors'][$key][$authorData]);
13944 if (isset($author['homepage']) && !$this->filterUrl($author['homepage'])) {
13945 $this->warnings[] = 'authors.'.$key.'.homepage : invalid value ('.$author['homepage'].'), must be an http/https URL';
13946 unset($this->config['authors'][$key]['homepage']);
13948 if (isset($author['email']) && !filter_var($author['email'], FILTER_VALIDATE_EMAIL)) {
13949 $this->warnings[] = 'authors.'.$key.'.email : invalid value ('.$author['email'].'), must be a valid email address';
13950 unset($this->config['authors'][$key]['email']);
13952 if (empty($this->config['authors'][$key])) {
13953 unset($this->config['authors'][$key]);
13956 if (empty($this->config['authors'])) {
13957 unset($this->config['authors']);
13961 if ($this->validateArray('support') && !empty($this->config['support'])) {
13962 foreach (array('issues', 'forum', 'wiki', 'source', 'email', 'irc') as $key) {
13963 if (isset($this->config['support'][$key]) && !is_string($this->config['support'][$key])) {
13964 $this->errors[] = 'support.'.$key.' : invalid value, must be a string';
13965 unset($this->config['support'][$key]);
13969 if (isset($this->config['support']['email']) && !filter_var($this->config['support']['email'], FILTER_VALIDATE_EMAIL)) {
13970 $this->warnings[] = 'support.email : invalid value ('.$this->config['support']['email'].'), must be a valid email address';
13971 unset($this->config['support']['email']);
13974 if (isset($this->config['support']['irc']) && !$this->filterUrl($this->config['support']['irc'], array('irc'))) {
13975 $this->warnings[] = 'support.irc : invalid value ('.$this->config['support']['irc'].'), must be a irc://<server>/<channel> URL';
13976 unset($this->config['support']['irc']);
13979 foreach (array('issues', 'forum', 'wiki', 'source') as $key) {
13980 if (isset($this->config['support'][$key]) && !$this->filterUrl($this->config['support'][$key])) {
13981 $this->warnings[] = 'support.'.$key.' : invalid value ('.$this->config['support'][$key].'), must be an http/https URL';
13982 unset($this->config['support'][$key]);
13985 if (empty($this->config['support'])) {
13986 unset($this->config['support']);
13990 $unboundConstraint = new VersionConstraint('=', $this->versionParser->normalize('dev-master'));
13992 foreach (array_keys(BasePackage::$supportedLinkTypes) as $linkType) {
13993 if ($this->validateArray($linkType) && isset($this->config[$linkType])) {
13994 foreach ($this->config[$linkType] as $package => $constraint) {
13995 if (!preg_match('{^[A-Za-z0-9_./-]+$}', $package)) {
13996 $this->warnings[] = $linkType.'.'.$package.' : invalid key, package names must be strings containing only [A-Za-z0-9_./-]';
13998 if (!is_string($constraint)) {
13999 $this->errors[] = $linkType.'.'.$package.' : invalid value, must be a string containing a version constraint';
14000 unset($this->config[$linkType][$package]);
14001 } elseif ('self.version' !== $constraint) {
14003 $linkConstraint = $this->versionParser->parseConstraints($constraint);
14004 } catch (\Exception $e) {
14005 $this->errors[] = $linkType.'.'.$package.' : invalid version constraint ('.$e->getMessage().')';
14006 unset($this->config[$linkType][$package]);
14012 ($this->flags & self::CHECK_UNBOUND_CONSTRAINTS)
14013 && 'require' === $linkType
14014 && $linkConstraint->matches($unboundConstraint)
14015 && !preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $package)
14017 $this->warnings[] = $linkType.'.'.$package.' : unbound version constraints ('.$constraint.') should be avoided';
14024 if ($this->validateArray('suggest') && !empty($this->config['suggest'])) {
14025 foreach ($this->config['suggest'] as $package => $description) {
14026 if (!is_string($description)) {
14027 $this->errors[] = 'suggest.'.$package.' : invalid value, must be a string describing why the package is suggested';
14028 unset($this->config['suggest'][$package]);
14033 if ($this->validateString('minimum-stability') && !empty($this->config['minimum-stability'])) {
14034 if (!isset(BasePackage::$stabilities[$this->config['minimum-stability']])) {
14035 $this->errors[] = 'minimum-stability : invalid value ('.$this->config['minimum-stability'].'), must be one of '.implode(', ', array_keys(BasePackage::$stabilities));
14036 unset($this->config['minimum-stability']);
14040 if ($this->validateArray('autoload') && !empty($this->config['autoload'])) {
14041 $types = array('psr-0', 'psr-4', 'classmap', 'files');
14042 foreach ($this->config['autoload'] as $type => $typeConfig) {
14043 if (!in_array($type, $types)) {
14044 $this->errors[] = 'autoload : invalid value ('.$type.'), must be one of '.implode(', ', $types);
14045 unset($this->config['autoload'][$type]);
14047 if ($type === 'psr-4') {
14048 foreach ($typeConfig as $namespace => $dirs) {
14049 if ($namespace !== '' && '\\' !== substr($namespace, -1)) {
14050 $this->errors[] = 'autoload.psr-4 : invalid value ('.$namespace.'), namespaces must end with a namespace separator, should be '.$namespace.'\\';
14057 if (!empty($this->config['autoload']['psr-4']) && !empty($this->config['target-dir'])) {
14058 $this->errors[] = 'target-dir : this can not be used together with the autoload.psr-4 setting, remove target-dir to upgrade to psr-4';
14061 unset($this->config['autoload']['psr-4']);
14070 $this->validateFlatArray('include-path');
14071 $this->validateArray('transport-options');
14074 if (isset($this->config['extra']['branch-alias'])) {
14075 if (!is_array($this->config['extra']['branch-alias'])) {
14076 $this->errors[] = 'extra.branch-alias : must be an array of versions => aliases';
14078 foreach ($this->config['extra']['branch-alias'] as $sourceBranch => $targetBranch) {
14080 if ('-dev' !== substr($targetBranch, -4)) {
14081 $this->warnings[] = 'extra.branch-alias.'.$sourceBranch.' : the target branch ('.$targetBranch.') must end in -dev';
14082 unset($this->config['extra']['branch-alias'][$sourceBranch]);
14088 $validatedTargetBranch = $this->versionParser->normalizeBranch(substr($targetBranch, 0, -4));
14089 if ('-dev' !== substr($validatedTargetBranch, -4)) {
14090 $this->warnings[] = 'extra.branch-alias.'.$sourceBranch.' : the target branch ('.$targetBranch.') must be a parseable number like 2.0-dev';
14091 unset($this->config['extra']['branch-alias'][$sourceBranch]);
14097 if ($this->errors) {
14098 throw new InvalidPackageException($this->errors, $this->warnings, $config);
14101 $package = $this->loader->load($this->config, $class);
14102 $this->config = null;
14107 public function getWarnings()
14109 return $this->warnings;
14112 public function getErrors()
14114 return $this->errors;
14117 private function validateRegex($property, $regex, $mandatory = false)
14119 if (!$this->validateString($property, $mandatory)) {
14123 if (!preg_match('{^'.$regex.'$}u', $this->config[$property])) {
14124 $message = $property.' : invalid value ('.$this->config[$property].'), must match '.$regex;
14126 $this->errors[] = $message;
14128 $this->warnings[] = $message;
14130 unset($this->config[$property]);
14138 private function validateString($property, $mandatory = false)
14140 if (isset($this->config[$property]) && !is_string($this->config[$property])) {
14141 $this->errors[] = $property.' : should be a string, '.gettype($this->config[$property]).' given';
14142 unset($this->config[$property]);
14147 if (!isset($this->config[$property]) || trim($this->config[$property]) === '') {
14149 $this->errors[] = $property.' : must be present';
14151 unset($this->config[$property]);
14159 private function validateArray($property, $mandatory = false)
14161 if (isset($this->config[$property]) && !is_array($this->config[$property])) {
14162 $this->errors[] = $property.' : should be an array, '.gettype($this->config[$property]).' given';
14163 unset($this->config[$property]);
14168 if (!isset($this->config[$property]) || !count($this->config[$property])) {
14170 $this->errors[] = $property.' : must be present and contain at least one element';
14172 unset($this->config[$property]);
14180 private function validateFlatArray($property, $regex = null, $mandatory = false)
14182 if (!$this->validateArray($property, $mandatory)) {
14187 foreach ($this->config[$property] as $key => $value) {
14188 if (!is_string($value) && !is_numeric($value)) {
14189 $this->errors[] = $property.'.'.$key.' : must be a string or int, '.gettype($value).' given';
14190 unset($this->config[$property][$key]);
14196 if ($regex && !preg_match('{^'.$regex.'$}u', $value)) {
14197 $this->warnings[] = $property.'.'.$key.' : invalid value ('.$value.'), must match '.$regex;
14198 unset($this->config[$property][$key]);
14206 private function validateUrl($property, $mandatory = false)
14208 if (!$this->validateString($property, $mandatory)) {
14212 if (!$this->filterUrl($this->config[$property])) {
14213 $this->warnings[] = $property.' : invalid value ('.$this->config[$property].'), must be an http/https URL';
14214 unset($this->config[$property]);
14222 private function filterUrl($value, array $schemes = array('http', 'https'))
14224 if ($value === '') {
14228 $bits = parse_url($value);
14229 if (empty($bits['scheme']) || empty($bits['host'])) {
14233 if (!in_array($bits['scheme'], $schemes, true)) {
14252 namespace Composer\Package\Loader;
14254 use Composer\Package\BasePackage;
14255 use Composer\Package\AliasPackage;
14256 use Composer\Config;
14257 use Composer\Factory;
14258 use Composer\Package\Version\VersionParser;
14259 use Composer\Repository\RepositoryManager;
14260 use Composer\Repository\Vcs\HgDriver;
14261 use Composer\IO\NullIO;
14262 use Composer\Util\ProcessExecutor;
14263 use Composer\Util\Git as GitUtil;
14264 use Composer\Util\Svn as SvnUtil;
14273 class RootPackageLoader extends ArrayLoader
14279 public function __construct(RepositoryManager $manager, Config $config, VersionParser $parser = null, ProcessExecutor $process = null)
14281 $this->manager = $manager;
14282 $this->config = $config;
14283 $this->process = $process ?: new ProcessExecutor();
14284 parent::__construct($parser);
14287 public function load(array $config, $class = 'Composer\Package\RootPackage')
14289 if (!isset($config['name'])) {
14290 $config['name'] = '__root__';
14292 if (!isset($config['version'])) {
14294 if (getenv('COMPOSER_ROOT_VERSION')) {
14295 $version = getenv('COMPOSER_ROOT_VERSION');
14297 $version = $this->guessVersion($config);
14301 $version = '1.0.0';
14304 $config['version'] = $version;
14307 $realPackage = $package = parent::load($config, $class);
14309 if ($realPackage instanceof AliasPackage) {
14310 $realPackage = $package->getAliasOf();
14313 if (isset($config['minimum-stability'])) {
14314 $realPackage->setMinimumStability(VersionParser::normalizeStability($config['minimum-stability']));
14317 $aliases = array();
14318 $stabilityFlags = array();
14319 $references = array();
14320 foreach (array('require', 'require-dev') as $linkType) {
14321 if (isset($config[$linkType])) {
14322 $linkInfo = BasePackage::$supportedLinkTypes[$linkType];
14323 $method = 'get'.ucfirst($linkInfo['method']);
14325 foreach ($realPackage->$method() as $link) {
14326 $links[$link->getTarget()] = $link->getConstraint()->getPrettyString();
14328 $aliases = $this->extractAliases($links, $aliases);
14329 $stabilityFlags = $this->extractStabilityFlags($links, $stabilityFlags, $realPackage->getMinimumStability());
14330 $references = $this->extractReferences($links, $references);
14334 $realPackage->setAliases($aliases);
14335 $realPackage->setStabilityFlags($stabilityFlags);
14336 $realPackage->setReferences($references);
14338 if (isset($config['prefer-stable'])) {
14339 $realPackage->setPreferStable((bool) $config['prefer-stable']);
14342 $repos = Factory::createDefaultRepositories(null, $this->config, $this->manager);
14343 foreach ($repos as $repo) {
14344 $this->manager->addRepository($repo);
14346 $realPackage->setRepositories($this->config->getRepositories());
14351 private function extractAliases(array $requires, array $aliases)
14353 foreach ($requires as $reqName => $reqVersion) {
14354 if (preg_match('{^([^,\s#]+)(?:#[^ ]+)? +as +([^,\s]+)$}', $reqVersion, $match)) {
14355 $aliases[] = array(
14356 'package' => strtolower($reqName),
14357 'version' => $this->versionParser->normalize($match[1], $reqVersion),
14358 'alias' => $match[2],
14359 'alias_normalized' => $this->versionParser->normalize($match[2], $reqVersion),
14367 private function extractStabilityFlags(array $requires, array $stabilityFlags, $minimumStability)
14369 $stabilities = BasePackage::$stabilities;
14370 $minimumStability = $stabilities[$minimumStability];
14371 foreach ($requires as $reqName => $reqVersion) {
14373 if (preg_match('{^[^@]*?@('.implode('|', array_keys($stabilities)).')$}i', $reqVersion, $match)) {
14374 $name = strtolower($reqName);
14375 $stability = $stabilities[VersionParser::normalizeStability($match[1])];
14377 if (isset($stabilityFlags[$name]) && $stabilityFlags[$name] > $stability) {
14380 $stabilityFlags[$name] = $stability;
14387 $reqVersion = preg_replace('{^([^,\s@]+) as .+$}', '$1', $reqVersion);
14388 if (preg_match('{^[^,\s@]+$}', $reqVersion) && 'stable' !== ($stabilityName = VersionParser::parseStability($reqVersion))) {
14389 $name = strtolower($reqName);
14390 $stability = $stabilities[$stabilityName];
14391 if ((isset($stabilityFlags[$name]) && $stabilityFlags[$name] > $stability) || ($minimumStability > $stability)) {
14394 $stabilityFlags[$name] = $stability;
14398 return $stabilityFlags;
14401 private function extractReferences(array $requires, array $references)
14403 foreach ($requires as $reqName => $reqVersion) {
14404 $reqVersion = preg_replace('{^([^,\s@]+) as .+$}', '$1', $reqVersion);
14405 if (preg_match('{^[^,\s@]+?#([a-f0-9]+)$}', $reqVersion, $match) && 'dev' === ($stabilityName = VersionParser::parseStability($reqVersion))) {
14406 $name = strtolower($reqName);
14407 $references[$name] = $match[1];
14411 return $references;
14414 private function guessVersion(array $config)
14416 if (function_exists('proc_open')) {
14417 $version = $this->guessGitVersion($config);
14418 if (null !== $version) {
14422 $version = $this->guessHgVersion($config);
14423 if (null !== $version) {
14427 return $this->guessSvnVersion($config);
14431 private function guessGitVersion(array $config)
14433 GitUtil::cleanEnv();
14436 if (0 === $this->process->execute('git describe --exact-match --tags', $output)) {
14438 return $this->versionParser->normalize(trim($output));
14439 } catch (\Exception $e) {
14444 if (0 === $this->process->execute('git branch --no-color --no-abbrev -v', $output)) {
14445 $branches = array();
14446 $isFeatureBranch = false;
14450 foreach ($this->process->splitLines($output) as $branch) {
14451 if ($branch && preg_match('{^(?:\* ) *(\(no branch\)|\(detached from \S+\)|\S+) *([a-f0-9]+) .*$}', $branch, $match)) {
14452 if ($match[1] === '(no branch)' || substr($match[1], 0, 10) === '(detached ') {
14453 $version = 'dev-'.$match[2];
14454 $isFeatureBranch = true;
14456 $version = $this->versionParser->normalizeBranch($match[1]);
14457 $isFeatureBranch = 0 === strpos($version, 'dev-');
14458 if ('9999999-dev' === $version) {
14459 $version = 'dev-'.$match[1];
14464 if ($branch && !preg_match('{^ *[^/]+/HEAD }', $branch)) {
14465 if (preg_match('{^(?:\* )? *(\S+) *([a-f0-9]+) .*$}', $branch, $match)) {
14466 $branches[] = $match[1];
14471 if (!$isFeatureBranch) {
14476 $version = $this->guessFeatureVersion($config, $version, $branches, 'git rev-list %candidate%..%branch%');
14482 private function guessHgVersion(array $config)
14485 if (0 === $this->process->execute('hg branch', $output)) {
14486 $branch = trim($output);
14487 $version = $this->versionParser->normalizeBranch($branch);
14488 $isFeatureBranch = 0 === strpos($version, 'dev-');
14490 if ('9999999-dev' === $version) {
14491 $version = 'dev-'.$branch;
14494 if (!$isFeatureBranch) {
14499 $config = array('url' => getcwd());
14500 $driver = new HgDriver($config, new NullIO(), $this->config, $this->process);
14501 $branches = array_keys($driver->getBranches());
14504 $version = $this->guessFeatureVersion($config, $version, $branches, 'hg log -r "not ancestors(\'%candidate%\') and ancestors(\'%branch%\')" --template "{node}\\n"');
14510 private function guessFeatureVersion(array $config, $version, array $branches, $scmCmdline)
14514 if ((isset($config['extra']['branch-alias']) && !isset($config['extra']['branch-alias'][$version]))
14515 || strpos(json_encode($config), '"self.version"')
14517 $branch = preg_replace('{^dev-}', '', $version);
14518 $length = PHP_INT_MAX;
14519 foreach ($branches as $candidate) {
14521 if ($candidate === $branch || !preg_match('{^(master|trunk|default|develop|\d+\..+)$}', $candidate, $match)) {
14525 $cmdLine = str_replace(array('%candidate%', '%branch%'), array($candidate, $branch), $scmCmdline);
14526 if (0 !== $this->process->execute($cmdLine, $output)) {
14530 if (strlen($output) < $length) {
14531 $length = strlen($output);
14532 $version = $this->versionParser->normalizeBranch($candidate);
14533 if ('9999999-dev' === $version) {
14534 $version = 'dev-'.$match[1];
14543 private function guessSvnVersion(array $config)
14545 SvnUtil::cleanEnv();
14548 if (0 === $this->process->execute('svn info --xml', $output)) {
14549 $trunkPath = isset($config['trunk-path']) ? preg_quote($config['trunk-path'], '#') : 'trunk';
14550 $branchesPath = isset($config['branches-path']) ? preg_quote($config['branches-path'], '#') : 'branches';
14551 $tagsPath = isset($config['tags-path']) ? preg_quote($config['tags-path'], '#') : 'tags';
14553 $urlPattern = '#<url>.*/('.$trunkPath.'|('.$branchesPath.'|'. $tagsPath .')/(.*))</url>#';
14555 if (preg_match($urlPattern, $output, $matches)) {
14556 if (isset($matches[2]) && ($branchesPath === $matches[2] || $tagsPath === $matches[2])) {
14558 $version = $this->versionParser->normalizeBranch($matches[3]);
14559 if ('9999999-dev' === $version) {
14560 $version = 'dev-'.$matches[3];
14566 return $this->versionParser->normalize(trim($matches[1]));
14583 namespace Composer\Package;
14585 use Composer\Json\JsonFile;
14586 use Composer\Installer\InstallationManager;
14587 use Composer\Repository\RepositoryManager;
14588 use Composer\Util\ProcessExecutor;
14589 use Composer\Repository\ArrayRepository;
14590 use Composer\Package\Dumper\ArrayDumper;
14591 use Composer\Package\Loader\ArrayLoader;
14592 use Composer\Package\Version\VersionParser;
14593 use Composer\Util\Git as GitUtil;
14594 use Composer\IO\IOInterface;
14605 private $repositoryManager;
14606 private $installationManager;
14611 private $lockDataCache;
14622 public function __construct(IOInterface $io, JsonFile $lockFile, RepositoryManager $repositoryManager, InstallationManager $installationManager, $hash)
14624 $this->lockFile = $lockFile;
14625 $this->repositoryManager = $repositoryManager;
14626 $this->installationManager = $installationManager;
14627 $this->hash = $hash;
14628 $this->loader = new ArrayLoader(null, true);
14629 $this->dumper = new ArrayDumper();
14630 $this->process = new ProcessExecutor($io);
14638 public function isLocked()
14640 if (!$this->lockFile->exists()) {
14644 $data = $this->getLockData();
14646 return isset($data['packages']);
14654 public function isFresh()
14656 $lock = $this->lockFile->read();
14658 return $this->hash === $lock['hash'];
14668 public function getLockedRepository($withDevReqs = false)
14670 $lockData = $this->getLockData();
14671 $packages = new ArrayRepository();
14673 $lockedPackages = $lockData['packages'];
14674 if ($withDevReqs) {
14675 if (isset($lockData['packages-dev'])) {
14676 $lockedPackages = array_merge($lockedPackages, $lockData['packages-dev']);
14678 throw new \RuntimeException('The lock file does not contain require-dev information, run install with the --no-dev option or run update to install those packages.');
14682 if (empty($lockedPackages)) {
14686 if (isset($lockedPackages[0]['name'])) {
14687 foreach ($lockedPackages as $info) {
14688 $packages->addPackage($this->loader->load($info));
14694 throw new \RuntimeException('Your composer.lock was created before 2012-09-15, and is not supported anymore. Run "composer update" to generate a new one.');
14703 public function getPlatformRequirements($withDevReqs = false)
14705 $lockData = $this->getLockData();
14706 $versionParser = new VersionParser();
14707 $requirements = array();
14709 if (!empty($lockData['platform'])) {
14710 $requirements = $versionParser->parseLinks(
14714 isset($lockData['platform']) ? $lockData['platform'] : array()
14718 if ($withDevReqs && !empty($lockData['platform-dev'])) {
14719 $devRequirements = $versionParser->parseLinks(
14723 isset($lockData['platform-dev']) ? $lockData['platform-dev'] : array()
14726 $requirements = array_merge($requirements, $devRequirements);
14729 return $requirements;
14732 public function getMinimumStability()
14734 $lockData = $this->getLockData();
14736 return isset($lockData['minimum-stability']) ? $lockData['minimum-stability'] : 'stable';
14739 public function getStabilityFlags()
14741 $lockData = $this->getLockData();
14743 return isset($lockData['stability-flags']) ? $lockData['stability-flags'] : array();
14746 public function getPreferStable()
14748 $lockData = $this->getLockData();
14752 return isset($lockData['prefer-stable']) ? $lockData['prefer-stable'] : null;
14755 public function getPreferLowest()
14757 $lockData = $this->getLockData();
14761 return isset($lockData['prefer-lowest']) ? $lockData['prefer-lowest'] : null;
14764 public function getAliases()
14766 $lockData = $this->getLockData();
14768 return isset($lockData['aliases']) ? $lockData['aliases'] : array();
14771 public function getLockData()
14773 if (null !== $this->lockDataCache) {
14774 return $this->lockDataCache;
14777 if (!$this->lockFile->exists()) {
14778 throw new \LogicException('No lockfile found. Unable to read locked packages');
14781 return $this->lockDataCache = $this->lockFile->read();
14799 public function setLockData(array $packages, $devPackages, array $platformReqs, $platformDevReqs, array $aliases, $minimumStability, array $stabilityFlags, $preferStable, $preferLowest)
14802 '_readme' => array('This file locks the dependencies of your project to a known state',
14803 'Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file',
14804 'This file is @gener'.'ated automatically'),
14805 'hash' => $this->hash,
14806 'packages' => null,
14807 'packages-dev' => null,
14808 'aliases' => array(),
14809 'minimum-stability' => $minimumStability,
14810 'stability-flags' => $stabilityFlags,
14811 'prefer-stable' => $preferStable,
14812 'prefer-lowest' => $preferLowest,
14815 foreach ($aliases as $package => $versions) {
14816 foreach ($versions as $version => $alias) {
14817 $lock['aliases'][] = array(
14818 'alias' => $alias['alias'],
14819 'alias_normalized' => $alias['alias_normalized'],
14820 'version' => $version,
14821 'package' => $package,
14826 $lock['packages'] = $this->lockPackages($packages);
14827 if (null !== $devPackages) {
14828 $lock['packages-dev'] = $this->lockPackages($devPackages);
14831 if (empty($lock['packages']) && empty($lock['packages-dev'])) {
14832 if ($this->lockFile->exists()) {
14833 unlink($this->lockFile->getPath());
14839 $lock['platform'] = $platformReqs;
14840 $lock['platform-dev'] = $platformDevReqs;
14842 if (!$this->isLocked() || $lock !== $this->getLockData()) {
14843 $this->lockFile->write($lock);
14844 $this->lockDataCache = null;
14852 private function lockPackages(array $packages)
14856 foreach ($packages as $package) {
14857 if ($package instanceof AliasPackage) {
14861 $name = $package->getPrettyName();
14862 $version = $package->getPrettyVersion();
14864 if (!$name || !$version) {
14865 throw new \LogicException(sprintf(
14866 'Package "%s" has no version or name and can not be locked', $package
14870 $spec = $this->dumper->dump($package);
14871 unset($spec['version_normalized']);
14874 $time = isset($spec['time']) ? $spec['time'] : null;
14875 unset($spec['time']);
14876 if ($package->isDev() && $package->getInstallationSource() === 'source') {
14878 $time = $this->getPackageTime($package) ?: $time;
14880 if (null !== $time) {
14881 $spec['time'] = $time;
14884 unset($spec['installation-source']);
14889 usort($locked, function ($a, $b) {
14890 $comparison = strcmp($a['name'], $b['name']);
14892 if (0 !== $comparison) {
14893 return $comparison;
14897 return strcmp($a['version'], $b['version']);
14909 private function getPackageTime(PackageInterface $package)
14911 if (!function_exists('proc_open')) {
14915 $path = realpath($this->installationManager->getInstallPath($package));
14916 $sourceType = $package->getSourceType();
14919 if ($path && in_array($sourceType, array('git', 'hg'))) {
14920 $sourceRef = $package->getSourceReference() ?: $package->getDistReference();
14921 switch ($sourceType) {
14923 GitUtil::cleanEnv();
14925 if (0 === $this->process->execute('git log -n1 --pretty=%ct '.ProcessExecutor::escape($sourceRef), $output, $path) && preg_match('{^\s*\d+\s*$}', $output)) {
14926 $datetime = new \DateTime('@'.trim($output), new \DateTimeZone('UTC'));
14931 if (0 === $this->process->execute('hg log --template "{date|hgdate}" -r '.ProcessExecutor::escape($sourceRef), $output, $path) && preg_match('{^\s*(\d+)\s*}', $output, $match)) {
14932 $datetime = new \DateTime('@'.$match[1], new \DateTimeZone('UTC'));
14938 return $datetime ? $datetime->format('Y-m-d H:i:s') : null;
14953 namespace Composer\Package;
14955 use Composer\Repository\RepositoryInterface;
14962 interface PackageInterface
14969 public function getName();
14976 public function getPrettyName();
14986 public function getNames();
14993 public function setId($id);
15000 public function getId();
15007 public function isDev();
15014 public function getType();
15021 public function getTargetDir();
15028 public function getExtra();
15035 public function setInstallationSource($type);
15042 public function getInstallationSource();
15049 public function getSourceType();
15056 public function getSourceUrl();
15063 public function getSourceUrls();
15070 public function getSourceReference();
15077 public function getSourceMirrors();
15084 public function getDistType();
15091 public function getDistUrl();
15098 public function getDistUrls();
15105 public function getDistReference();
15112 public function getDistSha1Checksum();
15119 public function getDistMirrors();
15126 public function getVersion();
15133 public function getPrettyVersion();
15140 public function getReleaseDate();
15147 public function getStability();
15155 public function getRequires();
15163 public function getConflicts();
15171 public function getProvides();
15179 public function getReplaces();
15187 public function getDevRequires();
15195 public function getSuggests();
15207 public function getAutoload();
15219 public function getDevAutoload();
15227 public function getIncludePaths();
15234 public function setRepository(RepositoryInterface $repository);
15241 public function getRepository();
15248 public function getBinaries();
15255 public function getUniqueName();
15262 public function getNotificationUrl();
15269 public function __toString();
15276 public function getPrettyString();
15283 public function getArchiveExcludes();
15290 public function getTransportOptions();
15304 namespace Composer\Package;
15306 use Composer\Repository\RepositoryInterface;
15307 use Composer\Repository\PlatformRepository;
15314 abstract class BasePackage implements PackageInterface
15316 public static $supportedLinkTypes = array(
15317 'require' => array('description' => 'requires', 'method' => 'requires'),
15318 'conflict' => array('description' => 'conflicts', 'method' => 'conflicts'),
15319 'provide' => array('description' => 'provides', 'method' => 'provides'),
15320 'replace' => array('description' => 'replaces', 'method' => 'replaces'),
15321 'require-dev' => array('description' => 'requires (for development)', 'method' => 'devRequires'),
15324 const STABILITY_STABLE = 0;
15325 const STABILITY_RC = 5;
15326 const STABILITY_BETA = 10;
15327 const STABILITY_ALPHA = 15;
15328 const STABILITY_DEV = 20;
15330 public static $stabilities = array(
15331 'stable' => self::STABILITY_STABLE,
15332 'RC' => self::STABILITY_RC,
15333 'beta' => self::STABILITY_BETA,
15334 'alpha' => self::STABILITY_ALPHA,
15335 'dev' => self::STABILITY_DEV,
15345 protected $prettyName;
15347 protected $repository;
15348 protected $transportOptions;
15355 public function __construct($name)
15357 $this->prettyName = $name;
15358 $this->name = strtolower($name);
15360 $this->transportOptions = array();
15366 public function getName()
15368 return $this->name;
15374 public function getPrettyName()
15376 return $this->prettyName;
15382 public function getNames()
15385 $this->getName() => true,
15388 foreach ($this->getProvides() as $link) {
15389 $names[$link->getTarget()] = true;
15392 foreach ($this->getReplaces() as $link) {
15393 $names[$link->getTarget()] = true;
15396 return array_keys($names);
15402 public function setId($id)
15410 public function getId()
15418 public function setRepository(RepositoryInterface $repository)
15420 if ($this->repository && $repository !== $this->repository) {
15421 throw new \LogicException('A package can only be added to one repository');
15423 $this->repository = $repository;
15429 public function getRepository()
15431 return $this->repository;
15437 public function getTransportOptions()
15439 return $this->transportOptions;
15447 public function setTransportOptions(array $options)
15449 $this->transportOptions = $options;
15457 public function isPlatform()
15459 return $this->getRepository() instanceof PlatformRepository;
15467 public function getUniqueName()
15469 return $this->getName().'-'.$this->getVersion();
15472 public function equals(PackageInterface $package)
15475 if ($this instanceof AliasPackage) {
15476 $self = $this->getAliasOf();
15478 if ($package instanceof AliasPackage) {
15479 $package = $package->getAliasOf();
15482 return $package === $self;
15490 public function __toString()
15492 return $this->getUniqueName();
15495 public function getPrettyString()
15497 return $this->getPrettyName().' '.$this->getPrettyVersion();
15500 public function __clone()
15502 $this->repository = null;
15518 namespace Composer\Package\Version;
15520 use Composer\DependencyResolver\Pool;
15521 use Composer\Package\PackageInterface;
15522 use Composer\Package\Loader\ArrayLoader;
15523 use Composer\Package\Dumper\ArrayDumper;
15530 class VersionSelector
15536 public function __construct(Pool $pool)
15538 $this->pool = $pool;
15549 public function findBestCandidate($packageName, $targetPackageVersion = null)
15551 $constraint = $targetPackageVersion ? $this->getParser()->parseConstraints($targetPackageVersion) : null;
15552 $candidates = $this->pool->whatProvides($packageName, $constraint, true);
15554 if (!$candidates) {
15559 $package = reset($candidates);
15560 foreach ($candidates as $candidate) {
15561 if (version_compare($package->getVersion(), $candidate->getVersion(), '<')) {
15562 $package = $candidate;
15584 public function findRecommendedRequireVersion(PackageInterface $package)
15586 $version = $package->getVersion();
15587 if (!$package->isDev()) {
15588 return $this->transformVersion($version, $package->getPrettyVersion(), $package->getStability());
15591 $loader = new ArrayLoader($this->getParser());
15592 $dumper = new ArrayDumper();
15593 $extra = $loader->getBranchAlias($dumper->dump($package));
15595 $extra = preg_replace('{^(\d+\.\d+\.\d+)(\.9999999)-dev$}', '$1.0', $extra, -1, $count);
15597 $extra = str_replace('.9999999', '.0', $extra);
15599 return $this->transformVersion($extra, $extra, 'dev');
15603 return $package->getPrettyVersion();
15606 private function transformVersion($version, $prettyVersion, $stability)
15610 $semanticVersionParts = explode('.', $version);
15614 if (count($semanticVersionParts) == 4 && preg_match('{^0\D?}', $semanticVersionParts[3])) {
15616 if ($semanticVersionParts[0] === '0') {
15617 if ($semanticVersionParts[1] === '0') {
15618 $semanticVersionParts[3] = '*';
15620 $semanticVersionParts[2] = '*';
15621 unset($semanticVersionParts[3]);
15625 unset($semanticVersionParts[2], $semanticVersionParts[3]);
15627 $version = implode('.', $semanticVersionParts);
15629 return $prettyVersion;
15633 if ($stability != 'stable') {
15634 $version .= '@'.$stability;
15638 return $op.$version;
15641 private function getParser()
15643 if ($this->parser === null) {
15644 $this->parser = new VersionParser();
15647 return $this->parser;
15662 namespace Composer\Package\Version;
15664 use Composer\Package\BasePackage;
15665 use Composer\Package\PackageInterface;
15666 use Composer\Package\Link;
15667 use Composer\Package\LinkConstraint\EmptyConstraint;
15668 use Composer\Package\LinkConstraint\MultiConstraint;
15669 use Composer\Package\LinkConstraint\VersionConstraint;
15676 class VersionParser
15678 private static $modifierRegex = '[._-]?(?:(stable|beta|b|RC|alpha|a|patch|pl|p)(?:[.-]?(\d+))?)?([.-]?dev)?';
15686 public static function parseStability($version)
15688 $version = preg_replace('{#.+$}i', '', $version);
15690 if ('dev-' === substr($version, 0, 4) || '-dev' === substr($version, -4)) {
15694 preg_match('{'.self::$modifierRegex.'$}i', strtolower($version), $match);
15695 if (!empty($match[3])) {
15699 if (!empty($match[1])) {
15700 if ('beta' === $match[1] || 'b' === $match[1]) {
15703 if ('alpha' === $match[1] || 'a' === $match[1]) {
15706 if ('rc' === $match[1]) {
15714 public static function normalizeStability($stability)
15716 $stability = strtolower($stability);
15718 return $stability === 'rc' ? 'RC' : $stability;
15721 public static function formatVersion(PackageInterface $package, $truncate = true)
15723 if (!$package->isDev() || !in_array($package->getSourceType(), array('hg', 'git'))) {
15724 return $package->getPrettyVersion();
15728 if ($truncate && strlen($package->getSourceReference()) === 40) {
15729 return $package->getPrettyVersion() . ' ' . substr($package->getSourceReference(), 0, 7);
15732 return $package->getPrettyVersion() . ' ' . $package->getSourceReference();
15743 public function normalize($version, $fullVersion = null)
15745 $version = trim($version);
15746 if (null === $fullVersion) {
15747 $fullVersion = $version;
15751 if (preg_match('{^([^,\s]+) +as +([^,\s]+)$}', $version, $match)) {
15752 $version = $match[1];
15756 if (preg_match('{^([^,\s+]+)\+[^\s]+$}', $version, $match)) {
15757 $version = $match[1];
15761 if (preg_match('{^(?:dev-)?(?:master|trunk|default)$}i', $version)) {
15762 return '9999999-dev';
15765 if ('dev-' === strtolower(substr($version, 0, 4))) {
15766 return 'dev-'.substr($version, 4);
15770 if (preg_match('{^v?(\d{1,3})(\.\d+)?(\.\d+)?(\.\d+)?'.self::$modifierRegex.'$}i', $version, $matches)) {
15771 $version = $matches[1]
15772 .(!empty($matches[2]) ? $matches[2] : '.0')
15773 .(!empty($matches[3]) ? $matches[3] : '.0')
15774 .(!empty($matches[4]) ? $matches[4] : '.0');
15776 } elseif (preg_match('{^v?(\d{4}(?:[.:-]?\d{2}){1,6}(?:[.:-]?\d{1,3})?)'.self::$modifierRegex.'$}i', $version, $matches)) {
15777 $version = preg_replace('{\D}', '-', $matches[1]);
15779 } elseif (preg_match('{^v?(\d{4,})(\.\d+)?(\.\d+)?(\.\d+)?'.self::$modifierRegex.'$}i', $version, $matches)) {
15780 $version = $matches[1]
15781 .(!empty($matches[2]) ? $matches[2] : '.0')
15782 .(!empty($matches[3]) ? $matches[3] : '.0')
15783 .(!empty($matches[4]) ? $matches[4] : '.0');
15788 if (isset($index)) {
15789 if (!empty($matches[$index])) {
15790 if ('stable' === $matches[$index]) {
15793 $version .= '-' . $this->expandStability($matches[$index]) . (!empty($matches[$index+1]) ? $matches[$index+1] : '');
15796 if (!empty($matches[$index+2])) {
15797 $version .= '-dev';
15804 if (preg_match('{(.*?)[.-]?dev$}i', $version, $match)) {
15806 return $this->normalizeBranch($match[1]);
15807 } catch (\Exception $e) {
15811 $extraMessage = '';
15812 if (preg_match('{ +as +'.preg_quote($version).'$}', $fullVersion)) {
15813 $extraMessage = ' in "'.$fullVersion.'", the alias must be an exact version';
15814 } elseif (preg_match('{^'.preg_quote($version).' +as +}', $fullVersion)) {
15815 $extraMessage = ' in "'.$fullVersion.'", the alias source must be an exact version, if it is a branch name you should prefix it with dev-';
15818 throw new \UnexpectedValueException('Invalid version string "'.$version.'"'.$extraMessage);
15827 public function normalizeBranch($name)
15829 $name = trim($name);
15831 if (in_array($name, array('master', 'trunk', 'default'))) {
15832 return $this->normalize($name);
15835 if (preg_match('#^v?(\d+)(\.(?:\d+|[xX*]))?(\.(?:\d+|[xX*]))?(\.(?:\d+|[xX*]))?$#i', $name, $matches)) {
15837 for ($i = 1; $i < 5; $i++) {
15838 $version .= isset($matches[$i]) ? str_replace(array('*', 'X'), 'x', $matches[$i]) : '.x';
15841 return str_replace('x', '9999999', $version).'-dev';
15844 return 'dev-'.$name;
15854 public function parseLinks($source, $sourceVersion, $description, $links)
15857 foreach ($links as $target => $constraint) {
15858 if ('self.version' === $constraint) {
15859 $parsedConstraint = $this->parseConstraints($sourceVersion);
15861 $parsedConstraint = $this->parseConstraints($constraint);
15863 $res[strtolower($target)] = new Link($source, $target, $parsedConstraint, $description, $constraint);
15875 public function parseConstraints($constraints)
15877 $prettyConstraint = $constraints;
15879 if (preg_match('{^([^,\s]*?)@('.implode('|', array_keys(BasePackage::$stabilities)).')$}i', $constraints, $match)) {
15880 $constraints = empty($match[1]) ? '*' : $match[1];
15883 if (preg_match('{^(dev-[^,\s@]+?|[^,\s@]+?\.x-dev)#.+$}i', $constraints, $match)) {
15884 $constraints = $match[1];
15887 $orConstraints = preg_split('{\s*\|\|?\s*}', trim($constraints));
15888 $orGroups = array();
15889 foreach ($orConstraints as $constraints) {
15890 $andConstraints = preg_split('{(?<!^|as|[=>< ,]) *(?<!-)[, ](?!-) *(?!,|as|$)}', $constraints);
15891 if (count($andConstraints) > 1) {
15892 $constraintObjects = array();
15893 foreach ($andConstraints as $constraint) {
15894 $constraintObjects = array_merge($constraintObjects, $this->parseConstraint($constraint));
15897 $constraintObjects = $this->parseConstraint($andConstraints[0]);
15900 if (1 === count($constraintObjects)) {
15901 $constraint = $constraintObjects[0];
15903 $constraint = new MultiConstraint($constraintObjects);
15906 $orGroups[] = $constraint;
15909 if (1 === count($orGroups)) {
15910 $constraint = $orGroups[0];
15912 $constraint = new MultiConstraint($orGroups, false);
15915 $constraint->setPrettyString($prettyConstraint);
15917 return $constraint;
15920 private function parseConstraint($constraint)
15922 if (preg_match('{^([^,\s]+?)@('.implode('|', array_keys(BasePackage::$stabilities)).')$}i', $constraint, $match)) {
15923 $constraint = $match[1];
15924 if ($match[2] !== 'stable') {
15925 $stabilityModifier = $match[2];
15929 if (preg_match('{^[xX*](\.[xX*])*$}i', $constraint)) {
15930 return array(new EmptyConstraint);
15933 $versionRegex = '(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:\.(\d+))?'.self::$modifierRegex;
15940 if (preg_match('{^~>?'.$versionRegex.'$}i', $constraint, $matches)) {
15941 if (substr($constraint, 0, 2) === '~>') {
15942 throw new \UnexpectedValueException(
15943 'Could not parse version constraint '.$constraint.': '.
15944 'Invalid operator "~>", you probably meant to use the "~" operator'
15949 if (isset($matches[4]) && '' !== $matches[4]) {
15951 } elseif (isset($matches[3]) && '' !== $matches[3]) {
15953 } elseif (isset($matches[2]) && '' !== $matches[2]) {
15960 $stabilitySuffix = '';
15961 if (!empty($matches[5])) {
15962 $stabilitySuffix .= '-' . $this->expandStability($matches[5]) . (!empty($matches[6]) ? $matches[6] : '');
15965 if (!empty($matches[7])) {
15966 $stabilitySuffix .= '-dev';
15969 if (!$stabilitySuffix) {
15970 $stabilitySuffix = "-dev";
15972 $lowVersion = $this->manipulateVersionString($matches, $position, 0) . $stabilitySuffix;
15973 $lowerBound = new VersionConstraint('>=', $lowVersion);
15977 $highPosition = max(1, $position - 1);
15978 $highVersion = $this->manipulateVersionString($matches, $highPosition, 1) . '-dev';
15979 $upperBound = new VersionConstraint('<', $highVersion);
15988 if (preg_match('{^\^'.$versionRegex.'($)}i', $constraint, $matches)) {
15990 if ('0' !== $matches[1] || '' === $matches[2]) {
15992 } elseif ('0' !== $matches[2] || '' === $matches[3]) {
15999 $stabilitySuffix = '';
16000 if (empty($matches[5]) && empty($matches[7])) {
16001 $stabilitySuffix .= '-dev';
16004 $lowVersion = $this->normalize(substr($constraint . $stabilitySuffix, 1));
16005 $lowerBound = new VersionConstraint('>=', $lowVersion);
16009 $highVersion = $this->manipulateVersionString($matches, $position, 1) . '-dev';
16010 $upperBound = new VersionConstraint('<', $highVersion);
16019 if (preg_match('{^(\d+)(?:\.(\d+))?(?:\.(\d+))?\.[xX*]$}', $constraint, $matches)) {
16020 if (isset($matches[3]) && '' !== $matches[3]) {
16022 } elseif (isset($matches[2]) && '' !== $matches[2]) {
16028 $lowVersion = $this->manipulateVersionString($matches, $position) . "-dev";
16029 $highVersion = $this->manipulateVersionString($matches, $position, 1) . "-dev";
16031 if ($lowVersion === "0.0.0.0-dev") {
16032 return array(new VersionConstraint('<', $highVersion));
16036 new VersionConstraint('>=', $lowVersion),
16037 new VersionConstraint('<', $highVersion),
16042 if (preg_match('{^(?P<from>'.$versionRegex.') +- +(?P<to>'.$versionRegex.')($)}i', $constraint, $matches)) {
16044 $lowStabilitySuffix = '';
16045 if (empty($matches[6]) && empty($matches[8])) {
16046 $lowStabilitySuffix = '-dev';
16049 $lowVersion = $this->normalize($matches['from']);
16050 $lowerBound = new VersionConstraint('>=', $lowVersion . $lowStabilitySuffix);
16052 $highVersion = $matches[10];
16053 if ((!empty($matches[11]) && !empty($matches[12])) || !empty($matches[14]) || !empty($matches[16])) {
16054 $highVersion = $this->normalize($matches['to']);
16055 $upperBound = new VersionConstraint('<=', $highVersion);
16057 $highMatch = array('', $matches[10], $matches[11], $matches[12], $matches[13]);
16058 $highVersion = $this->manipulateVersionString($highMatch, empty($matches[11]) ? 1 : 2, 1) . '-dev';
16059 $upperBound = new VersionConstraint('<', $highVersion);
16069 if (preg_match('{^(<>|!=|>=?|<=?|==?)?\s*(.*)}', $constraint, $matches)) {
16071 $version = $this->normalize($matches[2]);
16073 if (!empty($stabilityModifier) && $this->parseStability($version) === 'stable') {
16074 $version .= '-' . $stabilityModifier;
16075 } elseif ('<' === $matches[1]) {
16076 if (!preg_match('/-' . self::$modifierRegex . '$/', strtolower($matches[2]))) {
16077 $version .= '-dev';
16081 return array(new VersionConstraint($matches[1] ?: '=', $version));
16082 } catch (\Exception $e) {
16086 $message = 'Could not parse version constraint '.$constraint;
16088 $message .= ': '. $e->getMessage();
16091 throw new \UnexpectedValueException($message);
16105 private function manipulateVersionString($matches, $position, $increment = 0, $pad = '0')
16107 for ($i = 4; $i > 0; $i--) {
16108 if ($i > $position) {
16109 $matches[$i] = $pad;
16110 } elseif ($i == $position && $increment) {
16111 $matches[$i] += $increment;
16113 if ($matches[$i] < 0) {
16114 $matches[$i] = $pad;
16125 return $matches[1] . '.' . $matches[2] . '.' . $matches[3] . '.' . $matches[4];
16128 private function expandStability($stability)
16130 $stability = strtolower($stability);
16132 switch ($stability) {
16153 public function parseNameVersionPairs(array $pairs)
16155 $pairs = array_values($pairs);
16158 for ($i = 0, $count = count($pairs); $i < $count; $i++) {
16159 $pair = preg_replace('{^([^=: ]+)[=: ](.*)$}', '$1 $2', trim($pairs[$i]));
16160 if (false === strpos($pair, ' ') && isset($pairs[$i+1]) && false === strpos($pairs[$i+1], '/')) {
16161 $pair .= ' '.$pairs[$i+1];
16165 if (strpos($pair, ' ')) {
16166 list($name, $version) = explode(" ", $pair, 2);
16167 $result[] = array('name' => $name, 'version' => $version);
16169 $result[] = array('name' => $pair);
16188 namespace Composer\Package;
16195 interface CompletePackageInterface extends PackageInterface
16202 public function getScripts();
16211 public function getRepositories();
16218 public function getLicense();
16225 public function getKeywords();
16232 public function getDescription();
16239 public function getHomepage();
16248 public function getAuthors();
16255 public function getSupport();
16262 public function isAbandoned();
16269 public function getReplacementPackage();
16283 namespace Composer\Package;
16290 interface RootPackageInterface extends CompletePackageInterface
16297 public function getAliases();
16304 public function getMinimumStability();
16313 public function getStabilityFlags();
16322 public function getReferences();
16329 public function getPreferStable();
16336 public function setRequires(array $requires);
16343 public function setDevRequires(array $devRequires);
16357 namespace Composer\Package;
16364 class RootPackage extends CompletePackage implements RootPackageInterface
16366 protected $minimumStability = 'stable';
16367 protected $preferStable = false;
16368 protected $stabilityFlags = array();
16369 protected $references = array();
16370 protected $aliases = array();
16377 public function setMinimumStability($minimumStability)
16379 $this->minimumStability = $minimumStability;
16385 public function getMinimumStability()
16387 return $this->minimumStability;
16395 public function setStabilityFlags(array $stabilityFlags)
16397 $this->stabilityFlags = $stabilityFlags;
16403 public function getStabilityFlags()
16405 return $this->stabilityFlags;
16413 public function setPreferStable($preferStable)
16415 $this->preferStable = $preferStable;
16421 public function getPreferStable()
16423 return $this->preferStable;
16431 public function setReferences(array $references)
16433 $this->references = $references;
16439 public function getReferences()
16441 return $this->references;
16449 public function setAliases(array $aliases)
16451 $this->aliases = $aliases;
16457 public function getAliases()
16459 return $this->aliases;
16474 namespace Composer\Package\Archiver;
16481 interface ArchiverInterface
16493 public function archive($sources, $target, $format, array $excludes = array());
16503 public function supports($format, $sourceType);
16517 namespace Composer\Package\Archiver;
16524 class ComposerExcludeFilter extends BaseExcludeFilter
16530 public function __construct($sourcePath, array $excludeRules)
16532 parent::__construct($sourcePath);
16533 $this->excludePatterns = $this->generatePatterns($excludeRules);
16548 namespace Composer\Package\Archiver;
16555 class PharArchiver implements ArchiverInterface
16557 protected static $formats = array(
16558 'zip' => \Phar::ZIP,
16559 'tar' => \Phar::TAR,
16565 public function archive($sources, $target, $format, array $excludes = array())
16567 $sources = realpath($sources);
16570 if (file_exists($target)) {
16575 $phar = new \PharData($target, null, null, static::$formats[$format]);
16576 $files = new ArchivableFilesFinder($sources, $excludes);
16577 $phar->buildFromIterator($files, $sources);
16580 } catch (\UnexpectedValueException $e) {
16581 $message = sprintf("Could not create archive '%s' from '%s': %s",
16587 throw new \RuntimeException($message, $e->getCode(), $e);
16594 public function supports($format, $sourceType)
16596 return isset(static::$formats[$format]);
16611 namespace Composer\Package\Archiver;
16613 use Symfony\Component\Finder;
16618 abstract class BaseExcludeFilter
16623 protected $sourcePath;
16628 protected $excludePatterns;
16633 public function __construct($sourcePath)
16635 $this->sourcePath = $sourcePath;
16636 $this->excludePatterns = array();
16649 public function filter($relativePath, $exclude)
16651 foreach ($this->excludePatterns as $patternData) {
16652 list($pattern, $negate, $stripLeadingSlash) = $patternData;
16654 if ($stripLeadingSlash) {
16655 $path = substr($relativePath, 1);
16657 $path = $relativePath;
16660 if (preg_match($pattern, $path)) {
16661 $exclude = !$negate;
16676 protected function parseLines(array $lines, $lineParser)
16678 return array_filter(
16680 function ($line) use ($lineParser) {
16681 $line = trim($line);
16683 if (!$line || 0 === strpos($line, '#')) {
16687 return call_user_func($lineParser, $line);
16691 function ($pattern) {
16692 return $pattern !== null;
16704 protected function generatePatterns($rules)
16706 $patterns = array();
16707 foreach ($rules as $rule) {
16708 $patterns[] = $this->generatePattern($rule);
16721 protected function generatePattern($rule)
16726 if (strlen($rule) && $rule[0] === '!') {
16728 $rule = substr($rule, 1);
16731 if (strlen($rule) && $rule[0] === '/') {
16733 $rule = substr($rule, 1);
16734 } elseif (strlen($rule) - 1 === strpos($rule, '/')) {
16736 $rule = substr($rule, 0, -1);
16737 } elseif (false === strpos($rule, '/')) {
16742 $pattern .= substr(Finder\Glob::toRegex($rule), 2, -2) . '(?=$|/)';
16744 return array($pattern . '#', $negate, false);
16759 namespace Composer\Package\Archiver;
16768 class GitExcludeFilter extends BaseExcludeFilter
16775 public function __construct($sourcePath)
16777 parent::__construct($sourcePath);
16779 if (file_exists($sourcePath.'/.gitignore')) {
16780 $this->excludePatterns = $this->parseLines(
16781 file($sourcePath.'/.gitignore'),
16782 array($this, 'parseGitIgnoreLine')
16785 if (file_exists($sourcePath.'/.gitattributes')) {
16786 $this->excludePatterns = array_merge(
16787 $this->excludePatterns,
16789 file($sourcePath.'/.gitattributes'),
16790 array($this, 'parseGitAttributesLine')
16802 public function parseGitIgnoreLine($line)
16804 return $this->generatePattern($line);
16814 public function parseGitAttributesLine($line)
16816 $parts = preg_split('#\s+#', $line);
16818 if (count($parts) != 2) {
16822 if ($parts[1] === 'export-ignore') {
16823 return $this->generatePattern($parts[0]);
16839 namespace Composer\Package\Archiver;
16841 use Composer\Util\Filesystem;
16843 use Symfony\Component\Finder;
16853 class ArchivableFilesFinder extends \FilterIterator
16866 public function __construct($sources, array $excludes)
16868 $fs = new Filesystem();
16870 $sources = $fs->normalizePath($sources);
16873 new HgExcludeFilter($sources),
16874 new GitExcludeFilter($sources),
16875 new ComposerExcludeFilter($sources, $excludes),
16878 $this->finder = new Finder\Finder();
16880 $filter = function (\SplFileInfo $file) use ($sources, $filters, $fs) {
16881 if ($file->isLink() && strpos($file->getLinkTarget(), $sources) !== 0) {
16885 $relativePath = preg_replace(
16886 '#^'.preg_quote($sources, '#').'#',
16888 $fs->normalizePath($file->getRealPath())
16892 foreach ($filters as $filter) {
16893 $exclude = $filter->filter($relativePath, $exclude);
16899 if (method_exists($filter, 'bindTo')) {
16900 $filter = $filter->bindTo(null);
16907 ->ignoreDotFiles(false);
16909 parent::__construct($this->finder->getIterator());
16912 public function accept()
16914 return !$this->getInnerIterator()->current()->isDir();
16929 namespace Composer\Package\Archiver;
16931 use Composer\Downloader\DownloadManager;
16932 use Composer\Package\PackageInterface;
16933 use Composer\Package\RootPackageInterface;
16934 use Composer\Util\Filesystem;
16935 use Composer\Json\JsonFile;
16941 class ArchiveManager
16943 protected $downloadManager;
16945 protected $archivers = array();
16950 protected $overwriteFiles = true;
16955 public function __construct(DownloadManager $downloadManager)
16957 $this->downloadManager = $downloadManager;
16963 public function addArchiver(ArchiverInterface $archiver)
16965 $this->archivers[] = $archiver;
16975 public function setOverwriteFiles($overwriteFiles)
16977 $this->overwriteFiles = $overwriteFiles;
16989 public function getPackageFilename(PackageInterface $package)
16991 $nameParts = array(preg_replace('#[^a-z0-9-_]#i', '-', $package->getName()));
16993 if (preg_match('{^[a-f0-9]{40}$}', $package->getDistReference())) {
16994 $nameParts = array_merge($nameParts, array($package->getDistReference(), $package->getDistType()));
16996 $nameParts = array_merge($nameParts, array($package->getPrettyVersion(), $package->getDistReference()));
16999 if ($package->getSourceReference()) {
17000 $nameParts[] = substr(sha1($package->getSourceReference()), 0, 6);
17003 $name = implode('-', array_filter($nameParts, function ($p) {
17007 return str_replace('/', '-', $name);
17020 public function archive(PackageInterface $package, $format, $targetDir)
17022 if (empty($format)) {
17023 throw new \InvalidArgumentException('Format must be specified');
17027 $usableArchiver = null;
17028 foreach ($this->archivers as $archiver) {
17029 if ($archiver->supports($format, $package->getSourceType())) {
17030 $usableArchiver = $archiver;
17036 if (null === $usableArchiver) {
17037 throw new \RuntimeException(sprintf('No archiver found to support %s format', $format));
17040 $filesystem = new Filesystem();
17041 $packageName = $this->getPackageFilename($package);
17044 $filesystem->ensureDirectoryExists($targetDir);
17045 $target = realpath($targetDir).'/'.$packageName.'.'.$format;
17046 $filesystem->ensureDirectoryExists(dirname($target));
17048 if (!$this->overwriteFiles && file_exists($target)) {
17052 if ($package instanceof RootPackageInterface) {
17053 $sourcePath = realpath('.');
17056 $sourcePath = sys_get_temp_dir().'/composer_archive'.uniqid();
17057 $filesystem->ensureDirectoryExists($sourcePath);
17060 $this->downloadManager->download($package, $sourcePath);
17063 if (file_exists($composerJsonPath = $sourcePath.'/composer.json')) {
17064 $jsonFile = new JsonFile($composerJsonPath);
17065 $jsonData = $jsonFile->read();
17066 if (!empty($jsonData['archive']['exclude'])) {
17067 $package->setArchiveExcludes($jsonData['archive']['exclude']);
17073 $tempTarget = sys_get_temp_dir().'/composer_archive'.uniqid().'.'.$format;
17074 $filesystem->ensureDirectoryExists(dirname($tempTarget));
17076 $archivePath = $usableArchiver->archive($sourcePath, $tempTarget, $format, $package->getArchiveExcludes());
17077 rename($archivePath, $target);
17080 if (!$package instanceof RootPackageInterface) {
17081 $filesystem->removeDirectory($sourcePath);
17083 $filesystem->remove($tempTarget);
17100 namespace Composer\Package\Archiver;
17102 use Symfony\Component\Finder;
17109 class HgExcludeFilter extends BaseExcludeFilter
17111 const HG_IGNORE_REGEX = 1;
17112 const HG_IGNORE_GLOB = 2;
17118 protected $patternMode;
17125 public function __construct($sourcePath)
17127 parent::__construct($sourcePath);
17129 $this->patternMode = self::HG_IGNORE_REGEX;
17131 if (file_exists($sourcePath.'/.hgignore')) {
17132 $this->excludePatterns = $this->parseLines(
17133 file($sourcePath.'/.hgignore'),
17134 array($this, 'parseHgIgnoreLine')
17146 public function parseHgIgnoreLine($line)
17148 if (preg_match('#^syntax\s*:\s*(glob|regexp)$#', $line, $matches)) {
17149 if ($matches[1] === 'glob') {
17150 $this->patternMode = self::HG_IGNORE_GLOB;
17152 $this->patternMode = self::HG_IGNORE_REGEX;
17158 if ($this->patternMode == self::HG_IGNORE_GLOB) {
17159 return $this->patternFromGlob($line);
17161 return $this->patternFromRegex($line);
17172 protected function patternFromGlob($line)
17174 $pattern = '#'.substr(Finder\Glob::toRegex($line), 2, -1).'#';
17175 $pattern = str_replace('[^/]*', '.*', $pattern);
17177 return array($pattern, false, true);
17187 public function patternFromRegex($line)
17190 $pattern = '#'.preg_replace('/((?:\\\\\\\\)*)(\\\\?)#/', '\1\2\2\\#', $line).'#';
17192 return array($pattern, false, true);
17207 namespace Composer\Package;
17212 class RootAliasPackage extends AliasPackage implements RootPackageInterface
17214 public function __construct(RootPackageInterface $aliasOf, $version, $prettyVersion)
17216 parent::__construct($aliasOf, $version, $prettyVersion);
17222 public function getAliases()
17224 return $this->aliasOf->getAliases();
17230 public function getMinimumStability()
17232 return $this->aliasOf->getMinimumStability();
17238 public function getStabilityFlags()
17240 return $this->aliasOf->getStabilityFlags();
17246 public function getReferences()
17248 return $this->aliasOf->getReferences();
17254 public function getPreferStable()
17256 return $this->aliasOf->getPreferStable();
17262 public function setRequires(array $require)
17264 return $this->aliasOf->setRequires($require);
17270 public function setDevRequires(array $devRequire)
17272 return $this->aliasOf->setDevRequires($devRequire);
17275 public function __clone()
17278 $this->aliasOf = clone $this->aliasOf;
17293 namespace Composer\Package;
17295 use Composer\Package\Version\VersionParser;
17296 use Composer\Util\ComposerMirror;
17303 class Package extends BasePackage
17306 protected $targetDir;
17307 protected $installationSource;
17308 protected $sourceType;
17309 protected $sourceUrl;
17310 protected $sourceReference;
17311 protected $sourceMirrors;
17312 protected $distType;
17313 protected $distUrl;
17314 protected $distReference;
17315 protected $distSha1Checksum;
17316 protected $distMirrors;
17317 protected $version;
17318 protected $prettyVersion;
17319 protected $releaseDate;
17320 protected $extra = array();
17321 protected $binaries = array();
17323 protected $stability;
17324 protected $notificationUrl;
17326 protected $requires = array();
17327 protected $conflicts = array();
17328 protected $provides = array();
17329 protected $replaces = array();
17330 protected $devRequires = array();
17331 protected $suggests = array();
17332 protected $autoload = array();
17333 protected $devAutoload = array();
17334 protected $includePaths = array();
17335 protected $archiveExcludes = array();
17344 public function __construct($name, $version, $prettyVersion)
17346 parent::__construct($name);
17348 $this->version = $version;
17349 $this->prettyVersion = $prettyVersion;
17351 $this->stability = VersionParser::parseStability($version);
17352 $this->dev = $this->stability === 'dev';
17358 public function isDev()
17366 public function setType($type)
17368 $this->type = $type;
17374 public function getType()
17376 return $this->type ?: 'library';
17382 public function getStability()
17384 return $this->stability;
17390 public function setTargetDir($targetDir)
17392 $this->targetDir = $targetDir;
17398 public function getTargetDir()
17400 if (null === $this->targetDir) {
17404 return ltrim(preg_replace('{ (?:^|[\\\\/]+) \.\.? (?:[\\\\/]+|$) (?:\.\.? (?:[\\\\/]+|$) )*}x', '/', $this->targetDir), '/');
17410 public function setExtra(array $extra)
17412 $this->extra = $extra;
17418 public function getExtra()
17420 return $this->extra;
17426 public function setBinaries(array $binaries)
17428 $this->binaries = $binaries;
17434 public function getBinaries()
17436 return $this->binaries;
17442 public function setInstallationSource($type)
17444 $this->installationSource = $type;
17450 public function getInstallationSource()
17452 return $this->installationSource;
17458 public function setSourceType($type)
17460 $this->sourceType = $type;
17466 public function getSourceType()
17468 return $this->sourceType;
17474 public function setSourceUrl($url)
17476 $this->sourceUrl = $url;
17482 public function getSourceUrl()
17484 return $this->sourceUrl;
17490 public function setSourceReference($reference)
17492 $this->sourceReference = $reference;
17498 public function getSourceReference()
17500 return $this->sourceReference;
17506 public function setSourceMirrors($mirrors)
17508 $this->sourceMirrors = $mirrors;
17514 public function getSourceMirrors()
17516 return $this->sourceMirrors;
17522 public function getSourceUrls()
17524 return $this->getUrls($this->sourceUrl, $this->sourceMirrors, $this->sourceReference, $this->sourceType, 'source');
17530 public function setDistType($type)
17532 $this->distType = $type;
17538 public function getDistType()
17540 return $this->distType;
17546 public function setDistUrl($url)
17548 $this->distUrl = $url;
17554 public function getDistUrl()
17556 return $this->distUrl;
17562 public function setDistReference($reference)
17564 $this->distReference = $reference;
17570 public function getDistReference()
17572 return $this->distReference;
17578 public function setDistSha1Checksum($sha1checksum)
17580 $this->distSha1Checksum = $sha1checksum;
17586 public function getDistSha1Checksum()
17588 return $this->distSha1Checksum;
17594 public function setDistMirrors($mirrors)
17596 $this->distMirrors = $mirrors;
17602 public function getDistMirrors()
17604 return $this->distMirrors;
17610 public function getDistUrls()
17612 return $this->getUrls($this->distUrl, $this->distMirrors, $this->distReference, $this->distType, 'dist');
17618 public function getVersion()
17620 return $this->version;
17626 public function getPrettyVersion()
17628 return $this->prettyVersion;
17636 public function setReleaseDate(\DateTime $releaseDate)
17638 $this->releaseDate = $releaseDate;
17644 public function getReleaseDate()
17646 return $this->releaseDate;
17654 public function setRequires(array $requires)
17656 $this->requires = $requires;
17662 public function getRequires()
17664 return $this->requires;
17672 public function setConflicts(array $conflicts)
17674 $this->conflicts = $conflicts;
17680 public function getConflicts()
17682 return $this->conflicts;
17690 public function setProvides(array $provides)
17692 $this->provides = $provides;
17698 public function getProvides()
17700 return $this->provides;
17708 public function setReplaces(array $replaces)
17710 $this->replaces = $replaces;
17716 public function getReplaces()
17718 return $this->replaces;
17726 public function setDevRequires(array $devRequires)
17728 $this->devRequires = $devRequires;
17734 public function getDevRequires()
17736 return $this->devRequires;
17744 public function setSuggests(array $suggests)
17746 $this->suggests = $suggests;
17752 public function getSuggests()
17754 return $this->suggests;
17762 public function setAutoload(array $autoload)
17764 $this->autoload = $autoload;
17770 public function getAutoload()
17772 return $this->autoload;
17780 public function setDevAutoload(array $devAutoload)
17782 $this->devAutoload = $devAutoload;
17788 public function getDevAutoload()
17790 return $this->devAutoload;
17798 public function setIncludePaths(array $includePaths)
17800 $this->includePaths = $includePaths;
17806 public function getIncludePaths()
17808 return $this->includePaths;
17816 public function setNotificationUrl($notificationUrl)
17818 $this->notificationUrl = $notificationUrl;
17824 public function getNotificationUrl()
17826 return $this->notificationUrl;
17834 public function setArchiveExcludes(array $excludes)
17836 $this->archiveExcludes = $excludes;
17842 public function getArchiveExcludes()
17844 return $this->archiveExcludes;
17854 public function replaceVersion($version, $prettyVersion)
17856 $this->version = $version;
17857 $this->prettyVersion = $prettyVersion;
17859 $this->stability = VersionParser::parseStability($version);
17860 $this->dev = $this->stability === 'dev';
17863 protected function getUrls($url, $mirrors, $ref, $type, $urlType)
17868 $urls = array($url);
17870 foreach ($mirrors as $mirror) {
17871 if ($urlType === 'dist') {
17872 $mirrorUrl = ComposerMirror::processUrl($mirror['url'], $this->name, $this->version, $ref, $type);
17873 } elseif ($urlType === 'source' && $type === 'git') {
17874 $mirrorUrl = ComposerMirror::processGitUrl($mirror['url'], $this->name, $url, $type);
17875 } elseif ($urlType === 'source' && $type === 'hg') {
17876 $mirrorUrl = ComposerMirror::processHgUrl($mirror['url'], $this->name, $url, $type);
17878 if (!in_array($mirrorUrl, $urls)) {
17879 $func = $mirror['preferred'] ? 'array_unshift' : 'array_push';
17880 $func($urls, $mirrorUrl);
17900 namespace Composer\Package;
17902 use Composer\Package\LinkConstraint\VersionConstraint;
17903 use Composer\Package\Version\VersionParser;
17908 class AliasPackage extends BasePackage implements CompletePackageInterface
17910 protected $version;
17911 protected $prettyVersion;
17913 protected $aliasOf;
17914 protected $rootPackageAlias = false;
17915 protected $stability;
17917 protected $requires;
17918 protected $conflicts;
17919 protected $provides;
17920 protected $replaces;
17921 protected $recommends;
17922 protected $suggests;
17931 public function __construct(PackageInterface $aliasOf, $version, $prettyVersion)
17933 parent::__construct($aliasOf->getName());
17935 $this->version = $version;
17936 $this->prettyVersion = $prettyVersion;
17937 $this->aliasOf = $aliasOf;
17938 $this->stability = VersionParser::parseStability($version);
17939 $this->dev = $this->stability === 'dev';
17942 foreach (array('requires', 'devRequires') as $type) {
17943 $links = $aliasOf->{'get'.ucfirst($type)}();
17944 foreach ($links as $index => $link) {
17946 if ('self.version' === $link->getPrettyConstraint()) {
17947 $links[$index] = new Link($link->getSource(), $link->getTarget(), new VersionConstraint('=', $this->version), $type, $prettyVersion);
17950 $this->$type = $links;
17954 foreach (array('conflicts', 'provides', 'replaces') as $type) {
17955 $links = $aliasOf->{'get'.ucfirst($type)}();
17956 $newLinks = array();
17957 foreach ($links as $link) {
17959 if ('self.version' === $link->getPrettyConstraint()) {
17960 $newLinks[] = new Link($link->getSource(), $link->getTarget(), new VersionConstraint('=', $this->version), $type, $prettyVersion);
17963 $this->$type = array_merge($links, $newLinks);
17967 public function getAliasOf()
17969 return $this->aliasOf;
17975 public function getVersion()
17977 return $this->version;
17983 public function getStability()
17985 return $this->stability;
17991 public function getPrettyVersion()
17993 return $this->prettyVersion;
17999 public function isDev()
18007 public function getRequires()
18009 return $this->requires;
18015 public function getConflicts()
18017 return $this->conflicts;
18023 public function getProvides()
18025 return $this->provides;
18031 public function getReplaces()
18033 return $this->replaces;
18039 public function getDevRequires()
18041 return $this->devRequires;
18053 public function setRootPackageAlias($value)
18055 return $this->rootPackageAlias = $value;
18062 public function isRootPackageAlias()
18064 return $this->rootPackageAlias;
18071 public function getType()
18073 return $this->aliasOf->getType();
18075 public function getTargetDir()
18077 return $this->aliasOf->getTargetDir();
18079 public function getExtra()
18081 return $this->aliasOf->getExtra();
18083 public function setInstallationSource($type)
18085 $this->aliasOf->setInstallationSource($type);
18087 public function getInstallationSource()
18089 return $this->aliasOf->getInstallationSource();
18091 public function getSourceType()
18093 return $this->aliasOf->getSourceType();
18095 public function getSourceUrl()
18097 return $this->aliasOf->getSourceUrl();
18099 public function getSourceUrls()
18101 return $this->aliasOf->getSourceUrls();
18103 public function getSourceReference()
18105 return $this->aliasOf->getSourceReference();
18107 public function setSourceReference($reference)
18109 return $this->aliasOf->setSourceReference($reference);
18111 public function setSourceMirrors($mirrors)
18113 return $this->aliasOf->setSourceMirrors($mirrors);
18115 public function getSourceMirrors()
18117 return $this->aliasOf->getSourceMirrors();
18119 public function getDistType()
18121 return $this->aliasOf->getDistType();
18123 public function getDistUrl()
18125 return $this->aliasOf->getDistUrl();
18127 public function getDistUrls()
18129 return $this->aliasOf->getDistUrls();
18131 public function getDistReference()
18133 return $this->aliasOf->getDistReference();
18135 public function setDistReference($reference)
18137 return $this->aliasOf->setDistReference($reference);
18139 public function getDistSha1Checksum()
18141 return $this->aliasOf->getDistSha1Checksum();
18143 public function setTransportOptions(array $options)
18145 return $this->aliasOf->setTransportOptions($options);
18147 public function getTransportOptions()
18149 return $this->aliasOf->getTransportOptions();
18151 public function setDistMirrors($mirrors)
18153 return $this->aliasOf->setDistMirrors($mirrors);
18155 public function getDistMirrors()
18157 return $this->aliasOf->getDistMirrors();
18159 public function getScripts()
18161 return $this->aliasOf->getScripts();
18163 public function getLicense()
18165 return $this->aliasOf->getLicense();
18167 public function getAutoload()
18169 return $this->aliasOf->getAutoload();
18171 public function getDevAutoload()
18173 return $this->aliasOf->getDevAutoload();
18175 public function getIncludePaths()
18177 return $this->aliasOf->getIncludePaths();
18179 public function getRepositories()
18181 return $this->aliasOf->getRepositories();
18183 public function getReleaseDate()
18185 return $this->aliasOf->getReleaseDate();
18187 public function getBinaries()
18189 return $this->aliasOf->getBinaries();
18191 public function getKeywords()
18193 return $this->aliasOf->getKeywords();
18195 public function getDescription()
18197 return $this->aliasOf->getDescription();
18199 public function getHomepage()
18201 return $this->aliasOf->getHomepage();
18203 public function getSuggests()
18205 return $this->aliasOf->getSuggests();
18207 public function getAuthors()
18209 return $this->aliasOf->getAuthors();
18211 public function getSupport()
18213 return $this->aliasOf->getSupport();
18215 public function getNotificationUrl()
18217 return $this->aliasOf->getNotificationUrl();
18219 public function getArchiveExcludes()
18221 return $this->aliasOf->getArchiveExcludes();
18223 public function isAbandoned()
18225 return $this->aliasOf->isAbandoned();
18227 public function getReplacementPackage()
18229 return $this->aliasOf->getReplacementPackage();
18231 public function __toString()
18233 return parent::__toString().' (alias of '.$this->aliasOf->getVersion().')';
18248 namespace Composer\Package;
18250 use Composer\Package\LinkConstraint\LinkConstraintInterface;
18261 protected $constraint;
18262 protected $description;
18263 protected $prettyConstraint;
18274 public function __construct($source, $target, LinkConstraintInterface $constraint = null, $description = 'relates to', $prettyConstraint = null)
18276 $this->source = strtolower($source);
18277 $this->target = strtolower($target);
18278 $this->constraint = $constraint;
18279 $this->description = $description;
18280 $this->prettyConstraint = $prettyConstraint;
18283 public function getSource()
18285 return $this->source;
18288 public function getTarget()
18290 return $this->target;
18293 public function getConstraint()
18295 return $this->constraint;
18298 public function getPrettyConstraint()
18300 if (null === $this->prettyConstraint) {
18301 throw new \UnexpectedValueException(sprintf('Link %s has been misconfigured and had no prettyConstraint given.', $this));
18304 return $this->prettyConstraint;
18307 public function __toString()
18309 return $this->source.' '.$this->description.' '.$this->target.' ('.$this->constraint.')';
18312 public function getPrettyString(PackageInterface $sourcePackage)
18314 return $sourcePackage->getPrettyString().' '.$this->description.' '.$this->target.' '.$this->constraint->getPrettyString().'';
18329 namespace Composer\Package\LinkConstraint;
18336 class EmptyConstraint implements LinkConstraintInterface
18338 protected $prettyString;
18340 public function matches(LinkConstraintInterface $provider)
18345 public function setPrettyString($prettyString)
18347 $this->prettyString = $prettyString;
18350 public function getPrettyString()
18352 if ($this->prettyString) {
18353 return $this->prettyString;
18356 return $this->__toString();
18359 public function __toString()
18376 namespace Composer\Package\LinkConstraint;
18384 class MultiConstraint implements LinkConstraintInterface
18386 protected $constraints;
18387 protected $prettyString;
18388 protected $conjunctive;
18396 public function __construct(array $constraints, $conjunctive = true)
18398 $this->constraints = $constraints;
18399 $this->conjunctive = $conjunctive;
18402 public function matches(LinkConstraintInterface $provider)
18404 if (false === $this->conjunctive) {
18405 foreach ($this->constraints as $constraint) {
18406 if ($constraint->matches($provider)) {
18414 foreach ($this->constraints as $constraint) {
18415 if (!$constraint->matches($provider)) {
18423 public function setPrettyString($prettyString)
18425 $this->prettyString = $prettyString;
18428 public function getPrettyString()
18430 if ($this->prettyString) {
18431 return $this->prettyString;
18434 return $this->__toString();
18437 public function __toString()
18439 $constraints = array();
18440 foreach ($this->constraints as $constraint) {
18441 $constraints[] = $constraint->__toString();
18444 return '['.implode($this->conjunctive ? ' ' : ' || ', $constraints).']';
18459 namespace Composer\Package\LinkConstraint;
18468 class VersionConstraint extends SpecificConstraint
18479 public function __construct($operator, $version)
18481 if ('=' === $operator) {
18485 if ('<>' === $operator) {
18489 $this->operator = $operator;
18490 $this->version = $version;
18493 public function versionCompare($a, $b, $operator, $compareBranches = false)
18495 $aIsBranch = 'dev-' === substr($a, 0, 4);
18496 $bIsBranch = 'dev-' === substr($b, 0, 4);
18497 if ($aIsBranch && $bIsBranch) {
18498 return $operator == '==' && $a === $b;
18502 if (!$compareBranches && ($aIsBranch || $bIsBranch)) {
18506 return version_compare($a, $b, $operator);
18514 public function matchSpecific(VersionConstraint $provider, $compareBranches = false)
18516 static $cache = array();
18517 if (isset($cache[$this->operator][$this->version][$provider->operator][$provider->version][$compareBranches])) {
18518 return $cache[$this->operator][$this->version][$provider->operator][$provider->version][$compareBranches];
18521 return $cache[$this->operator][$this->version][$provider->operator][$provider->version][$compareBranches] =
18522 $this->doMatchSpecific($provider, $compareBranches);
18530 private function doMatchSpecific(VersionConstraint $provider, $compareBranches = false)
18532 $noEqualOp = str_replace('=', '', $this->operator);
18533 $providerNoEqualOp = str_replace('=', '', $provider->operator);
18535 $isEqualOp = '==' === $this->operator;
18536 $isNonEqualOp = '!=' === $this->operator;
18537 $isProviderEqualOp = '==' === $provider->operator;
18538 $isProviderNonEqualOp = '!=' === $provider->operator;
18542 if ($isNonEqualOp || $isProviderNonEqualOp) {
18543 return !$isEqualOp && !$isProviderEqualOp
18544 || $this->versionCompare($provider->version, $this->version, '!=', $compareBranches);
18549 if ($this->operator != '==' && $noEqualOp == $providerNoEqualOp) {
18553 if ($this->versionCompare($provider->version, $this->version, $this->operator, $compareBranches)) {
18556 if ($provider->version == $this->version && $provider->operator == $providerNoEqualOp && $this->operator != $noEqualOp) {
18566 public function __toString()
18568 return $this->operator.' '.$this->version;
18583 namespace Composer\Package\LinkConstraint;
18590 abstract class SpecificConstraint implements LinkConstraintInterface
18592 protected $prettyString;
18594 public function matches(LinkConstraintInterface $provider)
18596 if ($provider instanceof MultiConstraint) {
18598 return $provider->matches($this);
18599 } elseif ($provider instanceof $this) {
18600 return $this->matchSpecific($provider);
18606 public function setPrettyString($prettyString)
18608 $this->prettyString = $prettyString;
18611 public function getPrettyString()
18613 if ($this->prettyString) {
18614 return $this->prettyString;
18617 return $this->__toString();
18636 namespace Composer\Package\LinkConstraint;
18643 interface LinkConstraintInterface
18645 public function matches(LinkConstraintInterface $provider);
18646 public function setPrettyString($prettyString);
18647 public function getPrettyString();
18648 public function __toString();
18662 namespace Composer;
18664 use Composer\IO\IOInterface;
18665 use Composer\Util\Filesystem;
18666 use Symfony\Component\Finder\Finder;
18675 private static $cacheCollected = false;
18678 private $enabled = true;
18679 private $whitelist;
18680 private $filesystem;
18688 public function __construct(IOInterface $io, $cacheDir, $whitelist = 'a-z0-9.', Filesystem $filesystem = null)
18691 $this->root = rtrim($cacheDir, '/\\') . '/';
18692 $this->whitelist = $whitelist;
18693 $this->filesystem = $filesystem ?: new Filesystem();
18695 if (!is_dir($this->root)) {
18696 if (!@mkdir($this->root, 0777, true)) {
18697 $this->enabled = false;
18702 public function isEnabled()
18704 return $this->enabled;
18707 public function getRoot()
18709 return $this->root;
18712 public function read($file)
18714 $file = preg_replace('{[^'.$this->whitelist.']}i', '-', $file);
18715 if ($this->enabled && file_exists($this->root . $file)) {
18716 if ($this->io->isDebug()) {
18717 $this->io->write('Reading '.$this->root . $file.' from cache');
18720 return file_get_contents($this->root . $file);
18726 public function write($file, $contents)
18728 if ($this->enabled) {
18729 $file = preg_replace('{[^'.$this->whitelist.']}i', '-', $file);
18731 if ($this->io->isDebug()) {
18732 $this->io->write('Writing '.$this->root . $file.' into cache');
18736 return file_put_contents($this->root . $file, $contents);
18737 } catch (\ErrorException $e) {
18738 if (preg_match('{^file_put_contents\(\): Only ([0-9]+) of ([0-9]+) bytes written}', $e->getMessage(), $m)) {
18740 unlink($this->root . $file);
18742 $message = sprintf(
18743 '<warning>Writing %1$s into cache failed after %2$u of %3$u bytes written, only %4$u bytes of free space available</warning>',
18744 $this->root . $file,
18747 @disk_free_space($this->root . dirname($file))
18750 $this->io->write($message);
18765 public function copyFrom($file, $source)
18767 if ($this->enabled) {
18768 $file = preg_replace('{[^'.$this->whitelist.']}i', '-', $file);
18769 $this->filesystem->ensureDirectoryExists(dirname($this->root . $file));
18771 if ($this->io->isDebug()) {
18772 $this->io->write('Writing '.$this->root . $file.' into cache');
18775 return copy($source, $this->root . $file);
18784 public function copyTo($file, $target)
18786 $file = preg_replace('{[^'.$this->whitelist.']}i', '-', $file);
18787 if ($this->enabled && file_exists($this->root . $file)) {
18788 touch($this->root . $file);
18790 if ($this->io->isDebug()) {
18791 $this->io->write('Reading '.$this->root . $file.' from cache');
18794 return copy($this->root . $file, $target);
18800 public function gcIsNecessary()
18802 return (!self::$cacheCollected && !mt_rand(0, 50));
18805 public function remove($file)
18807 $file = preg_replace('{[^'.$this->whitelist.']}i', '-', $file);
18808 if ($this->enabled && file_exists($this->root . $file)) {
18809 return $this->filesystem->unlink($this->root . $file);
18815 public function gc($ttl, $maxSize)
18817 if ($this->enabled) {
18818 $expire = new \DateTime();
18819 $expire->modify('-'.$ttl.' seconds');
18821 $finder = $this->getFinder()->date('until '.$expire->format('Y-m-d H:i:s'));
18822 foreach ($finder as $file) {
18823 $this->filesystem->unlink($file->getPathname());
18826 $totalSize = $this->filesystem->size($this->root);
18827 if ($totalSize > $maxSize) {
18828 $iterator = $this->getFinder()->sortByAccessedTime()->getIterator();
18829 while ($totalSize > $maxSize && $iterator->valid()) {
18830 $filepath = $iterator->current()->getPathname();
18831 $totalSize -= $this->filesystem->size($filepath);
18832 $this->filesystem->unlink($filepath);
18837 self::$cacheCollected = true;
18845 public function sha1($file)
18847 $file = preg_replace('{[^'.$this->whitelist.']}i', '-', $file);
18848 if ($this->enabled && file_exists($this->root . $file)) {
18849 return sha1_file($this->root . $file);
18855 public function sha256($file)
18857 $file = preg_replace('{[^'.$this->whitelist.']}i', '-', $file);
18858 if ($this->enabled && file_exists($this->root . $file)) {
18859 return hash_file('sha256', $this->root . $file);
18865 protected function getFinder()
18867 return Finder::create()->in($this->root)->files();
18882 namespace Composer\DependencyResolver;
18884 use Composer\Package\PackageInterface;
18889 interface PolicyInterface
18891 public function versionCompare(PackageInterface $a, PackageInterface $b, $operator);
18892 public function findUpdatePackages(Pool $pool, array $installedMap, PackageInterface $package);
18893 public function selectPreferedPackages(Pool $pool, array $installedMap, array $literals);
18907 namespace Composer\DependencyResolver;
18912 class RuleSet implements \IteratorAggregate, \Countable
18915 const TYPE_PACKAGE = 0;
18916 const TYPE_JOB = 1;
18917 const TYPE_LEARNED = 4;
18926 protected static $types = array(
18928 self::TYPE_PACKAGE => 'PACKAGE',
18929 self::TYPE_JOB => 'JOB',
18930 self::TYPE_LEARNED => 'LEARNED',
18934 protected $nextRuleId;
18936 protected $rulesByHash;
18938 public function __construct()
18940 $this->nextRuleId = 0;
18942 foreach ($this->getTypes() as $type) {
18943 $this->rules[$type] = array();
18946 $this->rulesByHash = array();
18949 public function add(Rule $rule, $type)
18951 if (!isset(self::$types[$type])) {
18952 throw new \OutOfBoundsException('Unknown rule type: ' . $type);
18955 if (!isset($this->rules[$type])) {
18956 $this->rules[$type] = array();
18959 $this->rules[$type][] = $rule;
18960 $this->ruleById[$this->nextRuleId] = $rule;
18961 $rule->setType($type);
18963 $rule->setId($this->nextRuleId);
18964 $this->nextRuleId++;
18966 $hash = $rule->getHash();
18967 if (!isset($this->rulesByHash[$hash])) {
18968 $this->rulesByHash[$hash] = array($rule);
18970 $this->rulesByHash[$hash][] = $rule;
18974 public function count()
18976 return $this->nextRuleId;
18979 public function ruleById($id)
18981 return $this->ruleById[$id];
18984 public function getRules()
18986 return $this->rules;
18989 public function getIterator()
18991 return new RuleSetIterator($this->getRules());
18994 public function getIteratorFor($types)
18996 if (!is_array($types)) {
18997 $types = array($types);
19000 $allRules = $this->getRules();
19003 foreach ($types as $type) {
19004 $rules[$type] = $allRules[$type];
19007 return new RuleSetIterator($rules);
19010 public function getIteratorWithout($types)
19012 if (!is_array($types)) {
19013 $types = array($types);
19016 $rules = $this->getRules();
19018 foreach ($types as $type) {
19019 unset($rules[$type]);
19022 return new RuleSetIterator($rules);
19025 public function getTypes()
19027 $types = self::$types;
19030 return array_keys($types);
19033 public function containsEqual($rule)
19035 if (isset($this->rulesByHash[$rule->getHash()])) {
19036 $potentialDuplicates = $this->rulesByHash[$rule->getHash()];
19037 foreach ($potentialDuplicates as $potentialDuplicate) {
19038 if ($rule->equals($potentialDuplicate)) {
19047 public function getPrettyString(Pool $pool = null)
19050 foreach ($this->rules as $type => $rules) {
19051 $string .= str_pad(self::$types[$type], 8, ' ') . ": ";
19052 foreach ($rules as $rule) {
19053 $string .= ($pool ? $rule->getPrettyString($pool) : $rule)."\n";
19061 public function __toString()
19063 return $this->getPrettyString(null);
19078 namespace Composer\DependencyResolver;
19083 class SolverBugException extends \RuntimeException
19085 public function __construct($message)
19087 parent::__construct(
19088 $message."\nThis exception was most likely caused by a bug in Composer.\n".
19089 "Please report the command you ran, the exact error you received, and your composer.json on https://github.com/composer/composer/issues - thank you!\n");
19104 namespace Composer\DependencyResolver;
19106 use Composer\Package\PackageInterface;
19107 use Composer\Package\AliasPackage;
19108 use Composer\Package\BasePackage;
19109 use Composer\Package\LinkConstraint\VersionConstraint;
19115 class DefaultPolicy implements PolicyInterface
19117 private $preferStable;
19118 private $preferLowest;
19120 public function __construct($preferStable = false, $preferLowest = false)
19122 $this->preferStable = $preferStable;
19123 $this->preferLowest = $preferLowest;
19126 public function versionCompare(PackageInterface $a, PackageInterface $b, $operator)
19128 if ($this->preferStable && ($stabA = $a->getStability()) !== ($stabB = $b->getStability())) {
19129 return BasePackage::$stabilities[$stabA] < BasePackage::$stabilities[$stabB];
19132 $constraint = new VersionConstraint($operator, $b->getVersion());
19133 $version = new VersionConstraint('==', $a->getVersion());
19135 return $constraint->matchSpecific($version, true);
19138 public function findUpdatePackages(Pool $pool, array $installedMap, PackageInterface $package, $mustMatchName = false)
19140 $packages = array();
19142 foreach ($pool->whatProvides($package->getName(), null, $mustMatchName) as $candidate) {
19143 if ($candidate !== $package) {
19144 $packages[] = $candidate;
19151 public function getPriority(Pool $pool, PackageInterface $package)
19153 return $pool->getPriority($package->getRepository());
19156 public function selectPreferedPackages(Pool $pool, array $installedMap, array $literals, $requiredPackage = null)
19158 $packages = $this->groupLiteralsByNamePreferInstalled($pool, $installedMap, $literals);
19160 foreach ($packages as &$literals) {
19162 usort($literals, function ($a, $b) use ($policy, $pool, $installedMap, $requiredPackage) {
19163 return $policy->compareByPriorityPreferInstalled($pool, $installedMap, $pool->literalToPackage($a), $pool->literalToPackage($b), $requiredPackage, true);
19167 foreach ($packages as &$literals) {
19168 $literals = $this->pruneToBestVersion($pool, $literals);
19170 $literals = $this->pruneToHighestPriorityOrInstalled($pool, $installedMap, $literals);
19172 $literals = $this->pruneRemoteAliases($pool, $literals);
19175 $selected = call_user_func_array('array_merge', $packages);
19178 usort($selected, function ($a, $b) use ($policy, $pool, $installedMap, $requiredPackage) {
19179 return $policy->compareByPriorityPreferInstalled($pool, $installedMap, $pool->literalToPackage($a), $pool->literalToPackage($b), $requiredPackage);
19185 protected function groupLiteralsByNamePreferInstalled(Pool $pool, array $installedMap, $literals)
19187 $packages = array();
19188 foreach ($literals as $literal) {
19189 $packageName = $pool->literalToPackage($literal)->getName();
19191 if (!isset($packages[$packageName])) {
19192 $packages[$packageName] = array();
19195 if (isset($installedMap[abs($literal)])) {
19196 array_unshift($packages[$packageName], $literal);
19198 $packages[$packageName][] = $literal;
19208 public function compareByPriorityPreferInstalled(Pool $pool, array $installedMap, PackageInterface $a, PackageInterface $b, $requiredPackage = null, $ignoreReplace = false)
19210 if ($a->getRepository() === $b->getRepository()) {
19212 if ($a->getName() === $b->getName()) {
19213 $aAliased = $a instanceof AliasPackage;
19214 $bAliased = $b instanceof AliasPackage;
19215 if ($aAliased && !$bAliased) {
19218 if (!$aAliased && $bAliased) {
19223 if (!$ignoreReplace) {
19225 if ($this->replaces($a, $b)) {
19228 if ($this->replaces($b, $a)) {
19234 if ($requiredPackage && false !== ($pos = strpos($requiredPackage, '/'))) {
19235 $requiredVendor = substr($requiredPackage, 0, $pos);
19237 $aIsSameVendor = substr($a->getName(), 0, $pos) === $requiredVendor;
19238 $bIsSameVendor = substr($b->getName(), 0, $pos) === $requiredVendor;
19240 if ($bIsSameVendor !== $aIsSameVendor) {
19241 return $aIsSameVendor ? -1 : 1;
19247 if ($a->id === $b->id) {
19251 return ($a->id < $b->id) ? -1 : 1;
19254 if (isset($installedMap[$a->id])) {
19258 if (isset($installedMap[$b->id])) {
19262 return ($this->getPriority($pool, $a) > $this->getPriority($pool, $b)) ? -1 : 1;
19275 protected function replaces(PackageInterface $source, PackageInterface $target)
19277 foreach ($source->getReplaces() as $link) {
19278 if ($link->getTarget() === $target->getName()
19289 protected function pruneToBestVersion(Pool $pool, $literals)
19291 $operator = $this->preferLowest ? '<' : '>';
19292 $bestLiterals = array($literals[0]);
19293 $bestPackage = $pool->literalToPackage($literals[0]);
19294 foreach ($literals as $i => $literal) {
19299 $package = $pool->literalToPackage($literal);
19301 if ($this->versionCompare($package, $bestPackage, $operator)) {
19302 $bestPackage = $package;
19303 $bestLiterals = array($literal);
19304 } elseif ($this->versionCompare($package, $bestPackage, '==')) {
19305 $bestLiterals[] = $literal;
19309 return $bestLiterals;
19315 protected function pruneToHighestPriorityOrInstalled(Pool $pool, array $installedMap, array $literals)
19317 $selected = array();
19321 foreach ($literals as $literal) {
19322 $package = $pool->literalToPackage($literal);
19324 if (isset($installedMap[$package->id])) {
19325 $selected[] = $literal;
19329 if (null === $priority) {
19330 $priority = $this->getPriority($pool, $package);
19333 if ($this->getPriority($pool, $package) != $priority) {
19337 $selected[] = $literal;
19348 protected function pruneRemoteAliases(Pool $pool, array $literals)
19350 $hasLocalAlias = false;
19352 foreach ($literals as $literal) {
19353 $package = $pool->literalToPackage($literal);
19355 if ($package instanceof AliasPackage && $package->isRootPackageAlias()) {
19356 $hasLocalAlias = true;
19361 if (!$hasLocalAlias) {
19365 $selected = array();
19366 foreach ($literals as $literal) {
19367 $package = $pool->literalToPackage($literal);
19369 if ($package instanceof AliasPackage && $package->isRootPackageAlias()) {
19370 $selected[] = $literal;
19389 namespace Composer\DependencyResolver;
19396 class Decisions implements \Iterator, \Countable
19398 const DECISION_LITERAL = 0;
19399 const DECISION_REASON = 1;
19402 protected $decisionMap;
19403 protected $decisionQueue = array();
19405 public function __construct($pool)
19407 $this->pool = $pool;
19408 $this->decisionMap = array();
19411 public function decide($literal, $level, $why)
19413 $this->addDecision($literal, $level);
19414 $this->decisionQueue[] = array(
19415 self::DECISION_LITERAL => $literal,
19416 self::DECISION_REASON => $why,
19420 public function satisfy($literal)
19422 $packageId = abs($literal);
19425 $literal > 0 && isset($this->decisionMap[$packageId]) && $this->decisionMap[$packageId] > 0 ||
19426 $literal < 0 && isset($this->decisionMap[$packageId]) && $this->decisionMap[$packageId] < 0
19430 public function conflict($literal)
19432 $packageId = abs($literal);
19435 (isset($this->decisionMap[$packageId]) && $this->decisionMap[$packageId] > 0 && $literal < 0) ||
19436 (isset($this->decisionMap[$packageId]) && $this->decisionMap[$packageId] < 0 && $literal > 0)
19440 public function decided($literalOrPackageId)
19442 return !empty($this->decisionMap[abs($literalOrPackageId)]);
19445 public function undecided($literalOrPackageId)
19447 return empty($this->decisionMap[abs($literalOrPackageId)]);
19450 public function decidedInstall($literalOrPackageId)
19452 $packageId = abs($literalOrPackageId);
19454 return isset($this->decisionMap[$packageId]) && $this->decisionMap[$packageId] > 0;
19457 public function decisionLevel($literalOrPackageId)
19459 $packageId = abs($literalOrPackageId);
19460 if (isset($this->decisionMap[$packageId])) {
19461 return abs($this->decisionMap[$packageId]);
19467 public function decisionRule($literalOrPackageId)
19469 $packageId = abs($literalOrPackageId);
19471 foreach ($this->decisionQueue as $i => $decision) {
19472 if ($packageId === abs($decision[self::DECISION_LITERAL])) {
19473 return $decision[self::DECISION_REASON];
19480 public function atOffset($queueOffset)
19482 return $this->decisionQueue[$queueOffset];
19485 public function validOffset($queueOffset)
19487 return $queueOffset >= 0 && $queueOffset < count($this->decisionQueue);
19490 public function lastReason()
19492 return $this->decisionQueue[count($this->decisionQueue) - 1][self::DECISION_REASON];
19495 public function lastLiteral()
19497 return $this->decisionQueue[count($this->decisionQueue) - 1][self::DECISION_LITERAL];
19500 public function reset()
19502 while ($decision = array_pop($this->decisionQueue)) {
19503 $this->decisionMap[abs($decision[self::DECISION_LITERAL])] = 0;
19507 public function resetToOffset($offset)
19509 while (count($this->decisionQueue) > $offset + 1) {
19510 $decision = array_pop($this->decisionQueue);
19511 $this->decisionMap[abs($decision[self::DECISION_LITERAL])] = 0;
19515 public function revertLast()
19517 $this->decisionMap[abs($this->lastLiteral())] = 0;
19518 array_pop($this->decisionQueue);
19521 public function count()
19523 return count($this->decisionQueue);
19526 public function rewind()
19528 end($this->decisionQueue);
19531 public function current()
19533 return current($this->decisionQueue);
19536 public function key()
19538 return key($this->decisionQueue);
19541 public function next()
19543 return prev($this->decisionQueue);
19546 public function valid()
19548 return false !== current($this->decisionQueue);
19551 public function isEmpty()
19553 return count($this->decisionQueue) === 0;
19556 protected function addDecision($literal, $level)
19558 $packageId = abs($literal);
19560 $previousDecision = isset($this->decisionMap[$packageId]) ? $this->decisionMap[$packageId] : null;
19561 if ($previousDecision != 0) {
19562 $literalString = $this->pool->literalToString($literal);
19563 $package = $this->pool->literalToPackage($literal);
19564 throw new SolverBugException(
19565 "Trying to decide $literalString on level $level, even though $package was previously decided as ".(int) $previousDecision."."
19569 if ($literal > 0) {
19570 $this->decisionMap[$packageId] = $level;
19572 $this->decisionMap[$packageId] = -$level;
19588 namespace Composer\DependencyResolver;
19597 class RuleWatchNode
19609 public function __construct($rule)
19611 $this->rule = $rule;
19613 $literals = $rule->literals;
19615 $this->watch1 = count($literals) > 0 ? $literals[0] : 0;
19616 $this->watch2 = count($literals) > 1 ? $literals[1] : 0;
19627 public function watch2OnHighest(Decisions $decisions)
19629 $literals = $this->rule->literals;
19632 if (count($literals) < 3) {
19638 foreach ($literals as $literal) {
19639 $level = $decisions->decisionLevel($literal);
19641 if ($level > $watchLevel) {
19642 $this->watch2 = $literal;
19643 $watchLevel = $level;
19653 public function getRule()
19655 return $this->rule;
19664 public function getOtherWatch($literal)
19666 if ($this->watch1 == $literal) {
19667 return $this->watch2;
19669 return $this->watch1;
19679 public function moveWatch($from, $to)
19681 if ($this->watch1 == $from) {
19682 $this->watch1 = $to;
19684 $this->watch2 = $to;
19700 namespace Composer\DependencyResolver;
19705 class SolverProblemsException extends \RuntimeException
19707 protected $problems;
19708 protected $installedMap;
19710 public function __construct(array $problems, array $installedMap)
19712 $this->problems = $problems;
19713 $this->installedMap = $installedMap;
19715 parent::__construct($this->createMessage(), 2);
19718 protected function createMessage()
19721 foreach ($this->problems as $i => $problem) {
19722 $text .= " Problem ".($i+1).$problem->getPrettyString($this->installedMap)."\n";
19725 if (strpos($text, 'could not be found') || strpos($text, 'no matching package found')) {
19726 $text .= "\nPotential causes:\n - A typo in the package name\n - The package is not available in a stable-enough version according to your minimum-stability setting\n see <https://groups.google.com/d/topic/composer-dev/_g3ASeIFlrc/discussion> for more details.\n\nRead <http://getcomposer.org/doc/articles/troubleshooting.md> for further common problems.";
19732 public function getProblems()
19734 return $this->problems;
19749 namespace Composer\DependencyResolver;
19751 use Composer\Package\AliasPackage;
19760 protected $installedMap;
19761 protected $decisions;
19762 protected $transaction;
19764 public function __construct($policy, $pool, $installedMap, $decisions)
19766 $this->policy = $policy;
19767 $this->pool = $pool;
19768 $this->installedMap = $installedMap;
19769 $this->decisions = $decisions;
19770 $this->transaction = array();
19773 public function getOperations()
19775 $installMeansUpdateMap = $this->findUpdates();
19777 $updateMap = array();
19778 $installMap = array();
19779 $uninstallMap = array();
19781 foreach ($this->decisions as $i => $decision) {
19782 $literal = $decision[Decisions::DECISION_LITERAL];
19783 $reason = $decision[Decisions::DECISION_REASON];
19785 $package = $this->pool->literalToPackage($literal);
19788 if (($literal > 0) == (isset($this->installedMap[$package->id]))) {
19792 if ($literal > 0) {
19793 if (isset($installMeansUpdateMap[abs($literal)]) && !$package instanceof AliasPackage) {
19794 $source = $installMeansUpdateMap[abs($literal)];
19796 $updateMap[$package->id] = array(
19797 'package' => $package,
19798 'source' => $source,
19799 'reason' => $reason,
19803 unset($installMeansUpdateMap[abs($literal)]);
19804 $ignoreRemove[$source->id] = true;
19806 $installMap[$package->id] = array(
19807 'package' => $package,
19808 'reason' => $reason,
19814 foreach ($this->decisions as $i => $decision) {
19815 $literal = $decision[Decisions::DECISION_LITERAL];
19816 $reason = $decision[Decisions::DECISION_REASON];
19817 $package = $this->pool->literalToPackage($literal);
19819 if ($literal <= 0 &&
19820 isset($this->installedMap[$package->id]) &&
19821 !isset($ignoreRemove[$package->id])) {
19822 $uninstallMap[$package->id] = array(
19823 'package' => $package,
19824 'reason' => $reason,
19829 $this->transactionFromMaps($installMap, $updateMap, $uninstallMap);
19831 return $this->transaction;
19834 protected function transactionFromMaps($installMap, $updateMap, $uninstallMap)
19836 $queue = array_map(function ($operation) {
19837 return $operation['package'];
19839 $this->findRootPackages($installMap, $updateMap)
19842 $visited = array();
19844 while (!empty($queue)) {
19845 $package = array_pop($queue);
19846 $packageId = $package->id;
19848 if (!isset($visited[$packageId])) {
19849 array_push($queue, $package);
19851 if ($package instanceof AliasPackage) {
19852 array_push($queue, $package->getAliasOf());
19854 foreach ($package->getRequires() as $link) {
19855 $possibleRequires = $this->pool->whatProvides($link->getTarget(), $link->getConstraint());
19857 foreach ($possibleRequires as $require) {
19858 array_push($queue, $require);
19863 $visited[$package->id] = true;
19865 if (isset($installMap[$packageId])) {
19867 $installMap[$packageId]['package'],
19868 $installMap[$packageId]['reason']
19870 unset($installMap[$packageId]);
19872 if (isset($updateMap[$packageId])) {
19874 $updateMap[$packageId]['source'],
19875 $updateMap[$packageId]['package'],
19876 $updateMap[$packageId]['reason']
19878 unset($updateMap[$packageId]);
19883 foreach ($uninstallMap as $uninstall) {
19884 $this->uninstall($uninstall['package'], $uninstall['reason']);
19888 protected function findRootPackages($installMap, $updateMap)
19890 $packages = $installMap + $updateMap;
19891 $roots = $packages;
19893 foreach ($packages as $packageId => $operation) {
19894 $package = $operation['package'];
19896 if (!isset($roots[$packageId])) {
19900 foreach ($package->getRequires() as $link) {
19901 $possibleRequires = $this->pool->whatProvides($link->getTarget(), $link->getConstraint());
19903 foreach ($possibleRequires as $require) {
19904 unset($roots[$require->id]);
19912 protected function findUpdates()
19914 $installMeansUpdateMap = array();
19916 foreach ($this->decisions as $i => $decision) {
19917 $literal = $decision[Decisions::DECISION_LITERAL];
19918 $package = $this->pool->literalToPackage($literal);
19920 if ($package instanceof AliasPackage) {
19925 if ($literal <= 0 && isset($this->installedMap[$package->id])) {
19926 $updates = $this->policy->findUpdatePackages($this->pool, $this->installedMap, $package);
19928 $literals = array($package->id);
19930 foreach ($updates as $update) {
19931 $literals[] = $update->id;
19934 foreach ($literals as $updateLiteral) {
19935 if ($updateLiteral !== $literal) {
19936 $installMeansUpdateMap[abs($updateLiteral)] = $package;
19942 return $installMeansUpdateMap;
19945 protected function install($package, $reason)
19947 if ($package instanceof AliasPackage) {
19948 return $this->markAliasInstalled($package, $reason);
19951 $this->transaction[] = new Operation\InstallOperation($package, $reason);
19954 protected function update($from, $to, $reason)
19956 $this->transaction[] = new Operation\UpdateOperation($from, $to, $reason);
19959 protected function uninstall($package, $reason)
19961 if ($package instanceof AliasPackage) {
19962 return $this->markAliasUninstalled($package, $reason);
19965 $this->transaction[] = new Operation\UninstallOperation($package, $reason);
19968 protected function markAliasInstalled($package, $reason)
19970 $this->transaction[] = new Operation\MarkAliasInstalledOperation($package, $reason);
19973 protected function markAliasUninstalled($package, $reason)
19975 $this->transaction[] = new Operation\MarkAliasUninstalledOperation($package, $reason);
19990 namespace Composer\DependencyResolver\Operation;
19992 use Composer\Package\PackageInterface;
19999 class UninstallOperation extends SolverOperation
20001 protected $package;
20009 public function __construct(PackageInterface $package, $reason = null)
20011 parent::__construct($reason);
20013 $this->package = $package;
20021 public function getPackage()
20023 return $this->package;
20031 public function getJobType()
20033 return 'uninstall';
20039 public function __toString()
20041 return 'Uninstalling '.$this->package->getPrettyName().' ('.$this->formatVersion($this->package).')';
20056 namespace Composer\DependencyResolver\Operation;
20058 use Composer\Package\PackageInterface;
20065 class UpdateOperation extends SolverOperation
20067 protected $initialPackage;
20068 protected $targetPackage;
20077 public function __construct(PackageInterface $initial, PackageInterface $target, $reason = null)
20079 parent::__construct($reason);
20081 $this->initialPackage = $initial;
20082 $this->targetPackage = $target;
20090 public function getInitialPackage()
20092 return $this->initialPackage;
20100 public function getTargetPackage()
20102 return $this->targetPackage;
20110 public function getJobType()
20118 public function __toString()
20120 return 'Updating '.$this->initialPackage->getPrettyName().' ('.$this->formatVersion($this->initialPackage).') to '.
20121 $this->targetPackage->getPrettyName(). ' ('.$this->formatVersion($this->targetPackage).')';
20136 namespace Composer\DependencyResolver\Operation;
20138 use Composer\Package\AliasPackage;
20145 class MarkAliasInstalledOperation extends SolverOperation
20147 protected $package;
20155 public function __construct(AliasPackage $package, $reason = null)
20157 parent::__construct($reason);
20159 $this->package = $package;
20167 public function getPackage()
20169 return $this->package;
20177 public function getJobType()
20179 return 'markAliasInstalled';
20185 public function __toString()
20187 return 'Marking '.$this->package->getPrettyName().' ('.$this->formatVersion($this->package).') as installed, alias of '.$this->package->getAliasOf()->getPrettyName().' ('.$this->formatVersion($this->package->getAliasOf()).')';
20202 namespace Composer\DependencyResolver\Operation;
20204 use Composer\Package\PackageInterface;
20211 class InstallOperation extends SolverOperation
20213 protected $package;
20221 public function __construct(PackageInterface $package, $reason = null)
20223 parent::__construct($reason);
20225 $this->package = $package;
20233 public function getPackage()
20235 return $this->package;
20243 public function getJobType()
20251 public function __toString()
20253 return 'Installing '.$this->package->getPrettyName().' ('.$this->formatVersion($this->package).')';
20268 namespace Composer\DependencyResolver\Operation;
20270 use Composer\Package\Version\VersionParser;
20271 use Composer\Package\PackageInterface;
20278 abstract class SolverOperation implements OperationInterface
20287 public function __construct($reason = null)
20289 $this->reason = $reason;
20297 public function getReason()
20299 return $this->reason;
20302 protected function formatVersion(PackageInterface $package)
20304 return VersionParser::formatVersion($package);
20319 namespace Composer\DependencyResolver\Operation;
20321 use Composer\Package\AliasPackage;
20328 class MarkAliasUninstalledOperation extends SolverOperation
20330 protected $package;
20338 public function __construct(AliasPackage $package, $reason = null)
20340 parent::__construct($reason);
20342 $this->package = $package;
20350 public function getPackage()
20352 return $this->package;
20360 public function getJobType()
20362 return 'markAliasUninstalled';
20368 public function __toString()
20370 return 'Marking '.$this->package->getPrettyName().' ('.$this->formatVersion($this->package).') as uninstalled, alias of '.$this->package->getAliasOf()->getPrettyName().' ('.$this->formatVersion($this->package->getAliasOf()).')';
20385 namespace Composer\DependencyResolver\Operation;
20392 interface OperationInterface
20399 public function getJobType();
20406 public function getReason();
20413 public function __toString();
20427 namespace Composer\DependencyResolver;
20429 use Composer\Package\BasePackage;
20430 use Composer\Package\AliasPackage;
20431 use Composer\Package\Version\VersionParser;
20432 use Composer\Package\LinkConstraint\LinkConstraintInterface;
20433 use Composer\Package\LinkConstraint\VersionConstraint;
20434 use Composer\Package\LinkConstraint\EmptyConstraint;
20435 use Composer\Repository\RepositoryInterface;
20436 use Composer\Repository\CompositeRepository;
20437 use Composer\Repository\ComposerRepository;
20438 use Composer\Repository\InstalledRepositoryInterface;
20439 use Composer\Repository\PlatformRepository;
20440 use Composer\Package\PackageInterface;
20450 const MATCH_NAME = -1;
20451 const MATCH_NONE = 0;
20453 const MATCH_PROVIDE = 2;
20454 const MATCH_REPLACE = 3;
20455 const MATCH_FILTERED = 4;
20457 protected $repositories = array();
20458 protected $providerRepos = array();
20459 protected $packages = array();
20460 protected $packageByName = array();
20461 protected $packageByExactName = array();
20462 protected $acceptableStabilities;
20463 protected $stabilityFlags;
20464 protected $versionParser;
20465 protected $providerCache = array();
20466 protected $filterRequires;
20467 protected $whitelist = null;
20470 public function __construct($minimumStability = 'stable', array $stabilityFlags = array(), array $filterRequires = array())
20472 $stabilities = BasePackage::$stabilities;
20473 $this->versionParser = new VersionParser;
20474 $this->acceptableStabilities = array();
20475 foreach (BasePackage::$stabilities as $stability => $value) {
20476 if ($value <= BasePackage::$stabilities[$minimumStability]) {
20477 $this->acceptableStabilities[$stability] = $value;
20480 $this->stabilityFlags = $stabilityFlags;
20481 $this->filterRequires = $filterRequires;
20484 public function setWhitelist($whitelist)
20486 $this->whitelist = $whitelist;
20487 $this->providerCache = array();
20496 public function addRepository(RepositoryInterface $repo, $rootAliases = array())
20498 if ($repo instanceof CompositeRepository) {
20499 $repos = $repo->getRepositories();
20501 $repos = array($repo);
20504 foreach ($repos as $repo) {
20505 $this->repositories[] = $repo;
20507 $exempt = $repo instanceof PlatformRepository || $repo instanceof InstalledRepositoryInterface;
20509 if ($repo instanceof ComposerRepository && $repo->hasProviders()) {
20510 $this->providerRepos[] = $repo;
20511 $repo->setRootAliases($rootAliases);
20512 $repo->resetPackageIds();
20514 foreach ($repo->getPackages() as $package) {
20515 $names = $package->getNames();
20516 $stability = $package->getStability();
20517 if ($exempt || $this->isPackageAcceptable($names, $stability)) {
20518 $package->setId($this->id++);
20519 $this->packages[] = $package;
20520 $this->packageByExactName[$package->getName()][$package->id] = $package;
20522 foreach ($names as $provided) {
20523 $this->packageByName[$provided][] = $package;
20527 $name = $package->getName();
20528 if (isset($rootAliases[$name][$package->getVersion()])) {
20529 $alias = $rootAliases[$name][$package->getVersion()];
20530 if ($package instanceof AliasPackage) {
20531 $package = $package->getAliasOf();
20533 $aliasPackage = new AliasPackage($package, $alias['alias_normalized'], $alias['alias']);
20534 $aliasPackage->setRootPackageAlias(true);
20535 $aliasPackage->setId($this->id++);
20537 $package->getRepository()->addPackage($aliasPackage);
20538 $this->packages[] = $aliasPackage;
20539 $this->packageByExactName[$aliasPackage->getName()][$aliasPackage->id] = $aliasPackage;
20541 foreach ($aliasPackage->getNames() as $name) {
20542 $this->packageByName[$name][] = $aliasPackage;
20551 public function getPriority(RepositoryInterface $repo)
20553 $priority = array_search($repo, $this->repositories, true);
20555 if (false === $priority) {
20556 throw new \RuntimeException("Could not determine repository priority. The repository was not registered in the pool.");
20568 public function packageById($id)
20570 return $this->packages[$id - 1];
20583 public function whatProvides($name, LinkConstraintInterface $constraint = null, $mustMatchName = false)
20585 $key = ((int) $mustMatchName).$constraint;
20586 if (isset($this->providerCache[$name][$key])) {
20587 return $this->providerCache[$name][$key];
20590 return $this->providerCache[$name][$key] = $this->computeWhatProvides($name, $constraint, $mustMatchName);
20596 private function computeWhatProvides($name, $constraint, $mustMatchName = false)
20598 $candidates = array();
20600 foreach ($this->providerRepos as $repo) {
20601 foreach ($repo->whatProvides($this, $name) as $candidate) {
20602 $candidates[] = $candidate;
20603 if ($candidate->id < 1) {
20604 $candidate->setId($this->id++);
20605 $this->packages[$this->id - 2] = $candidate;
20610 if ($mustMatchName) {
20611 $candidates = array_filter($candidates, function ($candidate) use ($name) {
20612 return $candidate->getName() == $name;
20614 if (isset($this->packageByExactName[$name])) {
20615 $candidates = array_merge($candidates, $this->packageByExactName[$name]);
20617 } elseif (isset($this->packageByName[$name])) {
20618 $candidates = array_merge($candidates, $this->packageByName[$name]);
20621 $matches = $provideMatches = array();
20622 $nameMatch = false;
20624 foreach ($candidates as $candidate) {
20625 $aliasOfCandidate = null;
20629 if ($candidate instanceof AliasPackage) {
20630 $aliasOfCandidate = $candidate->getAliasOf();
20633 if ($this->whitelist !== null && (
20634 (!($candidate instanceof AliasPackage) && !isset($this->whitelist[$candidate->id])) ||
20635 ($candidate instanceof AliasPackage && !isset($this->whitelist[$aliasOfCandidate->id]))
20639 switch ($this->match($candidate, $name, $constraint)) {
20640 case self::MATCH_NONE:
20643 case self::MATCH_NAME:
20649 $matches[] = $candidate;
20652 case self::MATCH_PROVIDE:
20653 $provideMatches[] = $candidate;
20656 case self::MATCH_REPLACE:
20657 $matches[] = $candidate;
20660 case self::MATCH_FILTERED:
20664 throw new \UnexpectedValueException('Unexpected match type');
20673 return array_merge($matches, $provideMatches);
20676 public function literalToPackage($literal)
20678 $packageId = abs($literal);
20680 return $this->packageById($packageId);
20683 public function literalToString($literal)
20685 return ($literal > 0 ? '+' : '-') . $this->literalToPackage($literal);
20688 public function literalToPrettyString($literal, $installedMap)
20690 $package = $this->literalToPackage($literal);
20692 if (isset($installedMap[$package->id])) {
20693 $prefix = ($literal > 0 ? 'keep' : 'remove');
20695 $prefix = ($literal > 0 ? 'install' : 'don\'t install');
20698 return $prefix.' '.$package->getPrettyString();
20701 public function isPackageAcceptable($name, $stability)
20703 foreach ((array) $name as $n) {
20705 if (!isset($this->stabilityFlags[$n]) && isset($this->acceptableStabilities[$stability])) {
20710 if (isset($this->stabilityFlags[$n]) && BasePackage::$stabilities[$stability] <= $this->stabilityFlags[$n]) {
20727 private function match($candidate, $name, LinkConstraintInterface $constraint = null)
20729 $candidateName = $candidate->getName();
20730 $candidateVersion = $candidate->getVersion();
20731 $isDev = $candidate->getStability() === 'dev';
20732 $isAlias = $candidate instanceof AliasPackage;
20734 if (!$isDev && !$isAlias && isset($this->filterRequires[$name])) {
20735 $requireFilter = $this->filterRequires[$name];
20737 $requireFilter = new EmptyConstraint;
20740 if ($candidateName === $name) {
20741 $pkgConstraint = new VersionConstraint('==', $candidateVersion);
20743 if ($constraint === null || $constraint->matches($pkgConstraint)) {
20744 return $requireFilter->matches($pkgConstraint) ? self::MATCH : self::MATCH_FILTERED;
20747 return self::MATCH_NAME;
20750 $provides = $candidate->getProvides();
20751 $replaces = $candidate->getReplaces();
20754 if (isset($replaces[0]) || isset($provides[0])) {
20755 foreach ($provides as $link) {
20756 if ($link->getTarget() === $name && ($constraint === null || $constraint->matches($link->getConstraint()))) {
20757 return $requireFilter->matches($link->getConstraint()) ? self::MATCH_PROVIDE : self::MATCH_FILTERED;
20761 foreach ($replaces as $link) {
20762 if ($link->getTarget() === $name && ($constraint === null || $constraint->matches($link->getConstraint()))) {
20763 return $requireFilter->matches($link->getConstraint()) ? self::MATCH_REPLACE : self::MATCH_FILTERED;
20767 return self::MATCH_NONE;
20770 if (isset($provides[$name]) && ($constraint === null || $constraint->matches($provides[$name]->getConstraint()))) {
20771 return $requireFilter->matches($provides[$name]->getConstraint()) ? self::MATCH_PROVIDE : self::MATCH_FILTERED;
20774 if (isset($replaces[$name]) && ($constraint === null || $constraint->matches($replaces[$name]->getConstraint()))) {
20775 return $requireFilter->matches($replaces[$name]->getConstraint()) ? self::MATCH_REPLACE : self::MATCH_FILTERED;
20778 return self::MATCH_NONE;
20793 namespace Composer\DependencyResolver;
20800 const RULE_INTERNAL_ALLOW_UPDATE = 1;
20801 const RULE_JOB_INSTALL = 2;
20802 const RULE_JOB_REMOVE = 3;
20803 const RULE_PACKAGE_CONFLICT = 6;
20804 const RULE_PACKAGE_REQUIRES = 7;
20805 const RULE_PACKAGE_OBSOLETES = 8;
20806 const RULE_INSTALLED_PACKAGE_OBSOLETES = 9;
20807 const RULE_PACKAGE_SAME_NAME = 10;
20808 const RULE_PACKAGE_IMPLICIT_OBSOLETES = 11;
20809 const RULE_LEARNED = 12;
20810 const RULE_PACKAGE_ALIAS = 13;
20818 protected $disabled;
20822 protected $reasonData;
20826 protected $ruleHash;
20828 public function __construct(array $literals, $reason, $reasonData, $job = null)
20833 $this->literals = $literals;
20834 $this->reason = $reason;
20835 $this->reasonData = $reasonData;
20837 $this->disabled = false;
20843 $this->ruleHash = substr(md5(implode(',', $this->literals)), 0, 5);
20846 public function getHash()
20848 return $this->ruleHash;
20851 public function setId($id)
20856 public function getId()
20861 public function getJob()
20866 public function getReason()
20868 return $this->reason;
20871 public function getReasonData()
20873 return $this->reasonData;
20876 public function getRequiredPackage()
20878 if ($this->reason === self::RULE_JOB_INSTALL) {
20879 return $this->reasonData;
20882 if ($this->reason === self::RULE_PACKAGE_REQUIRES) {
20883 return $this->reasonData->getTarget();
20895 public function equals(Rule $rule)
20897 if ($this->ruleHash !== $rule->ruleHash) {
20901 if (count($this->literals) != count($rule->literals)) {
20905 for ($i = 0, $n = count($this->literals); $i < $n; $i++) {
20906 if ($this->literals[$i] !== $rule->literals[$i]) {
20914 public function setType($type)
20916 $this->type = $type;
20919 public function getType()
20921 return $this->type;
20924 public function disable()
20926 $this->disabled = true;
20929 public function enable()
20931 $this->disabled = false;
20934 public function isDisabled()
20936 return $this->disabled;
20939 public function isEnabled()
20941 return !$this->disabled;
20947 public function getLiterals()
20949 return $this->literals;
20952 public function isAssertion()
20954 return 1 === count($this->literals);
20957 public function getPrettyString(Pool $pool, array $installedMap = array())
20960 foreach ($this->literals as $i => $literal) {
20964 $ruleText .= $pool->literalToPrettyString($literal, $installedMap);
20967 switch ($this->reason) {
20968 case self::RULE_INTERNAL_ALLOW_UPDATE:
20971 case self::RULE_JOB_INSTALL:
20972 return "Install command rule ($ruleText)";
20974 case self::RULE_JOB_REMOVE:
20975 return "Remove command rule ($ruleText)";
20977 case self::RULE_PACKAGE_CONFLICT:
20978 $package1 = $pool->literalToPackage($this->literals[0]);
20979 $package2 = $pool->literalToPackage($this->literals[1]);
20981 return $package1->getPrettyString().' conflicts with '.$this->formatPackagesUnique($pool, array($package2)).'.';
20983 case self::RULE_PACKAGE_REQUIRES:
20984 $literals = $this->literals;
20985 $sourceLiteral = array_shift($literals);
20986 $sourcePackage = $pool->literalToPackage($sourceLiteral);
20988 $requires = array();
20989 foreach ($literals as $literal) {
20990 $requires[] = $pool->literalToPackage($literal);
20993 $text = $this->reasonData->getPrettyString($sourcePackage);
20995 $text .= ' -> satisfiable by ' . $this->formatPackagesUnique($pool, $requires) . '.';
20997 $targetName = $this->reasonData->getTarget();
21000 if (0 === strpos($targetName, 'ext-')) {
21001 $ext = substr($targetName, 4);
21002 $error = extension_loaded($ext) ? 'has the wrong version ('.(phpversion($ext) ?: '0').') installed' : 'is missing from your system';
21004 $text .= ' -> the requested PHP extension '.$ext.' '.$error.'.';
21005 } elseif (0 === strpos($targetName, 'lib-')) {
21007 $lib = substr($targetName, 4);
21009 $text .= ' -> the requested linked library '.$lib.' has the wrong version installed or is missing from your system, make sure to have the extension providing it.';
21011 $text .= ' -> no matching package found.';
21017 case self::RULE_PACKAGE_OBSOLETES:
21019 case self::RULE_INSTALLED_PACKAGE_OBSOLETES:
21021 case self::RULE_PACKAGE_SAME_NAME:
21022 return 'Can only install one of: ' . $this->formatPackagesUnique($pool, $this->literals) . '.';
21023 case self::RULE_PACKAGE_IMPLICIT_OBSOLETES:
21025 case self::RULE_LEARNED:
21026 return 'Conclusion: '.$ruleText;
21027 case self::RULE_PACKAGE_ALIAS:
21030 return '('.$ruleText.')';
21034 protected function formatPackagesUnique($pool, array $packages)
21036 $prepared = array();
21037 foreach ($packages as $package) {
21038 if (!is_object($package)) {
21039 $package = $pool->literalToPackage($package);
21041 $prepared[$package->getName()]['name'] = $package->getPrettyName();
21042 $prepared[$package->getName()]['versions'][$package->getVersion()] = $package->getPrettyVersion();
21044 foreach ($prepared as $name => $package) {
21045 $prepared[$name] = $package['name'].'['.implode(', ', $package['versions']).']';
21048 return implode(', ', $prepared);
21056 public function __toString()
21058 $result = ($this->isDisabled()) ? 'disabled(' : '(';
21060 foreach ($this->literals as $i => $literal) {
21064 $result .= $literal;
21084 namespace Composer\DependencyResolver;
21086 use Composer\Package\PackageInterface;
21087 use Composer\Package\AliasPackage;
21088 use Composer\Repository\PlatformRepository;
21093 class RuleSetGenerator
21099 protected $installedMap;
21100 protected $whitelistedMap;
21101 protected $addedMap;
21103 public function __construct(PolicyInterface $policy, Pool $pool)
21105 $this->policy = $policy;
21106 $this->pool = $pool;
21123 protected function createRequireRule(PackageInterface $package, array $providers, $reason, $reasonData = null)
21125 $literals = array(-$package->id);
21127 foreach ($providers as $provider) {
21129 if ($provider === $package) {
21132 $literals[] = $provider->id;
21135 return new Rule($literals, $reason, $reasonData);
21150 protected function createInstallOneOfRule(array $packages, $reason, $job)
21152 $literals = array();
21153 foreach ($packages as $package) {
21154 $literals[] = $package->id;
21157 return new Rule($literals, $reason, $job['packageName'], $job);
21171 protected function createRemoveRule(PackageInterface $package, $reason, $job)
21173 return new Rule(array(-$package->id), $reason, $job['packageName'], $job);
21190 protected function createConflictRule(PackageInterface $issuer, PackageInterface $provider, $reason, $reasonData = null)
21193 if ($issuer === $provider) {
21197 return new Rule(array(-$issuer->id, -$provider->id), $reason, $reasonData);
21209 private function addRule($type, Rule $newRule = null)
21211 if (!$newRule || $this->rules->containsEqual($newRule)) {
21215 $this->rules->add($newRule, $type);
21218 protected function whitelistFromPackage(PackageInterface $package)
21220 $workQueue = new \SplQueue;
21221 $workQueue->enqueue($package);
21223 while (!$workQueue->isEmpty()) {
21224 $package = $workQueue->dequeue();
21225 if (isset($this->whitelistedMap[$package->id])) {
21229 $this->whitelistedMap[$package->id] = true;
21231 foreach ($package->getRequires() as $link) {
21232 $possibleRequires = $this->pool->whatProvides($link->getTarget(), $link->getConstraint(), true);
21234 foreach ($possibleRequires as $require) {
21235 $workQueue->enqueue($require);
21239 $obsoleteProviders = $this->pool->whatProvides($package->getName(), null, true);
21241 foreach ($obsoleteProviders as $provider) {
21242 if ($provider === $package) {
21246 if (($package instanceof AliasPackage) && $package->getAliasOf() === $provider) {
21247 $workQueue->enqueue($provider);
21253 protected function addRulesForPackage(PackageInterface $package, $ignorePlatformReqs)
21255 $workQueue = new \SplQueue;
21256 $workQueue->enqueue($package);
21258 while (!$workQueue->isEmpty()) {
21259 $package = $workQueue->dequeue();
21260 if (isset($this->addedMap[$package->id])) {
21264 $this->addedMap[$package->id] = true;
21266 foreach ($package->getRequires() as $link) {
21267 if ($ignorePlatformReqs && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $link->getTarget())) {
21271 $possibleRequires = $this->pool->whatProvides($link->getTarget(), $link->getConstraint());
21273 $this->addRule(RuleSet::TYPE_PACKAGE, $rule = $this->createRequireRule($package, $possibleRequires, Rule::RULE_PACKAGE_REQUIRES, $link));
21275 foreach ($possibleRequires as $require) {
21276 $workQueue->enqueue($require);
21280 foreach ($package->getConflicts() as $link) {
21281 $possibleConflicts = $this->pool->whatProvides($link->getTarget(), $link->getConstraint());
21283 foreach ($possibleConflicts as $conflict) {
21284 $this->addRule(RuleSet::TYPE_PACKAGE, $this->createConflictRule($package, $conflict, Rule::RULE_PACKAGE_CONFLICT, $link));
21289 $isInstalled = (isset($this->installedMap[$package->id]));
21291 foreach ($package->getReplaces() as $link) {
21292 $obsoleteProviders = $this->pool->whatProvides($link->getTarget(), $link->getConstraint());
21294 foreach ($obsoleteProviders as $provider) {
21295 if ($provider === $package) {
21299 if (!$this->obsoleteImpossibleForAlias($package, $provider)) {
21300 $reason = ($isInstalled) ? Rule::RULE_INSTALLED_PACKAGE_OBSOLETES : Rule::RULE_PACKAGE_OBSOLETES;
21301 $this->addRule(RuleSet::TYPE_PACKAGE, $this->createConflictRule($package, $provider, $reason, $link));
21306 $obsoleteProviders = $this->pool->whatProvides($package->getName(), null);
21308 foreach ($obsoleteProviders as $provider) {
21309 if ($provider === $package) {
21313 if (($package instanceof AliasPackage) && $package->getAliasOf() === $provider) {
21314 $this->addRule(RuleSet::TYPE_PACKAGE, $rule = $this->createRequireRule($package, array($provider), Rule::RULE_PACKAGE_ALIAS, $package));
21315 } elseif (!$this->obsoleteImpossibleForAlias($package, $provider)) {
21316 $reason = ($package->getName() == $provider->getName()) ? Rule::RULE_PACKAGE_SAME_NAME : Rule::RULE_PACKAGE_IMPLICIT_OBSOLETES;
21317 $this->addRule(RuleSet::TYPE_PACKAGE, $rule = $this->createConflictRule($package, $provider, $reason, $package));
21323 protected function obsoleteImpossibleForAlias($package, $provider)
21325 $packageIsAlias = $package instanceof AliasPackage;
21326 $providerIsAlias = $provider instanceof AliasPackage;
21329 ($packageIsAlias && $package->getAliasOf() === $provider) ||
21330 ($providerIsAlias && $provider->getAliasOf() === $package) ||
21331 ($packageIsAlias && $providerIsAlias && $provider->getAliasOf() === $package->getAliasOf())
21334 return $impossible;
21337 protected function whitelistFromJobs()
21339 foreach ($this->jobs as $job) {
21340 switch ($job['cmd']) {
21342 $packages = $this->pool->whatProvides($job['packageName'], $job['constraint'], true);
21343 foreach ($packages as $package) {
21344 $this->whitelistFromPackage($package);
21351 protected function addRulesForJobs($ignorePlatformReqs)
21353 foreach ($this->jobs as $job) {
21354 switch ($job['cmd']) {
21356 if (!$job['fixed'] && $ignorePlatformReqs && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $job['packageName'])) {
21360 $packages = $this->pool->whatProvides($job['packageName'], $job['constraint']);
21362 foreach ($packages as $package) {
21363 if (!isset($this->installedMap[$package->id])) {
21364 $this->addRulesForPackage($package, $ignorePlatformReqs);
21368 $rule = $this->createInstallOneOfRule($packages, Rule::RULE_JOB_INSTALL, $job);
21369 $this->addRule(RuleSet::TYPE_JOB, $rule);
21375 $packages = $this->pool->whatProvides($job['packageName'], $job['constraint']);
21376 foreach ($packages as $package) {
21377 $rule = $this->createRemoveRule($package, Rule::RULE_JOB_REMOVE, $job);
21378 $this->addRule(RuleSet::TYPE_JOB, $rule);
21385 public function getRulesFor($jobs, $installedMap, $ignorePlatformReqs = false)
21387 $this->jobs = $jobs;
21388 $this->rules = new RuleSet;
21389 $this->installedMap = $installedMap;
21391 $this->whitelistedMap = array();
21392 foreach ($this->installedMap as $package) {
21393 $this->whitelistFromPackage($package);
21395 $this->whitelistFromJobs();
21397 $this->pool->setWhitelist($this->whitelistedMap);
21399 $this->addedMap = array();
21400 foreach ($this->installedMap as $package) {
21401 $this->addRulesForPackage($package, $ignorePlatformReqs);
21404 $this->addRulesForJobs($ignorePlatformReqs);
21406 return $this->rules;
21421 namespace Composer\DependencyResolver;
21426 class DebugSolver extends Solver
21428 protected function printDecisionMap()
21430 echo "\nDecisionMap: \n";
21431 foreach ($this->decisionMap as $packageId => $level) {
21432 if ($packageId === 0) {
21436 echo ' +' . $this->pool->packageById($packageId)."\n";
21437 } elseif ($level < 0) {
21438 echo ' -' . $this->pool->packageById($packageId)."\n";
21440 echo ' ?' . $this->pool->packageById($packageId)."\n";
21446 protected function printDecisionQueue()
21448 echo "DecisionQueue: \n";
21449 foreach ($this->decisionQueue as $i => $literal) {
21450 echo ' ' . $this->pool->literalToString($literal) . ' ' . $this->decisionQueueWhy[$i]." level ".$this->decisionMap[abs($literal)]."\n";
21455 protected function printWatches()
21457 echo "\nWatches:\n";
21458 foreach ($this->watches as $literalId => $watch) {
21459 echo ' '.$this->literalFromId($literalId)."\n";
21460 $queue = array(array(' ', $watch));
21462 while (!empty($queue)) {
21463 list($indent, $watch) = array_pop($queue);
21465 echo $indent.$watch;
21468 echo ' [id='.$watch->getId().',watch1='.$this->literalFromId($watch->watch1).',watch2='.$this->literalFromId($watch->watch2)."]";
21473 if ($watch && ($watch->next1 == $watch || $watch->next2 == $watch)) {
21474 if ($watch->next1 == $watch) {
21475 echo $indent." 1 *RECURSION*";
21477 if ($watch->next2 == $watch) {
21478 echo $indent." 2 *RECURSION*";
21480 } elseif ($watch && ($watch->next1 || $watch->next2)) {
21481 $indent = str_replace(array('1', '2'), ' ', $indent);
21483 array_push($queue, array($indent.' 2 ', $watch->next2));
21484 array_push($queue, array($indent.' 1 ', $watch->next1));
21504 namespace Composer\DependencyResolver;
21509 class RuleSetIterator implements \Iterator
21514 protected $currentOffset;
21515 protected $currentType;
21516 protected $currentTypeOffset;
21518 public function __construct(array $rules)
21520 $this->rules = $rules;
21521 $this->types = array_keys($rules);
21522 sort($this->types);
21527 public function current()
21529 return $this->rules[$this->currentType][$this->currentOffset];
21532 public function key()
21534 return $this->currentType;
21537 public function next()
21539 $this->currentOffset++;
21541 if (!isset($this->rules[$this->currentType])) {
21545 if ($this->currentOffset >= sizeof($this->rules[$this->currentType])) {
21546 $this->currentOffset = 0;
21549 $this->currentTypeOffset++;
21551 if (!isset($this->types[$this->currentTypeOffset])) {
21552 $this->currentType = -1;
21556 $this->currentType = $this->types[$this->currentTypeOffset];
21557 } while (isset($this->types[$this->currentTypeOffset]) && !sizeof($this->rules[$this->currentType]));
21561 public function rewind()
21563 $this->currentOffset = 0;
21565 $this->currentTypeOffset = -1;
21566 $this->currentType = -1;
21569 $this->currentTypeOffset++;
21571 if (!isset($this->types[$this->currentTypeOffset])) {
21572 $this->currentType = -1;
21576 $this->currentType = $this->types[$this->currentTypeOffset];
21577 } while (isset($this->types[$this->currentTypeOffset]) && !sizeof($this->rules[$this->currentType]));
21580 public function valid()
21582 return isset($this->rules[$this->currentType])
21583 && isset($this->rules[$this->currentType][$this->currentOffset]);
21598 namespace Composer\DependencyResolver;
21608 class RuleWatchChain extends \SplDoublyLinkedList
21610 protected $offset = 0;
21617 public function seek($offset)
21620 for ($i = 0; $i < $offset; $i++, $this->next());
21631 public function remove()
21633 $offset = $this->key();
21634 $this->offsetUnset($offset);
21635 $this->seek($offset);
21650 namespace Composer\DependencyResolver;
21652 use Composer\Repository\RepositoryInterface;
21653 use Composer\Repository\PlatformRepository;
21660 const BRANCH_LITERALS = 0;
21661 const BRANCH_LEVEL = 1;
21665 protected $installed;
21667 protected $ruleSetGenerator;
21668 protected $updateAll;
21670 protected $addedMap = array();
21671 protected $updateMap = array();
21672 protected $watchGraph;
21673 protected $decisions;
21674 protected $installedMap;
21676 protected $propagateIndex;
21677 protected $branches = array();
21678 protected $problems = array();
21679 protected $learnedPool = array();
21681 public function __construct(PolicyInterface $policy, Pool $pool, RepositoryInterface $installed)
21683 $this->policy = $policy;
21684 $this->pool = $pool;
21685 $this->installed = $installed;
21686 $this->ruleSetGenerator = new RuleSetGenerator($policy, $pool);
21690 private function makeAssertionRuleDecisions()
21692 $decisionStart = count($this->decisions) - 1;
21694 $rulesCount = count($this->rules);
21695 for ($ruleIndex = 0; $ruleIndex < $rulesCount; $ruleIndex++) {
21696 $rule = $this->rules->ruleById[$ruleIndex];
21698 if (!$rule->isAssertion() || $rule->isDisabled()) {
21702 $literals = $rule->literals;
21703 $literal = $literals[0];
21705 if (!$this->decisions->decided(abs($literal))) {
21706 $this->decisions->decide($literal, 1, $rule);
21710 if ($this->decisions->satisfy($literal)) {
21715 if (RuleSet::TYPE_LEARNED === $rule->getType()) {
21720 $conflict = $this->decisions->decisionRule($literal);
21722 if ($conflict && RuleSet::TYPE_PACKAGE === $conflict->getType()) {
21723 $problem = new Problem($this->pool);
21725 $problem->addRule($rule);
21726 $problem->addRule($conflict);
21727 $this->disableProblem($rule);
21728 $this->problems[] = $problem;
21733 $problem = new Problem($this->pool);
21734 $problem->addRule($rule);
21735 $problem->addRule($conflict);
21739 foreach ($this->rules->getIteratorFor(RuleSet::TYPE_JOB) as $assertRule) {
21740 if ($assertRule->isDisabled() || !$assertRule->isAssertion()) {
21744 $assertRuleLiterals = $assertRule->literals;
21745 $assertRuleLiteral = $assertRuleLiterals[0];
21747 if (abs($literal) !== abs($assertRuleLiteral)) {
21751 $problem->addRule($assertRule);
21752 $this->disableProblem($assertRule);
21754 $this->problems[] = $problem;
21756 $this->decisions->resetToOffset($decisionStart);
21761 protected function setupInstalledMap()
21763 $this->installedMap = array();
21764 foreach ($this->installed->getPackages() as $package) {
21765 $this->installedMap[$package->id] = $package;
21769 protected function checkForRootRequireProblems($ignorePlatformReqs)
21771 foreach ($this->jobs as $job) {
21772 switch ($job['cmd']) {
21774 $packages = $this->pool->whatProvides($job['packageName'], $job['constraint']);
21775 foreach ($packages as $package) {
21776 if (isset($this->installedMap[$package->id])) {
21777 $this->updateMap[$package->id] = true;
21783 foreach ($this->installedMap as $package) {
21784 $this->updateMap[$package->id] = true;
21789 if ($ignorePlatformReqs && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $job['packageName'])) {
21793 if (!$this->pool->whatProvides($job['packageName'], $job['constraint'])) {
21794 $problem = new Problem($this->pool);
21795 $problem->addRule(new Rule(array(), null, null, $job));
21796 $this->problems[] = $problem;
21803 public function solve(Request $request, $ignorePlatformReqs = false)
21805 $this->jobs = $request->getJobs();
21807 $this->setupInstalledMap();
21808 $this->rules = $this->ruleSetGenerator->getRulesFor($this->jobs, $this->installedMap, $ignorePlatformReqs);
21809 $this->checkForRootRequireProblems($ignorePlatformReqs);
21810 $this->decisions = new Decisions($this->pool);
21811 $this->watchGraph = new RuleWatchGraph;
21813 foreach ($this->rules as $rule) {
21814 $this->watchGraph->insert(new RuleWatchNode($rule));
21818 $this->makeAssertionRuleDecisions();
21820 $this->runSat(true);
21823 foreach ($this->installedMap as $packageId => $void) {
21824 if ($this->decisions->undecided($packageId)) {
21825 $this->decisions->decide(-$packageId, 1, null);
21829 if ($this->problems) {
21830 throw new SolverProblemsException($this->problems, $this->installedMap);
21833 $transaction = new Transaction($this->policy, $this->pool, $this->installedMap, $this->decisions);
21835 return $transaction->getOperations();
21838 protected function literalFromId($id)
21840 $package = $this->pool->packageById(abs($id));
21842 return new Literal($package, $id > 0);
21854 protected function propagate($level)
21856 while ($this->decisions->validOffset($this->propagateIndex)) {
21857 $decision = $this->decisions->atOffset($this->propagateIndex);
21859 $conflict = $this->watchGraph->propagateLiteral(
21860 $decision[Decisions::DECISION_LITERAL],
21865 $this->propagateIndex++;
21878 private function revert($level)
21880 while (!$this->decisions->isEmpty()) {
21881 $literal = $this->decisions->lastLiteral();
21883 if ($this->decisions->undecided($literal)) {
21887 $decisionLevel = $this->decisions->decisionLevel($literal);
21889 if ($decisionLevel <= $level) {
21893 $this->decisions->revertLast();
21894 $this->propagateIndex = count($this->decisions);
21897 while (!empty($this->branches) && $this->branches[count($this->branches) - 1][self::BRANCH_LEVEL] >= $level) {
21898 array_pop($this->branches);
21917 private function setPropagateLearn($level, $literal, $disableRules, Rule $rule)
21921 $this->decisions->decide($literal, $level, $rule);
21924 $rule = $this->propagate($level);
21931 return $this->analyzeUnsolvable($rule, $disableRules);
21935 list($learnLiteral, $newLevel, $newRule, $why) = $this->analyze($level, $rule);
21937 if ($newLevel <= 0 || $newLevel >= $level) {
21938 throw new SolverBugException(
21939 "Trying to revert to invalid level ".(int) $newLevel." from level ".(int) $level."."
21941 } elseif (!$newRule) {
21942 throw new SolverBugException(
21943 "No rule was learned from analyzing $rule at level $level."
21947 $level = $newLevel;
21949 $this->revert($level);
21951 $this->rules->add($newRule, RuleSet::TYPE_LEARNED);
21953 $this->learnedWhy[$newRule->getId()] = $why;
21955 $ruleNode = new RuleWatchNode($newRule);
21956 $ruleNode->watch2OnHighest($this->decisions);
21957 $this->watchGraph->insert($ruleNode);
21959 $this->decisions->decide($learnLiteral, $level, $newRule);
21965 private function selectAndInstall($level, array $decisionQueue, $disableRules, Rule $rule)
21968 $literals = $this->policy->selectPreferedPackages($this->pool, $this->installedMap, $decisionQueue, $rule->getRequiredPackage());
21970 $selectedLiteral = array_shift($literals);
21973 if (count($literals)) {
21974 $this->branches[] = array($literals, $level);
21977 return $this->setPropagateLearn($level, $selectedLiteral, $disableRules, $rule);
21980 protected function analyze($level, $rule)
21982 $analyzedRule = $rule;
21987 $learnedLiterals = array(null);
21989 $decisionId = count($this->decisions);
21991 $this->learnedPool[] = array();
21994 $this->learnedPool[count($this->learnedPool) - 1][] = $rule;
21996 foreach ($rule->literals as $literal) {
21998 if ($this->decisions->satisfy($literal)) {
22002 if (isset($seen[abs($literal)])) {
22005 $seen[abs($literal)] = true;
22007 $l = $this->decisions->decisionLevel($literal);
22011 } elseif ($level === $l) {
22015 $learnedLiterals[] = $literal;
22017 if ($l > $ruleLevel) {
22027 if (!$num && !--$l1num) {
22033 if ($decisionId <= 0) {
22034 throw new SolverBugException(
22035 "Reached invalid decision id $decisionId while looking through $rule for a literal present in the analyzed rule $analyzedRule."
22041 $decision = $this->decisions->atOffset($decisionId);
22042 $literal = $decision[Decisions::DECISION_LITERAL];
22044 if (isset($seen[abs($literal)])) {
22049 unset($seen[abs($literal)]);
22051 if ($num && 0 === --$num) {
22052 $learnedLiterals[0] = -abs($literal);
22058 foreach ($learnedLiterals as $i => $learnedLiteral) {
22060 unset($seen[abs($learnedLiteral)]);
22069 $decision = $this->decisions->atOffset($decisionId);
22070 $rule = $decision[Decisions::DECISION_REASON];
22073 $why = count($this->learnedPool) - 1;
22075 if (!$learnedLiterals[0]) {
22076 throw new SolverBugException(
22077 "Did not find a learnable literal in analyzed rule $analyzedRule."
22081 $newRule = new Rule($learnedLiterals, Rule::RULE_LEARNED, $why);
22083 return array($learnedLiterals[0], $ruleLevel, $newRule, $why);
22086 private function analyzeUnsolvableRule($problem, $conflictRule)
22088 $why = $conflictRule->getId();
22090 if ($conflictRule->getType() == RuleSet::TYPE_LEARNED) {
22091 $learnedWhy = $this->learnedWhy[$why];
22092 $problemRules = $this->learnedPool[$learnedWhy];
22094 foreach ($problemRules as $problemRule) {
22095 $this->analyzeUnsolvableRule($problem, $problemRule);
22101 if ($conflictRule->getType() == RuleSet::TYPE_PACKAGE) {
22106 $problem->nextSection();
22107 $problem->addRule($conflictRule);
22110 private function analyzeUnsolvable($conflictRule, $disableRules)
22112 $problem = new Problem($this->pool);
22113 $problem->addRule($conflictRule);
22115 $this->analyzeUnsolvableRule($problem, $conflictRule);
22117 $this->problems[] = $problem;
22120 $literals = $conflictRule->literals;
22122 foreach ($literals as $literal) {
22124 if ($this->decisions->satisfy($literal)) {
22127 $seen[abs($literal)] = true;
22130 foreach ($this->decisions as $decision) {
22131 $literal = $decision[Decisions::DECISION_LITERAL];
22134 if (!isset($seen[abs($literal)])) {
22138 $why = $decision[Decisions::DECISION_REASON];
22140 $problem->addRule($why);
22141 $this->analyzeUnsolvableRule($problem, $why);
22143 $literals = $why->literals;
22145 foreach ($literals as $literal) {
22147 if ($this->decisions->satisfy($literal)) {
22150 $seen[abs($literal)] = true;
22154 if ($disableRules) {
22155 foreach ($this->problems[count($this->problems) - 1] as $reason) {
22156 $this->disableProblem($reason['rule']);
22159 $this->resetSolver();
22167 private function disableProblem($why)
22169 $job = $why->getJob();
22178 foreach ($this->rules as $rule) {
22179 if ($job === $rule->getJob()) {
22185 private function resetSolver()
22187 $this->decisions->reset();
22189 $this->propagateIndex = 0;
22190 $this->branches = array();
22192 $this->enableDisableLearnedRules();
22193 $this->makeAssertionRuleDecisions();
22203 private function enableDisableLearnedRules()
22205 foreach ($this->rules->getIteratorFor(RuleSet::TYPE_LEARNED) as $rule) {
22206 $why = $this->learnedWhy[$rule->getId()];
22207 $problemRules = $this->learnedPool[$why];
22209 $foundDisabled = false;
22210 foreach ($problemRules as $problemRule) {
22211 if ($problemRule->isDisabled()) {
22212 $foundDisabled = true;
22217 if ($foundDisabled && $rule->isEnabled()) {
22219 } elseif (!$foundDisabled && $rule->isDisabled()) {
22225 private function runSat($disableRules = true)
22227 $this->propagateIndex = 0;
22239 $decisionQueue = array();
22240 $decisionSupplementQueue = array();
22241 $disableRules = array();
22244 $systemLevel = $level + 1;
22248 if (1 === $level) {
22249 $conflictRule = $this->propagate($level);
22250 if (null !== $conflictRule) {
22251 if ($this->analyzeUnsolvable($conflictRule, $disableRules)) {
22260 if ($level < $systemLevel) {
22261 $iterator = $this->rules->getIteratorFor(RuleSet::TYPE_JOB);
22262 foreach ($iterator as $rule) {
22263 if ($rule->isEnabled()) {
22264 $decisionQueue = array();
22265 $noneSatisfied = true;
22267 foreach ($rule->literals as $literal) {
22268 if ($this->decisions->satisfy($literal)) {
22269 $noneSatisfied = false;
22272 if ($literal > 0 && $this->decisions->undecided($literal)) {
22273 $decisionQueue[] = $literal;
22277 if ($noneSatisfied && count($decisionQueue)) {
22280 if (count($this->installed) != count($this->updateMap)) {
22281 $prunedQueue = array();
22282 foreach ($decisionQueue as $literal) {
22283 if (isset($this->installedMap[abs($literal)])) {
22284 $prunedQueue[] = $literal;
22285 if (isset($this->updateMap[abs($literal)])) {
22286 $prunedQueue = $decisionQueue;
22291 $decisionQueue = $prunedQueue;
22295 if ($noneSatisfied && count($decisionQueue)) {
22297 $level = $this->selectAndInstall($level, $decisionQueue, $disableRules, $rule);
22299 if (0 === $level) {
22302 if ($level <= $oLevel) {
22309 $systemLevel = $level + 1;
22313 if ($iterator->valid()) {
22318 if ($level < $systemLevel) {
22319 $systemLevel = $level;
22322 for ($i = 0, $n = 0; $n < count($this->rules); $i++, $n++) {
22323 if ($i == count($this->rules)) {
22327 $rule = $this->rules->ruleById[$i];
22328 $literals = $rule->literals;
22330 if ($rule->isDisabled()) {
22334 $decisionQueue = array();
22342 foreach ($literals as $literal) {
22343 if ($literal <= 0) {
22344 if (!$this->decisions->decidedInstall(abs($literal))) {
22348 if ($this->decisions->decidedInstall(abs($literal))) {
22351 if ($this->decisions->undecided(abs($literal))) {
22352 $decisionQueue[] = $literal;
22358 if (count($decisionQueue) < 2) {
22363 $level = $this->selectAndInstall($level, $decisionQueue, $disableRules, $rule);
22365 if (0 === $level) {
22373 if ($level < $systemLevel) {
22378 if (count($this->branches)) {
22379 $lastLiteral = null;
22381 $lastBranchIndex = 0;
22382 $lastBranchOffset = 0;
22385 for ($i = count($this->branches) - 1; $i >= 0; $i--) {
22386 list($literals, $l) = $this->branches[$i];
22388 foreach ($literals as $offset => $literal) {
22389 if ($literal && $literal > 0 && $this->decisions->decisionLevel($literal) > $l + 1) {
22390 $lastLiteral = $literal;
22391 $lastBranchIndex = $i;
22392 $lastBranchOffset = $offset;
22398 if ($lastLiteral) {
22399 unset($this->branches[$lastBranchIndex][self::BRANCH_LITERALS][$lastBranchOffset]);
22401 $level = $lastLevel;
22402 $this->revert($level);
22404 $why = $this->decisions->lastReason();
22407 $level = $this->setPropagateLearn($level, $lastLiteral, $disableRules, $why);
22433 namespace Composer\DependencyResolver;
22435 use Composer\Package\LinkConstraint\LinkConstraintInterface;
22445 public function __construct(Pool $pool)
22447 $this->pool = $pool;
22448 $this->jobs = array();
22451 public function install($packageName, LinkConstraintInterface $constraint = null)
22453 $this->addJob($packageName, 'install', $constraint);
22456 public function update($packageName, LinkConstraintInterface $constraint = null)
22458 $this->addJob($packageName, 'update', $constraint);
22461 public function remove($packageName, LinkConstraintInterface $constraint = null)
22463 $this->addJob($packageName, 'remove', $constraint);
22471 public function fix($packageName, LinkConstraintInterface $constraint = null)
22473 $this->addJob($packageName, 'install', $constraint, true);
22476 protected function addJob($packageName, $cmd, LinkConstraintInterface $constraint = null, $fixed = false)
22478 $packageName = strtolower($packageName);
22480 $this->jobs[] = array(
22482 'packageName' => $packageName,
22483 'constraint' => $constraint,
22488 public function updateAll()
22490 $this->jobs[] = array('cmd' => 'update-all');
22493 public function getJobs()
22495 return $this->jobs;
22510 namespace Composer\DependencyResolver;
22523 protected $reasonSeen;
22529 protected $reasons = array();
22531 protected $section = 0;
22535 public function __construct(Pool $pool)
22537 $this->pool = $pool;
22545 public function addRule(Rule $rule)
22547 $this->addReason($rule->getId(), array(
22549 'job' => $rule->getJob(),
22558 public function getReasons()
22560 return $this->reasons;
22569 public function getPrettyString(array $installedMap = array())
22571 $reasons = call_user_func_array('array_merge', array_reverse($this->reasons));
22573 if (count($reasons) === 1) {
22575 $reason = current($reasons);
22577 $rule = $reason['rule'];
22578 $job = $reason['job'];
22580 if (isset($job['constraint'])) {
22581 $packages = $this->pool->whatProvides($job['packageName'], $job['constraint']);
22583 $packages = array();
22586 if ($job && $job['cmd'] === 'install' && empty($packages)) {
22588 if (0 === stripos($job['packageName'], 'ext-')) {
22589 $ext = substr($job['packageName'], 4);
22590 $error = extension_loaded($ext) ? 'has the wrong version ('.(phpversion($ext) ?: '0').') installed' : 'is missing from your system';
22592 return "\n - The requested PHP extension ".$job['packageName'].$this->constraintToText($job['constraint']).' '.$error.'.';
22596 if (0 === stripos($job['packageName'], 'lib-')) {
22597 if (strtolower($job['packageName']) === 'lib-icu') {
22598 $error = extension_loaded('intl') ? 'has the wrong version installed, try upgrading the intl extension.' : 'is missing from your system, make sure the intl extension is loaded.';
22600 return "\n - The requested linked library ".$job['packageName'].$this->constraintToText($job['constraint']).' '.$error;
22603 return "\n - The requested linked library ".$job['packageName'].$this->constraintToText($job['constraint']).' has the wrong version installed or is missing from your system, make sure to load the extension providing it.';
22606 if (!preg_match('{^[A-Za-z0-9_./-]+$}', $job['packageName'])) {
22607 $illegalChars = preg_replace('{[A-Za-z0-9_./-]+}', '', $job['packageName']);
22609 return "\n - The requested package ".$job['packageName'].' could not be found, it looks like its name is invalid, "'.$illegalChars.'" is not allowed in package names.';
22612 if (!$this->pool->whatProvides($job['packageName'])) {
22613 return "\n - The requested package ".$job['packageName'].' could not be found in any version, there may be a typo in the package name.';
22616 return "\n - The requested package ".$job['packageName'].$this->constraintToText($job['constraint']).' could not be found.';
22620 $messages = array();
22622 foreach ($reasons as $reason) {
22623 $rule = $reason['rule'];
22624 $job = $reason['job'];
22627 $messages[] = $this->jobToText($job);
22629 if ($rule instanceof Rule) {
22630 $messages[] = $rule->getPrettyString($this->pool, $installedMap);
22635 return "\n - ".implode("\n - ", $messages);
22644 protected function addReason($id, $reason)
22646 if (!isset($this->reasonSeen[$id])) {
22647 $this->reasonSeen[$id] = true;
22648 $this->reasons[$this->section][] = $reason;
22652 public function nextSection()
22663 protected function jobToText($job)
22665 switch ($job['cmd']) {
22667 $packages = $this->pool->whatProvides($job['packageName'], $job['constraint']);
22669 return 'No package found to satisfy install request for '.$job['packageName'].$this->constraintToText($job['constraint']);
22672 return 'Installation request for '.$job['packageName'].$this->constraintToText($job['constraint']).' -> satisfiable by '.$this->getPackageList($packages).'.';
22674 return 'Update request for '.$job['packageName'].$this->constraintToText($job['constraint']).'.';
22676 return 'Removal request for '.$job['packageName'].$this->constraintToText($job['constraint']).'';
22679 if (isset($job['constraint'])) {
22680 $packages = $this->pool->whatProvides($job['packageName'], $job['constraint']);
22682 $packages = array();
22685 return 'Job(cmd='.$job['cmd'].', target='.$job['packageName'].', packages=['.$this->getPackageList($packages).'])';
22688 protected function getPackageList($packages)
22690 $prepared = array();
22691 foreach ($packages as $package) {
22692 $prepared[$package->getName()]['name'] = $package->getPrettyName();
22693 $prepared[$package->getName()]['versions'][$package->getVersion()] = $package->getPrettyVersion();
22695 foreach ($prepared as $name => $package) {
22696 $prepared[$name] = $package['name'].'['.implode(', ', $package['versions']).']';
22699 return implode(', ', $prepared);
22708 protected function constraintToText($constraint)
22710 return ($constraint) ? ' '.$constraint->getPrettyString() : '';
22725 namespace Composer\DependencyResolver;
22737 class RuleWatchGraph
22739 protected $watchChains = array();
22753 public function insert(RuleWatchNode $node)
22755 if ($node->getRule()->isAssertion()) {
22759 foreach (array($node->watch1, $node->watch2) as $literal) {
22760 if (!isset($this->watchChains[$literal])) {
22761 $this->watchChains[$literal] = new RuleWatchChain;
22764 $this->watchChains[$literal]->unshift($node);
22791 public function propagateLiteral($decidedLiteral, $level, $decisions)
22796 $literal = -$decidedLiteral;
22798 if (!isset($this->watchChains[$literal])) {
22802 $chain = $this->watchChains[$literal];
22805 while ($chain->valid()) {
22806 $node = $chain->current();
22807 $otherWatch = $node->getOtherWatch($literal);
22809 if (!$node->getRule()->isDisabled() && !$decisions->satisfy($otherWatch)) {
22810 $ruleLiterals = $node->getRule()->literals;
22812 $alternativeLiterals = array_filter($ruleLiterals, function ($ruleLiteral) use ($literal, $otherWatch, $decisions) {
22813 return $literal !== $ruleLiteral &&
22814 $otherWatch !== $ruleLiteral &&
22815 !$decisions->conflict($ruleLiteral);
22818 if ($alternativeLiterals) {
22819 reset($alternativeLiterals);
22820 $this->moveWatch($literal, current($alternativeLiterals), $node);
22824 if ($decisions->conflict($otherWatch)) {
22825 return $node->getRule();
22828 $decisions->decide($otherWatch, $level, $node->getRule());
22846 protected function moveWatch($fromLiteral, $toLiteral, $node)
22848 if (!isset($this->watchChains[$toLiteral])) {
22849 $this->watchChains[$toLiteral] = new RuleWatchChain;
22852 $node->moveWatch($fromLiteral, $toLiteral);
22853 $this->watchChains[$fromLiteral]->remove();
22854 $this->watchChains[$toLiteral]->unshift($node);
22869 namespace Composer\Config;
22877 interface ConfigSourceInterface
22885 public function addRepository($name, $config);
22892 public function removeRepository($name);
22900 public function addConfigSetting($name, $value);
22907 public function removeConfigSetting($name);
22916 public function addLink($type, $name, $value);
22924 public function removeLink($type, $name);
22931 public function getName();
22945 namespace Composer\Config;
22947 use Composer\Json\JsonFile;
22948 use Composer\Json\JsonManipulator;
22956 class JsonConfigSource implements ConfigSourceInterface
22966 private $authConfig;
22974 public function __construct(JsonFile $file, $authConfig = false)
22976 $this->file = $file;
22977 $this->authConfig = $authConfig;
22983 public function getName()
22985 return $this->file->getPath();
22991 public function addRepository($name, $config)
22993 $this->manipulateJson('addRepository', $name, $config, function (&$config, $repo, $repoConfig) {
22994 $config['repositories'][$repo] = $repoConfig;
23001 public function removeRepository($name)
23003 $this->manipulateJson('removeRepository', $name, function (&$config, $repo) {
23004 unset($config['repositories'][$repo]);
23011 public function addConfigSetting($name, $value)
23013 $this->manipulateJson('addConfigSetting', $name, $value, function (&$config, $key, $val) {
23014 if ($key === 'github-oauth' || $key === 'http-basic') {
23015 list($key, $host) = explode('.', $key, 2);
23016 if ($this->authConfig) {
23017 $config[$key][$host] = $val;
23019 $config['config'][$key][$host] = $val;
23022 $config['config'][$key] = $val;
23030 public function removeConfigSetting($name)
23032 $this->manipulateJson('removeConfigSetting', $name, function (&$config, $key) {
23033 if ($key === 'github-oauth' || $key === 'http-basic') {
23034 list($key, $host) = explode('.', $key, 2);
23035 if ($this->authConfig) {
23036 unset($config[$key][$host]);
23038 unset($config['config'][$key][$host]);
23041 unset($config['config'][$key]);
23049 public function addLink($type, $name, $value)
23051 $this->manipulateJson('addLink', $type, $name, $value, function (&$config, $type, $name, $value) {
23052 $config[$type][$name] = $value;
23059 public function removeLink($type, $name)
23061 $this->manipulateJson('removeSubNode', $type, $name, function (&$config, $type, $name) {
23062 unset($config[$type][$name]);
23066 protected function manipulateJson($method, $args, $fallback)
23068 $args = func_get_args();
23070 array_shift($args);
23071 $fallback = array_pop($args);
23073 if ($this->file->exists()) {
23074 $contents = file_get_contents($this->file->getPath());
23075 } elseif ($this->authConfig) {
23076 $contents = "{\n}\n";
23078 $contents = "{\n \"config\": {\n }\n}\n";
23081 $manipulator = new JsonManipulator($contents);
23083 $newFile = !$this->file->exists();
23086 if ($this->authConfig && $method === 'addConfigSetting') {
23087 $method = 'addSubNode';
23088 list($mainNode, $name) = explode('.', $args[0], 2);
23089 $args = array($mainNode, $name, $args[1]);
23090 } elseif ($this->authConfig && $method === 'removeConfigSetting') {
23091 $method = 'removeSubNode';
23092 list($mainNode, $name) = explode('.', $args[0], 2);
23093 $args = array($mainNode, $name);
23097 if (call_user_func_array(array($manipulator, $method), $args)) {
23098 file_put_contents($this->file->getPath(), $manipulator->getContents());
23101 $config = $this->file->read();
23102 $this->arrayUnshiftRef($args, $config);
23103 call_user_func_array($fallback, $args);
23104 $this->file->write($config);
23108 @chmod($this->file->getPath(), 0600);
23119 private function arrayUnshiftRef(&$array, &$value)
23121 $return = array_unshift($array, '');
23122 $array[0] = &$value;
23139 namespace Composer\Plugin;
23156 const COMMAND = 'command';
23166 const PRE_FILE_DOWNLOAD = 'pre-file-download';
23180 namespace Composer\Plugin;
23182 use Composer\EventDispatcher\Event;
23183 use Symfony\Component\Console\Input\InputInterface;
23184 use Symfony\Component\Console\Output\OutputInterface;
23191 class CommandEvent extends Event
23196 private $commandName;
23218 public function __construct($name, $commandName, $input, $output, array $args = array(), array $flags = array())
23220 parent::__construct($name, $args, $flags);
23221 $this->commandName = $commandName;
23222 $this->input = $input;
23223 $this->output = $output;
23231 public function getInput()
23233 return $this->input;
23241 public function getOutput()
23243 return $this->output;
23251 public function getCommandName()
23253 return $this->commandName;
23268 namespace Composer\Plugin;
23270 use Composer\EventDispatcher\Event;
23271 use Composer\Util\RemoteFilesystem;
23278 class PreFileDownloadEvent extends Event
23288 private $processedUrl;
23297 public function __construct($name, RemoteFilesystem $rfs, $processedUrl)
23299 parent::__construct($name);
23301 $this->processedUrl = $processedUrl;
23309 public function getRemoteFilesystem()
23319 public function setRemoteFilesystem(RemoteFilesystem $rfs)
23329 public function getProcessedUrl()
23331 return $this->processedUrl;
23346 namespace Composer\Plugin;
23348 use Composer\Composer;
23349 use Composer\IO\IOInterface;
23356 interface PluginInterface
23363 const PLUGIN_API_VERSION = '1.0.0';
23371 public function activate(Composer $composer, IOInterface $io);
23385 namespace Composer\Plugin;
23387 use Composer\Composer;
23388 use Composer\EventDispatcher\EventSubscriberInterface;
23389 use Composer\IO\IOInterface;
23390 use Composer\Package\Package;
23391 use Composer\Package\Version\VersionParser;
23392 use Composer\Repository\RepositoryInterface;
23393 use Composer\Package\AliasPackage;
23394 use Composer\Package\PackageInterface;
23395 use Composer\Package\Link;
23396 use Composer\Package\LinkConstraint\VersionConstraint;
23397 use Composer\DependencyResolver\Pool;
23405 class PluginManager
23407 protected $composer;
23409 protected $globalComposer;
23410 protected $versionParser;
23412 protected $plugins = array();
23413 protected $registeredPlugins = array();
23415 private static $classCounter = 0;
23424 public function __construct(IOInterface $io, Composer $composer, Composer $globalComposer = null)
23427 $this->composer = $composer;
23428 $this->globalComposer = $globalComposer;
23429 $this->versionParser = new VersionParser();
23435 public function loadInstalledPlugins()
23437 $repo = $this->composer->getRepositoryManager()->getLocalRepository();
23438 $globalRepo = $this->globalComposer ? $this->globalComposer->getRepositoryManager()->getLocalRepository() : null;
23440 $this->loadRepository($repo);
23443 $this->loadRepository($globalRepo);
23452 public function addPlugin(PluginInterface $plugin)
23454 $this->plugins[] = $plugin;
23455 $plugin->activate($this->composer, $this->io);
23457 if ($plugin instanceof EventSubscriberInterface) {
23458 $this->composer->getEventDispatcher()->addSubscriber($plugin);
23467 public function getPlugins()
23469 return $this->plugins;
23483 public function loadRepository(RepositoryInterface $repo)
23485 foreach ($repo->getPackages() as $package) {
23486 if ($package instanceof AliasPackage) {
23489 if ('composer-plugin' === $package->getType()) {
23490 $requiresComposer = null;
23491 foreach ($package->getRequires() as $link) {
23492 if ($link->getTarget() == 'composer-plugin-api') {
23493 $requiresComposer = $link->getConstraint();
23497 if (!$requiresComposer) {
23498 throw new \RuntimeException("Plugin ".$package->getName()." is missing a require statement for a version of the composer-plugin-api package.");
23501 if (!$requiresComposer->matches(new VersionConstraint('==', $this->versionParser->normalize(PluginInterface::PLUGIN_API_VERSION)))) {
23502 $this->io->write("<warning>The plugin ".$package->getName()." requires a version of composer-plugin-api that does not match your composer installation. You may need to run composer update with the '--no-plugins' option.</warning>");
23505 $this->registerPackage($package);
23508 if ('composer-installer' === $package->getType()) {
23509 $this->registerPackage($package);
23523 protected function collectDependencies(Pool $pool, array $collected, PackageInterface $package)
23525 $requires = array_merge(
23526 $package->getRequires(),
23527 $package->getDevRequires()
23530 foreach ($requires as $requireLink) {
23531 $requiredPackage = $this->lookupInstalledPackage($pool, $requireLink);
23532 if ($requiredPackage && !isset($collected[$requiredPackage->getName()])) {
23533 $collected[$requiredPackage->getName()] = $requiredPackage;
23534 $collected = $this->collectDependencies($pool, $collected, $requiredPackage);
23551 protected function lookupInstalledPackage(Pool $pool, Link $link)
23553 $packages = $pool->whatProvides($link->getTarget(), $link->getConstraint());
23555 return (!empty($packages)) ? $packages[0] : null;
23569 public function registerPackage(PackageInterface $package, $failOnMissingClasses = false)
23571 $oldInstallerPlugin = ($package->getType() === 'composer-installer');
23573 if (in_array($package->getName(), $this->registeredPlugins)) {
23577 $extra = $package->getExtra();
23578 if (empty($extra['class'])) {
23579 throw new \UnexpectedValueException('Error while installing '.$package->getPrettyName().', composer-plugin packages should have a class defined in their extra key to be usable.');
23581 $classes = is_array($extra['class']) ? $extra['class'] : array($extra['class']);
23583 $localRepo = $this->composer->getRepositoryManager()->getLocalRepository();
23584 $globalRepo = $this->globalComposer ? $this->globalComposer->getRepositoryManager()->getLocalRepository() : null;
23586 $pool = new Pool('dev');
23587 $pool->addRepository($localRepo);
23589 $pool->addRepository($globalRepo);
23592 $autoloadPackages = array($package->getName() => $package);
23593 $autoloadPackages = $this->collectDependencies($pool, $autoloadPackages, $package);
23595 $generator = $this->composer->getAutoloadGenerator();
23596 $autoloads = array();
23597 foreach ($autoloadPackages as $autoloadPackage) {
23598 $downloadPath = $this->getInstallPath($autoloadPackage, ($globalRepo && $globalRepo->hasPackage($autoloadPackage)));
23599 $autoloads[] = array($autoloadPackage, $downloadPath);
23602 $map = $generator->parseAutoloads($autoloads, new Package('dummy', '1.0.0.0', '1.0.0'));
23603 $classLoader = $generator->createLoader($map);
23604 $classLoader->register();
23606 foreach ($classes as $class) {
23607 if (class_exists($class, false)) {
23608 $code = file_get_contents($classLoader->findFile($class));
23609 $code = preg_replace('{^(\s*)class\s+(\S+)}mi', '$1class $2_composer_tmp'.self::$classCounter, $code);
23611 $class .= '_composer_tmp'.self::$classCounter;
23612 self::$classCounter++;
23615 if ($oldInstallerPlugin) {
23616 $installer = new $class($this->io, $this->composer);
23617 $this->composer->getInstallationManager()->addInstaller($installer);
23618 } elseif (class_exists($class)) {
23619 $plugin = new $class();
23620 $this->addPlugin($plugin);
23621 $this->registeredPlugins[] = $package->getName();
23622 } elseif ($failOnMissingClasses) {
23623 throw new \UnexpectedValueException('Plugin '.$package->getName().' could not be initialized, class not found: '.$class);
23636 public function getInstallPath(PackageInterface $package, $global = false)
23639 return $this->composer->getInstallationManager()->getInstallPath($package);
23642 return $this->globalComposer->getInstallationManager()->getInstallPath($package);
23657 namespace Composer;
23659 use Composer\Config\JsonConfigSource;
23660 use Composer\Json\JsonFile;
23661 use Composer\IO\IOInterface;
23662 use Composer\Package\Archiver;
23663 use Composer\Repository\RepositoryManager;
23664 use Composer\Repository\RepositoryInterface;
23665 use Composer\Repository\WritableRepositoryInterface;
23666 use Composer\Util\ProcessExecutor;
23667 use Composer\Util\RemoteFilesystem;
23668 use Composer\Util\Filesystem;
23669 use Symfony\Component\Console\Formatter\OutputFormatterStyle;
23670 use Composer\EventDispatcher\EventDispatcher;
23671 use Composer\Autoload\AutoloadGenerator;
23672 use Composer\Package\Version\VersionParser;
23688 protected static function getHomeDir()
23690 $home = getenv('COMPOSER_HOME');
23692 if (defined('PHP_WINDOWS_VERSION_MAJOR')) {
23693 if (!getenv('APPDATA')) {
23694 throw new \RuntimeException('The APPDATA or COMPOSER_HOME environment variable must be set for composer to run correctly');
23696 $home = strtr(getenv('APPDATA'), '\\', '/') . '/Composer';
23698 if (!getenv('HOME')) {
23699 throw new \RuntimeException('The HOME or COMPOSER_HOME environment variable must be set for composer to run correctly');
23701 $home = rtrim(getenv('HOME'), '/') . '/.composer';
23713 protected static function getCacheDir($home)
23715 $cacheDir = getenv('COMPOSER_CACHE_DIR');
23717 if (defined('PHP_WINDOWS_VERSION_MAJOR')) {
23718 if ($cacheDir = getenv('LOCALAPPDATA')) {
23719 $cacheDir .= '/Composer';
23721 $cacheDir = $home . '/cache';
23723 $cacheDir = strtr($cacheDir, '\\', '/');
23725 $cacheDir = $home.'/cache';
23736 public static function createConfig(IOInterface $io = null, $cwd = null)
23738 $cwd = $cwd ?: getcwd();
23741 $home = self::getHomeDir();
23742 $cacheDir = self::getCacheDir($home);
23747 foreach (array($home, $cacheDir) as $dir) {
23748 if (!file_exists($dir . '/.htaccess')) {
23749 if (!is_dir($dir)) {
23750 @mkdir($dir, 0777, true);
23752 @file_put_contents($dir . '/.htaccess', 'Deny from all');
23756 $config = new Config(true, $cwd);
23759 $config->merge(array('config' => array('home' => $home, 'cache-dir' => $cacheDir)));
23762 $file = new JsonFile($home.'/config.json');
23763 if ($file->exists()) {
23764 if ($io && $io->isDebug()) {
23765 $io->write('Loading config file ' . $file->getPath());
23767 $config->merge($file->read());
23769 $config->setConfigSource(new JsonConfigSource($file));
23772 $file = new JsonFile($config->get('home').'/auth.json');
23773 if ($file->exists()) {
23774 if ($io && $io->isDebug()) {
23775 $io->write('Loading config file ' . $file->getPath());
23777 $config->merge(array('config' => $file->read()));
23779 $config->setAuthConfigSource(new JsonConfigSource($file, true));
23784 public static function getComposerFile()
23786 return trim(getenv('COMPOSER')) ?: './composer.json';
23789 public static function createAdditionalStyles()
23792 'highlight' => new OutputFormatterStyle('red'),
23793 'warning' => new OutputFormatterStyle('black', 'yellow'),
23797 public static function createDefaultRepositories(IOInterface $io = null, Config $config = null, RepositoryManager $rm = null)
23802 $config = static::createConfig($io);
23806 throw new \InvalidArgumentException('This function requires either an IOInterface or a RepositoryManager');
23808 $factory = new static;
23809 $rm = $factory->createRepositoryManager($io, $config);
23812 foreach ($config->getRepositories() as $index => $repo) {
23813 if (!is_array($repo)) {
23814 throw new \UnexpectedValueException('Repository '.$index.' ('.json_encode($repo).') should be an array, '.gettype($repo).' given');
23816 if (!isset($repo['type'])) {
23817 throw new \UnexpectedValueException('Repository '.$index.' ('.json_encode($repo).') must have a type defined');
23819 $name = is_int($index) && isset($repo['url']) ? preg_replace('{^https?://}i', '', $repo['url']) : $index;
23820 while (isset($repos[$name])) {
23823 $repos[$name] = $rm->createRepository($repo['type'], $repo);
23841 public function createComposer(IOInterface $io, $localConfig = null, $disablePlugins = false, $cwd = null, $fullLoad = true)
23843 $cwd = $cwd ?: getcwd();
23846 if (null === $localConfig) {
23847 $localConfig = static::getComposerFile();
23850 if (is_string($localConfig)) {
23851 $composerFile = $localConfig;
23852 $file = new JsonFile($localConfig, new RemoteFilesystem($io));
23854 if (!$file->exists()) {
23855 if ($localConfig === './composer.json' || $localConfig === 'composer.json') {
23856 $message = 'Composer could not find a composer.json file in '.$cwd;
23858 $message = 'Composer could not find the config file: '.$localConfig;
23860 $instructions = 'To initialize a project, please create a composer.json file as described in the http://getcomposer.org/ "Getting Started" section';
23861 throw new \InvalidArgumentException($message.PHP_EOL.$instructions);
23864 $file->validateSchema(JsonFile::LAX_SCHEMA);
23865 $localConfig = $file->read();
23869 $config = static::createConfig($io, $cwd);
23870 $config->merge($localConfig);
23871 if (isset($composerFile)) {
23872 if ($io && $io->isDebug()) {
23873 $io->write('Loading config file ' . $composerFile);
23875 $localAuthFile = new JsonFile(dirname(realpath($composerFile)) . '/auth.json');
23876 if ($localAuthFile->exists()) {
23877 if ($io && $io->isDebug()) {
23878 $io->write('Loading config file ' . $localAuthFile->getPath());
23880 $config->merge(array('config' => $localAuthFile->read()));
23881 $config->setAuthConfigSource(new JsonConfigSource($localAuthFile, true));
23885 $vendorDir = $config->get('vendor-dir');
23886 $binDir = $config->get('bin-dir');
23889 $composer = new Composer();
23890 $composer->setConfig($config);
23894 $io->loadConfiguration($config);
23897 ProcessExecutor::setTimeout((int) $config->get('process-timeout'));
23901 $dispatcher = new EventDispatcher($composer, $io);
23902 $composer->setEventDispatcher($dispatcher);
23905 $rm = $this->createRepositoryManager($io, $config, $dispatcher);
23906 $composer->setRepositoryManager($rm);
23909 $this->addLocalRepository($rm, $vendorDir);
23912 $parser = new VersionParser;
23913 $loader = new Package\Loader\RootPackageLoader($rm, $config, $parser, new ProcessExecutor($io));
23914 $package = $loader->load($localConfig);
23915 $composer->setPackage($package);
23918 $im = $this->createInstallationManager();
23919 $composer->setInstallationManager($im);
23923 $dm = $this->createDownloadManager($io, $config, $dispatcher);
23924 $composer->setDownloadManager($dm);
23927 $generator = new AutoloadGenerator($dispatcher, $io);
23928 $composer->setAutoloadGenerator($generator);
23932 $this->createDefaultInstallers($im, $composer, $io);
23935 $globalComposer = $this->createGlobalComposer($io, $config, $disablePlugins);
23936 $pm = $this->createPluginManager($io, $composer, $globalComposer);
23937 $composer->setPluginManager($pm);
23939 if (!$disablePlugins) {
23940 $pm->loadInstalledPlugins();
23945 if ($rm->getLocalRepository()) {
23946 $this->purgePackages($rm->getLocalRepository(), $im);
23951 if ($fullLoad && isset($composerFile)) {
23952 $lockFile = "json" === pathinfo($composerFile, PATHINFO_EXTENSION)
23953 ? substr($composerFile, 0, -4).'lock'
23954 : $composerFile . '.lock';
23955 $locker = new Package\Locker($io, new JsonFile($lockFile, new RemoteFilesystem($io, $config)), $rm, $im, md5_file($composerFile));
23956 $composer->setLocker($locker);
23968 protected function createRepositoryManager(IOInterface $io, Config $config, EventDispatcher $eventDispatcher = null)
23970 $rm = new RepositoryManager($io, $config, $eventDispatcher);
23971 $rm->setRepositoryClass('composer', 'Composer\Repository\ComposerRepository');
23972 $rm->setRepositoryClass('vcs', 'Composer\Repository\VcsRepository');
23973 $rm->setRepositoryClass('package', 'Composer\Repository\PackageRepository');
23974 $rm->setRepositoryClass('pear', 'Composer\Repository\PearRepository');
23975 $rm->setRepositoryClass('git', 'Composer\Repository\VcsRepository');
23976 $rm->setRepositoryClass('svn', 'Composer\Repository\VcsRepository');
23977 $rm->setRepositoryClass('perforce', 'Composer\Repository\VcsRepository');
23978 $rm->setRepositoryClass('hg', 'Composer\Repository\VcsRepository');
23979 $rm->setRepositoryClass('artifact', 'Composer\Repository\ArtifactRepository');
23988 protected function addLocalRepository(RepositoryManager $rm, $vendorDir)
23990 $rm->setLocalRepository(new Repository\InstalledFilesystemRepository(new JsonFile($vendorDir.'/composer/installed.json')));
23997 protected function createGlobalComposer(IOInterface $io, Config $config, $disablePlugins)
23999 if (realpath($config->get('home')) === getcwd()) {
24005 $composer = self::createComposer($io, $config->get('home') . '/composer.json', $disablePlugins, $config->get('home'), false);
24006 } catch (\Exception $e) {
24007 if ($io->isDebug()) {
24008 $io->write('Failed to initialize global composer: '.$e->getMessage());
24021 public function createDownloadManager(IOInterface $io, Config $config, EventDispatcher $eventDispatcher = null)
24024 if ($config->get('cache-files-ttl') > 0) {
24025 $cache = new Cache($io, $config->get('cache-files-dir'), 'a-z0-9_./');
24028 $dm = new Downloader\DownloadManager($io);
24029 switch ($config->get('preferred-install')) {
24031 $dm->setPreferDist(true);
24034 $dm->setPreferSource(true);
24042 $dm->setDownloader('git', new Downloader\GitDownloader($io, $config));
24043 $dm->setDownloader('svn', new Downloader\SvnDownloader($io, $config));
24044 $dm->setDownloader('hg', new Downloader\HgDownloader($io, $config));
24045 $dm->setDownloader('perforce', new Downloader\PerforceDownloader($io, $config));
24046 $dm->setDownloader('zip', new Downloader\ZipDownloader($io, $config, $eventDispatcher, $cache));
24047 $dm->setDownloader('rar', new Downloader\RarDownloader($io, $config, $eventDispatcher, $cache));
24048 $dm->setDownloader('tar', new Downloader\TarDownloader($io, $config, $eventDispatcher, $cache));
24049 $dm->setDownloader('gzip', new Downloader\GzipDownloader($io, $config, $eventDispatcher, $cache));
24050 $dm->setDownloader('phar', new Downloader\PharDownloader($io, $config, $eventDispatcher, $cache));
24051 $dm->setDownloader('file', new Downloader\FileDownloader($io, $config, $eventDispatcher, $cache));
24062 public function createArchiveManager(Config $config, Downloader\DownloadManager $dm = null)
24064 if (null === $dm) {
24065 $io = new IO\NullIO();
24066 $io->loadConfiguration($config);
24067 $dm = $this->createDownloadManager($io, $config);
24070 $am = new Archiver\ArchiveManager($dm);
24071 $am->addArchiver(new Archiver\PharArchiver);
24082 protected function createPluginManager(IOInterface $io, Composer $composer, Composer $globalComposer = null)
24084 return new Plugin\PluginManager($io, $composer, $globalComposer);
24090 protected function createInstallationManager()
24092 return new Installer\InstallationManager();
24100 protected function createDefaultInstallers(Installer\InstallationManager $im, Composer $composer, IOInterface $io)
24102 $im->addInstaller(new Installer\LibraryInstaller($io, $composer, null));
24103 $im->addInstaller(new Installer\PearInstaller($io, $composer, 'pear-library'));
24104 $im->addInstaller(new Installer\PluginInstaller($io, $composer));
24105 $im->addInstaller(new Installer\MetapackageInstaller($io));
24112 protected function purgePackages(WritableRepositoryInterface $repo, Installer\InstallationManager $im)
24114 foreach ($repo->getPackages() as $package) {
24115 if (!$im->isPackageInstalled($repo, $package)) {
24116 $repo->removePackage($package);
24128 public static function create(IOInterface $io, $config = null, $disablePlugins = false)
24130 $factory = new static();
24132 return $factory->createComposer($io, $config, $disablePlugins);
24147 namespace Composer\Util;
24149 use RecursiveDirectoryIterator;
24150 use RecursiveIteratorIterator;
24151 use Symfony\Component\Finder\Finder;
24159 private $processExecutor;
24161 public function __construct(ProcessExecutor $executor = null)
24163 $this->processExecutor = $executor ?: new ProcessExecutor();
24166 public function remove($file)
24168 if (is_dir($file)) {
24169 return $this->removeDirectory($file);
24172 if (file_exists($file)) {
24173 return $this->unlink($file);
24185 public function isDirEmpty($dir)
24187 $finder = Finder::create()
24189 ->ignoreDotFiles(false)
24193 return count($finder) === 0;
24196 public function emptyDirectory($dir, $ensureDirectoryExists = true)
24198 if (file_exists($dir) && is_link($dir)) {
24199 $this->unlink($dir);
24202 if ($ensureDirectoryExists) {
24203 $this->ensureDirectoryExists($dir);
24206 if (is_dir($dir)) {
24207 $finder = Finder::create()
24209 ->ignoreDotFiles(false)
24213 foreach ($finder as $path) {
24214 $this->remove((string) $path);
24230 public function removeDirectory($directory)
24232 if ($this->isSymlinkedDirectory($directory)) {
24233 return $this->unlinkSymlinkedDirectory($directory);
24236 if (!file_exists($directory) || !is_dir($directory)) {
24240 if (preg_match('{^(?:[a-z]:)?[/\\\\]+$}i', $directory)) {
24241 throw new \RuntimeException('Aborting an attempted deletion of '.$directory.', this was probably not intended, if it is a real use case please report it.');
24244 if (!function_exists('proc_open')) {
24245 return $this->removeDirectoryPhp($directory);
24248 if (defined('PHP_WINDOWS_VERSION_BUILD')) {
24249 $cmd = sprintf('rmdir /S /Q %s', ProcessExecutor::escape(realpath($directory)));
24251 $cmd = sprintf('rm -rf %s', ProcessExecutor::escape($directory));
24254 $result = $this->getProcess()->execute($cmd, $output) === 0;
24259 if ($result && !file_exists($directory)) {
24263 return $this->removeDirectoryPhp($directory);
24276 public function removeDirectoryPhp($directory)
24278 $it = new RecursiveDirectoryIterator($directory, RecursiveDirectoryIterator::SKIP_DOTS);
24279 $ri = new RecursiveIteratorIterator($it, RecursiveIteratorIterator::CHILD_FIRST);
24281 foreach ($ri as $file) {
24282 if ($file->isDir()) {
24283 $this->rmdir($file->getPathname());
24285 $this->unlink($file->getPathname());
24289 return $this->rmdir($directory);
24292 public function ensureDirectoryExists($directory)
24294 if (!is_dir($directory)) {
24295 if (file_exists($directory)) {
24296 throw new \RuntimeException(
24297 $directory.' exists and is not a directory.'
24300 if (!@mkdir($directory, 0777, true)) {
24301 throw new \RuntimeException(
24302 $directory.' does not exist and could not be created.'
24316 public function unlink($path)
24318 if (!@$this->unlinkImplementation($path)) {
24320 if (!defined('PHP_WINDOWS_VERSION_BUILD') || (usleep(350000) && !@$this->unlinkImplementation($path))) {
24321 $error = error_get_last();
24322 $message = 'Could not delete '.$path.': ' . @$error['message'];
24323 if (defined('PHP_WINDOWS_VERSION_BUILD')) {
24324 $message .= "\nThis can be due to an antivirus or the Windows Search Indexer locking the file while they are analyzed";
24327 throw new \RuntimeException($message);
24342 public function rmdir($path)
24344 if (!@rmdir($path)) {
24346 if (!defined('PHP_WINDOWS_VERSION_BUILD') || (usleep(350000) && !@rmdir($path))) {
24347 $error = error_get_last();
24348 $message = 'Could not delete '.$path.': ' . @$error['message'];
24349 if (defined('PHP_WINDOWS_VERSION_BUILD')) {
24350 $message .= "\nThis can be due to an antivirus or the Windows Search Indexer locking the file while they are analyzed";
24353 throw new \RuntimeException($message);
24369 public function copyThenRemove($source, $target)
24371 if (!is_dir($source)) {
24372 copy($source, $target);
24373 $this->unlink($source);
24378 $it = new RecursiveDirectoryIterator($source, RecursiveDirectoryIterator::SKIP_DOTS);
24379 $ri = new RecursiveIteratorIterator($it, RecursiveIteratorIterator::SELF_FIRST);
24380 $this->ensureDirectoryExists($target);
24382 foreach ($ri as $file) {
24383 $targetPath = $target . DIRECTORY_SEPARATOR . $ri->getSubPathName();
24384 if ($file->isDir()) {
24385 $this->ensureDirectoryExists($targetPath);
24387 copy($file->getPathname(), $targetPath);
24391 $this->removeDirectoryPhp($source);
24394 public function rename($source, $target)
24396 if (true === @rename($source, $target)) {
24400 if (!function_exists('proc_open')) {
24401 return $this->copyThenRemove($source, $target);
24404 if (defined('PHP_WINDOWS_VERSION_BUILD')) {
24406 $command = sprintf('xcopy %s %s /E /I /Q', ProcessExecutor::escape($source), ProcessExecutor::escape($target));
24407 $result = $this->processExecutor->execute($command, $output);
24412 if (0 === $result) {
24413 $this->remove($source);
24420 $command = sprintf('mv %s %s', ProcessExecutor::escape($source), ProcessExecutor::escape($target));
24421 $result = $this->processExecutor->execute($command, $output);
24426 if (0 === $result) {
24431 return $this->copyThenRemove($source, $target);
24443 public function findShortestPath($from, $to, $directories = false)
24445 if (!$this->isAbsolutePath($from) || !$this->isAbsolutePath($to)) {
24446 throw new \InvalidArgumentException(sprintf('$from (%s) and $to (%s) must be absolute paths.', $from, $to));
24449 $from = lcfirst($this->normalizePath($from));
24450 $to = lcfirst($this->normalizePath($to));
24452 if ($directories) {
24453 $from .= '/dummy_file';
24456 if (dirname($from) === dirname($to)) {
24457 return './'.basename($to);
24461 while (strpos($from.'/', $commonPath.'/') !== 0 && '/' !== $commonPath && !preg_match('{^[a-z]:/?$}i', $commonPath)) {
24462 $commonPath = strtr(dirname($commonPath), '\\', '/');
24465 if (0 !== strpos($from, $commonPath) || '/' === $commonPath) {
24469 $commonPath = rtrim($commonPath, '/') . '/';
24470 $sourcePathDepth = substr_count(substr($from, strlen($commonPath)), '/');
24471 $commonPathCode = str_repeat('../', $sourcePathDepth);
24473 return ($commonPathCode . substr($to, strlen($commonPath))) ?: './';
24485 public function findShortestPathCode($from, $to, $directories = false)
24487 if (!$this->isAbsolutePath($from) || !$this->isAbsolutePath($to)) {
24488 throw new \InvalidArgumentException(sprintf('$from (%s) and $to (%s) must be absolute paths.', $from, $to));
24491 $from = lcfirst($this->normalizePath($from));
24492 $to = lcfirst($this->normalizePath($to));
24494 if ($from === $to) {
24495 return $directories ? '__DIR__' : '__FILE__';
24499 while (strpos($from.'/', $commonPath.'/') !== 0 && '/' !== $commonPath && !preg_match('{^[a-z]:/?$}i', $commonPath) && '.' !== $commonPath) {
24500 $commonPath = strtr(dirname($commonPath), '\\', '/');
24503 if (0 !== strpos($from, $commonPath) || '/' === $commonPath || '.' === $commonPath) {
24504 return var_export($to, true);
24507 $commonPath = rtrim($commonPath, '/') . '/';
24508 if (strpos($to, $from.'/') === 0) {
24509 return '__DIR__ . '.var_export(substr($to, strlen($from)), true);
24511 $sourcePathDepth = substr_count(substr($from, strlen($commonPath)), '/') + $directories;
24512 $commonPathCode = str_repeat('dirname(', $sourcePathDepth).'__DIR__'.str_repeat(')', $sourcePathDepth);
24513 $relTarget = substr($to, strlen($commonPath));
24515 return $commonPathCode . (strlen($relTarget) ? '.' . var_export('/' . $relTarget, true) : '');
24524 public function isAbsolutePath($path)
24526 return substr($path, 0, 1) === '/' || substr($path, 1, 1) === ':';
24537 public function size($path)
24539 if (!file_exists($path)) {
24540 throw new \RuntimeException("$path does not exist.");
24542 if (is_dir($path)) {
24543 return $this->directorySize($path);
24546 return filesize($path);
24556 public function normalizePath($path)
24559 $path = strtr($path, '\\', '/');
24563 if (preg_match('{^([0-9a-z]+:(?://(?:[a-z]:)?)?)}i', $path, $match)) {
24564 $prefix = $match[1];
24565 $path = substr($path, strlen($prefix));
24568 if (substr($path, 0, 1) === '/') {
24570 $path = substr($path, 1);
24574 foreach (explode('/', $path) as $chunk) {
24575 if ('..' === $chunk && ($absolute || $up)) {
24577 $up = !(empty($parts) || '..' === end($parts));
24578 } elseif ('.' !== $chunk && '' !== $chunk) {
24580 $up = '..' !== $chunk;
24584 return $prefix.($absolute ? '/' : '').implode('/', $parts);
24593 public static function isLocalPath($path)
24595 return (bool) preg_match('{^(file://|/|[a-z]:[\\\\/]|\.\.[\\\\/]|[a-z0-9_.-]+[\\\\/])}i', $path);
24598 public static function getPlatformPath($path)
24600 if (defined('PHP_WINDOWS_VERSION_BUILD')) {
24601 $path = preg_replace('{^(?:file:///([a-z])/)}i', 'file://$1:/', $path);
24604 return preg_replace('{^file://}i', '', $path);
24607 protected function directorySize($directory)
24609 $it = new RecursiveDirectoryIterator($directory, RecursiveDirectoryIterator::SKIP_DOTS);
24610 $ri = new RecursiveIteratorIterator($it, RecursiveIteratorIterator::CHILD_FIRST);
24613 foreach ($ri as $file) {
24614 if ($file->isFile()) {
24615 $size += $file->getSize();
24622 protected function getProcess()
24624 return new ProcessExecutor;
24636 private function unlinkImplementation($path)
24638 if (defined('PHP_WINDOWS_VERSION_BUILD') && is_dir($path) && is_link($path)) {
24639 return rmdir($path);
24642 return unlink($path);
24645 private function isSymlinkedDirectory($directory)
24647 if (!is_dir($directory)) {
24651 $resolved = $this->resolveSymlinkedDirectorySymlink($directory);
24653 return is_link($resolved);
24661 private function unlinkSymlinkedDirectory($directory)
24663 $resolved = $this->resolveSymlinkedDirectorySymlink($directory);
24665 return $this->unlink($resolved);
24675 private function resolveSymlinkedDirectorySymlink($pathname)
24677 if (!is_dir($pathname)) {
24681 $resolved = rtrim($pathname, '/');
24683 if (!strlen($resolved)) {
24702 namespace Composer\Util;
24704 use Composer\IO\IOInterface;
24705 use Composer\Config;
24706 use Composer\Downloader\TransportException;
24707 use Composer\Json\JsonFile;
24716 protected $process;
24717 protected $remoteFilesystem;
24727 public function __construct(IOInterface $io, Config $config, ProcessExecutor $process = null, RemoteFilesystem $remoteFilesystem = null)
24730 $this->config = $config;
24731 $this->process = $process ?: new ProcessExecutor;
24732 $this->remoteFilesystem = $remoteFilesystem ?: new RemoteFilesystem($io, $config);
24741 public function authorizeOAuth($originUrl)
24743 if (!in_array($originUrl, $this->config->get('github-domains'))) {
24748 if (0 === $this->process->execute('git config github.accesstoken', $output)) {
24749 $this->io->setAuthentication($originUrl, trim($output), 'x-oauth-basic');
24766 public function authorizeOAuthInteractively($originUrl, $message = null)
24768 $attemptCounter = 0;
24770 $apiUrl = ('github.com' === $originUrl) ? 'api.github.com' : $originUrl . '/api/v3';
24773 $this->io->write($message);
24775 $this->io->write('The credentials will be swapped for an OAuth token stored in '.$this->config->getAuthConfigSource()->getName().', your password will not be stored');
24776 $this->io->write('To revoke access to this token you can visit https://github.com/settings/applications');
24777 while ($attemptCounter++ < 5) {
24779 if (empty($otp) || !$this->io->hasAuthentication($originUrl)) {
24780 $username = $this->io->ask('Username: ');
24781 $password = $this->io->askAndHideAnswer('Password: ');
24784 $this->io->setAuthentication($originUrl, $username, $password);
24788 $appName = 'Composer';
24789 if ($this->config->get('github-expose-hostname') === true && 0 === $this->process->execute('hostname', $output)) {
24790 $appName .= ' on ' . trim($output);
24792 $appName .= ' [' . date('YmdHis') . ']';
24795 $headers = array();
24797 $headers = array('X-GitHub-OTP: ' . $otp);
24802 $auths = JsonFile::parseJson($this->remoteFilesystem->getContents($originUrl, 'https://'. $apiUrl . '/authorizations', false, array(
24803 'retry-auth-failure' => false,
24805 'header' => $headers
24808 foreach ($auths as $auth) {
24810 isset($auth['app']['name'])
24811 && 0 === strpos($auth['app']['name'], $appName)
24812 && $auth['app']['url'] === 'https://getcomposer.org/'
24814 $this->io->write('An existing OAuth token for Composer is present and will be reused');
24816 $contents['token'] = $auth['token'];
24822 if (empty($contents['token'])) {
24823 $headers[] = 'Content-Type: application/json';
24825 $contents = JsonFile::parseJson($this->remoteFilesystem->getContents($originUrl, 'https://'. $apiUrl . '/authorizations', false, array(
24826 'retry-auth-failure' => false,
24828 'method' => 'POST',
24829 'follow_location' => false,
24830 'header' => $headers,
24831 'content' => json_encode(array(
24832 'scopes' => array('repo'),
24833 'note' => $appName,
24834 'note_url' => 'https://getcomposer.org/',
24838 $this->io->write('Token successfully created');
24840 } catch (TransportException $e) {
24841 if (in_array($e->getCode(), array(403, 401))) {
24843 if ($this->io->hasAuthentication($originUrl)) {
24844 $headerNames = array_map(function ($header) {
24845 return strtolower(strstr($header, ':', true));
24846 }, $e->getHeaders());
24848 if ($key = array_search('x-github-otp', $headerNames)) {
24849 $headers = $e->getHeaders();
24850 list($required, $method) = array_map('trim', explode(';', substr(strstr($headers[$key], ':'), 1)));
24852 if ('required' === $required) {
24853 $this->io->write('Two-factor Authentication');
24855 if ('app' === $method) {
24856 $this->io->write('Open the two-factor authentication app on your device to view your authentication code and verify your identity.');
24859 if ('sms' === $method) {
24860 $this->io->write('You have been sent an SMS message with an authentication code to verify your identity.');
24863 $otp = $this->io->ask('Authentication Code: ');
24870 $this->io->write('Invalid credentials.');
24877 $this->io->setAuthentication($originUrl, $contents['token'], 'x-oauth-basic');
24880 $this->config->getConfigSource()->removeConfigSetting('github-oauth.'.$originUrl);
24881 $this->config->getAuthConfigSource()->addConfigSetting('github-oauth.'.$originUrl, $contents['token']);
24886 throw new \RuntimeException("Invalid GitHub credentials 5 times in a row, aborting.");
24901 namespace Composer\Util;
24908 class ComposerMirror
24910 public static function processUrl($mirrorUrl, $packageName, $version, $reference, $type)
24913 $reference = preg_match('{^([a-f0-9]*|%reference%)$}', $reference) ? $reference : md5($reference);
24915 $version = strpos($version, '/') === false ? $version : md5($version);
24917 return str_replace(
24918 array('%package%', '%version%', '%reference%', '%type%'),
24919 array($packageName, $version, $reference, $type),
24924 public static function processGitUrl($mirrorUrl, $packageName, $url, $type)
24926 if (preg_match('#^(?:(?:https?|git)://github\.com/|git@github\.com:)([^/]+)/(.+?)(?:\.git)?$#', $url, $match)) {
24927 $url = 'gh-'.$match[1].'/'.$match[2];
24928 } elseif (preg_match('#^https://bitbucket\.org/([^/]+)/(.+?)(?:\.git)?/?$#', $url, $match)) {
24929 $url = 'bb-'.$match[1].'/'.$match[2];
24931 $url = preg_replace('{[^a-z0-9_.-]}i', '-', trim($url, '/'));
24934 return str_replace(
24935 array('%package%', '%normalizedUrl%', '%type%'),
24936 array($packageName, $url, $type),
24941 public static function processHgUrl($mirrorUrl, $packageName, $url, $type)
24943 return self::processGitUrl($mirrorUrl, $packageName, $url, $type);
24958 namespace Composer\Util;
24960 use Composer\IO\IOInterface;
24961 use Symfony\Component\Process\Process;
24969 protected $p4Depot;
24970 protected $p4Client;
24972 protected $p4Password;
24974 protected $p4Stream;
24975 protected $p4ClientSpec;
24976 protected $p4DepotType;
24977 protected $p4Branch;
24978 protected $process;
24979 protected $uniquePerforceClientName;
24980 protected $windowsFlag;
24981 protected $commandResult;
24985 protected $filesystem;
24987 public function __construct($repoConfig, $port, $path, ProcessExecutor $process, $isWindows, IOInterface $io)
24989 $this->windowsFlag = $isWindows;
24990 $this->p4Port = $port;
24991 $this->initializePath($path);
24992 $this->process = $process;
24993 $this->initialize($repoConfig);
24997 public static function create($repoConfig, $port, $path, ProcessExecutor $process, IOInterface $io)
24999 $isWindows = defined('PHP_WINDOWS_VERSION_BUILD');
25000 $perforce = new Perforce($repoConfig, $port, $path, $process, $isWindows, $io);
25005 public static function checkServerExists($url, ProcessExecutor $processExecutor)
25009 return 0 === $processExecutor->execute('p4 -p ' . $url . ' info -s', $output);
25012 public function initialize($repoConfig)
25014 $this->uniquePerforceClientName = $this->generateUniquePerforceClientName();
25015 if (null == $repoConfig) {
25018 if (isset($repoConfig['unique_perforce_client_name'])) {
25019 $this->uniquePerforceClientName = $repoConfig['unique_perforce_client_name'];
25022 if (isset($repoConfig['depot'])) {
25023 $this->p4Depot = $repoConfig['depot'];
25025 if (isset($repoConfig['branch'])) {
25026 $this->p4Branch = $repoConfig['branch'];
25028 if (isset($repoConfig['p4user'])) {
25029 $this->p4User = $repoConfig['p4user'];
25031 $this->p4User = $this->getP4variable('P4USER');
25033 if (isset($repoConfig['p4password'])) {
25034 $this->p4Password = $repoConfig['p4password'];
25038 public function initializeDepotAndBranch($depot, $branch)
25040 if (isset($depot)) {
25041 $this->p4Depot = $depot;
25043 if (isset($branch)) {
25044 $this->p4Branch = $branch;
25048 public function generateUniquePerforceClientName()
25050 return gethostname() . "_" . time();
25053 public function cleanupClientSpec()
25055 $client = $this->getClient();
25056 $task = 'client -d ' . $client;
25057 $useP4Client = false;
25058 $command = $this->generateP4Command($task, $useP4Client);
25059 $this->executeCommand($command);
25060 $clientSpec = $this->getP4ClientSpec();
25061 $fileSystem = $this->getFilesystem();
25062 $fileSystem->remove($clientSpec);
25065 protected function executeCommand($command)
25067 $this->commandResult = "";
25068 $exit_code = $this->process->execute($command, $this->commandResult);
25073 public function getClient()
25075 if (!isset($this->p4Client)) {
25076 $cleanStreamName = str_replace('@', '', str_replace('/', '_', str_replace('//', '', $this->getStream())));
25077 $this->p4Client = 'composer_perforce_' . $this->uniquePerforceClientName . '_' . $cleanStreamName;
25080 return $this->p4Client;
25083 protected function getPath()
25085 return $this->path;
25088 public function initializePath($path)
25090 $this->path = $path;
25091 $fs = $this->getFilesystem();
25092 $fs->ensureDirectoryExists($path);
25095 protected function getPort()
25097 return $this->p4Port;
25100 public function setStream($stream)
25102 $this->p4Stream = $stream;
25103 $index = strrpos($stream, '/');
25106 $this->p4DepotType = 'stream';
25110 public function isStream()
25112 return (strcmp($this->p4DepotType, 'stream') === 0);
25115 public function getStream()
25117 if (!isset($this->p4Stream)) {
25118 if ($this->isStream()) {
25119 $this->p4Stream = '//' . $this->p4Depot . '/' . $this->p4Branch;
25121 $this->p4Stream = '//' . $this->p4Depot;
25125 return $this->p4Stream;
25128 public function getStreamWithoutLabel($stream)
25130 $index = strpos($stream, '@');
25131 if ($index === false) {
25135 return substr($stream, 0, $index);
25138 public function getP4ClientSpec()
25140 $p4clientSpec = $this->path . '/' . $this->getClient() . '.p4.spec';
25142 return $p4clientSpec;
25145 public function getUser()
25147 return $this->p4User;
25150 public function setUser($user)
25152 $this->p4User = $user;
25155 public function queryP4User()
25158 if (strlen($this->p4User) > 0) {
25161 $this->p4User = $this->getP4variable('P4USER');
25162 if (strlen($this->p4User) > 0) {
25165 $this->p4User = $this->io->ask('Enter P4 User:');
25166 if ($this->windowsFlag) {
25167 $command = 'p4 set P4USER=' . $this->p4User;
25169 $command = 'export P4USER=' . $this->p4User;
25171 $this->executeCommand($command);
25174 protected function getP4variable($name)
25176 if ($this->windowsFlag) {
25177 $command = 'p4 set';
25178 $this->executeCommand($command);
25179 $result = trim($this->commandResult);
25180 $resArray = explode(PHP_EOL, $result);
25181 foreach ($resArray as $line) {
25182 $fields = explode('=', $line);
25183 if (strcmp($name, $fields[0]) == 0) {
25184 $index = strpos($fields[1], ' ');
25185 if ($index === false) {
25186 $value = $fields[1];
25188 $value = substr($fields[1], 0, $index);
25190 $value = trim($value);
25196 $command = 'echo $' . $name;
25197 $this->executeCommand($command);
25198 $result = trim($this->commandResult);
25204 public function queryP4Password()
25206 if (isset($this->p4Password)) {
25207 return $this->p4Password;
25209 $password = $this->getP4variable('P4PASSWD');
25210 if (strlen($password) <= 0) {
25211 $password = $this->io->askAndHideAnswer('Enter password for Perforce user ' . $this->getUser() . ': ');
25213 $this->p4Password = $password;
25218 public function generateP4Command($command, $useClient = true)
25220 $p4Command = 'p4 ';
25221 $p4Command = $p4Command . '-u ' . $this->getUser() . ' ';
25223 $p4Command = $p4Command . '-c ' . $this->getClient() . ' ';
25225 $p4Command = $p4Command . '-p ' . $this->getPort() . ' ';
25226 $p4Command = $p4Command . $command;
25231 public function isLoggedIn()
25233 $command = $this->generateP4Command('login -s', false);
25234 $exitCode = $this->executeCommand($command);
25236 $errorOutput = $this->process->getErrorOutput();
25237 $index = strpos($errorOutput, $this->getUser());
25238 if ($index === false) {
25239 $index = strpos($errorOutput, 'p4');
25240 if ($index === false) {
25243 throw new \Exception('p4 command not found in path: ' . $errorOutput);
25245 throw new \Exception('Invalid user name: ' . $this->getUser() );
25251 public function connectClient()
25253 $p4CreateClientCommand = $this->generateP4Command('client -i < ' . str_replace( " ", "\\ ", $this->getP4ClientSpec() ));
25254 $this->executeCommand($p4CreateClientCommand);
25257 public function syncCodeBase($sourceReference)
25259 $prevDir = getcwd();
25260 chdir($this->path);
25261 $p4SyncCommand = $this->generateP4Command('sync -f ');
25262 if (null != $sourceReference) {
25263 $p4SyncCommand = $p4SyncCommand . '@' . $sourceReference;
25265 $this->executeCommand($p4SyncCommand);
25269 public function writeClientSpecToFile($spec)
25271 fwrite($spec, 'Client: ' . $this->getClient() . PHP_EOL . PHP_EOL);
25272 fwrite($spec, 'Update: ' . date('Y/m/d H:i:s') . PHP_EOL . PHP_EOL);
25273 fwrite($spec, 'Access: ' . date('Y/m/d H:i:s') . PHP_EOL);
25274 fwrite($spec, 'Owner: ' . $this->getUser() . PHP_EOL . PHP_EOL);
25275 fwrite($spec, 'Description:' . PHP_EOL);
25276 fwrite($spec, ' Created by ' . $this->getUser() . ' from composer.' . PHP_EOL . PHP_EOL);
25277 fwrite($spec, 'Root: ' . $this->getPath() . PHP_EOL . PHP_EOL);
25278 fwrite($spec, 'Options: noallwrite noclobber nocompress unlocked modtime rmdir' . PHP_EOL . PHP_EOL);
25279 fwrite($spec, 'SubmitOptions: revertunchanged' . PHP_EOL . PHP_EOL);
25280 fwrite($spec, 'LineEnd: local' . PHP_EOL . PHP_EOL);
25281 if ($this->isStream()) {
25282 fwrite($spec, 'Stream:' . PHP_EOL);
25283 fwrite($spec, ' ' . $this->getStreamWithoutLabel($this->p4Stream) . PHP_EOL);
25287 'View: ' . $this->getStream() . '/... //' . $this->getClient() . '/... ' . PHP_EOL
25292 public function writeP4ClientSpec()
25294 $clientSpec = $this->getP4ClientSpec();
25295 $spec = fopen($clientSpec, 'w');
25297 $this->writeClientSpecToFile($spec);
25298 } catch (\Exception $e) {
25305 protected function read($pipe, $name)
25310 $line = fgets($pipe);
25311 while ($line != false) {
25312 $line = fgets($pipe);
25318 public function windowsLogin($password)
25320 $command = $this->generateP4Command(' login -a');
25321 $process = new Process($command, null, null, $password);
25323 return $process->run();
25326 public function p4Login()
25328 $this->queryP4User();
25329 if (!$this->isLoggedIn()) {
25330 $password = $this->queryP4Password();
25331 if ($this->windowsFlag) {
25332 $this->windowsLogin($password);
25334 $command = 'echo ' . $password . ' | ' . $this->generateP4Command(' login -a', false);
25335 $exitCode = $this->executeCommand($command);
25336 $result = trim($this->commandResult);
25338 throw new \Exception("Error logging in:" . $this->process->getErrorOutput());
25344 public function getComposerInformation($identifier)
25346 $index = strpos($identifier, '@');
25347 if ($index === false) {
25348 $composerJson = $identifier. '/composer.json';
25350 return $this->getComposerInformationFromPath($composerJson);
25353 return $this->getComposerInformationFromLabel($identifier, $index);
25356 public function getComposerInformationFromPath($composerJson)
25358 $command = $this->generateP4Command(' print ' . $composerJson);
25359 $this->executeCommand($command);
25360 $result = $this->commandResult;
25361 $index = strpos($result, '{');
25362 if ($index === false) {
25366 $rawData = substr($result, $index);
25367 $composer_info = json_decode($rawData, true);
25369 return $composer_info;
25375 public function getComposerInformationFromLabel($identifier, $index)
25377 $composerJsonPath = substr($identifier, 0, $index) . '/composer.json' . substr($identifier, $index);
25378 $command = $this->generateP4Command(' files ' . $composerJsonPath, false);
25379 $this->executeCommand($command);
25380 $result = $this->commandResult;
25381 $index2 = strpos($result, 'no such file(s).');
25382 if ($index2 === false) {
25383 $index3 = strpos($result, 'change');
25384 if (!($index3 === false)) {
25385 $phrase = trim(substr($result, $index3));
25386 $fields = explode(' ', $phrase);
25388 $composerJson = substr($identifier, 0, $index) . '/composer.json@' . $id;
25390 return $this->getComposerInformationFromPath($composerJson);
25397 public function getBranches()
25399 $possibleBranches = array();
25400 if (!$this->isStream()) {
25401 $possibleBranches[$this->p4Branch] = $this->getStream();
25403 $command = $this->generateP4Command('streams //' . $this->p4Depot . '/...');
25404 $this->executeCommand($command);
25405 $result = $this->commandResult;
25406 $resArray = explode(PHP_EOL, $result);
25407 foreach ($resArray as $line) {
25408 $resBits = explode(' ', $line);
25409 if (count($resBits) > 4) {
25410 $branch = preg_replace('/[^A-Za-z0-9 ]/', '', $resBits[4]);
25411 $possibleBranches[$branch] = $resBits[1];
25415 $command = $this->generateP4Command('changes '. $this->getStream() . '/...', false);
25416 $this->executeCommand($command);
25417 $result = $this->commandResult;
25418 $resArray = explode(PHP_EOL, $result);
25419 $lastCommit = $resArray[0];
25420 $lastCommitArr = explode(' ', $lastCommit);
25421 $lastCommitNum = $lastCommitArr[1];
25423 $branches = array('master' => $possibleBranches[$this->p4Branch] . '@'. $lastCommitNum);
25428 public function getTags()
25430 $command = $this->generateP4Command('labels');
25431 $this->executeCommand($command);
25432 $result = $this->commandResult;
25433 $resArray = explode(PHP_EOL, $result);
25435 foreach ($resArray as $line) {
25436 $index = strpos($line, 'Label');
25437 if (!($index === false)) {
25438 $fields = explode(' ', $line);
25439 $tags[$fields[1]] = $this->getStream() . '@' . $fields[1];
25446 public function checkStream()
25448 $command = $this->generateP4Command('depots', false);
25449 $this->executeCommand($command);
25450 $result = $this->commandResult;
25451 $resArray = explode(PHP_EOL, $result);
25452 foreach ($resArray as $line) {
25453 $index = strpos($line, 'Depot');
25454 if (!($index === false)) {
25455 $fields = explode(' ', $line);
25456 if (strcmp($this->p4Depot, $fields[1]) === 0) {
25457 $this->p4DepotType = $fields[3];
25459 return $this->isStream();
25467 protected function getChangeList($reference)
25469 $index = strpos($reference, '@');
25470 if ($index === false) {
25473 $label = substr($reference, $index);
25474 $command = $this->generateP4Command(' changes -m1 ' . $label);
25475 $this->executeCommand($command);
25476 $changes = $this->commandResult;
25477 if (strpos($changes, 'Change') !== 0) {
25480 $fields = explode(' ', $changes);
25481 $changeList = $fields[1];
25483 return $changeList;
25486 public function getCommitLogs($fromReference, $toReference)
25488 $fromChangeList = $this->getChangeList($fromReference);
25489 if ($fromChangeList == null) {
25492 $toChangeList = $this->getChangeList($toReference);
25493 if ($toChangeList == null) {
25496 $index = strpos($fromReference, '@');
25497 $main = substr($fromReference, 0, $index) . '/...';
25498 $command = $this->generateP4Command('filelog ' . $main . '@' . $fromChangeList. ',' . $toChangeList);
25499 $this->executeCommand($command);
25500 $result = $this->commandResult;
25505 public function getFilesystem()
25507 if (empty($this->filesystem)) {
25508 $this->filesystem = new Filesystem($this->process);
25511 return $this->filesystem;
25514 public function setFilesystem(Filesystem $fs)
25516 $this->filesystem = $fs;
25531 namespace Composer\Util;
25533 use Symfony\Component\Process\Process;
25534 use Symfony\Component\Process\ProcessUtils;
25535 use Composer\IO\IOInterface;
25540 class ProcessExecutor
25542 protected static $timeout = 300;
25544 protected $captureOutput;
25545 protected $errorOutput;
25548 public function __construct(IOInterface $io = null)
25562 public function execute($command, &$output = null, $cwd = null)
25564 if ($this->io && $this->io->isDebug()) {
25565 $safeCommand = preg_replace('{(://[^:/\s]+:)[^@\s/]+}i', '$1****', $command);
25566 $this->io->write('Executing command ('.($cwd ?: 'CWD').'): '.$safeCommand);
25571 if (null === $cwd && defined('PHP_WINDOWS_VERSION_BUILD') && false !== strpos($command, 'git') && getcwd()) {
25572 $cwd = realpath(getcwd());
25575 $this->captureOutput = count(func_get_args()) > 1;
25576 $this->errorOutput = null;
25577 $process = new Process($command, $cwd, null, null, static::getTimeout());
25579 $callback = is_callable($output) ? $output : array($this, 'outputHandler');
25580 $process->run($callback);
25582 if ($this->captureOutput && !is_callable($output)) {
25583 $output = $process->getOutput();
25586 $this->errorOutput = $process->getErrorOutput();
25588 return $process->getExitCode();
25591 public function splitLines($output)
25593 $output = trim($output);
25595 return ((string) $output === '') ? array() : preg_split('{\r?\n}', $output);
25603 public function getErrorOutput()
25605 return $this->errorOutput;
25608 public function outputHandler($type, $buffer)
25610 if ($this->captureOutput) {
25617 public static function getTimeout()
25619 return static::$timeout;
25622 public static function setTimeout($timeout)
25624 static::$timeout = $timeout;
25635 public static function escape($argument)
25637 return ProcessUtils::escapeArgument($argument);
25652 namespace Composer\Util;
25654 use Composer\Config;
25655 use Composer\IO\IOInterface;
25664 protected $process;
25665 protected $filesystem;
25667 public function __construct(IOInterface $io, Config $config, ProcessExecutor $process, Filesystem $fs)
25670 $this->config = $config;
25671 $this->process = $process;
25672 $this->filesystem = $fs;
25675 public function runCommand($commandCallable, $url, $cwd, $initialClone = false)
25677 if ($initialClone) {
25682 if (preg_match('{^ssh://[^@]+@[^:]+:[^0-9]+}', $url)) {
25683 throw new \InvalidArgumentException('The source URL '.$url.' is invalid, ssh URLs should have a port number after ":".'."\n".'Use ssh://git@example.com:22/path or just git@example.com:path if you do not want to provide a password or custom port.');
25686 if (!$initialClone) {
25688 $this->process->execute('git remote -v', $output, $cwd);
25689 if (preg_match('{^(?:composer|origin)\s+https?://(.+):(.+)@([^/]+)}im', $output, $match)) {
25690 $this->io->setAuthentication($match[3], urldecode($match[1]), urldecode($match[2]));
25695 if (preg_match('{^(?:https?|git)://'.self::getGitHubDomainsRegex($this->config).'/(.*)}', $url, $match)) {
25696 $protocols = $this->config->get('github-protocols');
25697 if (!is_array($protocols)) {
25698 throw new \RuntimeException('Config value "github-protocols" must be an array, got '.gettype($protocols));
25700 $messages = array();
25701 foreach ($protocols as $protocol) {
25702 if ('ssh' === $protocol) {
25703 $url = "git@" . $match[1] . ":" . $match[2];
25705 $url = $protocol ."://" . $match[1] . "/" . $match[2];
25708 if (0 === $this->process->execute(call_user_func($commandCallable, $url), $ignoredOutput, $cwd)) {
25711 $messages[] = '- ' . $url . "\n" . preg_replace('#^#m', ' ', $this->process->getErrorOutput());
25712 if ($initialClone) {
25713 $this->filesystem->removeDirectory($origCwd);
25718 $this->throwException('Failed to clone ' . self::sanitizeUrl($url) .' via '.implode(', ', $protocols).' protocols, aborting.' . "\n\n" . implode("\n", $messages), $url);
25721 $command = call_user_func($commandCallable, $url);
25722 if (0 !== $this->process->execute($command, $ignoredOutput, $cwd)) {
25724 if (preg_match('{^git@'.self::getGitHubDomainsRegex($this->config).':(.+?)\.git$}i', $url, $match)) {
25725 if (!$this->io->hasAuthentication($match[1])) {
25726 $gitHubUtil = new GitHub($this->io, $this->config, $this->process);
25727 $message = 'Cloning failed using an ssh key for authentication, enter your GitHub credentials to access private repos';
25729 if (!$gitHubUtil->authorizeOAuth($match[1]) && $this->io->isInteractive()) {
25730 $gitHubUtil->authorizeOAuthInteractively($match[1], $message);
25734 if ($this->io->hasAuthentication($match[1])) {
25735 $auth = $this->io->getAuthentication($match[1]);
25736 $url = 'https://'.rawurlencode($auth['username']) . ':' . rawurlencode($auth['password']) . '@'.$match[1].'/'.$match[2].'.git';
25738 $command = call_user_func($commandCallable, $url);
25739 if (0 === $this->process->execute($command, $ignoredOutput, $cwd)) {
25744 preg_match('{(https?://)([^/]+)(.*)$}i', $url, $match) &&
25745 strpos($this->process->getErrorOutput(), 'fatal: Authentication failed') !== false
25747 if (strpos($match[2], '@')) {
25748 list($authParts, $match[2]) = explode('@', $match[2], 2);
25751 $storeAuth = false;
25752 if ($this->io->hasAuthentication($match[2])) {
25753 $auth = $this->io->getAuthentication($match[2]);
25754 } elseif ($this->io->isInteractive()) {
25755 $defaultUsername = null;
25756 if (isset($authParts) && $authParts) {
25757 if (false !== strpos($authParts, ':')) {
25758 list($defaultUsername,) = explode(':', $authParts, 2);
25760 $defaultUsername = $authParts;
25764 $this->io->write(' Authentication required (<info>'.parse_url($url, PHP_URL_HOST).'</info>):');
25766 'username' => $this->io->ask(' Username: ', $defaultUsername),
25767 'password' => $this->io->askAndHideAnswer(' Password: '),
25769 $storeAuth = $this->config->get('store-auths');
25773 $url = $match[1].rawurlencode($auth['username']).':'.rawurlencode($auth['password']).'@'.$match[2].$match[3];
25775 $command = call_user_func($commandCallable, $url);
25776 if (0 === $this->process->execute($command, $ignoredOutput, $cwd)) {
25777 $this->io->setAuthentication($match[2], $auth['username'], $auth['password']);
25778 $authHelper = new AuthHelper($this->io, $this->config);
25779 $authHelper->storeAuth($match[2], $storeAuth);
25786 if ($initialClone) {
25787 $this->filesystem->removeDirectory($origCwd);
25789 $this->throwException('Failed to execute ' . self::sanitizeUrl($command) . "\n\n" . $this->process->getErrorOutput(), $url);
25793 public static function cleanEnv()
25795 if (ini_get('safe_mode') && false === strpos(ini_get('safe_mode_allowed_env_vars'), 'GIT_ASKPASS')) {
25796 throw new \RuntimeException('safe_mode is enabled and safe_mode_allowed_env_vars does not contain GIT_ASKPASS, can not set env var. You can disable safe_mode with "-dsafe_mode=0" when running composer');
25800 if (getenv('GIT_ASKPASS') !== 'echo') {
25801 putenv('GIT_ASKPASS=echo');
25805 if (getenv('GIT_DIR')) {
25808 if (getenv('GIT_WORK_TREE')) {
25809 putenv('GIT_WORK_TREE');
25813 public static function getGitHubDomainsRegex(Config $config)
25815 return '('.implode('|', array_map('preg_quote', $config->get('github-domains'))).')';
25818 public static function sanitizeUrl($message)
25820 return preg_replace('{://([^@]+?):.+?@}', '://$1:***@', $message);
25823 private function throwException($message, $url)
25825 if (0 !== $this->process->execute('git --version', $ignoredOutput)) {
25826 throw new \RuntimeException('Failed to clone '.self::sanitizeUrl($url).', git was not found, check that it is installed and in your PATH env.' . "\n\n" . $this->process->getErrorOutput());
25829 throw new \RuntimeException($message);
25844 namespace Composer\Util;
25846 use Composer\Composer;
25847 use Composer\Config;
25848 use Composer\IO\IOInterface;
25849 use Composer\Downloader\TransportException;
25856 class RemoteFilesystem
25861 private $originUrl;
25866 private $lastProgress;
25868 private $retryAuthFailure;
25869 private $lastHeaders;
25870 private $storeAuth;
25879 public function __construct(IOInterface $io, Config $config = null, array $options = array())
25882 $this->config = $config;
25883 $this->options = $options;
25897 public function copy($originUrl, $fileUrl, $fileName, $progress = true, $options = array())
25899 return $this->get($originUrl, $fileUrl, $options, $fileName, $progress);
25912 public function getContents($originUrl, $fileUrl, $progress = true, $options = array())
25914 return $this->get($originUrl, $fileUrl, $options, null, $progress);
25922 public function getOptions()
25924 return $this->options;
25932 public function getLastHeaders()
25934 return $this->lastHeaders;
25951 protected function get($originUrl, $fileUrl, $additionalOptions = array(), $fileName = null, $progress = true)
25953 if (strpos($originUrl, '.github.com') === (strlen($originUrl) - 11)) {
25954 $originUrl = 'github.com';
25957 $this->bytesMax = 0;
25958 $this->originUrl = $originUrl;
25959 $this->fileUrl = $fileUrl;
25960 $this->fileName = $fileName;
25961 $this->progress = $progress;
25962 $this->lastProgress = null;
25963 $this->retryAuthFailure = true;
25964 $this->lastHeaders = array();
25967 if (preg_match('{^https?://(.+):(.+)@([^/]+)}i', $fileUrl, $match)) {
25968 $this->io->setAuthentication($originUrl, urldecode($match[1]), urldecode($match[2]));
25971 if (isset($additionalOptions['retry-auth-failure'])) {
25972 $this->retryAuthFailure = (bool) $additionalOptions['retry-auth-failure'];
25974 unset($additionalOptions['retry-auth-failure']);
25977 $options = $this->getOptionsForUrl($originUrl, $additionalOptions);
25979 if ($this->io->isDebug()) {
25980 $this->io->write((substr($fileUrl, 0, 4) === 'http' ? 'Downloading ' : 'Reading ') . $fileUrl);
25982 if (isset($options['github-token'])) {
25983 $fileUrl .= (false === strpos($fileUrl, '?') ? '?' : '&') . 'access_token='.$options['github-token'];
25984 unset($options['github-token']);
25986 if (isset($options['http'])) {
25987 $options['http']['ignore_errors'] = true;
25989 $ctx = StreamContextFactory::getContext($fileUrl, $options, array('notification' => array($this, 'callbackGet')));
25991 if ($this->progress) {
25992 $this->io->write(" Downloading: <comment>connection...</comment>", false);
25995 $errorMessage = '';
25998 set_error_handler(function ($code, $msg) use (&$errorMessage) {
25999 if ($errorMessage) {
26000 $errorMessage .= "\n";
26002 $errorMessage .= preg_replace('{^file_get_contents\(.*?\): }', '', $msg);
26005 $result = file_get_contents($fileUrl, false, $ctx);
26006 } catch (\Exception $e) {
26007 if ($e instanceof TransportException && !empty($http_response_header[0])) {
26008 $e->setHeaders($http_response_header);
26010 if ($e instanceof TransportException && $result !== false) {
26011 $e->setResponse($result);
26015 if ($errorMessage && !ini_get('allow_url_fopen')) {
26016 $errorMessage = 'allow_url_fopen must be enabled in php.ini ('.$errorMessage.')';
26018 restore_error_handler();
26019 if (isset($e) && !$this->retry) {
26024 if (!empty($http_response_header[0]) && preg_match('{^HTTP/\S+ ([45]\d\d)}i', $http_response_header[0], $match)) {
26025 $errorCode = $match[1];
26026 if (!$this->retry) {
26027 $e = new TransportException('The "'.$this->fileUrl.'" file could not be downloaded ('.$http_response_header[0].')', $errorCode);
26028 $e->setHeaders($http_response_header);
26029 $e->setResponse($result);
26036 if ($result && extension_loaded('zlib') && substr($fileUrl, 0, 4) === 'http') {
26038 foreach ($http_response_header as $header) {
26039 if (preg_match('{^content-encoding: *gzip *$}i', $header)) {
26042 } elseif (preg_match('{^HTTP/}i', $header)) {
26048 if (version_compare(PHP_VERSION, '5.4.0', '>=')) {
26049 $result = zlib_decode($result);
26052 $result = file_get_contents('compress.zlib://data:application/octet-stream;base64,'.base64_encode($result));
26056 throw new TransportException('Failed to decode zlib stream');
26061 if ($this->progress && !$this->retry) {
26062 $this->io->overwrite(" Downloading: <comment>100%</comment>");
26066 if (false !== $result && null !== $fileName) {
26067 if ('' === $result) {
26068 throw new TransportException('"'.$this->fileUrl.'" appears broken, and returned an empty 200 response');
26071 $errorMessage = '';
26072 set_error_handler(function ($code, $msg) use (&$errorMessage) {
26073 if ($errorMessage) {
26074 $errorMessage .= "\n";
26076 $errorMessage .= preg_replace('{^file_put_contents\(.*?\): }', '', $msg);
26078 $result = (bool) file_put_contents($fileName, $result);
26079 restore_error_handler();
26080 if (false === $result) {
26081 throw new TransportException('The "'.$this->fileUrl.'" file could not be written to '.$fileName.': '.$errorMessage);
26085 if ($this->retry) {
26086 $this->retry = false;
26088 $result = $this->get($this->originUrl, $this->fileUrl, $additionalOptions, $this->fileName, $this->progress);
26090 $authHelper = new AuthHelper($this->io, $this->config);
26091 $authHelper->storeAuth($this->originUrl, $this->storeAuth);
26092 $this->storeAuth = false;
26097 if (false === $result) {
26098 $e = new TransportException('The "'.$this->fileUrl.'" file could not be downloaded: '.$errorMessage, $errorCode);
26099 if (!empty($http_response_header[0])) {
26100 $e->setHeaders($http_response_header);
26106 if (!empty($http_response_header[0])) {
26107 $this->lastHeaders = $http_response_header;
26124 protected function callbackGet($notificationCode, $severity, $message, $messageCode, $bytesTransferred, $bytesMax)
26126 switch ($notificationCode) {
26127 case STREAM_NOTIFY_FAILURE:
26128 case STREAM_NOTIFY_AUTH_REQUIRED:
26129 if (401 === $messageCode) {
26131 if (!$this->retryAuthFailure) {
26135 $this->promptAuthAndRetry($messageCode);
26140 case STREAM_NOTIFY_AUTH_RESULT:
26141 if (403 === $messageCode) {
26142 $this->promptAuthAndRetry($messageCode, $message);
26147 case STREAM_NOTIFY_FILE_SIZE_IS:
26148 if ($this->bytesMax < $bytesMax) {
26149 $this->bytesMax = $bytesMax;
26153 case STREAM_NOTIFY_PROGRESS:
26154 if ($this->bytesMax > 0 && $this->progress) {
26157 if ($this->bytesMax > 0) {
26158 $progression = round($bytesTransferred / $this->bytesMax * 100);
26161 if ((0 === $progression % 5) && $progression !== $this->lastProgress) {
26162 $this->lastProgress = $progression;
26163 $this->io->overwrite(" Downloading: <comment>$progression%</comment>", false);
26173 protected function promptAuthAndRetry($httpStatus, $reason = null)
26175 if ($this->config && in_array($this->originUrl, $this->config->get('github-domains'), true)) {
26176 $message = "\n".'Could not fetch '.$this->fileUrl.', enter your GitHub credentials '.($httpStatus === 404 ? 'to access private repos' : 'to go over the API rate limit');
26177 $gitHubUtil = new GitHub($this->io, $this->config, null, $this);
26178 if (!$gitHubUtil->authorizeOAuth($this->originUrl)
26179 && (!$this->io->isInteractive() || !$gitHubUtil->authorizeOAuthInteractively($this->originUrl, $message))
26181 throw new TransportException('Could not authenticate against '.$this->originUrl, 401);
26185 if ($httpStatus === 404) {
26190 if (!$this->io->isInteractive()) {
26191 if ($httpStatus === 401) {
26192 $message = "The '" . $this->fileUrl . "' URL required authentication.\nYou must be using the interactive console to authenticate";
26194 if ($httpStatus === 403) {
26195 $message = "The '" . $this->fileUrl . "' URL could not be accessed: " . $reason;
26198 throw new TransportException($message, $httpStatus);
26201 if ($this->io->hasAuthentication($this->originUrl)) {
26202 throw new TransportException("Invalid credentials for '" . $this->fileUrl . "', aborting.", $httpStatus);
26205 $this->io->overwrite(' Authentication required (<info>'.parse_url($this->fileUrl, PHP_URL_HOST).'</info>):');
26206 $username = $this->io->ask(' Username: ');
26207 $password = $this->io->askAndHideAnswer(' Password: ');
26208 $this->io->setAuthentication($this->originUrl, $username, $password);
26209 $this->storeAuth = $this->config->get('store-auths');
26212 $this->retry = true;
26213 throw new TransportException('RETRY');
26216 protected function getOptionsForUrl($originUrl, $additionalOptions)
26218 if (defined('HHVM_VERSION')) {
26219 $phpVersion = 'HHVM ' . HHVM_VERSION;
26221 $phpVersion = 'PHP ' . PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION . '.' . PHP_RELEASE_VERSION;
26226 'User-Agent: Composer/%s (%s; %s; %s)',
26227 Composer::VERSION === '@package_version@' ? 'source' : Composer::VERSION,
26234 if (extension_loaded('zlib')) {
26235 $headers[] = 'Accept-Encoding: gzip';
26238 $options = array_replace_recursive($this->options, $additionalOptions);
26240 if ($this->io->hasAuthentication($originUrl)) {
26241 $auth = $this->io->getAuthentication($originUrl);
26242 if ('github.com' === $originUrl && 'x-oauth-basic' === $auth['password']) {
26243 $options['github-token'] = $auth['username'];
26245 $authStr = base64_encode($auth['username'] . ':' . $auth['password']);
26246 $headers[] = 'Authorization: Basic '.$authStr;
26250 if (isset($options['http']['header']) && !is_array($options['http']['header'])) {
26251 $options['http']['header'] = explode("\r\n", trim($options['http']['header'], "\r\n"));
26253 foreach ($headers as $header) {
26254 $options['http']['header'][] = $header;
26272 namespace Composer\Util;
26280 final class StreamContextFactory
26291 public static function getContext($url, array $defaultOptions = array(), array $defaultParams = array())
26293 $options = array('http' => array(
26295 'follow_location' => 1,
26296 'max_redirects' => 20,
26300 if (!empty($_SERVER['HTTP_PROXY']) || !empty($_SERVER['http_proxy'])) {
26302 $proxy = parse_url(!empty($_SERVER['http_proxy']) ? $_SERVER['http_proxy'] : $_SERVER['HTTP_PROXY']);
26306 if (preg_match('{^https://}i', $url) && (!empty($_SERVER['HTTPS_PROXY']) || !empty($_SERVER['https_proxy']))) {
26307 $proxy = parse_url(!empty($_SERVER['https_proxy']) ? $_SERVER['https_proxy'] : $_SERVER['HTTPS_PROXY']);
26311 if (!empty($_SERVER['no_proxy']) && parse_url($url, PHP_URL_HOST)) {
26312 $pattern = new NoProxyPattern($_SERVER['no_proxy']);
26313 if ($pattern->test($url)) {
26318 if (!empty($proxy)) {
26319 $proxyURL = isset($proxy['scheme']) ? $proxy['scheme'] . '://' : '';
26320 $proxyURL .= isset($proxy['host']) ? $proxy['host'] : '';
26322 if (isset($proxy['port'])) {
26323 $proxyURL .= ":" . $proxy['port'];
26324 } elseif ('http://' == substr($proxyURL, 0, 7)) {
26325 $proxyURL .= ":80";
26326 } elseif ('https://' == substr($proxyURL, 0, 8)) {
26327 $proxyURL .= ":443";
26331 $proxyURL = str_replace(array('http://', 'https://'), array('tcp://', 'ssl://'), $proxyURL);
26333 if (0 === strpos($proxyURL, 'ssl:') && !extension_loaded('openssl')) {
26334 throw new \RuntimeException('You must enable the openssl extension to use a proxy over https');
26337 $options['http']['proxy'] = $proxyURL;
26340 switch (parse_url($url, PHP_URL_SCHEME)) {
26342 $reqFullUriEnv = getenv('HTTP_PROXY_REQUEST_FULLURI');
26343 if ($reqFullUriEnv === false || $reqFullUriEnv === '' || (strtolower($reqFullUriEnv) !== 'false' && (bool) $reqFullUriEnv)) {
26344 $options['http']['request_fulluri'] = true;
26348 $reqFullUriEnv = getenv('HTTPS_PROXY_REQUEST_FULLURI');
26349 if ($reqFullUriEnv === false || $reqFullUriEnv === '' || (strtolower($reqFullUriEnv) !== 'false' && (bool) $reqFullUriEnv)) {
26350 $options['http']['request_fulluri'] = true;
26356 if ('https' === parse_url($url, PHP_URL_SCHEME)) {
26357 $options['ssl']['SNI_enabled'] = true;
26358 if (version_compare(PHP_VERSION, '5.6.0', '<')) {
26359 $options['ssl']['SNI_server_name'] = parse_url($url, PHP_URL_HOST);
26364 if (isset($proxy['user'])) {
26365 $auth = urldecode($proxy['user']);
26366 if (isset($proxy['pass'])) {
26367 $auth .= ':' . urldecode($proxy['pass']);
26369 $auth = base64_encode($auth);
26372 if (isset($defaultOptions['http']['header'])) {
26373 if (is_string($defaultOptions['http']['header'])) {
26374 $defaultOptions['http']['header'] = array($defaultOptions['http']['header']);
26376 $defaultOptions['http']['header'][] = "Proxy-Authorization: Basic {$auth}";
26378 $options['http']['header'] = array("Proxy-Authorization: Basic {$auth}");
26383 $options = array_replace_recursive($options, $defaultOptions);
26385 if (isset($options['http']['header'])) {
26386 $options['http']['header'] = self::fixHttpHeaderField($options['http']['header']);
26389 return stream_context_create($options, $defaultParams);
26402 private static function fixHttpHeaderField($header)
26404 if (!is_array($header)) {
26405 $header = explode("\r\n", $header);
26407 uasort($header, function ($el) {
26408 return preg_match('{^content-type}i', $el) ? 1 : -1;
26426 namespace Composer\Util;
26428 use Composer\Package\Loader\ArrayLoader;
26429 use Composer\Package\Loader\ValidatingArrayLoader;
26430 use Composer\Package\Loader\InvalidPackageException;
26431 use Composer\Json\JsonValidationException;
26432 use Composer\IO\IOInterface;
26433 use Composer\Json\JsonFile;
26441 class ConfigValidator
26445 public function __construct(IOInterface $io)
26458 public function validate($file, $arrayLoaderValidationFlags = ValidatingArrayLoader::CHECK_ALL)
26461 $publishErrors = array();
26462 $warnings = array();
26467 $json = new JsonFile($file, new RemoteFilesystem($this->io));
26468 $manifest = $json->read();
26470 $json->validateSchema(JsonFile::LAX_SCHEMA);
26472 $json->validateSchema();
26473 } catch (JsonValidationException $e) {
26474 foreach ($e->getErrors() as $message) {
26476 $publishErrors[] = $message;
26478 $errors[] = $message;
26481 } catch (\Exception $e) {
26482 $errors[] = $e->getMessage();
26484 return array($errors, $publishErrors, $warnings);
26488 if (!empty($manifest['license'])) {
26490 if (is_array($manifest['license'])) {
26491 foreach ($manifest['license'] as $key => $license) {
26492 if ('proprietary' === $license) {
26493 unset($manifest['license'][$key]);
26498 $licenseValidator = new SpdxLicenseIdentifier();
26499 if ('proprietary' !== $manifest['license'] && array() !== $manifest['license'] && !$licenseValidator->validate($manifest['license'])) {
26500 $warnings[] = sprintf(
26501 'License %s is not a valid SPDX license identifier, see http://www.spdx.org/licenses/ if you use an open license.'
26502 ."\nIf the software is closed-source, you may use \"proprietary\" as license.",
26503 json_encode($manifest['license'])
26507 $warnings[] = 'No license specified, it is recommended to do so. For closed-source software you may use "proprietary" as license.';
26510 if (isset($manifest['version'])) {
26511 $warnings[] = 'The version field is present, it is recommended to leave it out if the package is published on Packagist.';
26514 if (!empty($manifest['name']) && preg_match('{[A-Z]}', $manifest['name'])) {
26515 $suggestName = preg_replace('{(?:([a-z])([A-Z])|([A-Z])([A-Z][a-z]))}', '\\1\\3-\\2\\4', $manifest['name']);
26516 $suggestName = strtolower($suggestName);
26518 $warnings[] = sprintf(
26519 'Name "%s" does not match the best practice (e.g. lower-cased/with-dashes). We suggest using "%s" instead. As such you will not be able to submit it to Packagist.',
26525 if (!empty($manifest['type']) && $manifest['type'] == 'composer-installer') {
26526 $warnings[] = "The package type 'composer-installer' is deprecated. Please distribute your custom installers as plugins from now on. See http://getcomposer.org/doc/articles/plugins.md for plugin documentation.";
26530 if (isset($manifest['require']) && isset($manifest['require-dev'])) {
26531 $requireOverrides = array_intersect_key($manifest['require'], $manifest['require-dev']);
26533 if (!empty($requireOverrides)) {
26534 $plural = (count($requireOverrides) > 1) ? 'are' : 'is';
26535 $warnings[] = implode(', ', array_keys($requireOverrides)). " {$plural} required both in require and require-dev, this can lead to unexpected behavior";
26540 $loader = new ValidatingArrayLoader(new ArrayLoader(), true, null, $arrayLoaderValidationFlags);
26541 if (!isset($manifest['version'])) {
26542 $manifest['version'] = '1.0.0';
26544 if (!isset($manifest['name'])) {
26545 $manifest['name'] = 'dummy/dummy';
26547 $loader->load($manifest);
26548 } catch (InvalidPackageException $e) {
26549 $errors = array_merge($errors, $e->getErrors());
26552 $warnings = array_merge($warnings, $loader->getWarnings());
26554 return array($errors, $publishErrors, $warnings);
26569 namespace Composer\Util;
26589 public static function handle($level, $message, $file, $line)
26592 if (!error_reporting()) {
26596 if (ini_get('xdebug.scream')) {
26597 $message .= "\n\nWarning: You have xdebug.scream enabled, the warning above may be".
26598 "\na legitimately suppressed error that you were not supposed to see.";
26601 throw new \ErrorException($message, 0, $level, $file, $line);
26609 public static function register()
26611 set_error_handler(array(__CLASS__, 'handle'));
26626 namespace Composer\Util;
26628 use Composer\Config;
26629 use Composer\IO\IOInterface;
26639 public function __construct(IOInterface $io, Config $config)
26642 $this->config = $config;
26645 public function storeAuth($originUrl, $storeAuth)
26648 $configSource = $this->config->getAuthConfigSource();
26649 if ($storeAuth === true) {
26650 $store = $configSource;
26651 } elseif ($storeAuth === 'prompt') {
26652 $answer = $this->io->askAndValidate(
26653 'Do you want to store credentials for '.$originUrl.' in '.$configSource->getName().' ? [Yn] ',
26654 function ($value) {
26655 $input = strtolower(substr(trim($value), 0, 1));
26656 if (in_array($input, array('y','n'))) {
26659 throw new \RuntimeException('Please answer (y)es or (n)o');
26665 if ($answer === 'y') {
26666 $store = $configSource;
26670 $store->addConfigSetting(
26671 'http-basic.'.$originUrl,
26672 $this->io->getAuthentication($originUrl)
26689 namespace Composer\Util;
26691 use Composer\Json\JsonFile;
26699 class SpdxLicenseIdentifier
26704 private $identifiers;
26706 public function __construct()
26708 $this->initIdentifiers();
26717 public function validate($license)
26719 if (is_array($license)) {
26720 $count = count($license);
26721 if ($count !== count(array_filter($license, 'is_string'))) {
26722 throw new \InvalidArgumentException('Array of strings expected.');
26724 $license = $count > 1 ? '('.implode(' or ', $license).')' : (string) reset($license);
26726 if (!is_string($license)) {
26727 throw new \InvalidArgumentException(sprintf(
26728 'Array or String expected, %s given.', gettype($license)
26732 return $this->isValidLicenseString($license);
26738 private function initIdentifiers()
26740 $jsonFile = new JsonFile(__DIR__ . '/../../../res/spdx-identifier.json');
26741 $this->identifiers = $jsonFile->read();
26749 private function isValidLicenseIdentifier($identifier)
26751 return in_array($identifier, $this->identifiers);
26760 private function isValidLicenseString($license)
26765 'op' => '(?:or|and)',
26766 'lix' => '(?:NONE|NOASSERTION)',
26767 'lir' => 'LicenseRef-\d+',
26768 'lic' => '[-+_.a-zA-Z0-9]{3,}',
26773 $next = function () use ($license, $tokens) {
26774 static $offset = 0;
26776 if ($offset >= strlen($license)) {
26780 foreach ($tokens as $name => $token) {
26781 if (false === $r = preg_match('{' . $token . '}', $license, $matches, PREG_OFFSET_CAPTURE, $offset)) {
26782 throw new \RuntimeException('Pattern for token %s failed (regex error).', $name);
26787 if ($matches[0][1] !== $offset) {
26790 $offset += strlen($matches[0][0]);
26792 return array($name, $matches[0][0]);
26795 throw new \RuntimeException('At least the last pattern needs to match, but it did not (dot-match-all is missing?).');
26802 while (list($token, $string) = $next()) {
26805 if ($open || !$require) {
26811 if ($open !== 1 || $require || !$lastop) {
26817 if ($require || !$open) {
26820 $lastop || $lastop = $string;
26821 if ($lastop !== $string) {
26832 if (!$this->isValidLicenseIdentifier($string)) {
26848 throw new \RuntimeException(sprintf('Unparsed token: %s.', print_r($token, true)));
26852 return !($open % 2 || $require);
26867 namespace Composer\Util;
26872 class NoProxyPattern
26877 protected $rules = array();
26882 public function __construct($pattern)
26884 $this->rules = preg_split("/[\s,]+/", $pattern);
26894 public function test($url)
26896 $host = parse_url($url, PHP_URL_HOST);
26897 $port = parse_url($url, PHP_URL_PORT);
26899 if (empty($port)) {
26900 switch (parse_url($url, PHP_URL_SCHEME)) {
26910 foreach ($this->rules as $rule) {
26911 if ($rule == '*') {
26917 list($ruleHost) = explode(':', $rule);
26918 list($base) = explode('/', $ruleHost);
26920 if (filter_var($base, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
26924 $ip = gethostbyname($host);
26927 if (strpos($ruleHost, '/') === false) {
26928 $match = $ip === $ruleHost;
26932 if ($ip === $host) {
26936 $match = self::inCIDRBlock($ruleHost, $ip);
26942 $haystack = '.' . trim($host, '.') . '.';
26943 $needle = '.'. trim($ruleHost, '.') .'.';
26944 $match = stripos(strrev($haystack), strrev($needle)) === 0;
26948 if ($match && strpos($rule, ':') !== false) {
26949 list(, $rulePort) = explode(':', $rule);
26950 if (!empty($rulePort) && $port != $rulePort) {
26973 private static function inCIDRBlock($cidr, $ip)
26976 list($base, $bits) = explode('/', $cidr);
26979 list($a, $b, $c, $d) = explode('.', $base);
26982 $i = ($a << 24) + ($b << 16) + ($c << 8) + $d;
26983 $mask = $bits == 0 ? 0 : (~0 << (32 - $bits));
26989 $high = $i | (~$mask & 0xFFFFFFFF);
26992 list($a, $b, $c, $d) = explode('.', $ip);
26995 $check = ($a << 24) + ($b << 16) + ($c << 8) + $d;
26999 return $check >= $low && $check <= $high;
27014 namespace Composer\Util;
27016 use Composer\Config;
27017 use Composer\IO\IOInterface;
27025 const MAX_QTY_AUTH_TRIES = 5;
27030 protected $credentials;
27035 protected $hasAuth;
27050 protected $cacheCredentials = true;
27055 protected $process;
27060 protected $qtyAuthTries = 0;
27073 public function __construct($url, IOInterface $io, Config $config, ProcessExecutor $process = null)
27077 $this->config = $config;
27078 $this->process = $process ?: new ProcessExecutor;
27081 public static function cleanEnv()
27084 putenv("DYLD_LIBRARY_PATH");
27101 public function execute($command, $url, $cwd = null, $path = null, $verbose = false)
27103 $svnCommand = $this->getCommand($command, $url, $path);
27106 $handler = function ($type, $buffer) use (&$output, $io, $verbose) {
27107 if ($type !== 'out') {
27110 if ('Redirecting to URL ' === substr($buffer, 0, 19)) {
27113 $output .= $buffer;
27115 $io->write($buffer, false);
27118 $status = $this->process->execute($svnCommand, $handler, $cwd);
27119 if (0 === $status) {
27123 if (empty($output)) {
27124 $output = $this->process->getErrorOutput();
27128 if (false === stripos($output, 'Could not authenticate to server:')
27129 && false === stripos($output, 'authorization failed')
27130 && false === stripos($output, 'svn: E170001:')
27131 && false === stripos($output, 'svn: E215004:')) {
27132 throw new \RuntimeException($output);
27135 if (!$this->hasAuth()) {
27136 $this->doAuthDance();
27140 if ($this->qtyAuthTries++ < self::MAX_QTY_AUTH_TRIES) {
27142 return $this->execute($command, $url, $cwd, $path, $verbose);
27145 throw new \RuntimeException(
27146 'wrong credentials provided ('.$output.')'
27153 public function setCacheCredentials($cacheCredentials)
27155 $this->cacheCredentials = $cacheCredentials;
27164 protected function doAuthDance()
27167 if (!$this->io->isInteractive()) {
27168 throw new \RuntimeException(
27169 'can not ask for authentication in non interactive mode'
27173 $this->io->write("The Subversion server ({$this->url}) requested credentials:");
27175 $this->hasAuth = true;
27176 $this->credentials['username'] = $this->io->ask("Username: ");
27177 $this->credentials['password'] = $this->io->askAndHideAnswer("Password: ");
27179 $this->cacheCredentials = $this->io->askConfirmation("Should Subversion cache these credentials? (yes/no) ", true);
27193 protected function getCommand($cmd, $url, $path = null)
27195 $cmd = sprintf('%s %s%s %s',
27197 '--non-interactive ',
27198 $this->getCredentialString(),
27199 ProcessExecutor::escape($url)
27203 $cmd .= ' ' . ProcessExecutor::escape($path);
27216 protected function getCredentialString()
27218 if (!$this->hasAuth()) {
27223 ' %s--username %s --password %s ',
27224 $this->getAuthCache(),
27225 ProcessExecutor::escape($this->getUsername()),
27226 ProcessExecutor::escape($this->getPassword())
27236 protected function getPassword()
27238 if ($this->credentials === null) {
27239 throw new \LogicException("No svn auth detected.");
27242 return isset($this->credentials['password']) ? $this->credentials['password'] : '';
27251 protected function getUsername()
27253 if ($this->credentials === null) {
27254 throw new \LogicException("No svn auth detected.");
27257 return $this->credentials['username'];
27265 protected function hasAuth()
27267 if (null !== $this->hasAuth) {
27268 return $this->hasAuth;
27271 if (false === $this->createAuthFromConfig()) {
27272 $this->createAuthFromUrl();
27275 return $this->hasAuth;
27283 protected function getAuthCache()
27285 return $this->cacheCredentials ? '' : '--no-auth-cache ';
27293 private function createAuthFromConfig()
27295 if (!$this->config->has('http-basic')) {
27296 return $this->hasAuth = false;
27299 $authConfig = $this->config->get('http-basic');
27301 $host = parse_url($this->url, PHP_URL_HOST);
27302 if (isset($authConfig[$host])) {
27303 $this->credentials['username'] = $authConfig[$host]['username'];
27304 $this->credentials['password'] = $authConfig[$host]['password'];
27306 return $this->hasAuth = true;
27309 return $this->hasAuth = false;
27317 private function createAuthFromUrl()
27319 $uri = parse_url($this->url);
27320 if (empty($uri['user'])) {
27321 return $this->hasAuth = false;
27324 $this->credentials['username'] = $uri['user'];
27325 if (!empty($uri['pass'])) {
27326 $this->credentials['password'] = $uri['pass'];
27329 return $this->hasAuth = true;
27344 namespace Composer;
27346 use Composer\Package\RootPackageInterface;
27347 use Composer\Package\Locker;
27348 use Composer\Repository\RepositoryManager;
27349 use Composer\Installer\InstallationManager;
27350 use Composer\Plugin\PluginManager;
27351 use Composer\Downloader\DownloadManager;
27352 use Composer\EventDispatcher\EventDispatcher;
27353 use Composer\Autoload\AutoloadGenerator;
27362 const VERSION = 'c58b7d917c65692eeb00c22b2fbaaa251cb390dc';
27363 const BRANCH_ALIAS_VERSION = '1.0-dev';
27364 const RELEASE_DATE = '2015-01-05 16:31:16';
27379 private $repositoryManager;
27384 private $downloadManager;
27389 private $installationManager;
27394 private $pluginManager;
27404 private $eventDispatcher;
27409 private $autoloadGenerator;
27415 public function setPackage(RootPackageInterface $package)
27417 $this->package = $package;
27423 public function getPackage()
27425 return $this->package;
27431 public function setConfig(Config $config)
27433 $this->config = $config;
27439 public function getConfig()
27441 return $this->config;
27447 public function setLocker(Locker $locker)
27449 $this->locker = $locker;
27455 public function getLocker()
27457 return $this->locker;
27463 public function setRepositoryManager(RepositoryManager $manager)
27465 $this->repositoryManager = $manager;
27471 public function getRepositoryManager()
27473 return $this->repositoryManager;
27479 public function setDownloadManager(DownloadManager $manager)
27481 $this->downloadManager = $manager;
27487 public function getDownloadManager()
27489 return $this->downloadManager;
27495 public function setInstallationManager(InstallationManager $manager)
27497 $this->installationManager = $manager;
27503 public function getInstallationManager()
27505 return $this->installationManager;
27511 public function setPluginManager(PluginManager $manager)
27513 $this->pluginManager = $manager;
27519 public function getPluginManager()
27521 return $this->pluginManager;
27527 public function setEventDispatcher(EventDispatcher $eventDispatcher)
27529 $this->eventDispatcher = $eventDispatcher;
27535 public function getEventDispatcher()
27537 return $this->eventDispatcher;
27543 public function setAutoloadGenerator(AutoloadGenerator $autoloadGenerator)
27545 $this->autoloadGenerator = $autoloadGenerator;
27551 public function getAutoloadGenerator()
27553 return $this->autoloadGenerator;
27568 namespace Composer\Json;
27573 class JsonManipulator
27575 private static $RECURSE_BLOCKS;
27576 private static $RECURSE_ARRAYS;
27577 private static $JSON_VALUE;
27578 private static $JSON_STRING;
27584 public function __construct($contents)
27586 if (!self::$RECURSE_BLOCKS) {
27587 self::$RECURSE_BLOCKS = '(?:[^{}]*|\{(?:[^{}]*|\{(?:[^{}]*|\{(?:[^{}]*|\{[^{}]*\})*\})*\})*\})*';
27588 self::$RECURSE_ARRAYS = '(?:[^\]]*|\[(?:[^\]]*|\[(?:[^\]]*|\[(?:[^\]]*|\[[^\]]*\])*\])*\])*\]|'.self::$RECURSE_BLOCKS.')*';
27589 self::$JSON_STRING = '"(?:\\\\["bfnrt/\\\\]|\\\\u[a-fA-F0-9]{4}|[^\0-\x09\x0a-\x1f\\\\"])*"';
27590 self::$JSON_VALUE = '(?:[0-9.]+|null|true|false|'.self::$JSON_STRING.'|\['.self::$RECURSE_ARRAYS.'\]|\{'.self::$RECURSE_BLOCKS.'\})';
27593 $contents = trim($contents);
27594 if ($contents === '') {
27597 if (!$this->pregMatch('#^\{(.*)\}$#s', $contents)) {
27598 throw new \InvalidArgumentException('The json file must be an object ({})');
27600 $this->newline = false !== strpos($contents, "\r\n") ? "\r\n" : "\n";
27601 $this->contents = $contents === '{}' ? '{' . $this->newline . '}' : $contents;
27602 $this->detectIndenting();
27605 public function getContents()
27607 return $this->contents . $this->newline;
27610 public function addLink($type, $package, $constraint, $sortPackages = false)
27612 $decoded = JsonFile::parseJson($this->contents);
27615 if (!isset($decoded[$type])) {
27616 return $this->addMainKey($type, array($package => $constraint));
27619 $regex = '{^(\s*\{\s*(?:'.self::$JSON_STRING.'\s*:\s*'.self::$JSON_VALUE.'\s*,\s*)*?)'.
27620 '('.preg_quote(JsonFile::encode($type)).'\s*:\s*)('.self::$JSON_VALUE.')(.*)}s';
27621 if (!$this->pregMatch($regex, $this->contents, $matches)) {
27625 $links = $matches[3];
27627 if (isset($decoded[$type][$package])) {
27629 $packageRegex = str_replace('/', '\\\\?/', preg_quote($package));
27631 $links = preg_replace('{"'.$packageRegex.'"(\s*:\s*)'.self::$JSON_STRING.'}i', addcslashes(JsonFile::encode($package).'${1}"'.$constraint.'"', '\\'), $links);
27633 if ($this->pregMatch('#^\s*\{\s*\S+.*?(\s*\}\s*)$#s', $links, $match)) {
27635 $links = preg_replace(
27636 '{'.preg_quote($match[1]).'$}',
27637 addcslashes(',' . $this->newline . $this->indent . $this->indent . JsonFile::encode($package).': '.JsonFile::encode($constraint) . $match[1], '\\'),
27642 $links = '{' . $this->newline .
27643 $this->indent . $this->indent . JsonFile::encode($package).': '.JsonFile::encode($constraint) . $this->newline .
27644 $this->indent . '}';
27648 if (true === $sortPackages) {
27649 $requirements = json_decode($links, true);
27651 ksort($requirements);
27652 $links = $this->format($requirements);
27655 $this->contents = $matches[1] . $matches[2] . $links . $matches[4];
27660 public function addRepository($name, $config)
27662 return $this->addSubNode('repositories', $name, $config);
27665 public function removeRepository($name)
27667 return $this->removeSubNode('repositories', $name);
27670 public function addConfigSetting($name, $value)
27672 return $this->addSubNode('config', $name, $value);
27675 public function removeConfigSetting($name)
27677 return $this->removeSubNode('config', $name);
27680 public function addSubNode($mainNode, $name, $value)
27682 $decoded = JsonFile::parseJson($this->contents);
27685 if (!isset($decoded[$mainNode])) {
27686 $this->addMainKey($mainNode, array($name => $value));
27692 if (in_array($mainNode, array('config', 'repositories')) && false !== strpos($name, '.')) {
27693 list($name, $subName) = explode('.', $name, 2);
27697 $nodeRegex = '#("'.$mainNode.'":\s*\{)('.self::$RECURSE_BLOCKS.')(\})#s';
27698 if (!$this->pregMatch($nodeRegex, $this->contents, $match)) {
27702 $children = $match[2];
27705 if (!@json_decode('{'.$children.'}')) {
27712 if ($this->pregMatch('{("'.preg_quote($name).'"\s*:\s*)('.self::$JSON_VALUE.')(,?)}', $children, $matches)) {
27713 $children = preg_replace_callback('{("'.preg_quote($name).'"\s*:\s*)('.self::$JSON_VALUE.')(,?)}', function ($matches) use ($name, $subName, $value, $that) {
27714 if ($subName !== null) {
27715 $curVal = json_decode($matches[2], true);
27716 $curVal[$subName] = $value;
27720 return $matches[1] . $that->format($value, 1) . $matches[3];
27722 } elseif ($this->pregMatch('#[^\s](\s*)$#', $children, $match)) {
27723 if ($subName !== null) {
27724 $value = array($subName => $value);
27728 $children = preg_replace(
27729 '#'.$match[1].'$#',
27730 addcslashes(',' . $this->newline . $this->indent . $this->indent . JsonFile::encode($name).': '.$this->format($value, 1) . $match[1], '\\'),
27734 if ($subName !== null) {
27735 $value = array($subName => $value);
27739 $children = $this->newline . $this->indent . $this->indent . JsonFile::encode($name).': '.$this->format($value, 1) . $children;
27742 $this->contents = preg_replace($nodeRegex, addcslashes('${1}'.$children.'$3', '\\'), $this->contents);
27747 public function removeSubNode($mainNode, $name)
27749 $decoded = JsonFile::parseJson($this->contents);
27752 if (empty($decoded[$mainNode])) {
27757 $nodeRegex = '#("'.$mainNode.'":\s*\{)('.self::$RECURSE_BLOCKS.')(\})#s';
27758 if (!$this->pregMatch($nodeRegex, $this->contents, $match)) {
27762 $children = $match[2];
27765 if (!@json_decode('{'.$children.'}')) {
27770 if (in_array($mainNode, array('config', 'repositories')) && false !== strpos($name, '.')) {
27771 list($name, $subName) = explode('.', $name, 2);
27775 if (!isset($decoded[$mainNode][$name]) || ($subName && !isset($decoded[$mainNode][$name][$subName]))) {
27780 if ($this->pregMatch('{"'.preg_quote($name).'"\s*:}i', $children)) {
27782 if (preg_match_all('{"'.preg_quote($name).'"\s*:\s*(?:'.self::$JSON_VALUE.')}', $children, $matches)) {
27784 foreach ($matches[0] as $match) {
27785 if (strlen($bestMatch) < strlen($match)) {
27786 $bestMatch = $match;
27789 $childrenClean = preg_replace('{,\s*'.preg_quote($bestMatch).'}i', '', $children, -1, $count);
27790 if (1 !== $count) {
27791 $childrenClean = preg_replace('{'.preg_quote($bestMatch).'\s*,?\s*}i', '', $childrenClean, -1, $count);
27792 if (1 !== $count) {
27798 $childrenClean = $children;
27802 if (!trim($childrenClean)) {
27803 $this->contents = preg_replace($nodeRegex, '$1'.$this->newline.$this->indent.'}', $this->contents);
27806 if ($subName !== null) {
27807 $curVal = json_decode('{'.$children.'}', true);
27808 unset($curVal[$name][$subName]);
27809 $this->addSubNode($mainNode, $name, $curVal[$name]);
27816 $this->contents = preg_replace_callback($nodeRegex, function ($matches) use ($that, $name, $subName, $childrenClean) {
27817 if ($subName !== null) {
27818 $curVal = json_decode('{'.$matches[2].'}', true);
27819 unset($curVal[$name][$subName]);
27820 $childrenClean = substr($that->format($curVal, 0), 1, -1);
27823 return $matches[1] . $childrenClean . $matches[3];
27824 }, $this->contents);
27829 public function addMainKey($key, $content)
27831 $decoded = JsonFile::parseJson($this->contents);
27832 $content = $this->format($content);
27835 $regex = '{^(\s*\{\s*(?:'.self::$JSON_STRING.'\s*:\s*'.self::$JSON_VALUE.'\s*,\s*)*?)'.
27836 '('.preg_quote(JsonFile::encode($key)).'\s*:\s*'.self::$JSON_VALUE.')(.*)}s';
27837 if (isset($decoded[$key]) && $this->pregMatch($regex, $this->contents, $matches)) {
27839 if (!@json_decode('{'.$matches[2].'}')) {
27843 $this->contents = $matches[1] . JsonFile::encode($key).': '.$content . $matches[3];
27849 if ($this->pregMatch('#[^{\s](\s*)\}$#', $this->contents, $match)) {
27850 $this->contents = preg_replace(
27851 '#'.$match[1].'\}$#',
27852 addcslashes(',' . $this->newline . $this->indent . JsonFile::encode($key). ': '. $content . $this->newline . '}', '\\'),
27860 $this->contents = preg_replace(
27862 addcslashes($this->indent . JsonFile::encode($key). ': '.$content . $this->newline . '}', '\\'),
27869 public function format($data, $depth = 0)
27871 if (is_array($data)) {
27874 if (is_numeric(key($data))) {
27875 foreach ($data as $key => $val) {
27876 $data[$key] = $this->format($val, $depth + 1);
27879 return '['.implode(', ', $data).']';
27882 $out = '{' . $this->newline;
27884 foreach ($data as $key => $val) {
27885 $elems[] = str_repeat($this->indent, $depth + 2) . JsonFile::encode($key). ': '.$this->format($val, $depth + 1);
27888 return $out . implode(','.$this->newline, $elems) . $this->newline . str_repeat($this->indent, $depth + 1) . '}';
27891 return JsonFile::encode($data);
27894 protected function detectIndenting()
27896 if ($this->pregMatch('{^(\s+)"}m', $this->contents, $match)) {
27897 $this->indent = $match[1];
27899 $this->indent = ' ';
27903 protected function pregMatch($re, $str, &$matches = array())
27905 $count = preg_match($re, $str, $matches);
27907 if ($count === false) {
27908 switch (preg_last_error()) {
27909 case PREG_NO_ERROR:
27910 throw new \RuntimeException('Failed to execute regex: PREG_NO_ERROR');
27911 case PREG_INTERNAL_ERROR:
27912 throw new \RuntimeException('Failed to execute regex: PREG_INTERNAL_ERROR');
27913 case PREG_BACKTRACK_LIMIT_ERROR:
27914 throw new \RuntimeException('Failed to execute regex: PREG_BACKTRACK_LIMIT_ERROR');
27915 case PREG_RECURSION_LIMIT_ERROR:
27916 throw new \RuntimeException('Failed to execute regex: PREG_RECURSION_LIMIT_ERROR');
27917 case PREG_BAD_UTF8_ERROR:
27918 throw new \RuntimeException('Failed to execute regex: PREG_BAD_UTF8_ERROR');
27919 case PREG_BAD_UTF8_OFFSET_ERROR:
27920 throw new \RuntimeException('Failed to execute regex: PREG_BAD_UTF8_OFFSET_ERROR');
27922 throw new \RuntimeException('Failed to execute regex: Unknown error');
27941 namespace Composer\Json;
27943 use JsonSchema\Validator;
27944 use Seld\JsonLint\JsonParser;
27945 use Seld\JsonLint\ParsingException;
27946 use Composer\Util\RemoteFilesystem;
27947 use Composer\Downloader\TransportException;
27957 const LAX_SCHEMA = 1;
27958 const STRICT_SCHEMA = 2;
27960 const JSON_UNESCAPED_SLASHES = 64;
27961 const JSON_PRETTY_PRINT = 128;
27962 const JSON_UNESCAPED_UNICODE = 256;
27974 public function __construct($path, RemoteFilesystem $rfs = null)
27976 $this->path = $path;
27978 if (null === $rfs && preg_match('{^https?://}i', $path)) {
27979 throw new \InvalidArgumentException('http urls require a RemoteFilesystem instance to be passed');
27987 public function getPath()
27989 return $this->path;
27997 public function exists()
27999 return is_file($this->path);
28008 public function read()
28012 $json = $this->rfs->getContents($this->path, $this->path, false);
28014 $json = file_get_contents($this->path);
28016 } catch (TransportException $e) {
28017 throw new \RuntimeException($e->getMessage(), 0, $e);
28018 } catch (\Exception $e) {
28019 throw new \RuntimeException('Could not read '.$this->path."\n\n".$e->getMessage());
28022 return static::parseJson($json, $this->path);
28032 public function write(array $hash, $options = 448)
28034 $dir = dirname($this->path);
28035 if (!is_dir($dir)) {
28036 if (file_exists($dir)) {
28037 throw new \UnexpectedValueException(
28038 $dir.' exists and is not a directory.'
28041 if (!@mkdir($dir, 0777, true)) {
28042 throw new \UnexpectedValueException(
28043 $dir.' does not exist and could not be created.'
28049 while ($retries--) {
28051 file_put_contents($this->path, static::encode($hash, $options). ($options & self::JSON_PRETTY_PRINT ? "\n" : ''));
28053 } catch (\Exception $e) {
28071 public function validateSchema($schema = self::STRICT_SCHEMA)
28073 $content = file_get_contents($this->path);
28074 $data = json_decode($content);
28076 if (null === $data && 'null' !== $content) {
28077 self::validateSyntax($content, $this->path);
28080 $schemaFile = __DIR__ . '/../../../res/composer-schema.json';
28081 $schemaData = json_decode(file_get_contents($schemaFile));
28083 if ($schema === self::LAX_SCHEMA) {
28084 $schemaData->additionalProperties = true;
28085 $schemaData->required = array();
28088 $validator = new Validator();
28089 $validator->check($data, $schemaData);
28093 if (!$validator->isValid()) {
28095 foreach ((array) $validator->getErrors() as $error) {
28096 $errors[] = ($error['property'] ? $error['property'].' : ' : '').$error['message'];
28098 throw new JsonValidationException('"'.$this->path.'" does not match the expected JSON schema', $errors);
28111 public static function encode($data, $options = 448)
28113 if (version_compare(PHP_VERSION, '5.4', '>=')) {
28114 $json = json_encode($data, $options);
28117 if (PHP_VERSION_ID < 50428 || (PHP_VERSION_ID >= 50500 && PHP_VERSION_ID < 50512) || (defined('JSON_C_VERSION') && version_compare(phpversion('json'), '1.3.6', '<'))) {
28118 $json = preg_replace('/\[\s+\]/', '[]', $json);
28119 $json = preg_replace('/\{\s+\}/', '{}', $json);
28125 $json = json_encode($data);
28127 $prettyPrint = (bool) ($options & self::JSON_PRETTY_PRINT);
28128 $unescapeUnicode = (bool) ($options & self::JSON_UNESCAPED_UNICODE);
28129 $unescapeSlashes = (bool) ($options & self::JSON_UNESCAPED_SLASHES);
28131 if (!$prettyPrint && !$unescapeUnicode && !$unescapeSlashes) {
28135 $result = JsonFormatter::format($json, $unescapeUnicode, $unescapeSlashes);
28148 public static function parseJson($json, $file = null)
28150 $data = json_decode($json, true);
28151 if (null === $data && JSON_ERROR_NONE !== json_last_error()) {
28152 self::validateSyntax($json, $file);
28168 protected static function validateSyntax($json, $file = null)
28170 $parser = new JsonParser();
28171 $result = $parser->lint($json);
28172 if (null === $result) {
28173 if (defined('JSON_ERROR_UTF8') && JSON_ERROR_UTF8 === json_last_error()) {
28174 throw new \UnexpectedValueException('"'.$file.'" is not UTF-8, could not parse as JSON');
28180 throw new ParsingException('"'.$file.'" does not contain valid JSON'."\n".$result->getMessage(), $result->getDetails());
28195 namespace Composer\Json;
28205 class JsonFormatter
28220 public static function format($json, $unescapeUnicode, $unescapeSlashes)
28224 $strLen = strlen($json);
28227 $outOfQuotes = true;
28231 for ($i = 0; $i < $strLen; $i++) {
28233 $char = substr($json, $i, 1);
28236 if ('"' === $char && $noescape) {
28237 $outOfQuotes = !$outOfQuotes;
28240 if (!$outOfQuotes) {
28242 $noescape = '\\' === $char ? !$noescape : true;
28244 } elseif ('' !== $buffer) {
28245 if ($unescapeSlashes) {
28246 $buffer = str_replace('\\/', '/', $buffer);
28249 if ($unescapeUnicode && function_exists('mb_convert_encoding')) {
28251 $buffer = preg_replace_callback('/(\\\\+)u([0-9a-f]{4})/i', function ($match) {
28252 $l = strlen($match[1]);
28255 return str_repeat('\\', $l - 1) . mb_convert_encoding(
28256 pack('H*', $match[2]),
28266 $result .= $buffer.$char;
28271 if (':' === $char) {
28274 } elseif (('}' === $char || ']' === $char)) {
28276 $prevChar = substr($json, $i - 1, 1);
28278 if ('{' !== $prevChar && '[' !== $prevChar) {
28281 $result .= $newLine;
28282 for ($j = 0; $j < $pos; $j++) {
28283 $result .= $indentStr;
28287 $result = rtrim($result);
28295 if (',' === $char || '{' === $char || '[' === $char) {
28296 $result .= $newLine;
28298 if ('{' === $char || '[' === $char) {
28302 for ($j = 0; $j < $pos; $j++) {
28303 $result .= $indentStr;
28323 namespace Composer\Json;
28330 class JsonValidationException extends Exception
28334 public function __construct($message, $errors = array(), \Exception $previous = null)
28336 $this->errors = $errors;
28337 parent::__construct($message, 0, $previous);
28340 public function getErrors()
28342 return $this->errors;
28357 namespace Composer;
28359 use Composer\Config\ConfigSourceInterface;
28366 const RELATIVE_PATHS = 1;
28368 public static $defaultConfig = array(
28369 'process-timeout' => 300,
28370 'use-include-path' => false,
28371 'preferred-install' => 'auto',
28372 'notify-on-install' => true,
28373 'github-protocols' => array('git', 'https', 'ssh'),
28374 'vendor-dir' => 'vendor',
28375 'bin-dir' => '{$vendor-dir}/bin',
28376 'cache-dir' => '{$home}/cache',
28377 'cache-files-dir' => '{$cache-dir}/files',
28378 'cache-repo-dir' => '{$cache-dir}/repo',
28379 'cache-vcs-dir' => '{$cache-dir}/vcs',
28380 'cache-ttl' => 15552000,
28381 'cache-files-ttl' => null,
28382 'cache-files-maxsize' => '300MiB',
28383 'discard-changes' => false,
28384 'autoloader-suffix' => null,
28385 'optimize-autoloader' => false,
28386 'prepend-autoloader' => true,
28387 'github-domains' => array('github.com'),
28388 'github-expose-hostname' => true,
28389 'store-auths' => 'prompt',
28395 public static $defaultRepositories = array(
28396 'packagist' => array(
28397 'type' => 'composer',
28398 'url' => 'https?://packagist.org',
28399 'allow_ssl_downgrade' => true,
28405 private $repositories;
28406 private $configSource;
28407 private $authConfigSource;
28408 private $useEnvironment;
28413 public function __construct($useEnvironment = true, $baseDir = null)
28416 $this->config = static::$defaultConfig;
28417 $this->repositories = static::$defaultRepositories;
28418 $this->useEnvironment = (bool) $useEnvironment;
28419 $this->baseDir = $baseDir;
28422 public function setConfigSource(ConfigSourceInterface $source)
28424 $this->configSource = $source;
28427 public function getConfigSource()
28429 return $this->configSource;
28432 public function setAuthConfigSource(ConfigSourceInterface $source)
28434 $this->authConfigSource = $source;
28437 public function getAuthConfigSource()
28439 return $this->authConfigSource;
28447 public function merge($config)
28450 if (!empty($config['config']) && is_array($config['config'])) {
28451 foreach ($config['config'] as $key => $val) {
28452 if (in_array($key, array('github-oauth', 'http-basic')) && isset($this->config[$key])) {
28453 $this->config[$key] = array_merge($this->config[$key], $val);
28455 $this->config[$key] = $val;
28460 if (!empty($config['repositories']) && is_array($config['repositories'])) {
28461 $this->repositories = array_reverse($this->repositories, true);
28462 $newRepos = array_reverse($config['repositories'], true);
28463 foreach ($newRepos as $name => $repository) {
28465 if (false === $repository) {
28466 unset($this->repositories[$name]);
28471 if (1 === count($repository) && false === current($repository)) {
28472 unset($this->repositories[key($repository)]);
28477 if (is_int($name)) {
28478 $this->repositories[] = $repository;
28480 $this->repositories[$name] = $repository;
28483 $this->repositories = array_reverse($this->repositories, true);
28490 public function getRepositories()
28492 return $this->repositories;
28503 public function get($key, $flags = 0)
28508 case 'process-timeout':
28510 case 'cache-files-dir':
28511 case 'cache-repo-dir':
28512 case 'cache-vcs-dir':
28514 $env = 'COMPOSER_' . strtoupper(strtr($key, '-', '_'));
28516 $val = rtrim($this->process($this->getComposerEnv($env) ?: $this->config[$key], $flags), '/\\');
28517 $val = preg_replace('#^(\$HOME|~)(/|$)#', rtrim(getenv('HOME') ?: getenv('USERPROFILE'), '/\\') . '/', $val);
28519 if (substr($key, -4) !== '-dir') {
28523 return ($flags & self::RELATIVE_PATHS == 1) ? $val : $this->realpath($val);
28526 return (int) $this->config[$key];
28528 case 'cache-files-maxsize':
28529 if (!preg_match('/^\s*([0-9.]+)\s*(?:([kmg])(?:i?b)?)?\s*$/i', $this->config[$key], $matches)) {
28530 throw new \RuntimeException(
28531 "Could not parse the value of 'cache-files-maxsize': {$this->config[$key]}"
28534 $size = $matches[1];
28535 if (isset($matches[2])) {
28536 switch (strtolower($matches[2])) {
28551 case 'cache-files-ttl':
28552 if (isset($this->config[$key])) {
28553 return (int) $this->config[$key];
28556 return (int) $this->config['cache-ttl'];
28559 return rtrim($this->process($this->config[$key], $flags), '/\\');
28561 case 'discard-changes':
28562 if ($env = $this->getComposerEnv('COMPOSER_DISCARD_CHANGES')) {
28563 if (!in_array($env, array('stash', 'true', 'false', '1', '0'), true)) {
28564 throw new \RuntimeException(
28565 "Invalid value for COMPOSER_DISCARD_CHANGES: {$env}. Expected 1, 0, true, false or stash"
28568 if ('stash' === $env) {
28573 return $env !== 'false' && (bool) $env;
28576 if (!in_array($this->config[$key], array(true, false, 'stash'), true)) {
28577 throw new \RuntimeException(
28578 "Invalid value for 'discard-changes': {$this->config[$key]}. Expected true, false or stash"
28582 return $this->config[$key];
28584 case 'github-protocols':
28585 if (reset($this->config['github-protocols']) === 'http') {
28586 throw new \RuntimeException('The http protocol for github is not available anymore, update your config\'s github-protocols to use "https", "git" or "ssh"');
28589 return $this->config[$key];
28592 if (!isset($this->config[$key])) {
28596 return $this->process($this->config[$key], $flags);
28600 public function all($flags = 0)
28603 'repositories' => $this->getRepositories(),
28605 foreach (array_keys($this->config) as $key) {
28606 $all['config'][$key] = $this->get($key, $flags);
28612 public function raw()
28615 'repositories' => $this->getRepositories(),
28616 'config' => $this->config,
28626 public function has($key)
28628 return array_key_exists($key, $this->config);
28638 private function process($value, $flags)
28642 if (!is_string($value)) {
28646 return preg_replace_callback('#\{\$(.+)\}#', function ($match) use ($config, $flags) {
28647 return $config->get($match[1], $flags);
28659 private function realpath($path)
28661 if (substr($path, 0, 1) === '/' || substr($path, 1, 1) === ':') {
28665 return $this->baseDir . '/' . $path;
28677 private function getComposerEnv($var)
28679 if ($this->useEnvironment) {
28680 return getenv($var);
28698 namespace Composer\EventDispatcher;
28712 interface EventSubscriberInterface
28732 public static function getSubscribedEvents();
28746 namespace Composer\EventDispatcher;
28773 private $propagationStopped = false;
28782 public function __construct($name, array $args = array(), array $flags = array())
28784 $this->name = $name;
28785 $this->args = $args;
28786 $this->flags = $flags;
28794 public function getName()
28796 return $this->name;
28804 public function getArguments()
28806 return $this->args;
28814 public function getFlags()
28816 return $this->flags;
28824 public function isPropagationStopped()
28826 return $this->propagationStopped;
28832 public function stopPropagation()
28834 $this->propagationStopped = true;
28849 namespace Composer\EventDispatcher;
28851 use Composer\DependencyResolver\PolicyInterface;
28852 use Composer\DependencyResolver\Pool;
28853 use Composer\DependencyResolver\Request;
28854 use Composer\Installer\InstallerEvent;
28855 use Composer\IO\IOInterface;
28856 use Composer\Composer;
28857 use Composer\DependencyResolver\Operation\OperationInterface;
28858 use Composer\Repository\CompositeRepository;
28859 use Composer\Script;
28860 use Composer\Script\CommandEvent;
28861 use Composer\Script\PackageEvent;
28862 use Composer\Util\ProcessExecutor;
28877 class EventDispatcher
28879 protected $composer;
28882 protected $process;
28891 public function __construct(Composer $composer, IOInterface $io, ProcessExecutor $process = null)
28893 $this->composer = $composer;
28895 $this->process = $process ?: new ProcessExecutor($io);
28906 public function dispatch($eventName, Event $event = null)
28908 if (null == $event) {
28909 $event = new Event($eventName);
28912 return $this->doDispatch($event);
28925 public function dispatchScript($eventName, $devMode = false, $additionalArgs = array(), $flags = array())
28927 return $this->doDispatch(new Script\Event($eventName, $this->composer, $this->io, $devMode, $additionalArgs, $flags));
28939 public function dispatchPackageEvent($eventName, $devMode, OperationInterface $operation)
28941 return $this->doDispatch(new PackageEvent($eventName, $this->composer, $this->io, $devMode, $operation));
28954 public function dispatchCommandEvent($eventName, $devMode, $additionalArgs = array(), $flags = array())
28956 return $this->doDispatch(new CommandEvent($eventName, $this->composer, $this->io, $devMode, $additionalArgs, $flags));
28972 public function dispatchInstallerEvent($eventName, PolicyInterface $policy, Pool $pool, CompositeRepository $installedRepo, Request $request, array $operations = array())
28974 return $this->doDispatch(new InstallerEvent($eventName, $this->composer, $this->io, $policy, $pool, $installedRepo, $request, $operations));
28987 protected function doDispatch(Event $event)
28989 $listeners = $this->getListeners($event);
28992 foreach ($listeners as $callable) {
28993 if (!is_string($callable) && is_callable($callable)) {
28994 $event = $this->checkListenerExpectedEvent($callable, $event);
28995 $return = false === call_user_func($callable, $event) ? 1 : 0;
28996 } elseif ($this->isPhpScript($callable)) {
28997 $className = substr($callable, 0, strpos($callable, '::'));
28998 $methodName = substr($callable, strpos($callable, '::') + 2);
29000 if (!class_exists($className)) {
29001 $this->io->write('<warning>Class '.$className.' is not autoloadable, can not call '.$event->getName().' script</warning>');
29004 if (!is_callable($callable)) {
29005 $this->io->write('<warning>Method '.$callable.' is not callable, can not call '.$event->getName().' script</warning>');
29010 $return = false === $this->executeEventPhpScript($className, $methodName, $event) ? 1 : 0;
29011 } catch (\Exception $e) {
29012 $message = "Script %s handling the %s event terminated with an exception";
29013 $this->io->write('<error>'.sprintf($message, $callable, $event->getName()).'</error>');
29017 $args = implode(' ', array_map(array('Composer\Util\ProcessExecutor','escape'), $event->getArguments()));
29018 if (0 !== ($exitCode = $this->process->execute($callable . ($args === '' ? '' : ' '.$args)))) {
29019 $this->io->write(sprintf('<error>Script %s handling the %s event returned with an error</error>', $callable, $event->getName()));
29021 throw new \RuntimeException('Error Output: '.$this->process->getErrorOutput(), $exitCode);
29025 if ($event->isPropagationStopped()) {
29038 protected function executeEventPhpScript($className, $methodName, Event $event)
29040 $event = $this->checkListenerExpectedEvent(array($className, $methodName), $event);
29042 return $className::$methodName($event);
29050 protected function checkListenerExpectedEvent($target, Event $event)
29052 if (!$event instanceof Script\Event) {
29057 $reflected = new \ReflectionParameter($target, 0);
29058 } catch (\Exception $e) {
29062 $typehint = $reflected->getClass();
29064 if (!$typehint instanceof \ReflectionClass) {
29068 $expected = $typehint->getName();
29070 if (!$event instanceof $expected && $expected === 'Composer\Script\CommandEvent') {
29071 $event = new CommandEvent($event->getName(), $event->getComposer(), $event->getIO(), $event->isDevMode(), $event->getArguments());
29084 protected function addListener($eventName, $listener, $priority = 0)
29086 $this->listeners[$eventName][$priority][] = $listener;
29096 public function addSubscriber(EventSubscriberInterface $subscriber)
29098 foreach ($subscriber->getSubscribedEvents() as $eventName => $params) {
29099 if (is_string($params)) {
29100 $this->addListener($eventName, array($subscriber, $params));
29101 } elseif (is_string($params[0])) {
29102 $this->addListener($eventName, array($subscriber, $params[0]), isset($params[1]) ? $params[1] : 0);
29104 foreach ($params as $listener) {
29105 $this->addListener($eventName, array($subscriber, $listener[0]), isset($listener[1]) ? $listener[1] : 0);
29117 protected function getListeners(Event $event)
29119 $scriptListeners = $this->getScriptListeners($event);
29121 if (!isset($this->listeners[$event->getName()][0])) {
29122 $this->listeners[$event->getName()][0] = array();
29124 krsort($this->listeners[$event->getName()]);
29126 $listeners = $this->listeners;
29127 $listeners[$event->getName()][0] = array_merge($listeners[$event->getName()][0], $scriptListeners);
29129 return call_user_func_array('array_merge', $listeners[$event->getName()]);
29138 public function hasEventListeners(Event $event)
29140 $listeners = $this->getListeners($event);
29142 return count($listeners) > 0;
29151 protected function getScriptListeners(Event $event)
29153 $package = $this->composer->getPackage();
29154 $scripts = $package->getScripts();
29156 if (empty($scripts[$event->getName()])) {
29160 if ($this->loader) {
29161 $this->loader->unregister();
29164 $generator = $this->composer->getAutoloadGenerator();
29165 $packages = $this->composer->getRepositoryManager()->getLocalRepository()->getCanonicalPackages();
29166 $packageMap = $generator->buildPackageMap($this->composer->getInstallationManager(), $package, $packages);
29167 $map = $generator->parseAutoloads($packageMap, $package);
29168 $this->loader = $generator->createLoader($map);
29169 $this->loader->register();
29171 return $scripts[$event->getName()];
29180 protected function isPhpScript($callable)
29182 return false === strpos($callable, ' ') && false !== strpos($callable, '::');
29197 namespace Composer\Script;
29199 use Composer\Composer;
29200 use Composer\IO\IOInterface;
29201 use Composer\EventDispatcher\Event as BaseEvent;
29209 class Event extends BaseEvent
29236 public function __construct($name, Composer $composer, IOInterface $io, $devMode = false, array $args = array(), array $flags = array())
29238 parent::__construct($name, $args, $flags);
29239 $this->composer = $composer;
29241 $this->devMode = $devMode;
29249 public function getComposer()
29251 return $this->composer;
29259 public function getIO()
29269 public function isDevMode()
29271 return $this->devMode;
29286 namespace Composer\Script;
29303 const PRE_INSTALL_CMD = 'pre-install-cmd';
29312 const POST_INSTALL_CMD = 'post-install-cmd';
29321 const PRE_UPDATE_CMD = 'pre-update-cmd';
29330 const POST_UPDATE_CMD = 'post-update-cmd';
29339 const PRE_STATUS_CMD = 'pre-status-cmd';
29348 const POST_STATUS_CMD = 'post-status-cmd';
29357 const PRE_PACKAGE_INSTALL = 'pre-package-install';
29366 const POST_PACKAGE_INSTALL = 'post-package-install';
29375 const PRE_PACKAGE_UPDATE = 'pre-package-update';
29384 const POST_PACKAGE_UPDATE = 'post-package-update';
29393 const PRE_PACKAGE_UNINSTALL = 'pre-package-uninstall';
29402 const POST_PACKAGE_UNINSTALL = 'post-package-uninstall';
29411 const PRE_AUTOLOAD_DUMP = 'pre-autoload-dump';
29420 const POST_AUTOLOAD_DUMP = 'post-autoload-dump';
29429 const POST_ROOT_PACKAGE_INSTALL = 'post-root-package-install';
29439 const POST_CREATE_PROJECT_CMD = 'post-create-project-cmd';
29448 const PRE_ARCHIVE_CMD = 'pre-archive-cmd';
29457 const POST_ARCHIVE_CMD = 'post-archive-cmd';
29471 namespace Composer\Script;
29478 class CommandEvent extends Event
29493 namespace Composer\Script;
29495 use Composer\Composer;
29496 use Composer\IO\IOInterface;
29497 use Composer\DependencyResolver\Operation\OperationInterface;
29504 class PackageEvent extends Event
29509 private $operation;
29520 public function __construct($name, Composer $composer, IOInterface $io, $devMode, OperationInterface $operation)
29522 parent::__construct($name, $composer, $io, $devMode);
29523 $this->operation = $operation;
29531 public function getOperation()
29533 return $this->operation;
29548 namespace Composer\Installer;
29550 use Composer\Composer;
29551 use Composer\DependencyResolver\PolicyInterface;
29552 use Composer\DependencyResolver\Operation\OperationInterface;
29553 use Composer\DependencyResolver\Pool;
29554 use Composer\DependencyResolver\Request;
29555 use Composer\EventDispatcher\Event;
29556 use Composer\IO\IOInterface;
29557 use Composer\Repository\CompositeRepository;
29564 class InstallerEvent extends Event
29589 private $installedRepo;
29599 private $operations;
29613 public function __construct($eventName, Composer $composer, IOInterface $io, PolicyInterface $policy, Pool $pool, CompositeRepository $installedRepo, Request $request, array $operations = array())
29615 parent::__construct($eventName);
29617 $this->composer = $composer;
29619 $this->policy = $policy;
29620 $this->pool = $pool;
29621 $this->installedRepo = $installedRepo;
29622 $this->request = $request;
29623 $this->operations = $operations;
29629 public function getComposer()
29631 return $this->composer;
29637 public function getIO()
29645 public function getPolicy()
29647 return $this->policy;
29653 public function getPool()
29655 return $this->pool;
29661 public function getInstalledRepo()
29663 return $this->installedRepo;
29669 public function getRequest()
29671 return $this->request;
29677 public function getOperations()
29679 return $this->operations;
29694 namespace Composer\Installer;
29696 use Composer\Repository\InstalledRepositoryInterface;
29697 use Composer\Package\PackageInterface;
29706 class NoopInstaller implements InstallerInterface
29711 public function supports($packageType)
29719 public function isInstalled(InstalledRepositoryInterface $repo, PackageInterface $package)
29721 return $repo->hasPackage($package);
29727 public function install(InstalledRepositoryInterface $repo, PackageInterface $package)
29729 if (!$repo->hasPackage($package)) {
29730 $repo->addPackage(clone $package);
29737 public function update(InstalledRepositoryInterface $repo, PackageInterface $initial, PackageInterface $target)
29739 if (!$repo->hasPackage($initial)) {
29740 throw new \InvalidArgumentException('Package is not installed: '.$initial);
29743 $repo->removePackage($initial);
29744 if (!$repo->hasPackage($target)) {
29745 $repo->addPackage(clone $target);
29752 public function uninstall(InstalledRepositoryInterface $repo, PackageInterface $package)
29754 if (!$repo->hasPackage($package)) {
29755 throw new \InvalidArgumentException('Package is not installed: '.$package);
29757 $repo->removePackage($package);
29763 public function getInstallPath(PackageInterface $package)
29765 $targetDir = $package->getTargetDir();
29767 return $package->getPrettyName() . ($targetDir ? '/'.$targetDir : '');
29782 namespace Composer\Installer;
29784 use Composer\Repository\InstalledRepositoryInterface;
29785 use Composer\Package\PackageInterface;
29792 class MetapackageInstaller implements InstallerInterface
29797 public function supports($packageType)
29799 return $packageType === 'metapackage';
29805 public function isInstalled(InstalledRepositoryInterface $repo, PackageInterface $package)
29807 return $repo->hasPackage($package);
29813 public function install(InstalledRepositoryInterface $repo, PackageInterface $package)
29815 $repo->addPackage(clone $package);
29821 public function update(InstalledRepositoryInterface $repo, PackageInterface $initial, PackageInterface $target)
29823 if (!$repo->hasPackage($initial)) {
29824 throw new \InvalidArgumentException('Package is not installed: '.$initial);
29827 $repo->removePackage($initial);
29828 $repo->addPackage(clone $target);
29834 public function uninstall(InstalledRepositoryInterface $repo, PackageInterface $package)
29836 if (!$repo->hasPackage($package)) {
29837 throw new \InvalidArgumentException('Package is not installed: '.$package);
29840 $repo->removePackage($package);
29846 public function getInstallPath(PackageInterface $package)
29863 namespace Composer\Installer;
29865 use Composer\IO\IOInterface;
29866 use Composer\Composer;
29867 use Composer\Downloader\PearPackageExtractor;
29868 use Composer\Repository\InstalledRepositoryInterface;
29869 use Composer\Package\PackageInterface;
29870 use Composer\Util\ProcessExecutor;
29878 class PearInstaller extends LibraryInstaller
29887 public function __construct(IOInterface $io, Composer $composer, $type = 'pear-library')
29889 parent::__construct($io, $composer, $type);
29895 public function update(InstalledRepositoryInterface $repo, PackageInterface $initial, PackageInterface $target)
29897 $this->uninstall($repo, $initial);
29898 $this->install($repo, $target);
29901 protected function installCode(PackageInterface $package)
29903 parent::installCode($package);
29904 parent::initializeBinDir();
29906 $isWindows = defined('PHP_WINDOWS_VERSION_BUILD');
29907 $php_bin = $this->binDir . ($isWindows ? '/composer-php.bat' : '/composer-php');
29910 $php_bin = '/usr/bin/env ' . $php_bin;
29913 $installPath = $this->getInstallPath($package);
29915 'os' => $isWindows ? 'windows' : 'linux',
29916 'php_bin' => $php_bin,
29917 'pear_php' => $installPath,
29918 'php_dir' => $installPath,
29919 'bin_dir' => $installPath . '/bin',
29920 'data_dir' => $installPath . '/data',
29921 'version' => $package->getPrettyVersion(),
29924 $packageArchive = $this->getInstallPath($package).'/'.pathinfo($package->getDistUrl(), PATHINFO_BASENAME);
29925 $pearExtractor = new PearPackageExtractor($packageArchive);
29926 $pearExtractor->extractTo($this->getInstallPath($package), array('php' => '/', 'script' => '/bin', 'data' => '/data'), $vars);
29928 if ($this->io->isVerbose()) {
29929 $this->io->write(' Cleaning up');
29931 $this->filesystem->unlink($packageArchive);
29934 protected function getBinaries(PackageInterface $package)
29936 $binariesPath = $this->getInstallPath($package) . '/bin/';
29937 $binaries = array();
29938 if (file_exists($binariesPath)) {
29939 foreach (new \FilesystemIterator($binariesPath, \FilesystemIterator::KEY_AS_FILENAME | \FilesystemIterator::CURRENT_AS_FILEINFO) as $fileName => $value) {
29940 if (!$value->isDir()) {
29941 $binaries[] = 'bin/'.$fileName;
29949 protected function initializeBinDir()
29951 parent::initializeBinDir();
29952 file_put_contents($this->binDir.'/composer-php', $this->generateUnixyPhpProxyCode());
29953 @chmod($this->binDir.'/composer-php', 0777);
29954 file_put_contents($this->binDir.'/composer-php.bat', $this->generateWindowsPhpProxyCode());
29955 @chmod($this->binDir.'/composer-php.bat', 0777);
29958 protected function generateWindowsProxyCode($bin, $link)
29960 $binPath = $this->filesystem->findShortestPath($link, $bin);
29961 if ('.bat' === substr($bin, -4)) {
29964 $handle = fopen($bin, 'r');
29965 $line = fgets($handle);
29967 if (preg_match('{^#!/(?:usr/bin/env )?(?:[^/]+/)*(.+)$}m', $line, $match)) {
29968 $caller = trim($match[1]);
29973 if ($caller === 'php') {
29974 return "@echo off\r\n".
29977 "set PHP_PROXY=%CD%\\composer-php.bat\r\n".
29978 "cd ".ProcessExecutor::escape(dirname($binPath))."\r\n".
29979 "set BIN_TARGET=%CD%\\".basename($binPath)."\r\n".
29981 "%PHP_PROXY% \"%BIN_TARGET%\" %*\r\n";
29985 return "@echo off\r\n".
29988 "cd ".ProcessExecutor::escape(dirname($binPath))."\r\n".
29989 "set BIN_TARGET=%CD%\\".basename($binPath)."\r\n".
29991 $caller." \"%BIN_TARGET%\" %*\r\n";
29994 private function generateWindowsPhpProxyCode()
29996 $binToVendor = $this->filesystem->findShortestPath($this->binDir, $this->vendorDir, true);
30000 "setlocal enabledelayedexpansion\r\n" .
30001 "set BIN_DIR=%~dp0\r\n" .
30002 "set VENDOR_DIR=%BIN_DIR%\\".$binToVendor."\r\n" .
30004 "FOR /D %%V IN (%VENDOR_DIR%\\*) DO (\r\n" .
30005 " FOR /D %%P IN (%%V\\*) DO (\r\n" .
30006 " set DIRS=!DIRS!;%%~fP\r\n" .
30009 "php.exe -d include_path=!DIRS! %*\r\n";
30012 private function generateUnixyPhpProxyCode()
30014 $binToVendor = $this->filesystem->findShortestPath($this->binDir, $this->vendorDir, true);
30017 "#!/usr/bin/env sh\n".
30019 "BIN_DIR=`dirname $0`\n".
30020 "VENDOR_DIR=\$BIN_DIR/".escapeshellarg($binToVendor)."\n".
30022 "for vendor in \$VENDOR_DIR/*; do\n".
30023 " if [ -d \"\$vendor\" ]; then\n".
30024 " for package in \$vendor/*; do\n".
30025 " if [ -d \"\$package\" ]; then\n".
30026 " DIRS=\"\${DIRS}:\${package}\"\n".
30031 "php -d include_path=\".\$DIRS\" $@\n";
30046 namespace Composer\Installer;
30048 use Composer\Package\PackageInterface;
30049 use Composer\Downloader\DownloadManager;
30050 use Composer\Repository\InstalledRepositoryInterface;
30051 use Composer\Util\Filesystem;
30059 class ProjectInstaller implements InstallerInterface
30061 private $installPath;
30062 private $downloadManager;
30063 private $filesystem;
30065 public function __construct($installPath, DownloadManager $dm)
30067 $this->installPath = rtrim(strtr($installPath, '\\', '/'), '/').'/';
30068 $this->downloadManager = $dm;
30069 $this->filesystem = new Filesystem;
30078 public function supports($packageType)
30086 public function isInstalled(InstalledRepositoryInterface $repo, PackageInterface $package)
30094 public function install(InstalledRepositoryInterface $repo, PackageInterface $package)
30096 $installPath = $this->installPath;
30097 if (file_exists($installPath) && !$this->filesystem->isDirEmpty($installPath)) {
30098 throw new \InvalidArgumentException("Project directory $installPath is not empty.");
30100 if (!is_dir($installPath)) {
30101 mkdir($installPath, 0777, true);
30103 $this->downloadManager->download($package, $installPath);
30109 public function update(InstalledRepositoryInterface $repo, PackageInterface $initial, PackageInterface $target)
30111 throw new \InvalidArgumentException("not supported");
30117 public function uninstall(InstalledRepositoryInterface $repo, PackageInterface $package)
30119 throw new \InvalidArgumentException("not supported");
30128 public function getInstallPath(PackageInterface $package)
30130 return $this->installPath;
30145 namespace Composer\Installer;
30147 use Composer\Composer;
30148 use Composer\IO\IOInterface;
30149 use Composer\Repository\InstalledRepositoryInterface;
30150 use Composer\Package\PackageInterface;
30151 use Composer\Util\Filesystem;
30152 use Composer\Util\ProcessExecutor;
30160 class LibraryInstaller implements InstallerInterface
30162 protected $composer;
30163 protected $vendorDir;
30165 protected $downloadManager;
30168 protected $filesystem;
30178 public function __construct(IOInterface $io, Composer $composer, $type = 'library', Filesystem $filesystem = null)
30180 $this->composer = $composer;
30181 $this->downloadManager = $composer->getDownloadManager();
30183 $this->type = $type;
30185 $this->filesystem = $filesystem ?: new Filesystem();
30186 $this->vendorDir = rtrim($composer->getConfig()->get('vendor-dir'), '/');
30187 $this->binDir = rtrim($composer->getConfig()->get('bin-dir'), '/');
30193 public function supports($packageType)
30195 return $packageType === $this->type || null === $this->type;
30201 public function isInstalled(InstalledRepositoryInterface $repo, PackageInterface $package)
30203 return $repo->hasPackage($package) && is_readable($this->getInstallPath($package));
30209 public function install(InstalledRepositoryInterface $repo, PackageInterface $package)
30211 $this->initializeVendorDir();
30212 $downloadPath = $this->getInstallPath($package);
30215 if (!is_readable($downloadPath) && $repo->hasPackage($package)) {
30216 $this->removeBinaries($package);
30219 $this->installCode($package);
30220 $this->installBinaries($package);
30221 if (!$repo->hasPackage($package)) {
30222 $repo->addPackage(clone $package);
30229 public function update(InstalledRepositoryInterface $repo, PackageInterface $initial, PackageInterface $target)
30231 if (!$repo->hasPackage($initial)) {
30232 throw new \InvalidArgumentException('Package is not installed: '.$initial);
30235 $this->initializeVendorDir();
30237 $this->removeBinaries($initial);
30238 $this->updateCode($initial, $target);
30239 $this->installBinaries($target);
30240 $repo->removePackage($initial);
30241 if (!$repo->hasPackage($target)) {
30242 $repo->addPackage(clone $target);
30249 public function uninstall(InstalledRepositoryInterface $repo, PackageInterface $package)
30251 if (!$repo->hasPackage($package)) {
30252 throw new \InvalidArgumentException('Package is not installed: '.$package);
30255 $this->removeCode($package);
30256 $this->removeBinaries($package);
30257 $repo->removePackage($package);
30259 $downloadPath = $this->getPackageBasePath($package);
30260 if (strpos($package->getName(), '/')) {
30261 $packageVendorDir = dirname($downloadPath);
30262 if (is_dir($packageVendorDir) && $this->filesystem->isDirEmpty($packageVendorDir)) {
30263 @rmdir($packageVendorDir);
30271 public function getInstallPath(PackageInterface $package)
30273 $targetDir = $package->getTargetDir();
30275 return $this->getPackageBasePath($package) . ($targetDir ? '/'.$targetDir : '');
30278 protected function getPackageBasePath(PackageInterface $package)
30280 $this->initializeVendorDir();
30282 return ($this->vendorDir ? $this->vendorDir.'/' : '') . $package->getPrettyName();
30285 protected function installCode(PackageInterface $package)
30287 $downloadPath = $this->getInstallPath($package);
30288 $this->downloadManager->download($package, $downloadPath);
30291 protected function updateCode(PackageInterface $initial, PackageInterface $target)
30293 $initialDownloadPath = $this->getInstallPath($initial);
30294 $targetDownloadPath = $this->getInstallPath($target);
30295 if ($targetDownloadPath !== $initialDownloadPath) {
30298 if (substr($initialDownloadPath, 0, strlen($targetDownloadPath)) === $targetDownloadPath
30299 || substr($targetDownloadPath, 0, strlen($initialDownloadPath)) === $initialDownloadPath
30301 $this->removeCode($initial);
30302 $this->installCode($target);
30307 $this->filesystem->rename($initialDownloadPath, $targetDownloadPath);
30309 $this->downloadManager->update($initial, $target, $targetDownloadPath);
30312 protected function removeCode(PackageInterface $package)
30314 $downloadPath = $this->getPackageBasePath($package);
30315 $this->downloadManager->remove($package, $downloadPath);
30318 protected function getBinaries(PackageInterface $package)
30320 return $package->getBinaries();
30323 protected function installBinaries(PackageInterface $package)
30325 $binaries = $this->getBinaries($package);
30329 foreach ($binaries as $bin) {
30330 $binPath = $this->getInstallPath($package).'/'.$bin;
30331 if (!file_exists($binPath)) {
30332 $this->io->write(' <warning>Skipped installation of bin '.$bin.' for package '.$package->getName().': file not found in package</warning>');
30340 $binPath = realpath($binPath);
30342 $this->initializeBinDir();
30343 $link = $this->binDir.'/'.basename($bin);
30344 if (file_exists($link)) {
30345 if (is_link($link)) {
30349 @chmod($link, 0777 & ~umask());
30351 $this->io->write(' Skipped installation of bin '.$bin.' for package '.$package->getName().': name conflicts with an existing file');
30354 if (defined('PHP_WINDOWS_VERSION_BUILD')) {
30356 if ('.bat' !== substr($binPath, -4)) {
30357 file_put_contents($link, $this->generateUnixyProxyCode($binPath, $link));
30358 @chmod($link, 0777 & ~umask());
30360 if (file_exists($link)) {
30361 $this->io->write(' Skipped installation of bin '.$bin.'.bat proxy for package '.$package->getName().': a .bat proxy was already installed');
30364 if (!file_exists($link)) {
30365 file_put_contents($link, $this->generateWindowsProxyCode($binPath, $link));
30372 $relativeBin = $this->filesystem->findShortestPath($link, $binPath);
30373 chdir(dirname($link));
30374 if (false === symlink($relativeBin, $link)) {
30375 throw new \ErrorException();
30377 } catch (\ErrorException $e) {
30378 file_put_contents($link, $this->generateUnixyProxyCode($binPath, $link));
30382 @chmod($link, 0777 & ~umask());
30386 protected function removeBinaries(PackageInterface $package)
30388 $binaries = $this->getBinaries($package);
30392 foreach ($binaries as $bin) {
30393 $link = $this->binDir.'/'.basename($bin);
30394 if (is_link($link) || file_exists($link)) {
30395 $this->filesystem->unlink($link);
30397 if (file_exists($link.'.bat')) {
30398 $this->filesystem->unlink($link.'.bat');
30403 protected function initializeVendorDir()
30405 $this->filesystem->ensureDirectoryExists($this->vendorDir);
30406 $this->vendorDir = realpath($this->vendorDir);
30409 protected function initializeBinDir()
30411 $this->filesystem->ensureDirectoryExists($this->binDir);
30412 $this->binDir = realpath($this->binDir);
30415 protected function generateWindowsProxyCode($bin, $link)
30417 $binPath = $this->filesystem->findShortestPath($link, $bin);
30418 if ('.bat' === substr($bin, -4) || '.exe' === substr($bin, -4)) {
30421 $handle = fopen($bin, 'r');
30422 $line = fgets($handle);
30424 if (preg_match('{^#!/(?:usr/bin/env )?(?:[^/]+/)*(.+)$}m', $line, $match)) {
30425 $caller = trim($match[1]);
30431 return "@ECHO OFF\r\n".
30432 "SET BIN_TARGET=%~dp0/".trim(ProcessExecutor::escape($binPath), '"')."\r\n".
30433 "{$caller} \"%BIN_TARGET%\" %*\r\n";
30436 protected function generateUnixyProxyCode($bin, $link)
30438 $binPath = $this->filesystem->findShortestPath($link, $bin);
30440 return "#!/usr/bin/env sh\n".
30441 'SRC_DIR="`pwd`"'."\n".
30442 'cd "`dirname "$0"`"'."\n".
30443 'cd '.ProcessExecutor::escape(dirname($binPath))."\n".
30444 'BIN_TARGET="`pwd`/'.basename($binPath)."\"\n".
30445 'cd "$SRC_DIR"'."\n".
30446 '"$BIN_TARGET" "$@"'."\n";
30461 namespace Composer\Installer;
30463 use Composer\Package\PackageInterface;
30464 use Composer\Package\AliasPackage;
30465 use Composer\Repository\RepositoryInterface;
30466 use Composer\Repository\InstalledRepositoryInterface;
30467 use Composer\DependencyResolver\Operation\OperationInterface;
30468 use Composer\DependencyResolver\Operation\InstallOperation;
30469 use Composer\DependencyResolver\Operation\UpdateOperation;
30470 use Composer\DependencyResolver\Operation\UninstallOperation;
30471 use Composer\DependencyResolver\Operation\MarkAliasInstalledOperation;
30472 use Composer\DependencyResolver\Operation\MarkAliasUninstalledOperation;
30473 use Composer\Util\StreamContextFactory;
30482 class InstallationManager
30484 private $installers = array();
30485 private $cache = array();
30486 private $notifiablePackages = array();
30488 public function reset()
30490 $this->notifiablePackages = array();
30498 public function addInstaller(InstallerInterface $installer)
30500 array_unshift($this->installers, $installer);
30501 $this->cache = array();
30509 public function removeInstaller(InstallerInterface $installer)
30511 if (false !== ($key = array_search($installer, $this->installers, true))) {
30512 array_splice($this->installers, $key, 1);
30513 $this->cache = array();
30524 public function disablePlugins()
30526 foreach ($this->installers as $i => $installer) {
30527 if (!$installer instanceof PluginInstaller) {
30531 unset($this->installers[$i]);
30544 public function getInstaller($type)
30546 $type = strtolower($type);
30548 if (isset($this->cache[$type])) {
30549 return $this->cache[$type];
30552 foreach ($this->installers as $installer) {
30553 if ($installer->supports($type)) {
30554 return $this->cache[$type] = $installer;
30558 throw new \InvalidArgumentException('Unknown installer type: '.$type);
30569 public function isPackageInstalled(InstalledRepositoryInterface $repo, PackageInterface $package)
30571 if ($package instanceof AliasPackage) {
30572 return $repo->hasPackage($package) && $this->isPackageInstalled($repo, $package->getAliasOf());
30575 return $this->getInstaller($package->getType())->isInstalled($repo, $package);
30584 public function execute(RepositoryInterface $repo, OperationInterface $operation)
30586 $method = $operation->getJobType();
30587 $this->$method($repo, $operation);
30596 public function install(RepositoryInterface $repo, InstallOperation $operation)
30598 $package = $operation->getPackage();
30599 $installer = $this->getInstaller($package->getType());
30600 $installer->install($repo, $package);
30601 $this->markForNotification($package);
30610 public function update(RepositoryInterface $repo, UpdateOperation $operation)
30612 $initial = $operation->getInitialPackage();
30613 $target = $operation->getTargetPackage();
30615 $initialType = $initial->getType();
30616 $targetType = $target->getType();
30618 if ($initialType === $targetType) {
30619 $installer = $this->getInstaller($initialType);
30620 $installer->update($repo, $initial, $target);
30621 $this->markForNotification($target);
30623 $this->getInstaller($initialType)->uninstall($repo, $initial);
30624 $this->getInstaller($targetType)->install($repo, $target);
30634 public function uninstall(RepositoryInterface $repo, UninstallOperation $operation)
30636 $package = $operation->getPackage();
30637 $installer = $this->getInstaller($package->getType());
30638 $installer->uninstall($repo, $package);
30647 public function markAliasInstalled(RepositoryInterface $repo, MarkAliasInstalledOperation $operation)
30649 $package = $operation->getPackage();
30651 if (!$repo->hasPackage($package)) {
30652 $repo->addPackage(clone $package);
30662 public function markAliasUninstalled(RepositoryInterface $repo, MarkAliasUninstalledOperation $operation)
30664 $package = $operation->getPackage();
30666 $repo->removePackage($package);
30675 public function getInstallPath(PackageInterface $package)
30677 $installer = $this->getInstaller($package->getType());
30679 return $installer->getInstallPath($package);
30682 public function notifyInstalls()
30684 foreach ($this->notifiablePackages as $repoUrl => $packages) {
30686 if (strpos($repoUrl, '%package%')) {
30687 foreach ($packages as $package) {
30688 $url = str_replace('%package%', $package->getPrettyName(), $repoUrl);
30691 'version' => $package->getPrettyVersion(),
30692 'version_normalized' => $package->getVersion(),
30694 $opts = array('http' =>
30696 'method' => 'POST',
30697 'header' => array('Content-type: application/x-www-form-urlencoded'),
30698 'content' => http_build_query($params, '', '&'),
30703 $context = StreamContextFactory::getContext($url, $opts);
30704 @file_get_contents($url, false, $context);
30710 $postData = array('downloads' => array());
30711 foreach ($packages as $package) {
30712 $postData['downloads'][] = array(
30713 'name' => $package->getPrettyName(),
30714 'version' => $package->getVersion(),
30718 $opts = array('http' =>
30720 'method' => 'POST',
30721 'header' => array('Content-Type: application/json'),
30722 'content' => json_encode($postData),
30727 $context = StreamContextFactory::getContext($repoUrl, $opts);
30728 @file_get_contents($repoUrl, false, $context);
30734 private function markForNotification(PackageInterface $package)
30736 if ($package->getNotificationUrl()) {
30737 $this->notifiablePackages[$package->getNotificationUrl()][$package->getName()] = $package;
30753 namespace Composer\Installer;
30760 class InstallerEvents
30771 const PRE_DEPENDENCIES_SOLVING = 'pre-dependencies-solving';
30782 const POST_DEPENDENCIES_SOLVING = 'post-dependencies-solving';
30796 namespace Composer\Installer;
30798 use Composer\Composer;
30799 use Composer\Package\Package;
30800 use Composer\IO\IOInterface;
30801 use Composer\Repository\InstalledRepositoryInterface;
30802 use Composer\Package\PackageInterface;
30810 class PluginInstaller extends LibraryInstaller
30812 private $installationManager;
30813 private static $classCounter = 0;
30822 public function __construct(IOInterface $io, Composer $composer, $type = 'library')
30824 parent::__construct($io, $composer, 'composer-plugin');
30825 $this->installationManager = $composer->getInstallationManager();
30831 public function supports($packageType)
30833 return $packageType === 'composer-plugin' || $packageType === 'composer-installer';
30839 public function install(InstalledRepositoryInterface $repo, PackageInterface $package)
30841 $extra = $package->getExtra();
30842 if (empty($extra['class'])) {
30843 throw new \UnexpectedValueException('Error while installing '.$package->getPrettyName().', composer-plugin packages should have a class defined in their extra key to be usable.');
30846 parent::install($repo, $package);
30847 $this->composer->getPluginManager()->registerPackage($package, true);
30853 public function update(InstalledRepositoryInterface $repo, PackageInterface $initial, PackageInterface $target)
30855 $extra = $target->getExtra();
30856 if (empty($extra['class'])) {
30857 throw new \UnexpectedValueException('Error while installing '.$target->getPrettyName().', composer-plugin packages should have a class defined in their extra key to be usable.');
30860 parent::update($repo, $initial, $target);
30861 $this->composer->getPluginManager()->registerPackage($target, true);
30876 namespace Composer\Installer;
30878 use Composer\Package\PackageInterface;
30879 use Composer\Repository\InstalledRepositoryInterface;
30887 interface InstallerInterface
30895 public function supports($packageType);
30905 public function isInstalled(InstalledRepositoryInterface $repo, PackageInterface $package);
30913 public function install(InstalledRepositoryInterface $repo, PackageInterface $package);
30924 public function update(InstalledRepositoryInterface $repo, PackageInterface $initial, PackageInterface $target);
30932 public function uninstall(InstalledRepositoryInterface $repo, PackageInterface $package);
30940 public function getInstallPath(PackageInterface $package);
30954 namespace Composer\Console;
30956 use Symfony\Component\Console\Application as BaseApplication;
30957 use Symfony\Component\Console\Input\InputInterface;
30958 use Symfony\Component\Console\Input\InputOption;
30959 use Symfony\Component\Console\Output\OutputInterface;
30960 use Symfony\Component\Console\Output\ConsoleOutput;
30961 use Symfony\Component\Console\Formatter\OutputFormatter;
30962 use Composer\Command;
30963 use Composer\Command\Helper\DialogHelper;
30964 use Composer\Composer;
30965 use Composer\Factory;
30966 use Composer\IO\IOInterface;
30967 use Composer\IO\ConsoleIO;
30968 use Composer\Json\JsonValidationException;
30969 use Composer\Util\ErrorHandler;
30978 class Application extends BaseApplication
30983 protected $composer;
30990 private static $logo = ' ______
30991 / ____/___ ____ ___ ____ ____ ________ _____
30992 / / / __ \/ __ `__ \/ __ \/ __ \/ ___/ _ \/ ___/
30993 / /___/ /_/ / / / / / / /_/ / /_/ (__ ) __/ /
30994 \____/\____/_/ /_/ /_/ .___/\____/____/\___/_/
30998 public function __construct()
31000 if (function_exists('ini_set') && extension_loaded('xdebug')) {
31001 ini_set('xdebug.show_exception_trace', false);
31002 ini_set('xdebug.scream', false);
31005 if (function_exists('date_default_timezone_set') && function_exists('date_default_timezone_get')) {
31006 date_default_timezone_set(@date_default_timezone_get());
31009 ErrorHandler::register();
31010 parent::__construct('Composer', Composer::VERSION);
31016 public function run(InputInterface $input = null, OutputInterface $output = null)
31018 if (null === $output) {
31019 $styles = Factory::createAdditionalStyles();
31020 $formatter = new OutputFormatter(null, $styles);
31021 $output = new ConsoleOutput(ConsoleOutput::VERBOSITY_NORMAL, null, $formatter);
31024 return parent::run($input, $output);
31030 public function doRun(InputInterface $input, OutputInterface $output)
31032 $this->io = new ConsoleIO($input, $output, $this->getHelperSet());
31034 if (version_compare(PHP_VERSION, '5.3.2', '<')) {
31035 $output->writeln('<warning>Composer only officially supports PHP 5.3.2 and above, you will most likely encounter problems with your PHP '.PHP_VERSION.', upgrading is strongly recommended.</warning>');
31038 if (defined('COMPOSER_DEV_WARNING_TIME')) {
31040 if ($name = $this->getCommandName($input)) {
31042 $commandName = $this->find($name)->getName();
31043 } catch (\InvalidArgumentException $e) {
31046 if ($commandName !== 'self-update' && $commandName !== 'selfupdate') {
31047 if (time() > COMPOSER_DEV_WARNING_TIME) {
31048 $output->writeln(sprintf('<warning>Warning: This development build of composer is over 30 days old. It is recommended to update it by running "%s self-update" to get the latest version.</warning>', $_SERVER['PHP_SELF']));
31053 if (getenv('COMPOSER_NO_INTERACTION')) {
31054 $input->setInteractive(false);
31058 if ($newWorkDir = $this->getNewWorkingDir($input)) {
31059 $oldWorkingDir = getcwd();
31060 chdir($newWorkDir);
31061 if ($output->getVerbosity() >= 4) {
31062 $output->writeln('Changed CWD to ' . getcwd());
31067 $file = Factory::getComposerFile();
31068 if (is_file($file) && is_readable($file) && is_array($composer = json_decode(file_get_contents($file), true))) {
31069 if (isset($composer['scripts']) && is_array($composer['scripts'])) {
31070 foreach ($composer['scripts'] as $script => $dummy) {
31071 if (!defined('Composer\Script\ScriptEvents::'.str_replace('-', '_', strtoupper($script)))) {
31072 if ($this->has($script)) {
31073 $output->writeln('<warning>A script named '.$script.' would override a native Composer function and has been skipped</warning>');
31075 $this->add(new Command\ScriptAliasCommand($script));
31082 if ($input->hasParameterOption('--profile')) {
31083 $startTime = microtime(true);
31084 $this->io->enableDebugging($startTime);
31087 $result = parent::doRun($input, $output);
31089 if (isset($oldWorkingDir)) {
31090 chdir($oldWorkingDir);
31093 if (isset($startTime)) {
31094 $output->writeln('<info>Memory usage: '.round(memory_get_usage() / 1024 / 1024, 2).'MB (peak: '.round(memory_get_peak_usage() / 1024 / 1024, 2).'MB), time: '.round(microtime(true) - $startTime, 2).'s');
31105 private function getNewWorkingDir(InputInterface $input)
31107 $workingDir = $input->getParameterOption(array('--working-dir', '-d'));
31108 if (false !== $workingDir && !is_dir($workingDir)) {
31109 throw new \RuntimeException('Invalid working directory specified.');
31112 return $workingDir;
31118 public function renderException($exception, $output)
31121 $composer = $this->getComposer(false, true);
31123 $config = $composer->getConfig();
31125 $minSpaceFree = 1024*1024;
31126 if ((($df = @disk_free_space($dir = $config->get('home'))) !== false && $df < $minSpaceFree)
31127 || (($df = @disk_free_space($dir = $config->get('vendor-dir'))) !== false && $df < $minSpaceFree)
31128 || (($df = @disk_free_space($dir = sys_get_temp_dir())) !== false && $df < $minSpaceFree)
31130 $output->writeln('<error>The disk hosting '.$dir.' is full, this may be the cause of the following exception</error>');
31133 } catch (\Exception $e) {
31136 if (defined('PHP_WINDOWS_VERSION_BUILD') && false !== strpos($exception->getMessage(), 'The system cannot find the path specified')) {
31137 $output->writeln('<error>The following exception may be caused by a stale entry in your cmd.exe AutoRun</error>');
31138 $output->writeln('<error>Check https://getcomposer.org/doc/articles/troubleshooting.md#-the-system-cannot-find-the-path-specified-windows- for details</error>');
31141 if (false !== strpos($exception->getMessage(), 'fork failed - Cannot allocate memory')) {
31142 $output->writeln('<error>The following exception is caused by a lack of memory and not having swap configured</error>');
31143 $output->writeln('<error>Check https://getcomposer.org/doc/articles/troubleshooting.md#proc-open-fork-failed-errors for details</error>');
31146 return parent::renderException($exception, $output);
31155 public function getComposer($required = true, $disablePlugins = false)
31157 if (null === $this->composer) {
31159 $this->composer = Factory::create($this->io, null, $disablePlugins);
31160 } catch (\InvalidArgumentException $e) {
31162 $this->io->write($e->getMessage());
31165 } catch (JsonValidationException $e) {
31166 $errors = ' - ' . implode(PHP_EOL . ' - ', $e->getErrors());
31167 $message = $e->getMessage() . ':' . PHP_EOL . $errors;
31168 throw new JsonValidationException($message);
31172 return $this->composer;
31178 public function resetComposer()
31180 $this->composer = null;
31186 public function getIO()
31191 public function getHelp()
31193 return self::$logo . parent::getHelp();
31199 protected function getDefaultCommands()
31201 $commands = parent::getDefaultCommands();
31202 $commands[] = new Command\AboutCommand();
31203 $commands[] = new Command\ConfigCommand();
31204 $commands[] = new Command\DependsCommand();
31205 $commands[] = new Command\InitCommand();
31206 $commands[] = new Command\InstallCommand();
31207 $commands[] = new Command\CreateProjectCommand();
31208 $commands[] = new Command\UpdateCommand();
31209 $commands[] = new Command\SearchCommand();
31210 $commands[] = new Command\ValidateCommand();
31211 $commands[] = new Command\ShowCommand();
31212 $commands[] = new Command\RequireCommand();
31213 $commands[] = new Command\DumpAutoloadCommand();
31214 $commands[] = new Command\StatusCommand();
31215 $commands[] = new Command\ArchiveCommand();
31216 $commands[] = new Command\DiagnoseCommand();
31217 $commands[] = new Command\RunScriptCommand();
31218 $commands[] = new Command\LicensesCommand();
31219 $commands[] = new Command\GlobalCommand();
31220 $commands[] = new Command\ClearCacheCommand();
31221 $commands[] = new Command\RemoveCommand();
31222 $commands[] = new Command\HomeCommand();
31224 if ('phar:' === substr(__FILE__, 0, 5)) {
31225 $commands[] = new Command\SelfUpdateCommand();
31234 public function getLongVersion()
31236 if (Composer::BRANCH_ALIAS_VERSION) {
31238 '<info>%s</info> version <comment>%s (%s)</comment> %s',
31240 Composer::BRANCH_ALIAS_VERSION,
31241 $this->getVersion(),
31242 Composer::RELEASE_DATE
31246 return parent::getLongVersion() . ' ' . Composer::RELEASE_DATE;
31252 protected function getDefaultInputDefinition()
31254 $definition = parent::getDefaultInputDefinition();
31255 $definition->addOption(new InputOption('--profile', null, InputOption::VALUE_NONE, 'Display timing and memory usage information'));
31256 $definition->addOption(new InputOption('--working-dir', '-d', InputOption::VALUE_REQUIRED, 'If specified, use the given directory as working directory.'));
31258 return $definition;
31264 protected function getDefaultHelperSet()
31266 $helperSet = parent::getDefaultHelperSet();
31268 $helperSet->set(new DialogHelper());
31285 namespace Composer\Console;
31287 use Symfony\Component\Console\Formatter\OutputFormatter;
31292 class HtmlOutputFormatter extends OutputFormatter
31294 private static $availableForegroundColors = array(
31304 private static $availableBackgroundColors = array(
31314 private static $availableOptions = array(
31325 public function __construct(array $styles = array())
31327 parent::__construct(true, $styles);
31330 public function format($message)
31332 $formatted = parent::format($message);
31334 return preg_replace_callback("{\033\[([0-9;]+)m(.*?)\033\[0m}s", array($this, 'formatHtml'), $formatted);
31337 private function formatHtml($matches)
31339 $out = '<span style="';
31340 foreach (explode(';', $matches[1]) as $code) {
31341 if (isset(self::$availableForegroundColors[$code])) {
31342 $out .= 'color:'.self::$availableForegroundColors[$code].';';
31343 } elseif (isset(self::$availableBackgroundColors[$code])) {
31344 $out .= 'background-color:'.self::$availableBackgroundColors[$code].';';
31345 } elseif (isset(self::$availableOptions[$code])) {
31346 switch (self::$availableOptions[$code]) {
31348 $out .= 'font-weight:bold;';
31352 $out .= 'text-decoration:underline;';
31358 return $out . '">'.$matches[2].'</span>';
31373 namespace Composer\Autoload;
31375 use Composer\Config;
31376 use Composer\EventDispatcher\EventDispatcher;
31377 use Composer\Installer\InstallationManager;
31378 use Composer\IO\IOInterface;
31379 use Composer\Package\AliasPackage;
31380 use Composer\Package\PackageInterface;
31381 use Composer\Repository\InstalledRepositoryInterface;
31382 use Composer\Util\Filesystem;
31383 use Composer\Script\ScriptEvents;
31389 class AutoloadGenerator
31394 private $eventDispatcher;
31401 private $devMode = false;
31403 public function __construct(EventDispatcher $eventDispatcher, IOInterface $io = null)
31405 $this->eventDispatcher = $eventDispatcher;
31409 public function setDevMode($devMode = true)
31411 $this->devMode = (boolean) $devMode;
31414 public function dump(Config $config, InstalledRepositoryInterface $localRepo, PackageInterface $mainPackage, InstallationManager $installationManager, $targetDir, $scanPsr0Packages = false, $suffix = '')
31416 $this->eventDispatcher->dispatchScript(ScriptEvents::PRE_AUTOLOAD_DUMP, $this->devMode, array(), array(
31417 'optimize' => (bool) $scanPsr0Packages,
31420 $filesystem = new Filesystem();
31421 $filesystem->ensureDirectoryExists($config->get('vendor-dir'));
31422 $basePath = $filesystem->normalizePath(realpath(getcwd()));
31423 $vendorPath = $filesystem->normalizePath(realpath($config->get('vendor-dir')));
31424 $useGlobalIncludePath = (bool) $config->get('use-include-path');
31425 $prependAutoloader = $config->get('prepend-autoloader') === false ? 'false' : 'true';
31426 $targetDir = $vendorPath.'/'.$targetDir;
31427 $filesystem->ensureDirectoryExists($targetDir);
31429 $vendorPathCode = $filesystem->findShortestPathCode(realpath($targetDir), $vendorPath, true);
31430 $vendorPathCode52 = str_replace('__DIR__', 'dirname(__FILE__)', $vendorPathCode);
31431 $vendorPathToTargetDirCode = $filesystem->findShortestPathCode($vendorPath, realpath($targetDir), true);
31433 $appBaseDirCode = $filesystem->findShortestPathCode($vendorPath, $basePath, true);
31434 $appBaseDirCode = str_replace('__DIR__', '$vendorDir', $appBaseDirCode);
31436 $namespacesFile = <<<EOF
31439 // autoload_namespaces.php @generated by Composer
31441 \$vendorDir = $vendorPathCode52;
31442 \$baseDir = $appBaseDirCode;
31451 // autoload_psr4.php @generated by Composer
31453 \$vendorDir = $vendorPathCode52;
31454 \$baseDir = $appBaseDirCode;
31461 $packageMap = $this->buildPackageMap($installationManager, $mainPackage, $localRepo->getCanonicalPackages());
31462 $autoloads = $this->parseAutoloads($packageMap, $mainPackage);
31465 foreach ($autoloads['psr-0'] as $namespace => $paths) {
31466 $exportedPaths = array();
31467 foreach ($paths as $path) {
31468 $exportedPaths[] = $this->getPathCode($filesystem, $basePath, $vendorPath, $path);
31470 $exportedPrefix = var_export($namespace, true);
31471 $namespacesFile .= " $exportedPrefix => ";
31472 $namespacesFile .= "array(".implode(', ', $exportedPaths)."),\n";
31474 $namespacesFile .= ");\n";
31477 foreach ($autoloads['psr-4'] as $namespace => $paths) {
31478 $exportedPaths = array();
31479 foreach ($paths as $path) {
31480 $exportedPaths[] = $this->getPathCode($filesystem, $basePath, $vendorPath, $path);
31482 $exportedPrefix = var_export($namespace, true);
31483 $psr4File .= " $exportedPrefix => ";
31484 $psr4File .= "array(".implode(', ', $exportedPaths)."),\n";
31486 $psr4File .= ");\n";
31488 $classmapFile = <<<EOF
31491 // autoload_classmap.php @generated by Composer
31493 \$vendorDir = $vendorPathCode52;
31494 \$baseDir = $appBaseDirCode;
31501 $targetDirLoader = null;
31502 $mainAutoload = $mainPackage->getAutoload();
31503 if ($mainPackage->getTargetDir() && !empty($mainAutoload['psr-0'])) {
31504 $levels = count(explode('/', $filesystem->normalizePath($mainPackage->getTargetDir())));
31505 $prefixes = implode(', ', array_map(function ($prefix) {
31506 return var_export($prefix, true);
31507 }, array_keys($mainAutoload['psr-0'])));
31508 $baseDirFromTargetDirCode = $filesystem->findShortestPathCode($targetDir, $basePath, true);
31510 $targetDirLoader = <<<EOF
31512 public static function autoload(\$class)
31514 \$dir = $baseDirFromTargetDirCode . '/';
31515 \$prefixes = array($prefixes);
31516 foreach (\$prefixes as \$prefix) {
31517 if (0 !== strpos(\$class, \$prefix)) {
31520 \$path = \$dir . implode('/', array_slice(explode('\\\\', \$class), $levels)).'.php';
31521 if (!\$path = stream_resolve_include_path(\$path)) {
31534 $classMap = array();
31535 if ($scanPsr0Packages) {
31537 foreach (array('psr-0', 'psr-4') as $psrType) {
31538 foreach ($autoloads[$psrType] as $namespace => $paths) {
31539 foreach ($paths as $dir) {
31540 $dir = $filesystem->normalizePath($filesystem->isAbsolutePath($dir) ? $dir : $basePath.'/'.$dir);
31541 if (!is_dir($dir)) {
31544 $whitelist = sprintf(
31545 '{%s/%s.+(?<!(?<!/)Test\.php)$}',
31547 ($psrType === 'psr-0' && strpos($namespace, '_') === false) ? preg_quote(strtr($namespace, '\\', '/')) : ''
31550 $namespaceFilter = $namespace === '' ? null : $namespace;
31551 foreach (ClassMapGenerator::createMap($dir, $whitelist, $this->io, $namespaceFilter) as $class => $path) {
31552 if (!isset($classMap[$class])) {
31553 $path = $this->getPathCode($filesystem, $basePath, $vendorPath, $path);
31554 $classMap[$class] = $path.",\n";
31562 foreach ($autoloads['classmap'] as $dir) {
31563 foreach (ClassMapGenerator::createMap($dir, null, $this->io) as $class => $path) {
31564 $path = $this->getPathCode($filesystem, $basePath, $vendorPath, $path);
31565 $classMap[$class] = $path.",\n";
31570 foreach ($classMap as $class => $code) {
31571 $classmapFile .= ' '.var_export($class, true).' => '.$code;
31573 $classmapFile .= ");\n";
31576 $suffix = $config->get('autoloader-suffix') ?: md5(uniqid('', true));
31579 file_put_contents($targetDir.'/autoload_namespaces.php', $namespacesFile);
31580 file_put_contents($targetDir.'/autoload_psr4.php', $psr4File);
31581 file_put_contents($targetDir.'/autoload_classmap.php', $classmapFile);
31582 if ($includePathFile = $this->getIncludePathsFile($packageMap, $filesystem, $basePath, $vendorPath, $vendorPathCode52, $appBaseDirCode)) {
31583 file_put_contents($targetDir.'/include_paths.php', $includePathFile);
31585 if ($includeFilesFile = $this->getIncludeFilesFile($autoloads['files'], $filesystem, $basePath, $vendorPath, $vendorPathCode52, $appBaseDirCode)) {
31586 file_put_contents($targetDir.'/autoload_files.php', $includeFilesFile);
31588 file_put_contents($vendorPath.'/autoload.php', $this->getAutoloadFile($vendorPathToTargetDirCode, $suffix));
31589 file_put_contents($targetDir.'/autoload_real.php', $this->getAutoloadRealFile(true, (bool) $includePathFile, $targetDirLoader, (bool) $includeFilesFile, $vendorPathCode, $appBaseDirCode, $suffix, $useGlobalIncludePath, $prependAutoloader));
31593 $sourceLoader = fopen(__DIR__.'/ClassLoader.php', 'r');
31594 $targetLoader = fopen($targetDir.'/ClassLoader.php', 'w+');
31595 stream_copy_to_stream($sourceLoader, $targetLoader);
31596 fclose($sourceLoader);
31597 fclose($targetLoader);
31598 unset($sourceLoader, $targetLoader);
31600 $this->eventDispatcher->dispatchScript(ScriptEvents::POST_AUTOLOAD_DUMP, $this->devMode, array(), array(
31601 'optimize' => (bool) $scanPsr0Packages,
31605 public function buildPackageMap(InstallationManager $installationManager, PackageInterface $mainPackage, array $packages)
31608 $packageMap = array(array($mainPackage, ''));
31610 foreach ($packages as $package) {
31611 if ($package instanceof AliasPackage) {
31614 $this->validatePackage($package);
31616 $packageMap[] = array(
31618 $installationManager->getInstallPath($package),
31622 return $packageMap;
31630 protected function validatePackage(PackageInterface $package)
31632 $autoload = $package->getAutoload();
31633 if (!empty($autoload['psr-4']) && null !== $package->getTargetDir()) {
31634 $name = $package->getName();
31635 $package->getTargetDir();
31636 throw new \InvalidArgumentException("PSR-4 autoloading is incompatible with the target-dir property, remove the target-dir in package '$name'.");
31638 if (!empty($autoload['psr-4'])) {
31639 foreach ($autoload['psr-4'] as $namespace => $dirs) {
31640 if ($namespace !== '' && '\\' !== substr($namespace, -1)) {
31641 throw new \InvalidArgumentException("psr-4 namespaces must end with a namespace separator, '$namespace' does not, use '$namespace\\'.");
31654 public function parseAutoloads(array $packageMap, PackageInterface $mainPackage)
31656 $mainPackageMap = array_shift($packageMap);
31657 $sortedPackageMap = $this->sortPackageMap($packageMap);
31658 $sortedPackageMap[] = $mainPackageMap;
31659 array_unshift($packageMap, $mainPackageMap);
31661 $psr0 = $this->parseAutoloadsType($packageMap, 'psr-0', $mainPackage);
31662 $psr4 = $this->parseAutoloadsType($packageMap, 'psr-4', $mainPackage);
31663 $classmap = $this->parseAutoloadsType($sortedPackageMap, 'classmap', $mainPackage);
31664 $files = $this->parseAutoloadsType($sortedPackageMap, 'files', $mainPackage);
31669 return array('psr-0' => $psr0, 'psr-4' => $psr4, 'classmap' => $classmap, 'files' => $files);
31678 public function createLoader(array $autoloads)
31680 $loader = new ClassLoader();
31682 if (isset($autoloads['psr-0'])) {
31683 foreach ($autoloads['psr-0'] as $namespace => $path) {
31684 $loader->add($namespace, $path);
31688 if (isset($autoloads['psr-4'])) {
31689 foreach ($autoloads['psr-4'] as $namespace => $path) {
31690 $loader->addPsr4($namespace, $path);
31697 protected function getIncludePathsFile(array $packageMap, Filesystem $filesystem, $basePath, $vendorPath, $vendorPathCode, $appBaseDirCode)
31699 $includePaths = array();
31701 foreach ($packageMap as $item) {
31702 list($package, $installPath) = $item;
31704 if (null !== $package->getTargetDir() && strlen($package->getTargetDir()) > 0) {
31705 $installPath = substr($installPath, 0, -strlen('/'.$package->getTargetDir()));
31708 foreach ($package->getIncludePaths() as $includePath) {
31709 $includePath = trim($includePath, '/');
31710 $includePaths[] = empty($installPath) ? $includePath : $installPath.'/'.$includePath;
31714 if (!$includePaths) {
31718 $includePathsCode = '';
31719 foreach ($includePaths as $path) {
31720 $includePathsCode .= " " . $this->getPathCode($filesystem, $basePath, $vendorPath, $path) . ",\n";
31726 // include_paths.php @generated by Composer
31728 \$vendorDir = $vendorPathCode;
31729 \$baseDir = $appBaseDirCode;
31732 $includePathsCode);
31737 protected function getIncludeFilesFile(array $files, Filesystem $filesystem, $basePath, $vendorPath, $vendorPathCode, $appBaseDirCode)
31740 foreach ($files as $functionFile) {
31741 $filesCode .= ' '.$this->getPathCode($filesystem, $basePath, $vendorPath, $functionFile).",\n";
31751 // autoload_files.php @generated by Composer
31753 \$vendorDir = $vendorPathCode;
31754 \$baseDir = $appBaseDirCode;
31762 protected function getPathCode(Filesystem $filesystem, $basePath, $vendorPath, $path)
31764 if (!$filesystem->isAbsolutePath($path)) {
31765 $path = $basePath . '/' . $path;
31767 $path = $filesystem->normalizePath($path);
31770 if (strpos($path.'/', $vendorPath.'/') === 0) {
31771 $path = substr($path, strlen($vendorPath));
31772 $baseDir = '$vendorDir';
31774 if ($path !== false) {
31778 $path = $filesystem->normalizePath($filesystem->findShortestPath($basePath, $path, true));
31779 if (!$filesystem->isAbsolutePath($path)) {
31780 $baseDir = '$baseDir . ';
31781 $path = '/' . $path;
31785 if (preg_match('/\.phar$/', $path)) {
31786 $baseDir = "'phar://' . " . $baseDir;
31789 return $baseDir . (($path !== false) ? var_export($path, true) : "");
31792 protected function getAutoloadFile($vendorPathToTargetDirCode, $suffix)
31797 // autoload.php @generated by Composer
31799 require_once $vendorPathToTargetDirCode . '/autoload_real.php';
31801 return ComposerAutoloaderInit$suffix::getLoader();
31806 protected function getAutoloadRealFile($useClassMap, $useIncludePath, $targetDirLoader, $useIncludeFiles, $vendorPathCode, $appBaseDirCode, $suffix, $useGlobalIncludePath, $prependAutoloader)
31818 // autoload_real.php @generated by Composer
31820 class ComposerAutoloaderInit$suffix
31822 private static \$loader;
31824 public static function loadClassLoader(\$class)
31826 if ('Composer\\Autoload\\ClassLoader' === \$class) {
31827 require __DIR__ . '/ClassLoader.php';
31831 public static function getLoader()
31833 if (null !== self::\$loader) {
31834 return self::\$loader;
31837 spl_autoload_register(array('ComposerAutoloaderInit$suffix', 'loadClassLoader'), true, $prependAutoloader);
31838 self::\$loader = \$loader = new \\Composer\\Autoload\\ClassLoader();
31839 spl_autoload_unregister(array('ComposerAutoloaderInit$suffix', 'loadClassLoader'));
31844 if ($useIncludePath) {
31845 $file .= <<<'INCLUDE_PATH'
31846 $includePaths = require __DIR__ . '/include_paths.php';
31847 array_push($includePaths, get_include_path());
31848 set_include_path(join(PATH_SEPARATOR, $includePaths));
31855 $map = require __DIR__ . '/autoload_namespaces.php';
31856 foreach ($map as $namespace => $path) {
31857 $loader->set($namespace, $path);
31864 $map = require __DIR__ . '/autoload_psr4.php';
31865 foreach ($map as $namespace => $path) {
31866 $loader->setPsr4($namespace, $path);
31872 if ($useClassMap) {
31873 $file .= <<<'CLASSMAP'
31874 $classMap = require __DIR__ . '/autoload_classmap.php';
31876 $loader->addClassMap($classMap);
31883 if ($useGlobalIncludePath) {
31884 $file .= <<<'INCLUDEPATH'
31885 $loader->setUseIncludePath(true);
31890 if ($targetDirLoader) {
31891 $file .= <<<REGISTER_AUTOLOAD
31892 spl_autoload_register(array('ComposerAutoloaderInit$suffix', 'autoload'), true, true);
31898 $file .= <<<REGISTER_LOADER
31899 \$loader->register($prependAutoloader);
31904 if ($useIncludeFiles) {
31905 $file .= <<<INCLUDE_FILES
31906 \$includeFiles = require __DIR__ . '/autoload_files.php';
31907 foreach (\$includeFiles as \$file) {
31908 composerRequire$suffix(\$file);
31915 $file .= <<<METHOD_FOOTER
31921 $file .= $targetDirLoader;
31923 return $file . <<<FOOTER
31926 function composerRequire$suffix(\$file)
31934 protected function parseAutoloadsType(array $packageMap, $type, PackageInterface $mainPackage)
31936 $autoloads = array();
31938 foreach ($packageMap as $item) {
31939 list($package, $installPath) = $item;
31941 $autoload = $package->getAutoload();
31942 if ($this->devMode && $package === $mainPackage) {
31943 $autoload = array_merge_recursive($autoload, $package->getDevAutoload());
31947 if (!isset($autoload[$type]) || !is_array($autoload[$type])) {
31950 if (null !== $package->getTargetDir() && $package !== $mainPackage) {
31951 $installPath = substr($installPath, 0, -strlen('/'.$package->getTargetDir()));
31954 foreach ($autoload[$type] as $namespace => $paths) {
31955 foreach ((array) $paths as $path) {
31956 if (($type === 'files' || $type === 'classmap') && $package->getTargetDir() && !is_readable($installPath.'/'.$path)) {
31958 if ($package === $mainPackage) {
31959 $targetDir = str_replace('\\<dirsep\\>', '[\\\\/]', preg_quote(str_replace(array('/', '\\'), '<dirsep>', $package->getTargetDir())));
31960 $path = ltrim(preg_replace('{^'.$targetDir.'}', '', ltrim($path, '\\/')), '\\/');
31963 $path = $package->getTargetDir() . '/' . $path;
31967 $relativePath = empty($installPath) ? (empty($path) ? '.' : $path) : $installPath.'/'.$path;
31969 if ($type === 'files' || $type === 'classmap') {
31970 $autoloads[] = $relativePath;
31974 $autoloads[$namespace][] = $relativePath;
31990 protected function sortPackageMap(array $packageMap)
31992 $packages = array();
31994 $usageList = array();
31996 foreach ($packageMap as $item) {
31997 list($package, $path) = $item;
31998 $name = $package->getName();
31999 $packages[$name] = $package;
32000 $paths[$name] = $path;
32002 foreach (array_merge($package->getRequires(), $package->getDevRequires()) as $link) {
32003 $target = $link->getTarget();
32004 $usageList[$target][] = $name;
32008 $computing = array();
32009 $computed = array();
32010 $computeImportance = function ($name) use (&$computeImportance, &$computing, &$computed, $usageList) {
32012 if (isset($computed[$name])) {
32013 return $computed[$name];
32017 if (isset($computing[$name])) {
32021 $computing[$name] = true;
32024 if (isset($usageList[$name])) {
32025 foreach ($usageList[$name] as $user) {
32026 $weight -= 1 - $computeImportance($user);
32030 unset($computing[$name]);
32031 $computed[$name] = $weight;
32036 $weightList = array();
32038 foreach ($packages as $name => $package) {
32039 $weight = $computeImportance($name);
32040 $weightList[$name] = $weight;
32043 $stable_sort = function (&$array) {
32044 static $transform, $restore;
32049 $transform = function (&$v, $k) use (&$i) {
32050 $v = array($v, ++$i, $k, $v);
32053 $restore = function (&$v, $k) {
32058 array_walk($array, $transform);
32060 array_walk($array, $restore);
32063 $stable_sort($weightList);
32065 $sortedPackageMap = array();
32067 foreach (array_keys($weightList) as $name) {
32068 $sortedPackageMap[] = array($packages[$name], $paths[$name]);
32071 return $sortedPackageMap;
32087 namespace Composer\Autoload;
32089 use Symfony\Component\Finder\Finder;
32090 use Composer\IO\IOInterface;
32098 class ClassMapGenerator
32106 public static function dump($dirs, $file)
32110 foreach ($dirs as $dir) {
32111 $maps = array_merge($maps, static::createMap($dir));
32114 file_put_contents($file, sprintf('<?php return %s;', var_export($maps, true)));
32129 public static function createMap($path, $whitelist = null, IOInterface $io = null, $namespace = null)
32131 if (is_string($path)) {
32132 if (is_file($path)) {
32133 $path = array(new \SplFileInfo($path));
32134 } elseif (is_dir($path)) {
32135 $path = Finder::create()->files()->followLinks()->name('/\.(php|inc|hh)$/')->in($path);
32137 throw new \RuntimeException(
32138 'Could not scan for classes inside "'.$path.
32139 '" which does not appear to be a file nor a folder'
32146 foreach ($path as $file) {
32147 $filePath = $file->getRealPath();
32149 if (!in_array(pathinfo($filePath, PATHINFO_EXTENSION), array('php', 'inc', 'hh'))) {
32153 if ($whitelist && !preg_match($whitelist, strtr($filePath, '\\', '/'))) {
32157 $classes = self::findClasses($filePath);
32159 foreach ($classes as $class) {
32161 if (null !== $namespace && 0 !== strpos($class, $namespace)) {
32165 if (!isset($map[$class])) {
32166 $map[$class] = $filePath;
32167 } elseif ($io && $map[$class] !== $filePath && !preg_match('{/(test|fixture|example)s?/}i', strtr($map[$class].' '.$filePath, '\\', '/'))) {
32169 '<warning>Warning: Ambiguous class resolution, "'.$class.'"'.
32170 ' was found in both "'.$map[$class].'" and "'.$filePath.'", the first will be used.</warning>'
32186 private static function findClasses($path)
32188 $traits = version_compare(PHP_VERSION, '5.4', '<') ? '' : '|trait';
32191 $contents = @php_strip_whitespace($path);
32193 if (!file_exists($path)) {
32194 throw new \Exception('File does not exist');
32196 if (!is_readable($path)) {
32197 throw new \Exception('File is not readable');
32200 } catch (\Exception $e) {
32201 throw new \RuntimeException('Could not scan for classes inside '.$path.": \n".$e->getMessage(), 0, $e);
32205 if (!preg_match('{\b(?:class|interface'.$traits.')\s}i', $contents)) {
32210 $contents = preg_replace('{<<<\s*(\'?)(\w+)\\1(?:\r\n|\n|\r)(?:.*?)(?:\r\n|\n|\r)\\2(?=\r\n|\n|\r|;)}s', 'null', $contents);
32212 $contents = preg_replace('{"[^"\\\\]*(\\\\.[^"\\\\]*)*"|\'[^\'\\\\]*(\\\\.[^\'\\\\]*)*\'}s', 'null', $contents);
32214 if (substr($contents, 0, 2) !== '<?') {
32215 $contents = preg_replace('{^.+?<\?}s', '<?', $contents, 1, $replacements);
32216 if ($replacements === 0) {
32221 $contents = preg_replace('{\?>.+<\?}s', '?><?', $contents);
32223 $pos = strrpos($contents, '?>');
32224 if (false !== $pos && false === strpos(substr($contents, $pos), '<?')) {
32225 $contents = substr($contents, 0, $pos);
32230 \b(?<![\$:>])(?P<type>class|interface'.$traits.') \s+ (?P<name>[a-zA-Z_\x7f-\xff:][a-zA-Z0-9_\x7f-\xff:\-]*)
32231 | \b(?<![\$:>])(?P<ns>namespace) (?P<nsname>\s+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*(?:\s*\\\\\s*[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)*)? \s*[\{;]
32233 }ix', $contents, $matches);
32235 $classes = array();
32238 for ($i = 0, $len = count($matches['type']); $i < $len; $i++) {
32239 if (!empty($matches['ns'][$i])) {
32240 $namespace = str_replace(array(' ', "\t", "\r", "\n"), '', $matches['nsname'][$i]) . '\\';
32242 $name = $matches['name'][$i];
32243 if ($name[0] === ':') {
32245 $name = 'xhp'.substr(str_replace(array('-', ':'), array('_', '__'), $name), 1);
32247 $classes[] = ltrim($namespace . $name, '\\');
32266 namespace Composer;
32268 use Composer\Autoload\AutoloadGenerator;
32269 use Composer\DependencyResolver\DefaultPolicy;
32270 use Composer\DependencyResolver\Operation\UpdateOperation;
32271 use Composer\DependencyResolver\Operation\InstallOperation;
32272 use Composer\DependencyResolver\Operation\UninstallOperation;
32273 use Composer\DependencyResolver\Operation\OperationInterface;
32274 use Composer\DependencyResolver\Pool;
32275 use Composer\DependencyResolver\Request;
32276 use Composer\DependencyResolver\Rule;
32277 use Composer\DependencyResolver\Solver;
32278 use Composer\DependencyResolver\SolverProblemsException;
32279 use Composer\Downloader\DownloadManager;
32280 use Composer\EventDispatcher\EventDispatcher;
32281 use Composer\Installer\InstallationManager;
32282 use Composer\Installer\InstallerEvents;
32283 use Composer\Installer\NoopInstaller;
32284 use Composer\IO\IOInterface;
32285 use Composer\Json\JsonFile;
32286 use Composer\Package\AliasPackage;
32287 use Composer\Package\CompletePackage;
32288 use Composer\Package\Link;
32289 use Composer\Package\LinkConstraint\VersionConstraint;
32290 use Composer\Package\Locker;
32291 use Composer\Package\PackageInterface;
32292 use Composer\Package\RootPackageInterface;
32293 use Composer\Repository\CompositeRepository;
32294 use Composer\Repository\InstalledArrayRepository;
32295 use Composer\Repository\InstalledFilesystemRepository;
32296 use Composer\Repository\PlatformRepository;
32297 use Composer\Repository\RepositoryInterface;
32298 use Composer\Repository\RepositoryManager;
32299 use Composer\Script\ScriptEvents;
32322 protected $package;
32327 protected $downloadManager;
32332 protected $repositoryManager;
32342 protected $installationManager;
32347 protected $eventDispatcher;
32352 protected $autoloadGenerator;
32354 protected $preferSource = false;
32355 protected $preferDist = false;
32356 protected $optimizeAutoloader = false;
32357 protected $devMode = false;
32358 protected $dryRun = false;
32359 protected $verbose = false;
32360 protected $update = false;
32361 protected $dumpAutoloader = true;
32362 protected $runScripts = true;
32363 protected $ignorePlatformReqs = false;
32364 protected $preferStable = false;
32365 protected $preferLowest = false;
32371 protected $updateWhitelist = null;
32372 protected $whitelistDependencies = false;
32377 protected $suggestedPackages;
32382 protected $additionalInstalledRepository;
32397 public function __construct(IOInterface $io, Config $config, RootPackageInterface $package, DownloadManager $downloadManager, RepositoryManager $repositoryManager, Locker $locker, InstallationManager $installationManager, EventDispatcher $eventDispatcher, AutoloadGenerator $autoloadGenerator)
32400 $this->config = $config;
32401 $this->package = $package;
32402 $this->downloadManager = $downloadManager;
32403 $this->repositoryManager = $repositoryManager;
32404 $this->locker = $locker;
32405 $this->installationManager = $installationManager;
32406 $this->eventDispatcher = $eventDispatcher;
32407 $this->autoloadGenerator = $autoloadGenerator;
32417 public function run()
32419 gc_collect_cycles();
32422 if ($this->dryRun) {
32423 $this->verbose = true;
32424 $this->runScripts = false;
32425 $this->installationManager->addInstaller(new NoopInstaller);
32426 $this->mockLocalRepositories($this->repositoryManager);
32431 $devRepo = new InstalledFilesystemRepository(new JsonFile($this->config->get('vendor-dir').'/composer/installed_dev.json'));
32432 if ($devRepo->getPackages()) {
32433 $this->io->write('<warning>BC Notice: Removing old dev packages to migrate to the new require-dev handling.</warning>');
32434 foreach ($devRepo->getPackages() as $package) {
32435 if ($this->installationManager->isPackageInstalled($devRepo, $package)) {
32436 $this->installationManager->uninstall($devRepo, new UninstallOperation($package));
32439 unlink($this->config->get('vendor-dir').'/composer/installed_dev.json');
32441 unset($devRepo, $package);
32444 if ($this->runScripts) {
32446 $eventName = $this->update ? ScriptEvents::PRE_UPDATE_CMD : ScriptEvents::PRE_INSTALL_CMD;
32447 $this->eventDispatcher->dispatchCommandEvent($eventName, $this->devMode);
32450 $this->downloadManager->setPreferSource($this->preferSource);
32451 $this->downloadManager->setPreferDist($this->preferDist);
32456 $installedRootPackage = clone $this->package;
32457 $installedRootPackage->setRequires(array());
32458 $installedRootPackage->setDevRequires(array());
32461 $localRepo = $this->repositoryManager->getLocalRepository();
32462 $platformRepo = new PlatformRepository();
32465 new InstalledArrayRepository(array($installedRootPackage)),
32468 $installedRepo = new CompositeRepository($repos);
32469 if ($this->additionalInstalledRepository) {
32470 $installedRepo->addRepository($this->additionalInstalledRepository);
32473 $aliases = $this->getRootAliases();
32474 $this->aliasPlatformPackages($platformRepo, $aliases);
32477 $this->suggestedPackages = array();
32478 $res = $this->doInstall($localRepo, $installedRepo, $platformRepo, $aliases, $this->devMode);
32482 } catch (\Exception $e) {
32483 $this->installationManager->notifyInstalls();
32487 $this->installationManager->notifyInstalls();
32490 if ($this->devMode) {
32491 foreach ($this->suggestedPackages as $suggestion) {
32492 $target = $suggestion['target'];
32493 foreach ($installedRepo->getPackages() as $package) {
32494 if (in_array($target, $package->getNames())) {
32499 $this->io->write($suggestion['source'].' suggests installing '.$suggestion['target'].' ('.$suggestion['reason'].')');
32504 foreach ($localRepo->getPackages() as $package) {
32505 if (!$package instanceof CompletePackage || !$package->isAbandoned()) {
32509 $replacement = (is_string($package->getReplacementPackage()))
32510 ? 'Use ' . $package->getReplacementPackage() . ' instead'
32511 : 'No replacement was suggested';
32515 "<error>Package %s is abandoned, you should avoid using it. %s.</error>",
32516 $package->getPrettyName(),
32522 if (!$this->dryRun) {
32524 if ($this->update || !$this->locker->isLocked()) {
32525 $localRepo->reload();
32529 $devPackages = ($this->devMode || !$this->package->getDevRequires()) ? array() : null;
32532 if ($this->devMode && $this->package->getDevRequires()) {
32533 $policy = $this->createPolicy();
32534 $pool = $this->createPool(true);
32535 $pool->addRepository($installedRepo, $aliases);
32538 $request = $this->createRequest($pool, $this->package, $platformRepo);
32539 $request->updateAll();
32540 foreach ($this->package->getRequires() as $link) {
32541 $request->install($link->getTarget(), $link->getConstraint());
32544 $this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::PRE_DEPENDENCIES_SOLVING, $policy, $pool, $installedRepo, $request);
32545 $solver = new Solver($policy, $pool, $installedRepo);
32546 $ops = $solver->solve($request, $this->ignorePlatformReqs);
32547 $this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::POST_DEPENDENCIES_SOLVING, $policy, $pool, $installedRepo, $request, $ops);
32548 foreach ($ops as $op) {
32549 if ($op->getJobType() === 'uninstall') {
32550 $devPackages[] = $op->getPackage();
32555 $platformReqs = $this->extractPlatformRequirements($this->package->getRequires());
32556 $platformDevReqs = $this->devMode ? $this->extractPlatformRequirements($this->package->getDevRequires()) : array();
32558 $updatedLock = $this->locker->setLockData(
32559 array_diff($localRepo->getCanonicalPackages(), (array) $devPackages),
32564 $this->package->getMinimumStability(),
32565 $this->package->getStabilityFlags(),
32566 $this->preferStable || $this->package->getPreferStable(),
32567 $this->preferLowest
32569 if ($updatedLock) {
32570 $this->io->write('<info>Writing lock file</info>');
32574 if ($this->dumpAutoloader) {
32576 if ($this->optimizeAutoloader) {
32577 $this->io->write('<info>Generating optimized autoload files</info>');
32579 $this->io->write('<info>Generating autoload files</info>');
32582 $this->autoloadGenerator->setDevMode($this->devMode);
32583 $this->autoloadGenerator->dump($this->config, $localRepo, $this->package, $this->installationManager, 'composer', $this->optimizeAutoloader);
32586 if ($this->runScripts) {
32588 $eventName = $this->update ? ScriptEvents::POST_UPDATE_CMD : ScriptEvents::POST_INSTALL_CMD;
32589 $this->eventDispatcher->dispatchCommandEvent($eventName, $this->devMode);
32592 $vendorDir = $this->config->get('vendor-dir');
32593 if (is_dir($vendorDir)) {
32601 protected function doInstall($localRepo, $installedRepo, $platformRepo, $aliases, $withDevReqs)
32604 $lockedRepository = null;
32605 $repositories = null;
32608 $installFromLock = false;
32609 if (!$this->update && $this->locker->isLocked()) {
32610 $installFromLock = true;
32612 $lockedRepository = $this->locker->getLockedRepository($withDevReqs);
32613 } catch (\RuntimeException $e) {
32615 if ($this->package->getDevRequires()) {
32619 $lockedRepository = $this->locker->getLockedRepository();
32623 $this->whitelistUpdateDependencies(
32626 $this->package->getRequires(),
32627 $this->package->getDevRequires()
32630 $this->io->write('<info>Loading composer repositories with package information</info>');
32633 $policy = $this->createPolicy();
32634 $pool = $this->createPool($withDevReqs);
32635 $pool->addRepository($installedRepo, $aliases);
32636 if ($installFromLock) {
32637 $pool->addRepository($lockedRepository, $aliases);
32640 if (!$installFromLock) {
32641 $repositories = $this->repositoryManager->getRepositories();
32642 foreach ($repositories as $repository) {
32643 $pool->addRepository($repository, $aliases);
32648 $request = $this->createRequest($pool, $this->package, $platformRepo);
32650 if (!$installFromLock) {
32652 $removedUnstablePackages = array();
32653 foreach ($localRepo->getPackages() as $package) {
32655 !$pool->isPackageAcceptable($package->getNames(), $package->getStability())
32656 && $this->installationManager->isPackageInstalled($localRepo, $package)
32658 $removedUnstablePackages[$package->getName()] = true;
32659 $request->remove($package->getName(), new VersionConstraint('=', $package->getVersion()));
32664 if ($this->update) {
32665 $this->io->write('<info>Updating dependencies'.($withDevReqs ? ' (including require-dev)' : '').'</info>');
32667 $request->updateAll();
32669 if ($withDevReqs) {
32670 $links = array_merge($this->package->getRequires(), $this->package->getDevRequires());
32672 $links = $this->package->getRequires();
32675 foreach ($links as $link) {
32676 $request->install($link->getTarget(), $link->getConstraint());
32681 if ($this->updateWhitelist) {
32682 if ($this->locker->isLocked()) {
32684 $currentPackages = $this->locker->getLockedRepository($withDevReqs)->getPackages();
32685 } catch (\RuntimeException $e) {
32687 $currentPackages = $this->locker->getLockedRepository()->getPackages();
32690 $currentPackages = $installedRepo->getPackages();
32694 $candidates = array();
32695 foreach ($links as $link) {
32696 $candidates[$link->getTarget()] = true;
32698 foreach ($localRepo->getPackages() as $package) {
32699 $candidates[$package->getName()] = true;
32703 foreach ($candidates as $candidate => $dummy) {
32704 foreach ($currentPackages as $curPackage) {
32705 if ($curPackage->getName() === $candidate) {
32706 if (!$this->isUpdateable($curPackage) && !isset($removedUnstablePackages[$curPackage->getName()])) {
32707 $constraint = new VersionConstraint('=', $curPackage->getVersion());
32708 $request->install($curPackage->getName(), $constraint);
32715 } elseif ($installFromLock) {
32716 $this->io->write('<info>Installing dependencies'.($withDevReqs ? ' (including require-dev)' : '').' from lock file</info>');
32718 if (!$this->locker->isFresh()) {
32719 $this->io->write('<warning>Warning: The lock file is not up to date with the latest changes in composer.json. You may be getting outdated dependencies. Run update to update them.</warning>');
32722 foreach ($lockedRepository->getPackages() as $package) {
32723 $version = $package->getVersion();
32724 if (isset($aliases[$package->getName()][$version])) {
32725 $version = $aliases[$package->getName()][$version]['alias_normalized'];
32727 $constraint = new VersionConstraint('=', $version);
32728 $constraint->setPrettyString($package->getPrettyVersion());
32729 $request->install($package->getName(), $constraint);
32732 foreach ($this->locker->getPlatformRequirements($withDevReqs) as $link) {
32733 $request->install($link->getTarget(), $link->getConstraint());
32736 $this->io->write('<info>Installing dependencies'.($withDevReqs ? ' (including require-dev)' : '').'</info>');
32738 if ($withDevReqs) {
32739 $links = array_merge($this->package->getRequires(), $this->package->getDevRequires());
32741 $links = $this->package->getRequires();
32744 foreach ($links as $link) {
32745 $request->install($link->getTarget(), $link->getConstraint());
32750 $this->processDevPackages($localRepo, $pool, $policy, $repositories, $lockedRepository, $installFromLock, 'force-links');
32753 $this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::PRE_DEPENDENCIES_SOLVING, $policy, $pool, $installedRepo, $request);
32754 $solver = new Solver($policy, $pool, $installedRepo);
32756 $operations = $solver->solve($request, $this->ignorePlatformReqs);
32757 $this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::POST_DEPENDENCIES_SOLVING, $policy, $pool, $installedRepo, $request, $operations);
32758 } catch (SolverProblemsException $e) {
32759 $this->io->write('<error>Your requirements could not be resolved to an installable set of packages.</error>');
32760 $this->io->write($e->getMessage());
32762 return max(1, $e->getCode());
32766 $operations = $this->processDevPackages($localRepo, $pool, $policy, $repositories, $lockedRepository, $installFromLock, 'force-updates', $operations);
32769 if (!$operations) {
32770 $this->io->write('Nothing to install or update');
32773 $operations = $this->movePluginsToFront($operations);
32774 $operations = $this->moveUninstallsToFront($operations);
32776 foreach ($operations as $operation) {
32778 if ('install' === $operation->getJobType()) {
32779 foreach ($operation->getPackage()->getSuggests() as $target => $reason) {
32780 $this->suggestedPackages[] = array(
32781 'source' => $operation->getPackage()->getPrettyName(),
32782 'target' => $target,
32783 'reason' => $reason,
32789 if (!$installFromLock) {
32791 if ('update' === $operation->getJobType()) {
32792 $package = $operation->getTargetPackage();
32793 } elseif ('install' === $operation->getJobType()) {
32794 $package = $operation->getPackage();
32796 if ($package && $package->isDev()) {
32797 $references = $this->package->getReferences();
32798 if (isset($references[$package->getName()])) {
32799 $package->setSourceReference($references[$package->getName()]);
32800 $package->setDistReference($references[$package->getName()]);
32803 if ('update' === $operation->getJobType()
32804 && $operation->getTargetPackage()->isDev()
32805 && $operation->getTargetPackage()->getVersion() === $operation->getInitialPackage()->getVersion()
32806 && $operation->getTargetPackage()->getSourceReference() === $operation->getInitialPackage()->getSourceReference()
32808 if ($this->io->isDebug()) {
32809 $this->io->write(' - Skipping update of '. $operation->getTargetPackage()->getPrettyName().' to the same reference-locked version');
32810 $this->io->write('');
32817 $event = 'Composer\Script\ScriptEvents::PRE_PACKAGE_'.strtoupper($operation->getJobType());
32818 if (defined($event) && $this->runScripts) {
32819 $this->eventDispatcher->dispatchPackageEvent(constant($event), $this->devMode, $operation);
32823 if ($this->dryRun && false === strpos($operation->getJobType(), 'Alias')) {
32824 $this->io->write(' - ' . $operation);
32825 $this->io->write('');
32826 } elseif ($this->io->isDebug() && false !== strpos($operation->getJobType(), 'Alias')) {
32827 $this->io->write(' - ' . $operation);
32828 $this->io->write('');
32831 $this->installationManager->execute($localRepo, $operation);
32834 if ($this->verbose && $this->io->isVeryVerbose() && in_array($operation->getJobType(), array('install', 'update'))) {
32835 $reason = $operation->getReason();
32836 if ($reason instanceof Rule) {
32837 switch ($reason->getReason()) {
32838 case Rule::RULE_JOB_INSTALL:
32839 $this->io->write(' REASON: Required by root: '.$reason->getPrettyString($pool));
32840 $this->io->write('');
32842 case Rule::RULE_PACKAGE_REQUIRES:
32843 $this->io->write(' REASON: '.$reason->getPrettyString($pool));
32844 $this->io->write('');
32850 $event = 'Composer\Script\ScriptEvents::POST_PACKAGE_'.strtoupper($operation->getJobType());
32851 if (defined($event) && $this->runScripts) {
32852 $this->eventDispatcher->dispatchPackageEvent(constant($event), $this->devMode, $operation);
32855 if (!$this->dryRun) {
32856 $localRepo->write();
32876 private function movePluginsToFront(array $operations)
32878 $installerOps = array();
32879 foreach ($operations as $idx => $op) {
32880 if ($op instanceof InstallOperation) {
32881 $package = $op->getPackage();
32882 } elseif ($op instanceof UpdateOperation) {
32883 $package = $op->getTargetPackage();
32888 if ($package->getType() === 'composer-plugin' || $package->getType() === 'composer-installer') {
32890 $requires = array_keys($package->getRequires());
32891 foreach ($requires as $index => $req) {
32892 if ($req === 'composer-plugin-api' || preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $req)) {
32893 unset($requires[$index]);
32897 if (!count($requires)) {
32898 $installerOps[] = $op;
32899 unset($operations[$idx]);
32904 return array_merge($installerOps, $operations);
32914 private function moveUninstallsToFront(array $operations)
32916 $uninstOps = array();
32917 foreach ($operations as $idx => $op) {
32918 if ($op instanceof UninstallOperation) {
32919 $uninstOps[] = $op;
32920 unset($operations[$idx]);
32924 return array_merge($uninstOps, $operations);
32927 private function createPool($withDevReqs)
32929 $minimumStability = $this->package->getMinimumStability();
32930 $stabilityFlags = $this->package->getStabilityFlags();
32932 if (!$this->update && $this->locker->isLocked()) {
32933 $minimumStability = $this->locker->getMinimumStability();
32934 $stabilityFlags = $this->locker->getStabilityFlags();
32937 $requires = $this->package->getRequires();
32938 if ($withDevReqs) {
32939 $requires = array_merge($requires, $this->package->getDevRequires());
32941 $rootConstraints = array();
32942 foreach ($requires as $req => $constraint) {
32944 if ($this->ignorePlatformReqs && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $req)) {
32947 $rootConstraints[$req] = $constraint->getConstraint();
32950 return new Pool($minimumStability, $stabilityFlags, $rootConstraints);
32953 private function createPolicy()
32955 $preferStable = null;
32956 $preferLowest = null;
32957 if (!$this->update && $this->locker->isLocked()) {
32958 $preferStable = $this->locker->getPreferStable();
32959 $preferLowest = $this->locker->getPreferLowest();
32963 if (null === $preferStable) {
32964 $preferStable = $this->preferStable || $this->package->getPreferStable();
32966 if (null === $preferLowest) {
32967 $preferLowest = $this->preferLowest;
32970 return new DefaultPolicy($preferStable, $preferLowest);
32973 private function createRequest(Pool $pool, RootPackageInterface $rootPackage, PlatformRepository $platformRepo)
32975 $request = new Request($pool);
32977 $constraint = new VersionConstraint('=', $rootPackage->getVersion());
32978 $constraint->setPrettyString($rootPackage->getPrettyVersion());
32979 $request->install($rootPackage->getName(), $constraint);
32981 $fixedPackages = $platformRepo->getPackages();
32982 if ($this->additionalInstalledRepository) {
32983 $additionalFixedPackages = $this->additionalInstalledRepository->getPackages();
32984 $fixedPackages = array_merge($fixedPackages, $additionalFixedPackages);
32989 $provided = $rootPackage->getProvides();
32990 foreach ($fixedPackages as $package) {
32991 $constraint = new VersionConstraint('=', $package->getVersion());
32992 $constraint->setPrettyString($package->getPrettyVersion());
32995 if ($package->getRepository() !== $platformRepo
32996 || !isset($provided[$package->getName()])
32997 || !$provided[$package->getName()]->getConstraint()->matches($constraint)
32999 $request->fix($package->getName(), $constraint);
33006 private function processDevPackages($localRepo, $pool, $policy, $repositories, $lockedRepository, $installFromLock, $task, array $operations = null)
33008 if ($task === 'force-updates' && null === $operations) {
33009 throw new \InvalidArgumentException('Missing operations argument');
33011 if ($task === 'force-links') {
33012 $operations = array();
33015 foreach ($localRepo->getCanonicalPackages() as $package) {
33017 if (!$package->isDev()) {
33022 foreach ($operations as $operation) {
33023 if (('update' === $operation->getJobType() && $operation->getInitialPackage()->equals($package))
33024 || ('uninstall' === $operation->getJobType() && $operation->getPackage()->equals($package))
33031 if ($installFromLock) {
33032 foreach ($lockedRepository->findPackages($package->getName()) as $lockedPackage) {
33033 if ($lockedPackage->isDev() && $lockedPackage->getVersion() === $package->getVersion()) {
33034 if ($task === 'force-links') {
33035 $package->setRequires($lockedPackage->getRequires());
33036 $package->setConflicts($lockedPackage->getConflicts());
33037 $package->setProvides($lockedPackage->getProvides());
33038 $package->setReplaces($lockedPackage->getReplaces());
33039 } elseif ($task === 'force-updates') {
33040 if (($lockedPackage->getSourceReference() && $lockedPackage->getSourceReference() !== $package->getSourceReference())
33041 || ($lockedPackage->getDistReference() && $lockedPackage->getDistReference() !== $package->getDistReference())
33043 $operations[] = new UpdateOperation($package, $lockedPackage);
33052 if ($this->update) {
33054 if ($this->updateWhitelist && !$this->isUpdateable($package)) {
33059 $matches = $pool->whatProvides($package->getName(), new VersionConstraint('=', $package->getVersion()));
33060 foreach ($matches as $index => $match) {
33062 if (!in_array($match->getRepository(), $repositories, true)) {
33063 unset($matches[$index]);
33068 if ($match->getName() !== $package->getName()) {
33069 unset($matches[$index]);
33073 $matches[$index] = $match->getId();
33077 if ($matches && $matches = $policy->selectPreferedPackages($pool, array(), $matches)) {
33078 $newPackage = $pool->literalToPackage($matches[0]);
33080 if ($task === 'force-links' && $newPackage) {
33081 $package->setRequires($newPackage->getRequires());
33082 $package->setConflicts($newPackage->getConflicts());
33083 $package->setProvides($newPackage->getProvides());
33084 $package->setReplaces($newPackage->getReplaces());
33087 if ($task === 'force-updates' && $newPackage && (
33088 (($newPackage->getSourceReference() && $newPackage->getSourceReference() !== $package->getSourceReference())
33089 || ($newPackage->getDistReference() && $newPackage->getDistReference() !== $package->getDistReference())
33092 $operations[] = new UpdateOperation($package, $newPackage);
33097 if ($task === 'force-updates') {
33099 $references = $this->package->getReferences();
33101 if (isset($references[$package->getName()]) && $references[$package->getName()] !== $package->getSourceReference()) {
33103 $operations[] = new UpdateOperation($package, clone $package);
33109 return $operations;
33112 private function getRootAliases()
33114 if (!$this->update && $this->locker->isLocked()) {
33115 $aliases = $this->locker->getAliases();
33117 $aliases = $this->package->getAliases();
33120 $normalizedAliases = array();
33122 foreach ($aliases as $alias) {
33123 $normalizedAliases[$alias['package']][$alias['version']] = array(
33124 'alias' => $alias['alias'],
33125 'alias_normalized' => $alias['alias_normalized']
33129 return $normalizedAliases;
33132 private function aliasPlatformPackages(PlatformRepository $platformRepo, $aliases)
33134 foreach ($aliases as $package => $versions) {
33135 foreach ($versions as $version => $alias) {
33136 $packages = $platformRepo->findPackages($package, $version);
33137 foreach ($packages as $package) {
33138 $aliasPackage = new AliasPackage($package, $alias['alias_normalized'], $alias['alias']);
33139 $aliasPackage->setRootPackageAlias(true);
33140 $platformRepo->addPackage($aliasPackage);
33146 private function isUpdateable(PackageInterface $package)
33148 if (!$this->updateWhitelist) {
33149 throw new \LogicException('isUpdateable should only be called when a whitelist is present');
33152 foreach ($this->updateWhitelist as $whiteListedPattern => $void) {
33153 $patternRegexp = $this->packageNameToRegexp($whiteListedPattern);
33154 if (preg_match($patternRegexp, $package->getName())) {
33168 private function packageNameToRegexp($whiteListedPattern)
33170 $cleanedWhiteListedPattern = str_replace('\\*', '.*', preg_quote($whiteListedPattern));
33172 return "{^" . $cleanedWhiteListedPattern . "$}i";
33175 private function extractPlatformRequirements($links)
33177 $platformReqs = array();
33178 foreach ($links as $link) {
33179 if (preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $link->getTarget())) {
33180 $platformReqs[$link->getTarget()] = $link->getPrettyConstraint();
33184 return $platformReqs;
33199 private function whitelistUpdateDependencies($localRepo, $devMode, array $rootRequires, array $rootDevRequires)
33201 if (!$this->updateWhitelist) {
33205 $requiredPackageNames = array();
33206 foreach (array_merge($rootRequires, $rootDevRequires) as $require) {
33207 $requiredPackageNames[] = $require->getTarget();
33211 $rootRequires = array_merge($rootRequires, $rootDevRequires);
33214 $skipPackages = array();
33215 foreach ($rootRequires as $require) {
33216 $skipPackages[$require->getTarget()] = true;
33220 $pool->addRepository($localRepo);
33224 $rootRequiredPackageNames = array_keys($rootRequires);
33226 foreach ($this->updateWhitelist as $packageName => $void) {
33227 $packageQueue = new \SplQueue;
33229 $depPackages = $pool->whatProvides($packageName);
33231 $nameMatchesRequiredPackage = in_array($packageName, $requiredPackageNames, true);
33234 if (!$nameMatchesRequiredPackage) {
33235 $whitelistPatternRegexp = $this->packageNameToRegexp($packageName);
33236 foreach ($rootRequiredPackageNames as $rootRequiredPackageName) {
33237 if (preg_match($whitelistPatternRegexp, $rootRequiredPackageName)) {
33238 $nameMatchesRequiredPackage = true;
33244 if (count($depPackages) == 0 && !$nameMatchesRequiredPackage && !in_array($packageName, array('nothing', 'lock'))) {
33245 $this->io->write('<warning>Package "' . $packageName . '" listed for update is not installed. Ignoring.</warning>');
33248 foreach ($depPackages as $depPackage) {
33249 $packageQueue->enqueue($depPackage);
33252 while (!$packageQueue->isEmpty()) {
33253 $package = $packageQueue->dequeue();
33254 if (isset($seen[$package->getId()])) {
33258 $seen[$package->getId()] = true;
33259 $this->updateWhitelist[$package->getName()] = true;
33261 if (!$this->whitelistDependencies) {
33265 $requires = $package->getRequires();
33267 foreach ($requires as $require) {
33268 $requirePackages = $pool->whatProvides($require->getTarget());
33270 foreach ($requirePackages as $requirePackage) {
33271 if (isset($skipPackages[$requirePackage->getName()])) {
33274 $packageQueue->enqueue($requirePackage);
33288 private function mockLocalRepositories(RepositoryManager $rm)
33290 $packages = array();
33291 foreach ($rm->getLocalRepository()->getPackages() as $package) {
33292 $packages[(string) $package] = clone $package;
33294 foreach ($packages as $key => $package) {
33295 if ($package instanceof AliasPackage) {
33296 $alias = (string) $package->getAliasOf();
33297 $packages[$key] = new AliasPackage($packages[$alias], $package->getVersion(), $package->getPrettyVersion());
33300 $rm->setLocalRepository(
33301 new InstalledArrayRepository($packages)
33312 public static function create(IOInterface $io, Composer $composer)
33316 $composer->getConfig(),
33317 $composer->getPackage(),
33318 $composer->getDownloadManager(),
33319 $composer->getRepositoryManager(),
33320 $composer->getLocker(),
33321 $composer->getInstallationManager(),
33322 $composer->getEventDispatcher(),
33323 $composer->getAutoloadGenerator()
33327 public function setAdditionalInstalledRepository(RepositoryInterface $additionalInstalledRepository)
33329 $this->additionalInstalledRepository = $additionalInstalledRepository;
33340 public function setDryRun($dryRun = true)
33342 $this->dryRun = (boolean) $dryRun;
33352 public function isDryRun()
33354 return $this->dryRun;
33363 public function setPreferSource($preferSource = true)
33365 $this->preferSource = (boolean) $preferSource;
33376 public function setPreferDist($preferDist = true)
33378 $this->preferDist = (boolean) $preferDist;
33389 public function setOptimizeAutoloader($optimizeAutoloader = false)
33391 $this->optimizeAutoloader = (boolean) $optimizeAutoloader;
33402 public function setUpdate($update = true)
33404 $this->update = (boolean) $update;
33415 public function setDevMode($devMode = true)
33417 $this->devMode = (boolean) $devMode;
33429 public function setDumpAutoloader($dumpAutoloader = true)
33431 $this->dumpAutoloader = (boolean) $dumpAutoloader;
33443 public function setRunScripts($runScripts = true)
33445 $this->runScripts = (boolean) $runScripts;
33456 public function setConfig(Config $config)
33458 $this->config = $config;
33469 public function setVerbose($verbose = true)
33471 $this->verbose = (boolean) $verbose;
33481 public function isVerbose()
33483 return $this->verbose;
33492 public function setIgnorePlatformRequirements($ignorePlatformReqs = false)
33494 $this->ignorePlatformReqs = (boolean) $ignorePlatformReqs;
33506 public function setUpdateWhitelist(array $packages)
33508 $this->updateWhitelist = array_flip(array_map('strtolower', $packages));
33519 public function setWhitelistDependencies($updateDependencies = true)
33521 $this->whitelistDependencies = (boolean) $updateDependencies;
33532 public function setPreferStable($preferStable = true)
33534 $this->preferStable = (boolean) $preferStable;
33545 public function setPreferLowest($preferLowest = true)
33547 $this->preferLowest = (boolean) $preferLowest;
33561 public function disablePlugins()
33563 $this->installationManager->disablePlugins();
33571 * This file is part of Composer.
33573 * (c) Nils Adermann <naderman@naderman.de>
33574 * Jordi Boggiano <j.boggiano@seld.be>
33576 * For the full copyright and license information, please view the LICENSE
33577 * file that was distributed with this source code.
33580 namespace Composer\Autoload;
33583 * ClassLoader implements a PSR-0 class loader
33585 * See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md
33587 * $loader = new \Composer\Autoload\ClassLoader();
33589 * // register classes with namespaces
33590 * $loader->add('Symfony\Component', __DIR__.'/component');
33591 * $loader->add('Symfony', __DIR__.'/framework');
33593 * // activate the autoloader
33594 * $loader->register();
33596 * // to enable searching the include path (eg. for PEAR packages)
33597 * $loader->setUseIncludePath(true);
33599 * In this example, if you try to use a class in the Symfony\Component
33600 * namespace or one of its children (Symfony\Component\Console for instance),
33601 * the autoloader will first look for the class under the component/
33602 * directory, and it will then fallback to the framework/ directory if not
33603 * found before giving up.
33605 * This class is loosely based on the Symfony UniversalClassLoader.
33607 * @author Fabien Potencier <fabien@symfony.com>
33608 * @author Jordi Boggiano <j.boggiano@seld.be>
33613 private $prefixLengthsPsr4 = array();
33614 private $prefixDirsPsr4 = array();
33615 private $fallbackDirsPsr4 = array();
33618 private $prefixesPsr0 = array();
33619 private $fallbackDirsPsr0 = array();
33621 private $useIncludePath = false;
33622 private $classMap = array();
33624 public function getPrefixes()
33626 if (!empty($this->prefixesPsr0)) {
33627 return call_user_func_array('array_merge', $this->prefixesPsr0);
33633 public function getPrefixesPsr4()
33635 return $this->prefixDirsPsr4;
33638 public function getFallbackDirs()
33640 return $this->fallbackDirsPsr0;
33643 public function getFallbackDirsPsr4()
33645 return $this->fallbackDirsPsr4;
33648 public function getClassMap()
33650 return $this->classMap;
33654 * @param array $classMap Class to filename map
33656 public function addClassMap(array $classMap)
33658 if ($this->classMap) {
33659 $this->classMap = array_merge($this->classMap, $classMap);
33661 $this->classMap = $classMap;
33666 * Registers a set of PSR-0 directories for a given prefix, either
33667 * appending or prepending to the ones previously set for this prefix.
33669 * @param string $prefix The prefix
33670 * @param array|string $paths The PSR-0 root directories
33671 * @param bool $prepend Whether to prepend the directories
33673 public function add($prefix, $paths, $prepend = false)
33677 $this->fallbackDirsPsr0 = array_merge(
33679 $this->fallbackDirsPsr0
33682 $this->fallbackDirsPsr0 = array_merge(
33683 $this->fallbackDirsPsr0,
33691 $first = $prefix[0];
33692 if (!isset($this->prefixesPsr0[$first][$prefix])) {
33693 $this->prefixesPsr0[$first][$prefix] = (array) $paths;
33698 $this->prefixesPsr0[$first][$prefix] = array_merge(
33700 $this->prefixesPsr0[$first][$prefix]
33703 $this->prefixesPsr0[$first][$prefix] = array_merge(
33704 $this->prefixesPsr0[$first][$prefix],
33711 * Registers a set of PSR-4 directories for a given namespace, either
33712 * appending or prepending to the ones previously set for this namespace.
33714 * @param string $prefix The prefix/namespace, with trailing '\\'
33715 * @param array|string $paths The PSR-0 base directories
33716 * @param bool $prepend Whether to prepend the directories
33718 * @throws \InvalidArgumentException
33720 public function addPsr4($prefix, $paths, $prepend = false)
33723 // Register directories for the root namespace.
33725 $this->fallbackDirsPsr4 = array_merge(
33727 $this->fallbackDirsPsr4
33730 $this->fallbackDirsPsr4 = array_merge(
33731 $this->fallbackDirsPsr4,
33735 } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
33736 // Register directories for a new namespace.
33737 $length = strlen($prefix);
33738 if ('\\' !== $prefix[$length - 1]) {
33739 throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
33741 $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
33742 $this->prefixDirsPsr4[$prefix] = (array) $paths;
33743 } elseif ($prepend) {
33744 // Prepend directories for an already registered namespace.
33745 $this->prefixDirsPsr4[$prefix] = array_merge(
33747 $this->prefixDirsPsr4[$prefix]
33750 // Append directories for an already registered namespace.
33751 $this->prefixDirsPsr4[$prefix] = array_merge(
33752 $this->prefixDirsPsr4[$prefix],
33759 * Registers a set of PSR-0 directories for a given prefix,
33760 * replacing any others previously set for this prefix.
33762 * @param string $prefix The prefix
33763 * @param array|string $paths The PSR-0 base directories
33765 public function set($prefix, $paths)
33768 $this->fallbackDirsPsr0 = (array) $paths;
33770 $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
33775 * Registers a set of PSR-4 directories for a given namespace,
33776 * replacing any others previously set for this namespace.
33778 * @param string $prefix The prefix/namespace, with trailing '\\'
33779 * @param array|string $paths The PSR-4 base directories
33781 * @throws \InvalidArgumentException
33783 public function setPsr4($prefix, $paths)
33786 $this->fallbackDirsPsr4 = (array) $paths;
33788 $length = strlen($prefix);
33789 if ('\\' !== $prefix[$length - 1]) {
33790 throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
33792 $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
33793 $this->prefixDirsPsr4[$prefix] = (array) $paths;
33798 * Turns on searching the include path for class files.
33800 * @param bool $useIncludePath
33802 public function setUseIncludePath($useIncludePath)
33804 $this->useIncludePath = $useIncludePath;
33808 * Can be used to check if the autoloader uses the include path to check
33813 public function getUseIncludePath()
33815 return $this->useIncludePath;
33819 * Registers this instance as an autoloader.
33821 * @param bool $prepend Whether to prepend the autoloader or not
33823 public function register($prepend = false)
33825 spl_autoload_register(array($this, 'loadClass'), true, $prepend);
33829 * Unregisters this instance as an autoloader.
33831 public function unregister()
33833 spl_autoload_unregister(array($this, 'loadClass'));
33837 * Loads the given class or interface.
33839 * @param string $class The name of the class
33840 * @return bool|null True if loaded, null otherwise
33842 public function loadClass($class)
33844 if ($file = $this->findFile($class)) {
33845 includeFile($file);
33852 * Finds the path to the file where the class is defined.
33854 * @param string $class The name of the class
33856 * @return string|false The path if found, false otherwise
33858 public function findFile($class)
33860 // work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731
33861 if ('\\' == $class[0]) {
33862 $class = substr($class, 1);
33865 // class map lookup
33866 if (isset($this->classMap[$class])) {
33867 return $this->classMap[$class];
33870 $file = $this->findFileWithExtension($class, '.php');
33872 // Search for Hack files if we are running on HHVM
33873 if ($file === null && defined('HHVM_VERSION')) {
33874 $file = $this->findFileWithExtension($class, '.hh');
33877 if ($file === null) {
33878 // Remember that this class does not exist.
33879 return $this->classMap[$class] = false;
33885 private function findFileWithExtension($class, $ext)
33888 $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
33890 $first = $class[0];
33891 if (isset($this->prefixLengthsPsr4[$first])) {
33892 foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) {
33893 if (0 === strpos($class, $prefix)) {
33894 foreach ($this->prefixDirsPsr4[$prefix] as $dir) {
33895 if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
33903 // PSR-4 fallback dirs
33904 foreach ($this->fallbackDirsPsr4 as $dir) {
33905 if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
33911 if (false !== $pos = strrpos($class, '\\')) {
33912 // namespaced class name
33913 $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
33914 . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
33916 // PEAR-like class name
33917 $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
33920 if (isset($this->prefixesPsr0[$first])) {
33921 foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
33922 if (0 === strpos($class, $prefix)) {
33923 foreach ($dirs as $dir) {
33924 if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
33932 // PSR-0 fallback dirs
33933 foreach ($this->fallbackDirsPsr0 as $dir) {
33934 if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
33939 // PSR-0 include paths.
33940 if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
33947 * Scope isolated include.
33949 * Prevents access to $this/self from included files.
33951 function includeFile($file)
33956 "Glide", "Abstyles", "AFL-1.1", "AFL-1.2", "AFL-2.0", "AFL-2.1", "AFL-3.0",
33957 "AMPAS", "APL-1.0", "Adobe-Glyph", "APAFML", "Adobe-2006", "AGPL-1.0",
33958 "Afmparse", "Aladdin", "ADSL", "AMDPLPA", "ANTLR-PD", "Apache-1.0",
33959 "Apache-1.1", "Apache-2.0", "AML", "APSL-1.0", "APSL-1.1", "APSL-1.2",
33960 "APSL-2.0", "Artistic-1.0", "Artistic-1.0-Perl", "Artistic-1.0-cl8",
33961 "Artistic-2.0", "AAL", "Bahyph", "Barr", "Beerware", "BitTorrent-1.0",
33962 "BitTorrent-1.1", "BSL-1.0", "Borceux", "BSD-2-Clause",
33963 "BSD-2-Clause-FreeBSD", "BSD-2-Clause-NetBSD", "BSD-3-Clause",
33964 "BSD-3-Clause-Clear", "BSD-4-Clause", "BSD-Protection",
33965 "BSD-3-Clause-Attribution", "BSD-4-Clause-UC", "bzip2-1.0.5", "bzip2-1.0.6",
33966 "Caldera", "CECILL-1.0", "CECILL-1.1", "CECILL-2.0", "CECILL-B", "CECILL-C",
33967 "ClArtistic", "MIT-CMU", "CNRI-Python", "CNRI-Python-GPL-Compatible",
33968 "CPOL-1.02", "CDDL-1.0", "CDDL-1.1", "CPAL-1.0", "CPL-1.0", "CATOSL-1.1",
33969 "Condor-1.1", "CC-BY-1.0", "CC-BY-2.0", "CC-BY-2.5", "CC-BY-3.0",
33970 "CC-BY-4.0", "CC-BY-ND-1.0", "CC-BY-ND-2.0", "CC-BY-ND-2.5", "CC-BY-ND-3.0",
33971 "CC-BY-ND-4.0", "CC-BY-NC-1.0", "CC-BY-NC-2.0", "CC-BY-NC-2.5",
33972 "CC-BY-NC-3.0", "CC-BY-NC-4.0", "CC-BY-NC-ND-1.0", "CC-BY-NC-ND-2.0",
33973 "CC-BY-NC-ND-2.5", "CC-BY-NC-ND-3.0", "CC-BY-NC-ND-4.0", "CC-BY-NC-SA-1.0",
33974 "CC-BY-NC-SA-2.0", "CC-BY-NC-SA-2.5", "CC-BY-NC-SA-3.0", "CC-BY-NC-SA-4.0",
33975 "CC-BY-SA-1.0", "CC-BY-SA-2.0", "CC-BY-SA-2.5", "CC-BY-SA-3.0",
33976 "CC-BY-SA-4.0", "CC0-1.0", "Crossword", "CUA-OPL-1.0", "Cube", "D-FSL-1.0",
33977 "diffmark", "WTFPL", "DOC", "Dotseqn", "DSDP", "dvipdfm", "EPL-1.0",
33978 "eCos-2.0", "ECL-1.0", "ECL-2.0", "eGenix", "EFL-1.0", "EFL-2.0",
33979 "MIT-advertising", "MIT-enna", "Entessa", "ErlPL-1.1", "EUDatagrid",
33980 "EUPL-1.0", "EUPL-1.1", "Eurosym", "Fair", "MIT-feh", "Frameworx-1.0",
33981 "FTL", "FSFUL", "FSFULLR", "Giftware", "GL2PS", "Glulxe", "AGPL-3.0",
33982 "GFDL-1.1", "GFDL-1.2", "GFDL-1.3", "GPL-1.0", "GPL-1.0+", "GPL-2.0",
33983 "GPL-2.0+", "GPL-2.0-with-autoconf-exception",
33984 "GPL-2.0-with-bison-exception", "GPL-2.0-with-classpath-exception",
33985 "GPL-2.0-with-font-exception", "GPL-2.0-with-GCC-exception", "GPL-3.0",
33986 "GPL-3.0+", "GPL-3.0-with-autoconf-exception", "GPL-3.0-with-GCC-exception",
33987 "LGPL-2.1", "LGPL-2.1+", "LGPL-3.0", "LGPL-3.0+", "LGPL-2.0", "LGPL-2.0+",
33988 "gnuplot", "gSOAP-1.3b", "HaskellReport", "HPND", "IBM-pibs", "IPL-1.0",
33989 "ImageMagick", "iMatix", "Imlib2", "IJG", "Intel-ACPI", "Intel", "IPA",
33990 "ISC", "JasPer-2.0", "JSON", "LPPL-1.3a", "LPPL-1.0", "LPPL-1.1",
33991 "LPPL-1.2", "LPPL-1.3c", "Latex2e", "BSD-3-Clause-LBNL", "Leptonica",
33992 "Libpng", "libtiff", "LPL-1.02", "LPL-1.0", "MakeIndex", "MTLL", "MS-PL",
33993 "MS-RL", "MirOS", "MITNFA", "MIT", "Motosoto", "MPL-1.0", "MPL-1.1",
33994 "MPL-2.0", "MPL-2.0-no-copyleft-exception", "mpich2", "Multics", "Mup",
33995 "NASA-1.3", "Naumen", "NBPL-1.0", "NetCDF", "NGPL", "NOSL", "NPL-1.0",
33996 "NPL-1.1", "Newsletr", "NLPL", "Nokia", "NPOSL-3.0", "Noweb", "NRL", "NTP",
33997 "Nunit", "OCLC-2.0", "ODbL-1.0", "PDDL-1.0", "OGTSL", "OLDAP-2.2.2",
33998 "OLDAP-1.1", "OLDAP-1.2", "OLDAP-1.3", "OLDAP-1.4", "OLDAP-2.0",
33999 "OLDAP-2.0.1", "OLDAP-2.1", "OLDAP-2.2", "OLDAP-2.2.1", "OLDAP-2.3",
34000 "OLDAP-2.4", "OLDAP-2.5", "OLDAP-2.6", "OLDAP-2.7", "OML", "OPL-1.0",
34001 "OSL-1.0", "OSL-1.1", "OSL-2.0", "OSL-2.1", "OSL-3.0", "OLDAP-2.8",
34002 "OpenSSL", "PHP-3.0", "PHP-3.01", "Plexus", "PostgreSQL", "psfrag",
34003 "psutils", "Python-2.0", "QPL-1.0", "Qhull", "Rdisc", "RPSL-1.0", "RPL-1.1",
34004 "RPL-1.5", "RHeCos-1.1", "RSCPL", "Ruby", "SAX-PD", "Saxpath", "SCEA",
34005 "SWL", "SGI-B-1.0", "SGI-B-1.1", "SGI-B-2.0", "OFL-1.0", "OFL-1.1",
34006 "SimPL-2.0", "Sleepycat", "SNIA", "SMLNJ", "StandardML-NJ",
34007 "SugarCRM-1.1.3", "SISSL", "SISSL-1.2", "SPL-1.0", "Watcom-1.0", "TCL",
34008 "Unlicense", "TMate", "TORQUE-1.1", "TOSL", "Unicode-TOU", "NCSA", "Vim",
34009 "VOSTROM", "VSL-1.0", "W3C", "Wsuipa", "WXwindows", "Xnet", "X11", "Xerox",
34010 "XFree86-1.1", "xinetd", "xpp", "XSkat", "YPL-1.0", "YPL-1.1", "Zed",
34011 "Zend-2.0", "Zimbra-1.3", "Zlib", "zlib-acknowledgement", "ZPL-1.1",
34012 "ZPL-2.0", "ZPL-2.1"
34015 "$schema": "http://json-schema.org/draft-04/schema#",
34018 "additionalProperties": false,
34019 "required": [ "name", "description" ],
34023 "description": "Package name, including 'vendor-name/' prefix."
34026 "description": "Package type, either 'library' for common packages, 'composer-plugin' for plugins, 'metapackage' for empty packages, or a custom type ([a-z0-9-]+) defined by whatever project this package applies to.",
34030 "description": "DEPRECATED: Forces the package to be installed into the given subdirectory path. This is used for autoloading PSR-0 packages that do not contain their full path. Use forward slashes for cross-platform compatibility.",
34035 "description": "Short package description."
34041 "description": "A tag/keyword that this package relates to."
34046 "description": "Homepage URL for the project.",
34051 "description": "Package version, see http://getcomposer.org/doc/04-schema.md#version for more info on valid schemes."
34055 "description": "Package release date, in 'YYYY-MM-DD', 'YYYY-MM-DD HH:MM:SS' or 'YYYY-MM-DDTHH:MM:SSZ' format."
34058 "type": ["string", "array"],
34059 "description": "License name. Or an array of license names."
34063 "description": "List of authors that contributed to the package. This is typically the main maintainers, not the full list.",
34066 "additionalProperties": false,
34067 "required": [ "name"],
34071 "description": "Full name of the author."
34075 "description": "Email address of the author.",
34080 "description": "Homepage URL for the author.",
34085 "description": "Author's role in the project."
34092 "description": "This is a hash of package name (keys) and version constraints (values) that are required to run this package.",
34093 "additionalProperties": true
34097 "description": "This is a hash of package name (keys) and version constraints (values) that can be replaced by this package.",
34098 "additionalProperties": true
34102 "description": "This is a hash of package name (keys) and version constraints (values) that conflict with this package.",
34103 "additionalProperties": true
34107 "description": "This is a hash of package name (keys) and version constraints (values) that this package provides in addition to this package's name.",
34108 "additionalProperties": true
34112 "description": "This is a hash of package name (keys) and version constraints (values) that this package requires for developing it (testing tools and such).",
34113 "additionalProperties": true
34117 "description": "This is a hash of package name (keys) and descriptions (values) that this package suggests work well with it (this will be suggested to the user during installation).",
34118 "additionalProperties": true
34122 "description": "Composer options.",
34124 "process-timeout": {
34126 "description": "The timeout in seconds for process executions, defaults to 300 (5mins)."
34128 "use-include-path": {
34130 "description": "If true, the Composer autoloader will also look for classes in the PHP include path."
34132 "preferred-install": {
34134 "description": "The install method Composer will prefer to use, defaults to auto and can be any of source, dist or auto."
34136 "notify-on-install": {
34138 "description": "Composer allows repositories to define a notification URL, so that they get notified whenever a package from that repository is installed. This option allows you to disable that behaviour, defaults to true."
34140 "github-protocols": {
34142 "description": "A list of protocols to use for github.com clones, in priority order, defaults to [\"git\", \"https\", \"http\"].",
34149 "description": "A hash of domain name => github API oauth tokens, typically {\"github.com\":\"<token>\"}.",
34150 "additionalProperties": true
34154 "description": "A hash of domain name => {\"username\": \"...\", \"password\": \"...\"}.",
34155 "additionalProperties": true
34158 "type": ["string", "boolean"],
34159 "description": "What to do after prompting for authentication, one of: true (store), false (do not store) or \"prompt\" (ask every time), defaults to prompt."
34163 "description": "The location where all packages are installed, defaults to \"vendor\"."
34167 "description": "The location where all binaries are linked, defaults to \"vendor/bin\"."
34171 "description": "The location where all caches are located, defaults to \"~/.composer/cache\" on *nix and \"%LOCALAPPDATA%\\Composer\" on windows."
34173 "cache-files-dir": {
34175 "description": "The location where files (zip downloads) are cached, defaults to \"{$cache-dir}/files\"."
34177 "cache-repo-dir": {
34179 "description": "The location where repo (git/hg repo clones) are cached, defaults to \"{$cache-dir}/repo\"."
34183 "description": "The location where vcs infos (git clones, github api calls, etc. when reading vcs repos) are cached, defaults to \"{$cache-dir}/vcs\"."
34187 "description": "The default cache time-to-live, defaults to 15552000 (6 months)."
34189 "cache-files-ttl": {
34191 "description": "The cache time-to-live for files, defaults to the value of cache-ttl."
34193 "cache-files-maxsize": {
34194 "type": ["string", "integer"],
34195 "description": "The cache max size for the files cache, defaults to \"300MiB\"."
34197 "discard-changes": {
34198 "type": ["string", "boolean"],
34199 "description": "The default style of handling dirty updates, defaults to false and can be any of true, false or \"stash\"."
34201 "autoloader-suffix": {
34203 "description": "Optional string to be used as a suffix for the generated Composer autoloader. When null a random one will be generated."
34205 "optimize-autoloader": {
34207 "description": "Always optimize when dumping the autoloader."
34209 "prepend-autoloader": {
34211 "description": "If false, the composer autoloader will not be prepended to existing autoloaders, defaults to true."
34213 "github-domains": {
34215 "description": "A list of domains to use in github mode. This is used for GitHub Enterprise setups, defaults to [\"github.com\"].",
34220 "github-expose-hostname": {
34222 "description": "Defaults to true. If set to false, the OAuth tokens created to access the github API will have a date instead of the machine hostname."
34227 "type": ["object", "array"],
34228 "description": "Arbitrary extra data that can be used by plugins, for example, package of type composer-plugin may have a 'class' key defining an installer class name.",
34229 "additionalProperties": true
34233 "description": "Description of how the package can be autoloaded.",
34237 "description": "This is a hash of namespaces (keys) and the directories they can be found into (values, can be arrays of paths) by the autoloader.",
34238 "additionalProperties": true
34242 "description": "This is a hash of namespaces (keys) and the PSR-4 directories they can map to (values, can be arrays of paths) by the autoloader.",
34243 "additionalProperties": true
34247 "description": "This is an array of directories that contain classes to be included in the class-map generation process."
34251 "description": "This is an array of files that are always required on every request."
34257 "description": "Description of additional autoload rules for development purpose (eg. a test suite).",
34261 "description": "This is a hash of namespaces (keys) and the directories they can be found into (values, can be arrays of paths) by the autoloader.",
34262 "additionalProperties": true
34266 "description": "This is a hash of namespaces (keys) and the PSR-4 directories they can map to (values, can be arrays of paths) by the autoloader.",
34267 "additionalProperties": true
34271 "description": "This is an array of directories that contain classes to be included in the class-map generation process."
34275 "description": "This is an array of files that are always required on every request."
34280 "type": ["object"],
34281 "description": "Options for creating package archives for distribution.",
34285 "description": "A list of patterns for paths to exclude or include if prefixed with an exclamation mark."
34290 "type": ["object", "array"],
34291 "description": "A set of additional repositories where packages can be found.",
34292 "additionalProperties": true
34294 "minimum-stability": {
34295 "type": ["string"],
34296 "description": "The minimum stability the packages must have to be install-able. Possible values are: dev, alpha, beta, RC, stable."
34299 "type": ["boolean"],
34300 "description": "If set to true, stable packages will be prefered to dev packages when possible, even if the minimum-stability allows unstable packages."
34304 "description": "A set of files that should be treated as binaries and symlinked into bin-dir (from config).",
34311 "description": "DEPRECATED: A list of directories which should get added to PHP's include path. This is only present to support legacy projects, and all new code should preferably use autoloading.",
34317 "type": ["object"],
34318 "description": "Scripts listeners that will be executed before/after some events.",
34320 "pre-install-cmd": {
34321 "type": ["array", "string"],
34322 "description": "Occurs before the install command is executed, contains one or more Class::method callables or shell commands."
34324 "post-install-cmd": {
34325 "type": ["array", "string"],
34326 "description": "Occurs after the install command is executed, contains one or more Class::method callables or shell commands."
34328 "pre-update-cmd": {
34329 "type": ["array", "string"],
34330 "description": "Occurs before the update command is executed, contains one or more Class::method callables or shell commands."
34332 "post-update-cmd": {
34333 "type": ["array", "string"],
34334 "description": "Occurs after the update command is executed, contains one or more Class::method callables or shell commands."
34336 "pre-status-cmd": {
34337 "type": ["array", "string"],
34338 "description": "Occurs before the status command is executed, contains one or more Class::method callables or shell commands."
34340 "post-status-cmd": {
34341 "type": ["array", "string"],
34342 "description": "Occurs after the status command is executed, contains one or more Class::method callables or shell commands."
34344 "pre-package-install": {
34345 "type": ["array", "string"],
34346 "description": "Occurs before a package is installed, contains one or more Class::method callables or shell commands."
34348 "post-package-install": {
34349 "type": ["array", "string"],
34350 "description": "Occurs after a package is installed, contains one or more Class::method callables or shell commands."
34352 "pre-package-update": {
34353 "type": ["array", "string"],
34354 "description": "Occurs before a package is updated, contains one or more Class::method callables or shell commands."
34356 "post-package-update": {
34357 "type": ["array", "string"],
34358 "description": "Occurs after a package is updated, contains one or more Class::method callables or shell commands."
34360 "pre-package-uninstall": {
34361 "type": ["array", "string"],
34362 "description": "Occurs before a package has been uninstalled, contains one or more Class::method callables or shell commands."
34364 "post-package-uninstall": {
34365 "type": ["array", "string"],
34366 "description": "Occurs after a package has been uninstalled, contains one or more Class::method callables or shell commands."
34368 "pre-autoload-dump": {
34369 "type": ["array", "string"],
34370 "description": "Occurs before the autoloader is dumped, contains one or more Class::method callables or shell commands."
34372 "post-autoload-dump": {
34373 "type": ["array", "string"],
34374 "description": "Occurs after the autoloader is dumped, contains one or more Class::method callables or shell commands."
34376 "post-root-package-install": {
34377 "type": ["array", "string"],
34378 "description": "Occurs after the root-package is installed, contains one or more Class::method callables or shell commands."
34380 "post-create-project-cmd": {
34381 "type": ["array", "string"],
34382 "description": "Occurs after the create-project command is executed, contains one or more Class::method callables or shell commands."
34391 "description": "Email address for support.",
34396 "description": "URL to the Issue Tracker.",
34401 "description": "URL to the Forum.",
34406 "description": "URL to the Wiki.",
34411 "description": "IRC channel for support, as irc://server/channel.",
34416 "description": "URL to browse or download the sources.",
34423 MZ
\90\0\ 3\0\0\0\ 4\0\0\0ÿÿ
\0\0¸
\0\0\0\0\0\0\0@
\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0è
\0\0\0\ e\1fº
\ e\0´ Í!¸
\ 1LÍ!This program cannot be run in DOS mode.
\r\r
34424 $
\0\0\0\0\0\0\0\7fÆ,Í;§B
\9e;§B
\9e;§B
\9e2ß×
\9e:§B
\9e2ßÁ
\9e-§B
\9e2ßÆ
\9e9§B
\9e2ßÑ
\9e?§B
\9e\1ca9
\9e8§B
\9e;§C
\9e\b§B
\9e2ßÈ
\9e:§B
\9e2ßÖ
\9e:§B
\9e2ßÓ
\9e:§B
\9eRich;§B
\9e\0\0\0\0\0\0\0\0PE
\0\0L
\ 1\ 5\0¬MoO
\0\0\0\0\0\0\0\0à
\0\ 2\ 1\v\ 1 \0\0
34425 \0\0\0\16\0\0\0\0\0\08
\13\0\0\0\10\0\0\0 \0\0\0\0@
\0\0\10\0\0\0\ 2\0\0\ 5\0\0\0\0\0\0\0\ 5\0\0\0\0\0\0\0\0`
\0\0\0\ 4\0\0?
\9c\0\0\ 3\0@
\81\0\0\10\0\0\10\0\0\0\0\10\0\0\10\0\0\0\0\0\0\10\0\0\0\0\0\0\0\0\0\0\0\90"
\0\0P
\0\0\0\0@
\0\0 \ 6\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0P
\0\0p
\ 1\0\0\0!
\0\0\1c\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\08!
\0\0@
\0\0\0\0\0\0\0\0\0\0\0\0 \0\0Ø
\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0.text
\0\0\0\v \0\0\0\10\0\0\0
34426 \0\0\0\ 4\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0\0`.rdata
\0\0Î
\0\0\0 \0\0\0
34427 \0\0\0\ e\0\0\0\0\0\0\0\0\0\0\0\0\0\0@
\0\0@.data
\0\0\0\90\ 3\0\0\00
\0\0\0\ 2\0\0\0\18\0\0\0\0\0\0\0\0\0\0\0\0\0\0@
\0\0À.rsrc
\0\0\0 \ 6\0\0\0@
\0\0\0\b\0\0\0\1a\0\0\0\0\0\0\0\0\0\0\0\0\0\0@
\0\0@.reloc
\0\0Ì
\ 1\0\0\0P
\0\0\0\ 2\0\0\0"
\0\0\0\0\0\0\0\0\0\0\0\0\0\0@
\0\0B
\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0j$¸æ
\18@
\0èx
\b\0\0jöÿ
\15\b @
\0\83eÐ
\0\8bð
\8dEÐPVÿ
\15\0 @
\0\8bEÐ
\83àûPVÿ
\15\ 4 @
\0\8dMÔÿ
\15X @
\0\83eü
\0\8dEÔPÿ5H @
\0ÿ
\15L @
\0YYÿ5\ @
\0\8dEÔPÿ5` @
\0ÿ
\15D @
\0YY
\8bÈÿ
\15P @
\0\83Müÿ
\8dMÔÿ
\15T @
\03ÀèH
\b\0\0Ã;
\r\00@
\0u
\ 2óÃé¬
\ 2\0\0h
\80\15@
\0è£
\ 4\0\0¡l3@
\0Ç
\ 4$40@
\0ÿ5h3@
\0£40@
\0h$0@
\0h(0@
\0h 0@
\0ÿ
\15 @
\0\83Ä
\14£00@
\0\85À}
\bj
\bè¹
\ 3\0\0YÃj
\10h
\b"@
\0è
\1f\ 6\0\03Û
\89]üd¡
\18\0\0\0\8bp
\ 4\89]ä¿
\803@
\0SVWÿ
\150 @
\0;Ãt
\19;Æu
\b3öF
\89uäë
\10hè
\ 3\0\0ÿ
\154 @
\0ëÚ3öF¡|3@
\0;Æu
34428 j
\1fè\
\ 3\0\0Yë;¡|3@
\0\85Àu,
\895|3@
\0hð @
\0hä @
\0è§
\ 5\0\0YY
\85Àt
\17ÇEüþÿÿÿ¸ÿ
\0\0\0éÝ
\0\0\0\895<0@
\0¡|3@
\0;Æu
\ehà @
\0hØ @
\0èl
\ 5\0\0YYÇ
\ 5|3@
\0\ 2\0\0\09]äu
\bSWÿ
\158 @
\09
\1d\8c3@
\0t
\19h
\8c3@
\0è
\83\ 4\0\0Y
\85Àt
34429 Sj
\ 2Sÿ
\15\8c3@
\0¡$0@
\0\8b\r¼ @
\0\89\ 1ÿ5$0@
\0ÿ5(0@
\0ÿ5 0@
\0è
\10þÿÿ
\83Ä
\f£80@
\09
\1d,0@
\0u7Pÿ
\15À @
\0\8bEì
\8b\b\8b \89MàPQè
\8e\ 3\0\0YYÃ
\8beè
\8bEà£80@
\03Û9
\1d,0@
\0u
\aPÿ
\15h @
\09
\1d<0@
\0u
\ 6ÿ
\15\9c @
\0ÇEüþÿÿÿ¡80@
\0èû
\ 4\0\0øMZ
\0\0f9
\ 5\0\0@
\0t
\ 43ÀëM¡<
\0@
\0\8d\80\0\0@
\0\818PE
\0\0ué
\ f·H
\18\81ù
\v\ 1\0\0t
\e\81ù
\v\ 2\0\0uÕ
\83¸
\84\0\0\0\ evÌ3É9
\88ø
\0\0\0ë
\ e\83xt
\ ev¼3É9
\88è
\0\0\0\ f\95Á
\8bÁj
\ 1£,0@
\0ÿ
\15p @
\0jÿÿ
\15l @
\0YY£
\843@
\0£
\883@
\0ÿ
\15Ì @
\0\8b\rt3@
\0\89\bÿ
\15\88 @
\0\8b\rp3@
\0\89\b¡¨ @
\0\8b\0£x3@
\0èV
\ 2\0\0è¬
\ 4\0\0\83=
\140@
\0\0u
\fhµ
\17@
\0ÿ
\15¬ @
\0Yèg
\ 4\0\0\83=
\100@
\0ÿu jÿÿ
\15° @
\0Y3ÀÃè{
\ 4\0\0é
\9fýÿÿ
\8bÿU
\8bì
\81ì(
\ 3\0\0£H1@
\0\89\rD1@
\0\89\15@1@
\0\89\1d<1@
\0\89581@
\0\89=41@
\0f
\8c\15`1@
\0f
\8c\rT1@
\0f
\8c\1d01@
\0f
\8c\ 5,1@
\0f
\8c%(1@
\0f
\8c-$1@
\0\9c\8f\ 5X1@
\0\8bE
\0£L1@
\0\8bE
\ 4£P1@
\0\8dE
\b£\1@
\0\8b\85àüÿÿÇ
\ 5\980@
\0\ 1\0\ 1\0¡P1@
\0£L0@
\0Ç
\ 5@0@
\0 \ 4\0ÀÇ
\ 5D0@
\0\ 1\0\0\0¡
\00@
\0\89\85Øüÿÿ¡
\ 40@
\0\89\85Üüÿÿÿ
\15\1c @
\0£
\900@
\0j
\ 1è?
\ 4\0\0Yj
\0ÿ
\15 @
\0h
\1c!@
\0ÿ
\15$ @
\0\83=
\900@
\0\0u
\bj
\ 1è
\e\ 4\0\0Yh
\ 4\0Àÿ
\15( @
\0Pÿ
\15, @
\0ÉÃ
\8bÿU
\8bì
\8bE
\b\8b\0\818csmàu*
\83x
\10\ 3u$
\8b@
\14=
\ 5\93\19t
\15=!
\ 5\93\19t
\ e="
\ 5\93\19t
\a=
\0@
\99\ 1u
\ 5èÐ
\ 3\0\03À]Â
\ 4\0hH
\14@
\0ÿ
\15 @
\03ÀÃÿ%¤ @
\0j
\14h("@
\0èb
\ 2\0\0ÿ5
\883@
\0\8b5
\8c @
\0ÿÖY
\89Eä
\83øÿu
\fÿu
\bÿ
\15Ä @
\0Yëgj
\bè
\92\ 3\0\0Y
\83eü
\0ÿ5
\883@
\0ÿÖ
\89Eäÿ5
\843@
\0ÿÖYY
\89Eà
\8dEàP
\8dEäPÿu
\b\8b5l @
\0ÿÖYPèU
\ 3\0\0\89EÜÿuäÿÖ£
\883@
\0ÿuàÿÖ
\83Ä
\14£
\843@
\0ÇEüþÿÿÿè
\0\0\0\8bEÜè
\18\ 2\0\0Ãj
\bè
\19\ 3\0\0YÃ
\8bÿU
\8bìÿu
\bèNÿÿÿ÷Ø
\eÀ÷ØYH]Ã
\8bÿV¸ü!@
\0¾ü!@
\0W
\8bø;Æs
\ f\8b\a\85Àt
\ 2ÿÐ
\83Ç
\ 4;þrñ_^Ã
\8bÿV¸
\ 4"@
\0¾
\ 4"@
\0W
\8bø;Æs
\ f\8b\a\85Àt
\ 2ÿÐ
\83Ç
\ 4;þrñ_^Ãÿ%È @
\0ÌÌÌÌ
\8bÿU
\8bì
\8bM
\b¸MZ
\0\0f9
\ 1t
\ 43À]Ã
\8bA<
\ 3Á
\818PE
\0\0uï3Ò¹
\v\ 1\0\0f9H
\18\ f\94Â
\8bÂ]ÃÌÌÌÌÌÌÌÌÌÌÌ
\8bÿU
\8bì
\8bE
\b\8bH<
\ 3È
\ f·A
\14SV
\ f·q
\ 63ÒW
\8dD
\b\18\85öv
\e\8b}
\f\8bH
\f;ùr
\8bX
\b\ 3Ù;ûr
34430 B
\83À(;Örè3À_^[]ÃÌÌÌÌÌÌÌÌÌÌÌÌ
\8bÿU
\8bìjþhH"@
\0he
\17@
\0d¡
\0\0\0\0P
\83ì
\bSVW¡
\00@
\01Eø3ÅP
\8dEðd£
\0\0\0\0\89eèÇEü
\0\0\0\0h
\0\0@
\0è*ÿÿÿ
\83Ä
\ 4\85ÀtU
\8bE
\b-
\0\0@
\0Ph
\0\0@
\0èPÿÿÿ
\83Ä
\b\85Àt;
\8b@$Áè
\1f÷Ð
\83à
\ 1ÇEüþÿÿÿ
\8bMðd
\89\r\0\0\0\0Y_^[
\8bå]Ã
\8bEì
\8b\b\8b\ 13Ò=
\ 5\0\0À
\ f\94Â
\8bÂÃ
\8beèÇEüþÿÿÿ3À
\8bMðd
\89\r\0\0\0\0Y_^[
\8bå]ÃÌÿ%¸ @
\0ÿ%´ @
\0ÌÌhe
\17@
\0dÿ5
\0\0\0\0\8bD$
\10\89l$
\10\8dl$
\10+àSVW¡
\00@
\01Eü3ÅP
\89eèÿuø
\8bEüÇEüþÿÿÿ
\89Eø
\8dEðd£
\0\0\0\0Ã
\8bMðd
\89\r\0\0\0\0Y__^[
\8bå]QÃ
\8bÿU
\8bìÿu
\14ÿu
\10ÿu
\fÿu
\bh
\87\10@
\0h
\00@
\0èç
\0\0\0\83Ä
\18]Ã
\8bÿVh
\0\0\ 3\0h
\0\0\ 1\03öVèÙ
\0\0\0\83Ä
\f\85Àt
\rVVVVVèÂ
\0\0\0\83Ä
\14^Ã3ÀÃ
\8bÿU
\8bì
\83ì
\10¡
\00@
\0\83eø
\0\83eü
\0SW¿Næ@»»
\0\0ÿÿ;Çt
\r\85Ãt ÷У
\ 40@
\0ë`V
\8dEøPÿ
\15< @
\0\8buü3uøÿ
\15\f @
\03ðÿ
\15\10 @
\03ðÿ
\15\14 @
\03ð
\8dEðPÿ
\15\18 @
\0\8bEô3Eð3ð;÷u
\a¾Oæ@»ë
\v\85óu
\a\8bÆÁà
\10\vð
\895
\00@
\0÷Ö
\895
\ 40@
\0^_[ÉÃÿ%t @
\0ÿ%x @
\0ÿ%| @
\0ÿ%
\80 @
\0ÿ%
\84 @
\0ÿ%
\90 @
\0ÿ%
\94 @
\0ÿ%
\98 @
\0ÿ%Ð @
\0Pdÿ5
\0\0\0\0\8dD$
\f+d$
\fSVW
\89(
\8bè¡
\00@
\03ÅP
\89EðÿuüÇEüÿÿÿÿ
\8dEôd£
\0\0\0\0Ã
\8bMôd
\89\r\0\0\0\0Y__^[
\8bå]QÃ
\8bMð3Íè¯÷ÿÿéÝÿÿÿ
\8dMÔÿ%T @
\0\8bT$
\b\8dB
\f\8bJÌ3Èè
\90÷ÿÿ
\8bJü3Èè
\86÷ÿÿ¸l"@
\0ésÿÿÿ
\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0¸#
\0\0Ê#
\0\0Ü#
\0\0\88)
\0\0r)
\0\0b)
\0\0H)
\0\04)
\0\0\16)
\0\0ú(
\0\0æ(
\0\0Ò(
\0\0´(
\0\0¬(
\0\0\96(
\0\0\9e)
\0\0\0\0\0\0ú#
\0\0à$
\0\0\1a%
\0\0Ê%
\0\0\1a&
\0\0d&
\0\0®&
\0\0¤$
\0\0\0\0\0\0('
\0\0Ä'
\0\0Ö'
\0\0è'
\0\0þ'
\0\0\1e(
\0\0((
\0\06(
\0\0¦'
\0\0H(
\0\0Z(
\0\0t(
\0\0\86(
\0\0\1e'
\0\0\ e'
\0\0\0'
\0\0\96'
\0\0\82'
\0\0l'
\0\0^'
\0\0R'
\0\0F'
\0\0>'
\0\0>(
\0\00'
\0\0¶'
\0\0¸)
\0\0\0\0\0\0\0\0\0\0\96\10@
\0\0\0\0\0\0\0\0\0W
\12@
\0\8a\14@
\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0¬MoO
\0\0\0\0\ 2\0\0\0l
\0\0\0\80!
\0\0\80\ f\0\0@0@
\0\980@
\0bad allocation
\0\0\0\0\0\0H
\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\00@
\0ð!@
\0\ 2\0\0\0RSDSÑ
\8c³
\10´
\8f\ 1J¨!öÌëLZ
\0\ 1\0\0\0c:\users\seld\documents\visual studio 2010\Projects\hiddeninp\Release\hiddeninp.pdb
\0\0\0\0\0e
\17\0\0æ
\18\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0þÿÿÿ
\0\0\0\0Ðÿÿÿ
\0\0\0\0þÿÿÿ
\a\12@
\0\e\12@
\0\0\0\0\0þÿÿÿ
\0\0\0\0Ìÿÿÿ
\0\0\0\0þÿÿÿ
\0\0\0\0:
\15@
\0\0\0\0\0þÿÿÿ
\0\0\0\0Øÿÿÿ
\0\0\0\0þÿÿÿË
\16@
\0ß
\16@
\0ÿÿÿÿÝ
\18@
\0"
\ 5\93\19\ 1\0\0\0d"@
\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ 1\0\0\0à"
\0\0\0\0\0\0\0\0\0\0ì#
\0\0\0 \0\0$#
\0\0\0\0\0\0\0\0\0\0ô&
\0\0D
\0\0H#
\0\0\0\0\0\0\0\0\0\0\12(
\0\0h
\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0¸#
\0\0Ê#
\0\0Ü#
\0\0\88)
\0\0r)
\0\0b)
\0\0H)
\0\04)
\0\0\16)
\0\0ú(
\0\0æ(
\0\0Ò(
\0\0´(
\0\0¬(
\0\0\96(
\0\0\9e)
\0\0\0\0\0\0ú#
\0\0à$
\0\0\1a%
\0\0Ê%
\0\0\1a&
\0\0d&
\0\0®&
\0\0¤$
\0\0\0\0\0\0('
\0\0Ä'
\0\0Ö'
\0\0è'
\0\0þ'
\0\0\1e(
\0\0((
\0\06(
\0\0¦'
\0\0H(
\0\0Z(
\0\0t(
\0\0\86(
\0\0\1e'
\0\0\ e'
\0\0\0'
\0\0\96'
\0\0\82'
\0\0l'
\0\0^'
\0\0R'
\0\0F'
\0\0>'
\0\0>(
\0\00'
\0\0¶'
\0\0¸)
\0\0\0\0\0\0\95\ 1GetConsoleMode
\0\0·
\ 3SetConsoleMode
\0\0;
\ 2GetStdHandle
\0\0KERNEL32.dll
\0\0\16\0??$?6DU?$char_traits@D@std@@V?$allocator@D@1@@std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@0@AAV10@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@0@@Z
\0\91\ 6?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A
\0\0J
\ 6?cin@std@@3V?$basic_istream@DU?$char_traits@D@std@@@1@A
\0Â
\0??$getline@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@YAAAV?$basic_istream@DU?$char_traits@D@std@@@0@AAV10@AAV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@0@@Z
\0\1d\ 3??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@P6AAAV01@AAV01@@Z@Z
\0\0_
\ 2??1?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QAE@XZ
\0\0{
\ 1??0?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QAE@XZ
\0\0³
\a?endl@std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@1@AAV21@@Z
\0\0MSVCP90.dll
\0\15\ 1_amsg_exit
\0\0\9f\0__getmainargs
\0,
\ 1_cexit
\0\0|
\ 1_exit
\0f
\0_XcptFilter
\0Ì
\ 4exit
\0\0 \0__initenv
\0\ 4\ 2_initterm
\0\ 5\ 2_initterm_e
\0<
\ 1_configthreadlocale
\0ã
\0__setusermatherr
\0\0\v\ 1_adjust_fdiv
\0\0Ë
\0__p__commode
\0\0Ï
\0__p__fmode
\0\0j
\ 1_encode_pointer
\0à
\0__set_app_type
\0\0K
\ 1_crt_debugger_hook
\0\0C
\0?terminate@@YAXXZ
\0MSVCR90.dll
\0æ
\ 3_unlock
\0\96\0__dllonexit
\0v
\ 2_lock
\0\1c\ 3_onexit
\0`
\ 1_decode_pointer
\0s
\ 1_except_handler4_common
\0\v\ 2_invoke_watson
\0\0?
\ 1_controlfp_s
\0\0½
\ 2InterlockedExchange
\0!
\ 4Sleep
\0º
\ 2InterlockedCompareExchange
\0\0-
\ 4TerminateProcess
\0\0©
\ 1GetCurrentProcess
\0>
\ 4UnhandledExceptionFilter
\0\0\15\ 4SetUnhandledExceptionFilter
\0Ñ
\ 2IsDebuggerPresent
\0T
\ 3QueryPerformanceCounter
\0f
\ 2GetTickCount
\0\0
\ 1GetCurrentThreadId
\0\0ª
\ 1GetCurrentProcessId
\0O
\ 2GetSystemTimeAsFileTime
\0s
\0__CxxFrameHandler3
\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0Næ@»±
\19¿Dÿÿÿÿÿÿÿÿþÿÿÿ
\ 1\0\0\0$!@
\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ 4\0\0\0\0\0\ 2\0\10\0\0\0 \0\0\80\18\0\0\08
\0\0\80\0\0\0\0\0\0\0\0\ 4\0\0\0\0\0\ 1\0\ 1\0\0\0P
\0\0\80\0\0\0\0\0\0\0\0\ 4\0\0\0\0\0\ 1\0\ 1\0\0\0h
\0\0\80\0\0\0\0\0\0\0\0\ 4\0\0\0\0\0\ 1\0 \ 4\0\0\80\0\0\0\0\0\0\0\0\0\0\0\ 4\0\0\0\0\0\ 1\0 \ 4\0\0\90\0\0\0 @
\0\0(
\ 3\0\0ä
\ 4\0\0\0\0\0\0ÈC
\0\0V
\ 2\0\0ä
\ 4\0\0\0\0\0\0(
\ 34
\0\0\0V
\0S
\0_
\0V
\0E
\0R
\0S
\0I
\0O
\0N
\0_
\0I
\0N
\0F
\0O
\0\0\0\0\0½
\ 4ïþ
\0\0\ 1\0\0\0\ 1\0\0\0\0\0\0\0\ 1\0\0\0\0\0\17\0\0\0\0\0\0\0\ 4\0\0\0\ 1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\86\ 2\0\0\ 1\0S
\0t
\0r
\0i
\0n
\0g
\0F
\0i
\0l
\0e
\0I
\0n
\0f
\0o
\0\0\0b
\ 2\0\0\ 1\00
\04
\00
\09
\00
\04
\0b
\00
\0\0\0Ê
\0Q
\0\ 1\0F
\0i
\0l
\0e
\0D
\0e
\0s
\0c
\0r
\0i
\0p
\0t
\0i
\0o
\0n
\0\0\0\0\0R
\0e
\0a
\0d
\0s
\0 \0f
\0r
\0o
\0m
\0 \0s
\0t
\0d
\0i
\0n
\0 \0w
\0i
\0t
\0h
\0o
\0u
\0t
\0 \0l
\0e
\0a
\0k
\0i
\0n
\0g
\0 \0i
\0n
\0f
\0o
\0 \0t
\0o
\0 \0t
\0h
\0e
\0 \0t
\0e
\0r
\0m
\0i
\0n
\0a
\0l
\0 \0a
\0n
\0d
\0 \0o
\0u
\0t
\0p
\0u
\0t
\0s
\0 \0b
\0a
\0c
\0k
\0 \0t
\0o
\0 \0s
\0t
\0d
\0o
\0u
\0t
\0\0\0\0\06
\0\v\0\ 1\0F
\0i
\0l
\0e
\0V
\0e
\0r
\0s
\0i
\0o
\0n
\0\0\0\0\01
\0,
\0 \00
\0,
\0 \00
\0,
\0 \00
\0\0\0\0\08
\0\f\0\ 1\0I
\0n
\0t
\0e
\0r
\0n
\0a
\0l
\0N
\0a
\0m
\0e
\0\0\0h
\0i
\0d
\0d
\0e
\0n
\0i
\0n
\0p
\0u
\0t
\0\0\0P
\0\16\0\ 1\0L
\0e
\0g
\0a
\0l
\0C
\0o
\0p
\0y
\0r
\0i
\0g
\0h
\0t
\0\0\0J
\0o
\0r
\0d
\0i
\0 \0B
\0o
\0g
\0g
\0i
\0a
\0n
\0o
\0 \0-
\0 \02
\00
\01
\02
\0\0\0H
\0\10\0\ 1\0O
\0r
\0i
\0g
\0i
\0n
\0a
\0l
\0F
\0i
\0l
\0e
\0n
\0a
\0m
\0e
\0\0\0h
\0i
\0d
\0d
\0e
\0n
\0i
\0n
\0p
\0u
\0t
\0.
\0e
\0x
\0e
\0\0\0:
\0\r\0\ 1\0P
\0r
\0o
\0d
\0u
\0c
\0t
\0N
\0a
\0m
\0e
\0\0\0\0\0H
\0i
\0d
\0d
\0e
\0n
\0 \0I
\0n
\0p
\0u
\0t
\0\0\0\0\0:
\0\v\0\ 1\0P
\0r
\0o
\0d
\0u
\0c
\0t
\0V
\0e
\0r
\0s
\0i
\0o
\0n
\0\0\01
\0,
\0 \00
\0,
\0 \00
\0,
\0 \00
\0\0\0\0\0D
\0\0\0\ 1\0V
\0a
\0r
\0F
\0i
\0l
\0e
\0I
\0n
\0f
\0o
\0\0\0\0\0$
\0\ 4\0\0\0T
\0r
\0a
\0n
\0s
\0l
\0a
\0t
\0i
\0o
\0n
\0\0\0\0\0 \ 4°
\ 4<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
\r
34431 <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
\r
34433 <requestedPrivileges>
\r
34434 <requestedExecutionLevel level="asInvoker" uiAccess="false"></requestedExecutionLevel>
\r
34435 </requestedPrivileges>
\r
34439 <dependentAssembly>
\r
34440 <assemblyIdentity type="win32" name="Microsoft.VC90.CRT" version="9.0.21022.8" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
\r
34441 </dependentAssembly>
\r
34443 </assembly>PAPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDING
\0\10\0\0@
\ 1\0\0\ 30
\100!0/080F0L0T0^0d0n0{0
\890
\970¡0¨0®0³0¸0½0Â0È0Ð0ä0ÿ0
\b1#1-1@1J1O1T1v1{1
\841
\891
\961§11´1È1Í1Ó1Û1á1ç1ô1ú1
\ 32"2*23292A2M2_2j2p2¹2¿2Ç2Î2Ó2Ù2ß2ç2í2ô2û2
\v3
\133
\193%303N3T3Z3`3f3l3s3z3
\813
\883
\8f3
\963
\9d3¥33µ3Á3Ê3Ï3Õ3ß3è3ó3ÿ3
\ 44
\144
\194
\1f4%4;4B4
\8b4
\914
\9a4¡4¬4²4Æ4Û4æ4þ4
\145!5^5c5
\845
\895¨5H6M6_6}6
\916
\976
\07
\ 67
\r7*7w7|7Á7ä7ñ7ý7
\ 58
\r8
\198=8E8P8V8\8b8h8n8t8z8
\808
\9c8â8
\ 29
\0\0\0 \0\0$
\0\0\0Ü0è0ì0
\1c1 1t1x1
\1c2 2@2\2`2h2t2
\00
\0\0\f\0\0\0\180
\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0<?php
34454 namespace Symfony\Component\Process;
34456 use Symfony\Component\Process\Exception\RuntimeException;
34469 class PhpProcess extends Process
34471 private $executableFinder;
34484 public function __construct($script, $cwd = null, array $env = array(), $timeout = 60, array $options = array())
34486 parent::__construct(null, $cwd, $env, $script, $timeout, $options);
34488 $this->executableFinder = new PhpExecutableFinder();
34496 public function setPhpBinary($php)
34498 $this->setCommandLine($php);
34504 public function start($callback = null)
34506 if (null === $this->getCommandLine()) {
34507 if (false === $php = $this->executableFinder->find()) {
34508 throw new RuntimeException('Unable to find the PHP executable.');
34510 $this->setCommandLine($php);
34513 parent::start($callback);
34527 namespace Symfony\Component\Process;
34535 class ExecutableFinder
34537 private $suffixes = array('.exe', '.bat', '.cmd', '.com');
34544 public function setSuffixes(array $suffixes)
34546 $this->suffixes = $suffixes;
34554 public function addSuffix($suffix)
34556 $this->suffixes[] = $suffix;
34568 public function find($name, $default = null, array $extraDirs = array())
34570 if (ini_get('open_basedir')) {
34571 $searchPath = explode(PATH_SEPARATOR, ini_get('open_basedir'));
34573 foreach ($searchPath as $path) {
34574 if (is_dir($path)) {
34577 if (basename($path) == $name && is_executable($path)) {
34583 $dirs = array_merge(
34584 explode(PATH_SEPARATOR, getenv('PATH') ?: getenv('Path')),
34589 $suffixes = array('');
34590 if (defined('PHP_WINDOWS_VERSION_BUILD')) {
34591 $pathExt = getenv('PATHEXT');
34592 $suffixes = $pathExt ? explode(PATH_SEPARATOR, $pathExt) : $this->suffixes;
34594 foreach ($suffixes as $suffix) {
34595 foreach ($dirs as $dir) {
34596 if (is_file($file = $dir.DIRECTORY_SEPARATOR.$name.$suffix) && (defined('PHP_WINDOWS_VERSION_BUILD') || is_executable($file))) {
34616 namespace Symfony\Component\Process;
34618 use Symfony\Component\Process\Exception\InvalidArgumentException;
34619 use Symfony\Component\Process\Exception\LogicException;
34620 use Symfony\Component\Process\Exception\ProcessFailedException;
34621 use Symfony\Component\Process\Exception\ProcessTimedOutException;
34622 use Symfony\Component\Process\Exception\RuntimeException;
34623 use Symfony\Component\Process\Pipes\PipesInterface;
34624 use Symfony\Component\Process\Pipes\UnixPipes;
34625 use Symfony\Component\Process\Pipes\WindowsPipes;
34641 const STATUS_READY = 'ready';
34642 const STATUS_STARTED = 'started';
34643 const STATUS_TERMINATED = 'terminated';
34650 const TIMEOUT_PRECISION = 0.2;
34653 private $commandline;
34657 private $starttime;
34658 private $lastOutputTime;
34660 private $idleTimeout;
34663 private $fallbackExitcode;
34664 private $processInformation;
34665 private $outputDisabled = false;
34668 private $enhanceWindowsCompatibility = true;
34669 private $enhanceSigchildCompatibility;
34671 private $status = self::STATUS_READY;
34672 private $incrementalOutputOffset = 0;
34673 private $incrementalErrorOutputOffset = 0;
34677 private $useFileHandles = false;
34679 private $processPipes;
34681 private $latestSignal;
34683 private static $sigchild;
34692 public static $exitCodes = array(
34694 1 => 'General error',
34695 2 => 'Misuse of shell builtins',
34697 126 => 'Invoked command cannot execute',
34698 127 => 'Command not found',
34699 128 => 'Invalid exit argument',
34703 130 => 'Interrupt',
34704 131 => 'Quit and dump core',
34705 132 => 'Illegal instruction',
34706 133 => 'Trace/breakpoint trap',
34707 134 => 'Process aborted',
34708 135 => 'Bus error: "access to undefined portion of memory object"',
34709 136 => 'Floating point exception: "erroneous arithmetic operation"',
34710 137 => 'Kill (terminate immediately)',
34711 138 => 'User-defined 1',
34712 139 => 'Segmentation violation',
34713 140 => 'User-defined 2',
34714 141 => 'Write to pipe with no one reading',
34715 142 => 'Signal raised by alarm',
34716 143 => 'Termination (request to terminate)',
34718 145 => 'Child process terminated, stopped (or continued*)',
34719 146 => 'Continue if stopped',
34720 147 => 'Stop executing temporarily',
34721 148 => 'Terminal stop signal',
34722 149 => 'Background process attempting to read from tty ("in")',
34723 150 => 'Background process attempting to write to tty ("out")',
34724 151 => 'Urgent data available on socket',
34725 152 => 'CPU time limit exceeded',
34726 153 => 'File size limit exceeded',
34727 154 => 'Signal raised by timer counting virtual time: "virtual timer expired"',
34728 155 => 'Profiling timer expired',
34730 157 => 'Pollable event',
34732 159 => 'Bad syscall',
34749 public function __construct($commandline, $cwd = null, array $env = null, $input = null, $timeout = 60, array $options = array())
34751 if (!function_exists('proc_open')) {
34752 throw new RuntimeException('The Process class relies on proc_open, which is not available on your PHP installation.');
34755 $this->commandline = $commandline;
34762 if (null === $this->cwd && (defined('ZEND_THREAD_SAFE') || defined('PHP_WINDOWS_VERSION_BUILD'))) {
34763 $this->cwd = getcwd();
34765 if (null !== $env) {
34766 $this->setEnv($env);
34769 $this->input = $input;
34770 $this->setTimeout($timeout);
34771 $this->useFileHandles = defined('PHP_WINDOWS_VERSION_BUILD');
34772 $this->pty = false;
34773 $this->enhanceWindowsCompatibility = true;
34774 $this->enhanceSigchildCompatibility = !defined('PHP_WINDOWS_VERSION_BUILD') && $this->isSigchildEnabled();
34775 $this->options = array_replace(array('suppress_errors' => true, 'binary_pipes' => true), $options);
34778 public function __destruct()
34784 public function __clone()
34786 $this->resetProcessData();
34810 public function run($callback = null)
34812 $this->start($callback);
34814 return $this->wait();
34830 public function mustRun($callback = null)
34832 if ($this->isSigchildEnabled() && !$this->enhanceSigchildCompatibility) {
34833 throw new RuntimeException('This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method.');
34836 if (0 !== $this->run($callback)) {
34837 throw new ProcessFailedException($this);
34867 public function start($callback = null)
34869 if ($this->isRunning()) {
34870 throw new RuntimeException('Process is already running');
34872 if ($this->outputDisabled && null !== $callback) {
34873 throw new LogicException('Output has been disabled, enable it to allow the use of a callback.');
34876 $this->resetProcessData();
34877 $this->starttime = $this->lastOutputTime = microtime(true);
34878 $this->callback = $this->buildCallback($callback);
34879 $descriptors = $this->getDescriptors();
34881 $commandline = $this->commandline;
34883 if (defined('PHP_WINDOWS_VERSION_BUILD') && $this->enhanceWindowsCompatibility) {
34884 $commandline = 'cmd /V:ON /E:ON /C "('.$commandline.')';
34885 foreach ($this->processPipes->getFiles() as $offset => $filename) {
34886 $commandline .= ' '.$offset.'>'.ProcessUtils::escapeArgument($filename);
34888 $commandline .= '"';
34890 if (!isset($this->options['bypass_shell'])) {
34891 $this->options['bypass_shell'] = true;
34895 $this->process = proc_open($commandline, $descriptors, $this->processPipes->pipes, $this->cwd, $this->env, $this->options);
34897 if (!is_resource($this->process)) {
34898 throw new RuntimeException('Unable to launch a new process.');
34900 $this->status = self::STATUS_STARTED;
34906 $this->updateStatus(false);
34907 $this->checkTimeout();
34925 public function restart($callback = null)
34927 if ($this->isRunning()) {
34928 throw new RuntimeException('Process is already running');
34931 $process = clone $this;
34932 $process->start($callback);
34952 public function wait($callback = null)
34954 $this->requireProcessIsStarted(__FUNCTION__);
34956 $this->updateStatus(false);
34957 if (null !== $callback) {
34958 $this->callback = $this->buildCallback($callback);
34962 $this->checkTimeout();
34963 $running = defined('PHP_WINDOWS_VERSION_BUILD') ? $this->isRunning() : $this->processPipes->areOpen();
34964 $close = !defined('PHP_WINDOWS_VERSION_BUILD') || !$running;
34965 $this->readPipes(true, $close);
34966 } while ($running);
34968 while ($this->isRunning()) {
34972 if ($this->processInformation['signaled'] && $this->processInformation['termsig'] !== $this->latestSignal) {
34973 throw new RuntimeException(sprintf('The process has been signaled with signal "%s".', $this->processInformation['termsig']));
34976 return $this->exitcode;
34986 public function getPid()
34988 if ($this->isSigchildEnabled()) {
34989 throw new RuntimeException('This PHP has been compiled with --enable-sigchild. The process identifier can not be retrieved.');
34992 $this->updateStatus(false);
34994 return $this->isRunning() ? $this->processInformation['pid'] : null;
35008 public function signal($signal)
35010 $this->doSignal($signal, true);
35023 public function disableOutput()
35025 if ($this->isRunning()) {
35026 throw new RuntimeException('Disabling output while the process is running is not possible.');
35028 if (null !== $this->idleTimeout) {
35029 throw new LogicException('Output can not be disabled while an idle timeout is set.');
35032 $this->outputDisabled = true;
35044 public function enableOutput()
35046 if ($this->isRunning()) {
35047 throw new RuntimeException('Enabling output while the process is running is not possible.');
35050 $this->outputDisabled = false;
35060 public function isOutputDisabled()
35062 return $this->outputDisabled;
35075 public function getOutput()
35077 if ($this->outputDisabled) {
35078 throw new LogicException('Output has been disabled.');
35081 $this->requireProcessIsStarted(__FUNCTION__);
35083 $this->readPipes(false, defined('PHP_WINDOWS_VERSION_BUILD') ? !$this->processInformation['running'] : true);
35085 return $this->stdout;
35099 public function getIncrementalOutput()
35101 $this->requireProcessIsStarted(__FUNCTION__);
35103 $data = $this->getOutput();
35105 $latest = substr($data, $this->incrementalOutputOffset);
35106 $this->incrementalOutputOffset = strlen($data);
35116 public function clearOutput()
35118 $this->stdout = '';
35119 $this->incrementalOutputOffset = 0;
35134 public function getErrorOutput()
35136 if ($this->outputDisabled) {
35137 throw new LogicException('Output has been disabled.');
35140 $this->requireProcessIsStarted(__FUNCTION__);
35142 $this->readPipes(false, defined('PHP_WINDOWS_VERSION_BUILD') ? !$this->processInformation['running'] : true);
35144 return $this->stderr;
35159 public function getIncrementalErrorOutput()
35161 $this->requireProcessIsStarted(__FUNCTION__);
35163 $data = $this->getErrorOutput();
35165 $latest = substr($data, $this->incrementalErrorOutputOffset);
35166 $this->incrementalErrorOutputOffset = strlen($data);
35176 public function clearErrorOutput()
35178 $this->stderr = '';
35179 $this->incrementalErrorOutputOffset = 0;
35193 public function getExitCode()
35195 if ($this->isSigchildEnabled() && !$this->enhanceSigchildCompatibility) {
35196 throw new RuntimeException('This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method.');
35199 $this->updateStatus(false);
35201 return $this->exitcode;
35217 public function getExitCodeText()
35219 if (null === $exitcode = $this->getExitCode()) {
35223 return isset(self::$exitCodes[$exitcode]) ? self::$exitCodes[$exitcode] : 'Unknown error';
35233 public function isSuccessful()
35235 return 0 === $this->getExitCode();
35250 public function hasBeenSignaled()
35252 $this->requireProcessIsTerminated(__FUNCTION__);
35254 if ($this->isSigchildEnabled()) {
35255 throw new RuntimeException('This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved.');
35258 $this->updateStatus(false);
35260 return $this->processInformation['signaled'];
35275 public function getTermSignal()
35277 $this->requireProcessIsTerminated(__FUNCTION__);
35279 if ($this->isSigchildEnabled()) {
35280 throw new RuntimeException('This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved.');
35283 $this->updateStatus(false);
35285 return $this->processInformation['termsig'];
35299 public function hasBeenStopped()
35301 $this->requireProcessIsTerminated(__FUNCTION__);
35303 $this->updateStatus(false);
35305 return $this->processInformation['stopped'];
35319 public function getStopSignal()
35321 $this->requireProcessIsTerminated(__FUNCTION__);
35323 $this->updateStatus(false);
35325 return $this->processInformation['stopsig'];
35333 public function isRunning()
35335 if (self::STATUS_STARTED !== $this->status) {
35339 $this->updateStatus(false);
35341 return $this->processInformation['running'];
35349 public function isStarted()
35351 return $this->status != self::STATUS_READY;
35359 public function isTerminated()
35361 $this->updateStatus(false);
35363 return $this->status == self::STATUS_TERMINATED;
35373 public function getStatus()
35375 $this->updateStatus(false);
35377 return $this->status;
35390 public function stop($timeout = 10, $signal = null)
35392 $timeoutMicro = microtime(true) + $timeout;
35393 if ($this->isRunning()) {
35394 if (defined('PHP_WINDOWS_VERSION_BUILD') && !$this->isSigchildEnabled()) {
35395 exec(sprintf("taskkill /F /T /PID %d 2>&1", $this->getPid()), $output, $exitCode);
35396 if ($exitCode > 0) {
35397 throw new RuntimeException('Unable to kill the process');
35401 $this->doSignal(15, false);
35404 } while ($this->isRunning() && microtime(true) < $timeoutMicro);
35406 if ($this->isRunning() && !$this->isSigchildEnabled()) {
35407 if (null !== $signal || defined('SIGKILL')) {
35412 $this->doSignal($signal ?: SIGKILL, false);
35417 $this->updateStatus(false);
35418 if ($this->processInformation['running']) {
35422 return $this->exitcode;
35430 public function addOutput($line)
35432 $this->lastOutputTime = microtime(true);
35433 $this->stdout .= $line;
35441 public function addErrorOutput($line)
35443 $this->lastOutputTime = microtime(true);
35444 $this->stderr .= $line;
35452 public function getCommandLine()
35454 return $this->commandline;
35464 public function setCommandLine($commandline)
35466 $this->commandline = $commandline;
35476 public function getTimeout()
35478 return $this->timeout;
35486 public function getIdleTimeout()
35488 return $this->idleTimeout;
35502 public function setTimeout($timeout)
35504 $this->timeout = $this->validateTimeout($timeout);
35521 public function setIdleTimeout($timeout)
35523 if (null !== $timeout && $this->outputDisabled) {
35524 throw new LogicException('Idle timeout can not be set while the output is disabled.');
35527 $this->idleTimeout = $this->validateTimeout($timeout);
35541 public function setTty($tty)
35543 if (defined('PHP_WINDOWS_VERSION_BUILD') && $tty) {
35544 throw new RuntimeException('TTY mode is not supported on Windows platform.');
35547 $this->tty = (bool) $tty;
35557 public function isTty()
35569 public function setPty($bool)
35571 $this->pty = (bool) $bool;
35581 public function isPty()
35591 public function getWorkingDirectory()
35593 if (null === $this->cwd) {
35596 return getcwd() ?: null;
35609 public function setWorkingDirectory($cwd)
35621 public function getEnv()
35639 public function setEnv(array $env)
35642 $env = array_filter($env, function ($value) {
35643 return !is_array($value);
35646 $this->env = array();
35647 foreach ($env as $key => $value) {
35648 $this->env[(binary) $key] = (binary) $value;
35662 public function getStdin()
35664 return $this->getInput();
35672 public function getInput()
35674 return $this->input;
35690 public function setStdin($stdin)
35692 return $this->setInput($stdin);
35706 public function setInput($input)
35708 if ($this->isRunning()) {
35709 throw new LogicException('Input can not be set while the process is running.');
35712 $this->input = ProcessUtils::validateInput(sprintf('%s::%s', __CLASS__, __FUNCTION__), $input);
35722 public function getOptions()
35724 return $this->options;
35734 public function setOptions(array $options)
35736 $this->options = $options;
35748 public function getEnhanceWindowsCompatibility()
35750 return $this->enhanceWindowsCompatibility;
35760 public function setEnhanceWindowsCompatibility($enhance)
35762 $this->enhanceWindowsCompatibility = (bool) $enhance;
35772 public function getEnhanceSigchildCompatibility()
35774 return $this->enhanceSigchildCompatibility;
35788 public function setEnhanceSigchildCompatibility($enhance)
35790 $this->enhanceSigchildCompatibility = (bool) $enhance;
35803 public function checkTimeout()
35805 if ($this->status !== self::STATUS_STARTED) {
35809 if (null !== $this->timeout && $this->timeout < microtime(true) - $this->starttime) {
35812 throw new ProcessTimedOutException($this, ProcessTimedOutException::TYPE_GENERAL);
35815 if (null !== $this->idleTimeout && $this->idleTimeout < microtime(true) - $this->lastOutputTime) {
35818 throw new ProcessTimedOutException($this, ProcessTimedOutException::TYPE_IDLE);
35827 public static function isPtySupported()
35831 if (null !== $result) {
35835 if (defined('PHP_WINDOWS_VERSION_BUILD')) {
35836 return $result = false;
35839 $proc = @proc_open('echo 1', array(array('pty'), array('pty'), array('pty')), $pipes);
35840 if (is_resource($proc)) {
35843 return $result = true;
35846 return $result = false;
35854 private function getDescriptors()
35856 if (defined('PHP_WINDOWS_VERSION_BUILD')) {
35857 $this->processPipes = WindowsPipes::create($this, $this->input);
35859 $this->processPipes = UnixPipes::create($this, $this->input);
35861 $descriptors = $this->processPipes->getDescriptors($this->outputDisabled);
35863 if (!$this->useFileHandles && $this->enhanceSigchildCompatibility && $this->isSigchildEnabled()) {
35865 $descriptors = array_merge($descriptors, array(array('pipe', 'w')));
35867 $this->commandline = '('.$this->commandline.') 3>/dev/null; code=$?; echo $code >&3; exit $code';
35870 return $descriptors;
35883 protected function buildCallback($callback)
35888 $callback = function ($type, $data) use ($that, $callback, $out, $err) {
35889 if ($out == $type) {
35890 $that->addOutput($data);
35892 $that->addErrorOutput($data);
35895 if (null !== $callback) {
35896 call_user_func($callback, $type, $data);
35908 protected function updateStatus($blocking)
35910 if (self::STATUS_STARTED !== $this->status) {
35914 $this->processInformation = proc_get_status($this->process);
35915 $this->captureExitCode();
35917 $this->readPipes($blocking, defined('PHP_WINDOWS_VERSION_BUILD') ? !$this->processInformation['running'] : true);
35919 if (!$this->processInformation['running']) {
35929 protected function isSigchildEnabled()
35931 if (null !== self::$sigchild) {
35932 return self::$sigchild;
35935 if (!function_exists('phpinfo')) {
35936 return self::$sigchild = false;
35940 phpinfo(INFO_GENERAL);
35942 return self::$sigchild = false !== strpos(ob_get_clean(), '--enable-sigchild');
35954 private function validateTimeout($timeout)
35956 $timeout = (float) $timeout;
35958 if (0.0 === $timeout) {
35960 } elseif ($timeout < 0) {
35961 throw new InvalidArgumentException('The timeout value must be a valid positive integer or float number.');
35973 private function readPipes($blocking, $close)
35975 $result = $this->processPipes->readAndWrite($blocking, $close);
35977 foreach ($result as $type => $data) {
35979 $this->fallbackExitcode = (int) $data;
35981 call_user_func($this->callback, $type === self::STDOUT ? self::OUT : self::ERR, $data);
35989 private function captureExitCode()
35991 if (isset($this->processInformation['exitcode']) && -1 != $this->processInformation['exitcode']) {
35992 $this->exitcode = $this->processInformation['exitcode'];
36001 private function close()
36003 $this->processPipes->close();
36004 if (is_resource($this->process)) {
36005 $exitcode = proc_close($this->process);
36010 $this->exitcode = -1 !== $exitcode ? $exitcode : (null !== $this->exitcode ? $this->exitcode : -1);
36011 $this->status = self::STATUS_TERMINATED;
36013 if (-1 === $this->exitcode && null !== $this->fallbackExitcode) {
36014 $this->exitcode = $this->fallbackExitcode;
36015 } elseif (-1 === $this->exitcode && $this->processInformation['signaled'] && 0 < $this->processInformation['termsig']) {
36017 $this->exitcode = 128 + $this->processInformation['termsig'];
36020 return $this->exitcode;
36026 private function resetProcessData()
36028 $this->starttime = null;
36029 $this->callback = null;
36030 $this->exitcode = null;
36031 $this->fallbackExitcode = null;
36032 $this->processInformation = null;
36033 $this->stdout = null;
36034 $this->stderr = null;
36035 $this->process = null;
36036 $this->latestSignal = null;
36037 $this->status = self::STATUS_READY;
36038 $this->incrementalOutputOffset = 0;
36039 $this->incrementalErrorOutputOffset = 0;
36054 private function doSignal($signal, $throwException)
36056 if (!$this->isRunning()) {
36057 if ($throwException) {
36058 throw new LogicException('Can not send signal on a non running process.');
36064 if ($this->isSigchildEnabled()) {
36065 if ($throwException) {
36066 throw new RuntimeException('This PHP has been compiled with --enable-sigchild. The process can not be signaled.');
36072 if (true !== @proc_terminate($this->process, $signal)) {
36073 if ($throwException) {
36074 throw new RuntimeException(sprintf('Error while sending signal `%s`.', $signal));
36080 $this->latestSignal = $signal;
36092 private function requireProcessIsStarted($functionName)
36094 if (!$this->isStarted()) {
36095 throw new LogicException(sprintf('Process must be started before calling %s.', $functionName));
36106 private function requireProcessIsTerminated($functionName)
36108 if (!$this->isTerminated()) {
36109 throw new LogicException(sprintf('Process must be terminated before calling %s.', $functionName));
36124 namespace Symfony\Component\Process;
36126 use Symfony\Component\Process\Exception\InvalidArgumentException;
36127 use Symfony\Component\Process\Exception\LogicException;
36134 class ProcessBuilder
36136 private $arguments;
36138 private $env = array();
36140 private $timeout = 60;
36141 private $options = array();
36142 private $inheritEnv = true;
36143 private $prefix = array();
36144 private $outputDisabled = false;
36151 public function __construct(array $arguments = array())
36153 $this->arguments = $arguments;
36163 public static function create(array $arguments = array())
36165 return new static($arguments);
36175 public function add($argument)
36177 $this->arguments[] = $argument;
36191 public function setPrefix($prefix)
36193 $this->prefix = is_array($prefix) ? $prefix : array($prefix);
36208 public function setArguments(array $arguments)
36210 $this->arguments = $arguments;
36222 public function setWorkingDirectory($cwd)
36236 public function inheritEnvironmentVariables($inheritEnv = true)
36238 $this->inheritEnv = $inheritEnv;
36254 public function setEnv($name, $value)
36256 $this->env[$name] = $value;
36272 public function addEnvironmentVariables(array $variables)
36274 $this->env = array_replace($this->env, $variables);
36290 public function setInput($input)
36292 $this->input = ProcessUtils::validateInput(sprintf('%s::%s', __CLASS__, __FUNCTION__), $input);
36308 public function setTimeout($timeout)
36310 if (null === $timeout) {
36311 $this->timeout = null;
36316 $timeout = (float) $timeout;
36318 if ($timeout < 0) {
36319 throw new InvalidArgumentException('The timeout value must be a valid positive integer or float number.');
36322 $this->timeout = $timeout;
36335 public function setOption($name, $value)
36337 $this->options[$name] = $value;
36347 public function disableOutput()
36349 $this->outputDisabled = true;
36359 public function enableOutput()
36361 $this->outputDisabled = false;
36373 public function getProcess()
36375 if (0 === count($this->prefix) && 0 === count($this->arguments)) {
36376 throw new LogicException('You must add() command arguments before calling getProcess().');
36379 $options = $this->options;
36381 $arguments = array_merge($this->prefix, $this->arguments);
36382 $script = implode(' ', array_map(array(__NAMESPACE__.'\\ProcessUtils', 'escapeArgument'), $arguments));
36384 if ($this->inheritEnv) {
36386 $env = array_replace($_ENV, $_SERVER, $this->env);
36391 $process = new Process($script, $this->cwd, $env, $this->input, $this->timeout, $options);
36393 if ($this->outputDisabled) {
36394 $process->disableOutput();
36411 namespace Symfony\Component\Process;
36413 use Symfony\Component\Process\Exception\InvalidArgumentException;
36427 private function __construct()
36438 public static function escapeArgument($argument)
36444 if (defined('PHP_WINDOWS_VERSION_BUILD')) {
36445 if ('' === $argument) {
36446 return escapeshellarg($argument);
36449 $escapedArgument = '';
36451 foreach (preg_split('/(")/i', $argument, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE) as $part) {
36452 if ('"' === $part) {
36453 $escapedArgument .= '\\"';
36454 } elseif (self::isSurroundedBy($part, '%')) {
36456 $escapedArgument .= '^%"'.substr($part, 1, -1).'"^%';
36459 if ('\\' === substr($part, -1)) {
36463 $escapedArgument .= $part;
36467 $escapedArgument = '"'.$escapedArgument.'"';
36470 return $escapedArgument;
36473 return escapeshellarg($argument);
36486 public static function validateInput($caller, $input)
36488 if (null !== $input) {
36489 if (is_resource($input)) {
36492 if (is_scalar($input)) {
36493 return (string) $input;
36496 if (is_object($input) && method_exists($input, '__toString')) {
36497 return (string) $input;
36500 throw new InvalidArgumentException(sprintf('%s only accepts strings or stream resources.', $caller));
36506 private static function isSurroundedBy($arg, $char)
36508 return 2 < strlen($arg) && $char === $arg[0] && $char === $arg[strlen($arg) - 1];
36522 namespace Symfony\Component\Process\Exception;
36524 use Symfony\Component\Process\Process;
36531 class ProcessTimedOutException extends RuntimeException
36533 const TYPE_GENERAL = 1;
36534 const TYPE_IDLE = 2;
36537 private $timeoutType;
36539 public function __construct(Process $process, $timeoutType)
36541 $this->process = $process;
36542 $this->timeoutType = $timeoutType;
36544 parent::__construct(sprintf(
36545 'The process "%s" exceeded the timeout of %s seconds.',
36546 $process->getCommandLine(),
36547 $this->getExceededTimeout()
36551 public function getProcess()
36553 return $this->process;
36556 public function isGeneralTimeout()
36558 return $this->timeoutType === self::TYPE_GENERAL;
36561 public function isIdleTimeout()
36563 return $this->timeoutType === self::TYPE_IDLE;
36566 public function getExceededTimeout()
36568 switch ($this->timeoutType) {
36569 case self::TYPE_GENERAL:
36570 return $this->process->getTimeout();
36572 case self::TYPE_IDLE:
36573 return $this->process->getIdleTimeout();
36576 throw new \LogicException(sprintf('Unknown timeout type "%d".', $this->timeoutType));
36591 namespace Symfony\Component\Process\Exception;
36598 interface ExceptionInterface
36612 namespace Symfony\Component\Process\Exception;
36619 class RuntimeException extends \RuntimeException implements ExceptionInterface
36633 namespace Symfony\Component\Process\Exception;
36640 class LogicException extends \LogicException implements ExceptionInterface
36654 namespace Symfony\Component\Process\Exception;
36656 use Symfony\Component\Process\Process;
36663 class ProcessFailedException extends RuntimeException
36667 public function __construct(Process $process)
36669 if ($process->isSuccessful()) {
36670 throw new InvalidArgumentException('Expected a failed process, but the given process was successful.');
36673 $error = sprintf('The command "%s" failed.'."\nExit Code: %s(%s)",
36674 $process->getCommandLine(),
36675 $process->getExitCode(),
36676 $process->getExitCodeText()
36679 if (!$process->isOutputDisabled()) {
36680 $error .= sprintf("\n\nOutput:\n================\n%s\n\nError Output:\n================\n%s",
36681 $process->getOutput(),
36682 $process->getErrorOutput()
36686 parent::__construct($error);
36688 $this->process = $process;
36691 public function getProcess()
36693 return $this->process;
36707 namespace Symfony\Component\Process\Exception;
36714 class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
36728 namespace Symfony\Component\Process;
36736 class PhpExecutableFinder
36738 private $executableFinder;
36740 public function __construct()
36742 $this->executableFinder = new ExecutableFinder();
36752 public function find($includeArgs = true)
36755 if (defined('HHVM_VERSION')) {
36756 return (false !== ($hhvm = getenv('PHP_BINARY')) ? $hhvm : PHP_BINARY).($includeArgs ? ' '.implode(' ', $this->findArguments()) : '');
36760 if (defined('PHP_BINARY') && PHP_BINARY && in_array(PHP_SAPI, array('cli', 'cli-server')) && is_file(PHP_BINARY)) {
36764 if ($php = getenv('PHP_PATH')) {
36765 if (!is_executable($php)) {
36772 if ($php = getenv('PHP_PEAR_PHP_BIN')) {
36773 if (is_executable($php)) {
36778 $dirs = array(PHP_BINDIR);
36779 if (defined('PHP_WINDOWS_VERSION_BUILD')) {
36780 $dirs[] = 'C:\xampp\php\\';
36783 return $this->executableFinder->find('php', false, $dirs);
36791 public function findArguments()
36793 $arguments = array();
36796 if (defined('HHVM_VERSION')) {
36797 $arguments[] = '--php';
36814 namespace Symfony\Component\Process\Pipes;
36823 interface PipesInterface
36825 const CHUNK_SIZE = 16384;
36832 public function getDescriptors();
36839 public function getFiles();
36849 public function readAndWrite($blocking, $close = false);
36856 public function areOpen();
36861 public function close();
36874 namespace Symfony\Component\Process\Pipes;
36881 abstract class AbstractPipes implements PipesInterface
36884 public $pipes = array();
36887 protected $inputBuffer = '';
36892 private $blocked = true;
36897 public function close()
36899 foreach ($this->pipes as $pipe) {
36902 $this->pipes = array();
36910 protected function hasSystemCallBeenInterrupted()
36912 $lastError = error_get_last();
36915 return isset($lastError['message']) && false !== stripos($lastError['message'], 'interrupted system call');
36921 protected function unblock()
36923 if (!$this->blocked) {
36927 foreach ($this->pipes as $pipe) {
36928 stream_set_blocking($pipe, 0);
36930 if (null !== $this->input) {
36931 stream_set_blocking($this->input, 0);
36934 $this->blocked = false;
36948 namespace Symfony\Component\Process\Pipes;
36950 use Symfony\Component\Process\Process;
36951 use Symfony\Component\Process\Exception\RuntimeException;
36963 class WindowsPipes extends AbstractPipes
36966 private $files = array();
36968 private $fileHandles = array();
36970 private $readBytes = array(
36971 Process::STDOUT => 0,
36972 Process::STDERR => 0,
36975 private $disableOutput;
36977 public function __construct($disableOutput, $input)
36979 $this->disableOutput = (bool) $disableOutput;
36981 if (!$this->disableOutput) {
36986 $this->files = array(
36987 Process::STDOUT => tempnam(sys_get_temp_dir(), 'sf_proc_stdout'),
36988 Process::STDERR => tempnam(sys_get_temp_dir(), 'sf_proc_stderr'),
36990 foreach ($this->files as $offset => $file) {
36991 $this->fileHandles[$offset] = fopen($this->files[$offset], 'rb');
36992 if (false === $this->fileHandles[$offset]) {
36993 throw new RuntimeException('A temporary file could not be opened to write the process output to, verify that your TEMP environment variable is writable');
36998 if (is_resource($input)) {
36999 $this->input = $input;
37001 $this->inputBuffer = $input;
37005 public function __destruct()
37008 $this->removeFiles();
37014 public function getDescriptors()
37016 if ($this->disableOutput) {
37017 $nullstream = fopen('NUL', 'c');
37020 array('pipe', 'r'),
37030 array('pipe', 'r'),
37031 array('file', 'NUL', 'w'),
37032 array('file', 'NUL', 'w'),
37039 public function getFiles()
37041 return $this->files;
37047 public function readAndWrite($blocking, $close = false)
37049 $this->write($blocking, $close);
37052 $fh = $this->fileHandles;
37053 foreach ($fh as $type => $fileHandle) {
37054 if (0 !== fseek($fileHandle, $this->readBytes[$type])) {
37059 while (!feof($fileHandle)) {
37060 if (false !== $dataread = fread($fileHandle, self::CHUNK_SIZE)) {
37061 $data .= $dataread;
37064 if (0 < $length = strlen($data)) {
37065 $this->readBytes[$type] += $length;
37066 $read[$type] = $data;
37069 if (false === $dataread || (true === $close && feof($fileHandle) && '' === $data)) {
37070 fclose($this->fileHandles[$type]);
37071 unset($this->fileHandles[$type]);
37081 public function areOpen()
37083 return (bool) $this->pipes && (bool) $this->fileHandles;
37089 public function close()
37092 foreach ($this->fileHandles as $handle) {
37095 $this->fileHandles = array();
37106 public static function create(Process $process, $input)
37108 return new static($process->isOutputDisabled(), $input);
37114 private function removeFiles()
37116 foreach ($this->files as $filename) {
37117 if (file_exists($filename)) {
37118 @unlink($filename);
37121 $this->files = array();
37130 private function write($blocking, $close)
37132 if (empty($this->pipes)) {
37138 $r = null !== $this->input ? array('input' => $this->input) : null;
37139 $w = isset($this->pipes[0]) ? array($this->pipes[0]) : null;
37143 if (false === $n = @stream_select($r, $w, $e, 0, $blocking ? Process::TIMEOUT_PRECISION * 1E6 : 0)) {
37146 if (!$this->hasSystemCallBeenInterrupted()) {
37147 $this->pipes = array();
37158 if (null !== $w && 0 < count($r)) {
37160 while ($dataread = fread($r['input'], self::CHUNK_SIZE)) {
37161 $data .= $dataread;
37164 $this->inputBuffer .= $data;
37166 if (false === $data || (true === $close && feof($r['input']) && '' === $data)) {
37169 unset($this->input);
37173 if (null !== $w && 0 < count($w)) {
37174 while ($len = strlen($this->inputBuffer)) {
37175 $written = fwrite($w[0], $this->inputBuffer, 2 << 18);
37176 if ($written > 0) {
37177 $this->inputBuffer = (string) substr($this->inputBuffer, $written);
37185 if ('' === $this->inputBuffer && null === $this->input && isset($this->pipes[0])) {
37186 fclose($this->pipes[0]);
37187 unset($this->pipes[0]);
37202 namespace Symfony\Component\Process\Pipes;
37204 use Symfony\Component\Process\Process;
37213 class UnixPipes extends AbstractPipes
37220 private $disableOutput;
37222 public function __construct($ttyMode, $ptyMode, $input, $disableOutput)
37224 $this->ttyMode = (bool) $ttyMode;
37225 $this->ptyMode = (bool) $ptyMode;
37226 $this->disableOutput = (bool) $disableOutput;
37228 if (is_resource($input)) {
37229 $this->input = $input;
37231 $this->inputBuffer = (string) $input;
37235 public function __destruct()
37243 public function getDescriptors()
37245 if ($this->disableOutput) {
37246 $nullstream = fopen('/dev/null', 'c');
37249 array('pipe', 'r'),
37255 if ($this->ttyMode) {
37257 array('file', '/dev/tty', 'r'),
37258 array('file', '/dev/tty', 'w'),
37259 array('file', '/dev/tty', 'w'),
37263 if ($this->ptyMode && Process::isPtySupported()) {
37272 array('pipe', 'r'),
37273 array('pipe', 'w'),
37274 array('pipe', 'w'),
37281 public function getFiles()
37289 public function readAndWrite($blocking, $close = false)
37293 if (1 === count($this->pipes) && array(0) === array_keys($this->pipes)) {
37294 fclose($this->pipes[0]);
37295 unset($this->pipes[0]);
37298 if (empty($this->pipes)) {
37306 if (null !== $this->input) {
37309 $r = array_merge($this->pipes, array('input' => $this->input));
37316 $w = isset($this->pipes[0]) ? array($this->pipes[0]) : null;
37320 if (false === $n = @stream_select($r, $w, $e, 0, $blocking ? Process::TIMEOUT_PRECISION * 1E6 : 0)) {
37323 if (!$this->hasSystemCallBeenInterrupted()) {
37324 $this->pipes = array();
37335 foreach ($r as $pipe) {
37338 $type = (false !== $found = array_search($pipe, $this->pipes)) ? $found : 'input';
37340 while ('' !== $dataread = (string) fread($pipe, self::CHUNK_SIZE)) {
37341 $data .= $dataread;
37344 if ('' !== $data) {
37345 if ($type === 'input') {
37346 $this->inputBuffer .= $data;
37348 $read[$type] = $data;
37352 if (false === $data || (true === $close && feof($pipe) && '' === $data)) {
37353 if ($type === 'input') {
37356 $this->input = null;
37358 fclose($this->pipes[$type]);
37359 unset($this->pipes[$type]);
37364 if (null !== $w && 0 < count($w)) {
37365 while ($len = strlen($this->inputBuffer)) {
37366 $written = fwrite($w[0], $this->inputBuffer, 2 << 18);
37367 if ($written > 0) {
37368 $this->inputBuffer = (string) substr($this->inputBuffer, $written);
37376 if ('' === $this->inputBuffer && null === $this->input && isset($this->pipes[0])) {
37377 fclose($this->pipes[0]);
37378 unset($this->pipes[0]);
37387 public function areOpen()
37389 return (bool) $this->pipes;
37400 public static function create(Process $process, $input)
37402 return new static($process->isTty(), $process->isPty(), $input, $process->isOutputDisabled());
37416 namespace Symfony\Component\Console\Command;
37418 use Symfony\Component\Console\Helper\DescriptorHelper;
37419 use Symfony\Component\Console\Input\InputArgument;
37420 use Symfony\Component\Console\Input\InputOption;
37421 use Symfony\Component\Console\Input\InputInterface;
37422 use Symfony\Component\Console\Output\OutputInterface;
37429 class HelpCommand extends Command
37436 protected function configure()
37438 $this->ignoreValidationErrors();
37442 ->setDefinition(array(
37443 new InputArgument('command_name', InputArgument::OPTIONAL, 'The command name', 'help'),
37444 new InputOption('xml', null, InputOption::VALUE_NONE, 'To output help as XML'),
37445 new InputOption('format', null, InputOption::VALUE_REQUIRED, 'To output help in other formats', 'txt'),
37446 new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command help'),
37448 ->setDescription('Displays help for a command')
37450 The <info>%command.name%</info> command displays help for a given command:
37452 <info>php %command.full_name% list</info>
37454 You can also output the help in other formats by using the <comment>--format</comment> option:
37456 <info>php %command.full_name% --format=xml list</info>
37458 To display the list of available commands, please use the <info>list</info> command.
37469 public function setCommand(Command $command)
37471 $this->command = $command;
37477 protected function execute(InputInterface $input, OutputInterface $output)
37479 if (null === $this->command) {
37480 $this->command = $this->getApplication()->find($input->getArgument('command_name'));
37483 if ($input->getOption('xml')) {
37484 $input->setOption('format', 'xml');
37487 $helper = new DescriptorHelper();
37488 $helper->describe($output, $this->command, array(
37489 'format' => $input->getOption('format'),
37490 'raw' => $input->getOption('raw'),
37493 $this->command = null;
37507 namespace Symfony\Component\Console\Command;
37509 use Symfony\Component\Console\Descriptor\TextDescriptor;
37510 use Symfony\Component\Console\Descriptor\XmlDescriptor;
37511 use Symfony\Component\Console\Input\InputDefinition;
37512 use Symfony\Component\Console\Input\InputOption;
37513 use Symfony\Component\Console\Input\InputArgument;
37514 use Symfony\Component\Console\Input\InputInterface;
37515 use Symfony\Component\Console\Output\BufferedOutput;
37516 use Symfony\Component\Console\Output\OutputInterface;
37517 use Symfony\Component\Console\Application;
37518 use Symfony\Component\Console\Helper\HelperSet;
37529 private $application;
37531 private $processTitle;
37532 private $aliases = array();
37533 private $definition;
37535 private $description;
37536 private $ignoreValidationErrors = false;
37537 private $applicationDefinitionMerged = false;
37538 private $applicationDefinitionMergedWithArgs = false;
37541 private $helperSet;
37552 public function __construct($name = null)
37554 $this->definition = new InputDefinition();
37556 if (null !== $name) {
37557 $this->setName($name);
37560 $this->configure();
37562 if (!$this->name) {
37563 throw new \LogicException(sprintf('The command defined in "%s" cannot have an empty name.', get_class($this)));
37572 public function ignoreValidationErrors()
37574 $this->ignoreValidationErrors = true;
37584 public function setApplication(Application $application = null)
37586 $this->application = $application;
37587 if ($application) {
37588 $this->setHelperSet($application->getHelperSet());
37590 $this->helperSet = null;
37599 public function setHelperSet(HelperSet $helperSet)
37601 $this->helperSet = $helperSet;
37609 public function getHelperSet()
37611 return $this->helperSet;
37621 public function getApplication()
37623 return $this->application;
37634 public function isEnabled()
37642 protected function configure()
37662 protected function execute(InputInterface $input, OutputInterface $output)
37664 throw new \LogicException('You must override the execute() method in the concrete command class.');
37673 protected function interact(InputInterface $input, OutputInterface $output)
37686 protected function initialize(InputInterface $input, OutputInterface $output)
37709 public function run(InputInterface $input, OutputInterface $output)
37712 $this->getSynopsis();
37715 $this->mergeApplicationDefinition();
37719 $input->bind($this->definition);
37720 } catch (\Exception $e) {
37721 if (!$this->ignoreValidationErrors) {
37726 $this->initialize($input, $output);
37728 if (null !== $this->processTitle) {
37729 if (function_exists('cli_set_process_title')) {
37730 cli_set_process_title($this->processTitle);
37731 } elseif (function_exists('setproctitle')) {
37732 setproctitle($this->processTitle);
37733 } elseif (OutputInterface::VERBOSITY_VERY_VERBOSE === $output->getVerbosity()) {
37734 $output->writeln('<comment>Install the proctitle PECL to be able to change the process title.</comment>');
37738 if ($input->isInteractive()) {
37739 $this->interact($input, $output);
37742 $input->validate();
37745 $statusCode = call_user_func($this->code, $input, $output);
37747 $statusCode = $this->execute($input, $output);
37750 return is_numeric($statusCode) ? (int) $statusCode : 0;
37769 public function setCode($code)
37771 if (!is_callable($code)) {
37772 throw new \InvalidArgumentException('Invalid callable provided to Command::setCode.');
37775 $this->code = $code;
37787 public function mergeApplicationDefinition($mergeArgs = true)
37789 if (null === $this->application || (true === $this->applicationDefinitionMerged && ($this->applicationDefinitionMergedWithArgs || !$mergeArgs))) {
37794 $currentArguments = $this->definition->getArguments();
37795 $this->definition->setArguments($this->application->getDefinition()->getArguments());
37796 $this->definition->addArguments($currentArguments);
37799 $this->definition->addOptions($this->application->getDefinition()->getOptions());
37801 $this->applicationDefinitionMerged = true;
37803 $this->applicationDefinitionMergedWithArgs = true;
37816 public function setDefinition($definition)
37818 if ($definition instanceof InputDefinition) {
37819 $this->definition = $definition;
37821 $this->definition->setDefinition($definition);
37824 $this->applicationDefinitionMerged = false;
37836 public function getDefinition()
37838 return $this->definition;
37851 public function getNativeDefinition()
37853 return $this->getDefinition();
37868 public function addArgument($name, $mode = null, $description = '', $default = null)
37870 $this->definition->addArgument(new InputArgument($name, $mode, $description, $default));
37888 public function addOption($name, $shortcut = null, $mode = null, $description = '', $default = null)
37890 $this->definition->addOption(new InputOption($name, $shortcut, $mode, $description, $default));
37911 public function setName($name)
37913 $this->validateName($name);
37915 $this->name = $name;
37932 public function setProcessTitle($title)
37934 $this->processTitle = $title;
37946 public function getName()
37948 return $this->name;
37960 public function setDescription($description)
37962 $this->description = $description;
37974 public function getDescription()
37976 return $this->description;
37988 public function setHelp($help)
37990 $this->help = $help;
38002 public function getHelp()
38004 return $this->help;
38013 public function getProcessedHelp()
38015 $name = $this->name;
38017 $placeholders = array(
38019 '%command.full_name%',
38021 $replacements = array(
38023 $_SERVER['PHP_SELF'].' '.$name,
38026 return str_replace($placeholders, $replacements, $this->getHelp());
38040 public function setAliases($aliases)
38042 if (!is_array($aliases) && !$aliases instanceof \Traversable) {
38043 throw new \InvalidArgumentException('$aliases must be an array or an instance of \Traversable');
38046 foreach ($aliases as $alias) {
38047 $this->validateName($alias);
38050 $this->aliases = $aliases;
38062 public function getAliases()
38064 return $this->aliases;
38072 public function getSynopsis()
38074 if (null === $this->synopsis) {
38075 $this->synopsis = trim(sprintf('%s %s', $this->name, $this->definition->getSynopsis()));
38078 return $this->synopsis;
38092 public function getHelper($name)
38094 return $this->helperSet->get($name);
38104 public function asText()
38106 $descriptor = new TextDescriptor();
38107 $output = new BufferedOutput(BufferedOutput::VERBOSITY_NORMAL, true);
38108 $descriptor->describe($output, $this, array('raw_output' => true));
38110 return $output->fetch();
38122 public function asXml($asDom = false)
38124 $descriptor = new XmlDescriptor();
38127 return $descriptor->getCommandDocument($this);
38130 $output = new BufferedOutput();
38131 $descriptor->describe($output, $this);
38133 return $output->fetch();
38145 private function validateName($name)
38147 if (!preg_match('/^[^\:]++(\:[^\:]++)*$/', $name)) {
38148 throw new \InvalidArgumentException(sprintf('Command name "%s" is invalid.', $name));
38163 namespace Symfony\Component\Console\Command;
38165 use Symfony\Component\Console\Helper\DescriptorHelper;
38166 use Symfony\Component\Console\Input\InputArgument;
38167 use Symfony\Component\Console\Input\InputOption;
38168 use Symfony\Component\Console\Input\InputInterface;
38169 use Symfony\Component\Console\Output\OutputInterface;
38170 use Symfony\Component\Console\Input\InputDefinition;
38177 class ListCommand extends Command
38182 protected function configure()
38186 ->setDefinition($this->createDefinition())
38187 ->setDescription('Lists commands')
38189 The <info>%command.name%</info> command lists all commands:
38191 <info>php %command.full_name%</info>
38193 You can also display the commands for a specific namespace:
38195 <info>php %command.full_name% test</info>
38197 You can also output the information in other formats by using the <comment>--format</comment> option:
38199 <info>php %command.full_name% --format=xml</info>
38201 It's also possible to get raw list of commands (useful for embedding command runner):
38203 <info>php %command.full_name% --raw</info>
38212 public function getNativeDefinition()
38214 return $this->createDefinition();
38220 protected function execute(InputInterface $input, OutputInterface $output)
38222 if ($input->getOption('xml')) {
38223 $input->setOption('format', 'xml');
38226 $helper = new DescriptorHelper();
38227 $helper->describe($output, $this->getApplication(), array(
38228 'format' => $input->getOption('format'),
38229 'raw_text' => $input->getOption('raw'),
38230 'namespace' => $input->getArgument('namespace'),
38237 private function createDefinition()
38239 return new InputDefinition(array(
38240 new InputArgument('namespace', InputArgument::OPTIONAL, 'The namespace name'),
38241 new InputOption('xml', null, InputOption::VALUE_NONE, 'To output list as XML'),
38242 new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command list'),
38243 new InputOption('format', null, InputOption::VALUE_REQUIRED, 'To output list in other formats', 'txt'),
38258 namespace Symfony\Component\Console\Tester;
38260 use Symfony\Component\Console\Application;
38261 use Symfony\Component\Console\Input\ArrayInput;
38262 use Symfony\Component\Console\Input\InputInterface;
38263 use Symfony\Component\Console\Output\OutputInterface;
38264 use Symfony\Component\Console\Output\StreamOutput;
38276 class ApplicationTester
38278 private $application;
38281 private $statusCode;
38288 public function __construct(Application $application)
38290 $this->application = $application;
38307 public function run(array $input, $options = array())
38309 $this->input = new ArrayInput($input);
38310 if (isset($options['interactive'])) {
38311 $this->input->setInteractive($options['interactive']);
38314 $this->output = new StreamOutput(fopen('php://memory', 'w', false));
38315 if (isset($options['decorated'])) {
38316 $this->output->setDecorated($options['decorated']);
38318 if (isset($options['verbosity'])) {
38319 $this->output->setVerbosity($options['verbosity']);
38322 return $this->statusCode = $this->application->run($this->input, $this->output);
38332 public function getDisplay($normalize = false)
38334 rewind($this->output->getStream());
38336 $display = stream_get_contents($this->output->getStream());
38339 $display = str_replace(PHP_EOL, "\n", $display);
38350 public function getInput()
38352 return $this->input;
38360 public function getOutput()
38362 return $this->output;
38370 public function getStatusCode()
38372 return $this->statusCode;
38386 namespace Symfony\Component\Console\Tester;
38388 use Symfony\Component\Console\Command\Command;
38389 use Symfony\Component\Console\Input\ArrayInput;
38390 use Symfony\Component\Console\Output\StreamOutput;
38391 use Symfony\Component\Console\Input\InputInterface;
38392 use Symfony\Component\Console\Output\OutputInterface;
38399 class CommandTester
38404 private $statusCode;
38411 public function __construct(Command $command)
38413 $this->command = $command;
38430 public function execute(array $input, array $options = array())
38434 if (!isset($input['command'])
38435 && (null !== $application = $this->command->getApplication())
38436 && $application->getDefinition()->hasArgument('command')
38438 $input['command'] = $this->command->getName();
38441 $this->input = new ArrayInput($input);
38442 if (isset($options['interactive'])) {
38443 $this->input->setInteractive($options['interactive']);
38446 $this->output = new StreamOutput(fopen('php://memory', 'w', false));
38447 if (isset($options['decorated'])) {
38448 $this->output->setDecorated($options['decorated']);
38450 if (isset($options['verbosity'])) {
38451 $this->output->setVerbosity($options['verbosity']);
38454 return $this->statusCode = $this->command->run($this->input, $this->output);
38464 public function getDisplay($normalize = false)
38466 rewind($this->output->getStream());
38468 $display = stream_get_contents($this->output->getStream());
38471 $display = str_replace(PHP_EOL, "\n", $display);
38482 public function getInput()
38484 return $this->input;
38492 public function getOutput()
38494 return $this->output;
38502 public function getStatusCode()
38504 return $this->statusCode;
38518 namespace Symfony\Component\Console\Formatter;
38523 class OutputFormatterStyleStack
38533 private $emptyStyle;
38540 public function __construct(OutputFormatterStyleInterface $emptyStyle = null)
38542 $this->emptyStyle = $emptyStyle ?: new OutputFormatterStyle();
38549 public function reset()
38551 $this->styles = array();
38559 public function push(OutputFormatterStyleInterface $style)
38561 $this->styles[] = $style;
38573 public function pop(OutputFormatterStyleInterface $style = null)
38575 if (empty($this->styles)) {
38576 return $this->emptyStyle;
38579 if (null === $style) {
38580 return array_pop($this->styles);
38583 foreach (array_reverse($this->styles, true) as $index => $stackedStyle) {
38584 if ($style->apply('') === $stackedStyle->apply('')) {
38585 $this->styles = array_slice($this->styles, 0, $index);
38587 return $stackedStyle;
38591 throw new \InvalidArgumentException('Incorrectly nested style tag found.');
38599 public function getCurrent()
38601 if (empty($this->styles)) {
38602 return $this->emptyStyle;
38605 return $this->styles[count($this->styles)-1];
38613 public function setEmptyStyle(OutputFormatterStyleInterface $emptyStyle)
38615 $this->emptyStyle = $emptyStyle;
38623 public function getEmptyStyle()
38625 return $this->emptyStyle;
38639 namespace Symfony\Component\Console\Formatter;
38648 class OutputFormatterStyle implements OutputFormatterStyleInterface
38650 private static $availableForegroundColors = array(
38651 'black' => array('set' => 30, 'unset' => 39),
38652 'red' => array('set' => 31, 'unset' => 39),
38653 'green' => array('set' => 32, 'unset' => 39),
38654 'yellow' => array('set' => 33, 'unset' => 39),
38655 'blue' => array('set' => 34, 'unset' => 39),
38656 'magenta' => array('set' => 35, 'unset' => 39),
38657 'cyan' => array('set' => 36, 'unset' => 39),
38658 'white' => array('set' => 37, 'unset' => 39),
38660 private static $availableBackgroundColors = array(
38661 'black' => array('set' => 40, 'unset' => 49),
38662 'red' => array('set' => 41, 'unset' => 49),
38663 'green' => array('set' => 42, 'unset' => 49),
38664 'yellow' => array('set' => 43, 'unset' => 49),
38665 'blue' => array('set' => 44, 'unset' => 49),
38666 'magenta' => array('set' => 45, 'unset' => 49),
38667 'cyan' => array('set' => 46, 'unset' => 49),
38668 'white' => array('set' => 47, 'unset' => 49),
38670 private static $availableOptions = array(
38671 'bold' => array('set' => 1, 'unset' => 22),
38672 'underscore' => array('set' => 4, 'unset' => 24),
38673 'blink' => array('set' => 5, 'unset' => 25),
38674 'reverse' => array('set' => 7, 'unset' => 27),
38675 'conceal' => array('set' => 8, 'unset' => 28),
38678 private $foreground;
38679 private $background;
38680 private $options = array();
38691 public function __construct($foreground = null, $background = null, array $options = array())
38693 if (null !== $foreground) {
38694 $this->setForeground($foreground);
38696 if (null !== $background) {
38697 $this->setBackground($background);
38699 if (count($options)) {
38700 $this->setOptions($options);
38713 public function setForeground($color = null)
38715 if (null === $color) {
38716 $this->foreground = null;
38721 if (!isset(static::$availableForegroundColors[$color])) {
38722 throw new \InvalidArgumentException(sprintf(
38723 'Invalid foreground color specified: "%s". Expected one of (%s)',
38725 implode(', ', array_keys(static::$availableForegroundColors))
38729 $this->foreground = static::$availableForegroundColors[$color];
38741 public function setBackground($color = null)
38743 if (null === $color) {
38744 $this->background = null;
38749 if (!isset(static::$availableBackgroundColors[$color])) {
38750 throw new \InvalidArgumentException(sprintf(
38751 'Invalid background color specified: "%s". Expected one of (%s)',
38753 implode(', ', array_keys(static::$availableBackgroundColors))
38757 $this->background = static::$availableBackgroundColors[$color];
38769 public function setOption($option)
38771 if (!isset(static::$availableOptions[$option])) {
38772 throw new \InvalidArgumentException(sprintf(
38773 'Invalid option specified: "%s". Expected one of (%s)',
38775 implode(', ', array_keys(static::$availableOptions))
38779 if (false === array_search(static::$availableOptions[$option], $this->options)) {
38780 $this->options[] = static::$availableOptions[$option];
38792 public function unsetOption($option)
38794 if (!isset(static::$availableOptions[$option])) {
38795 throw new \InvalidArgumentException(sprintf(
38796 'Invalid option specified: "%s". Expected one of (%s)',
38798 implode(', ', array_keys(static::$availableOptions))
38802 $pos = array_search(static::$availableOptions[$option], $this->options);
38803 if (false !== $pos) {
38804 unset($this->options[$pos]);
38813 public function setOptions(array $options)
38815 $this->options = array();
38817 foreach ($options as $option) {
38818 $this->setOption($option);
38829 public function apply($text)
38831 $setCodes = array();
38832 $unsetCodes = array();
38834 if (null !== $this->foreground) {
38835 $setCodes[] = $this->foreground['set'];
38836 $unsetCodes[] = $this->foreground['unset'];
38838 if (null !== $this->background) {
38839 $setCodes[] = $this->background['set'];
38840 $unsetCodes[] = $this->background['unset'];
38842 if (count($this->options)) {
38843 foreach ($this->options as $option) {
38844 $setCodes[] = $option['set'];
38845 $unsetCodes[] = $option['unset'];
38849 if (0 === count($setCodes)) {
38853 return sprintf("\033[%sm%s\033[%sm", implode(';', $setCodes), $text, implode(';', $unsetCodes));
38867 namespace Symfony\Component\Console\Formatter;
38876 interface OutputFormatterStyleInterface
38885 public function setForeground($color = null);
38894 public function setBackground($color = null);
38903 public function setOption($option);
38910 public function unsetOption($option);
38917 public function setOptions(array $options);
38926 public function apply($text);
38939 namespace Symfony\Component\Console\Formatter;
38948 class OutputFormatter implements OutputFormatterInterface
38950 private $decorated;
38951 private $styles = array();
38952 private $styleStack;
38961 public static function escape($text)
38963 return preg_replace('/([^\\\\]?)</is', '$1\\<', $text);
38974 public function __construct($decorated = false, array $styles = array())
38976 $this->decorated = (bool) $decorated;
38978 $this->setStyle('error', new OutputFormatterStyle('white', 'red'));
38979 $this->setStyle('info', new OutputFormatterStyle('green'));
38980 $this->setStyle('comment', new OutputFormatterStyle('yellow'));
38981 $this->setStyle('question', new OutputFormatterStyle('black', 'cyan'));
38983 foreach ($styles as $name => $style) {
38984 $this->setStyle($name, $style);
38987 $this->styleStack = new OutputFormatterStyleStack();
38997 public function setDecorated($decorated)
38999 $this->decorated = (bool) $decorated;
39009 public function isDecorated()
39011 return $this->decorated;
39022 public function setStyle($name, OutputFormatterStyleInterface $style)
39024 $this->styles[strtolower($name)] = $style;
39036 public function hasStyle($name)
39038 return isset($this->styles[strtolower($name)]);
39052 public function getStyle($name)
39054 if (!$this->hasStyle($name)) {
39055 throw new \InvalidArgumentException(sprintf('Undefined style: %s', $name));
39058 return $this->styles[strtolower($name)];
39070 public function format($message)
39074 $tagRegex = '[a-z][a-z0-9_=;-]*';
39075 preg_match_all("#<(($tagRegex) | /($tagRegex)?)>#isx", $message, $matches, PREG_OFFSET_CAPTURE);
39076 foreach ($matches[0] as $i => $match) {
39081 $output .= $this->applyCurrentStyle(substr($message, $offset, $pos - $offset));
39082 $offset = $pos + strlen($text);
39085 if ($open = '/' != $text[1]) {
39086 $tag = $matches[1][$i][0];
39088 $tag = isset($matches[3][$i][0]) ? $matches[3][$i][0] : '';
39091 if (!$open && !$tag) {
39093 $this->styleStack->pop();
39094 } elseif ($pos && '\\' == $message[$pos - 1]) {
39096 $output .= $this->applyCurrentStyle($text);
39097 } elseif (false === $style = $this->createStyleFromString(strtolower($tag))) {
39098 $output .= $this->applyCurrentStyle($text);
39100 $this->styleStack->push($style);
39102 $this->styleStack->pop($style);
39106 $output .= $this->applyCurrentStyle(substr($message, $offset));
39108 return str_replace('\\<', '<', $output);
39114 public function getStyleStack()
39116 return $this->styleStack;
39126 private function createStyleFromString($string)
39128 if (isset($this->styles[$string])) {
39129 return $this->styles[$string];
39132 if (!preg_match_all('/([^=]+)=([^;]+)(;|$)/', strtolower($string), $matches, PREG_SET_ORDER)) {
39136 $style = new OutputFormatterStyle();
39137 foreach ($matches as $match) {
39138 array_shift($match);
39140 if ('fg' == $match[0]) {
39141 $style->setForeground($match[1]);
39142 } elseif ('bg' == $match[0]) {
39143 $style->setBackground($match[1]);
39146 $style->setOption($match[1]);
39147 } catch (\InvalidArgumentException $e) {
39163 private function applyCurrentStyle($text)
39165 return $this->isDecorated() && strlen($text) > 0 ? $this->styleStack->getCurrent()->apply($text) : $text;
39179 namespace Symfony\Component\Console\Formatter;
39188 interface OutputFormatterInterface
39197 public function setDecorated($decorated);
39206 public function isDecorated();
39216 public function setStyle($name, OutputFormatterStyleInterface $style);
39227 public function hasStyle($name);
39238 public function getStyle($name);
39249 public function format($message);
39262 namespace Symfony\Component\Console;
39264 use Symfony\Component\Console\Descriptor\TextDescriptor;
39265 use Symfony\Component\Console\Descriptor\XmlDescriptor;
39266 use Symfony\Component\Console\Helper\DebugFormatterHelper;
39267 use Symfony\Component\Console\Helper\ProcessHelper;
39268 use Symfony\Component\Console\Helper\QuestionHelper;
39269 use Symfony\Component\Console\Input\InputInterface;
39270 use Symfony\Component\Console\Input\ArgvInput;
39271 use Symfony\Component\Console\Input\ArrayInput;
39272 use Symfony\Component\Console\Input\InputDefinition;
39273 use Symfony\Component\Console\Input\InputOption;
39274 use Symfony\Component\Console\Input\InputArgument;
39275 use Symfony\Component\Console\Input\InputAwareInterface;
39276 use Symfony\Component\Console\Output\BufferedOutput;
39277 use Symfony\Component\Console\Output\OutputInterface;
39278 use Symfony\Component\Console\Output\ConsoleOutput;
39279 use Symfony\Component\Console\Output\ConsoleOutputInterface;
39280 use Symfony\Component\Console\Command\Command;
39281 use Symfony\Component\Console\Command\HelpCommand;
39282 use Symfony\Component\Console\Command\ListCommand;
39283 use Symfony\Component\Console\Helper\HelperSet;
39284 use Symfony\Component\Console\Helper\FormatterHelper;
39285 use Symfony\Component\Console\Helper\DialogHelper;
39286 use Symfony\Component\Console\Helper\ProgressHelper;
39287 use Symfony\Component\Console\Helper\TableHelper;
39288 use Symfony\Component\Console\Event\ConsoleCommandEvent;
39289 use Symfony\Component\Console\Event\ConsoleExceptionEvent;
39290 use Symfony\Component\Console\Event\ConsoleTerminateEvent;
39291 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
39312 private $commands = array();
39313 private $wantHelps = false;
39314 private $runningCommand;
39317 private $catchExceptions = true;
39318 private $autoExit = true;
39319 private $definition;
39320 private $helperSet;
39321 private $dispatcher;
39322 private $terminalDimensions;
39323 private $defaultCommand;
39333 public function __construct($name = 'UNKNOWN', $version = 'UNKNOWN')
39335 $this->name = $name;
39336 $this->version = $version;
39337 $this->defaultCommand = 'list';
39338 $this->helperSet = $this->getDefaultHelperSet();
39339 $this->definition = $this->getDefaultInputDefinition();
39341 foreach ($this->getDefaultCommands() as $command) {
39342 $this->add($command);
39346 public function setDispatcher(EventDispatcherInterface $dispatcher)
39348 $this->dispatcher = $dispatcher;
39363 public function run(InputInterface $input = null, OutputInterface $output = null)
39365 if (null === $input) {
39366 $input = new ArgvInput();
39369 if (null === $output) {
39370 $output = new ConsoleOutput();
39373 $this->configureIO($input, $output);
39376 $exitCode = $this->doRun($input, $output);
39377 } catch (\Exception $e) {
39378 if (!$this->catchExceptions) {
39382 if ($output instanceof ConsoleOutputInterface) {
39383 $this->renderException($e, $output->getErrorOutput());
39385 $this->renderException($e, $output);
39388 $exitCode = $e->getCode();
39389 if (is_numeric($exitCode)) {
39390 $exitCode = (int) $exitCode;
39391 if (0 === $exitCode) {
39399 if ($this->autoExit) {
39400 if ($exitCode > 255) {
39418 public function doRun(InputInterface $input, OutputInterface $output)
39420 if (true === $input->hasParameterOption(array('--version', '-V'))) {
39421 $output->writeln($this->getLongVersion());
39426 $name = $this->getCommandName($input);
39427 if (true === $input->hasParameterOption(array('--help', '-h'))) {
39430 $input = new ArrayInput(array('command' => 'help'));
39432 $this->wantHelps = true;
39437 $name = $this->defaultCommand;
39438 $input = new ArrayInput(array('command' => $this->defaultCommand));
39442 $command = $this->find($name);
39444 $this->runningCommand = $command;
39445 $exitCode = $this->doRunCommand($command, $input, $output);
39446 $this->runningCommand = null;
39458 public function setHelperSet(HelperSet $helperSet)
39460 $this->helperSet = $helperSet;
39470 public function getHelperSet()
39472 return $this->helperSet;
39482 public function setDefinition(InputDefinition $definition)
39484 $this->definition = $definition;
39492 public function getDefinition()
39494 return $this->definition;
39502 public function getHelp()
39504 return $this->getLongVersion();
39514 public function setCatchExceptions($boolean)
39516 $this->catchExceptions = (bool) $boolean;
39526 public function setAutoExit($boolean)
39528 $this->autoExit = (bool) $boolean;
39538 public function getName()
39540 return $this->name;
39550 public function setName($name)
39552 $this->name = $name;
39562 public function getVersion()
39564 return $this->version;
39574 public function setVersion($version)
39576 $this->version = $version;
39586 public function getLongVersion()
39588 if ('UNKNOWN' !== $this->getName() && 'UNKNOWN' !== $this->getVersion()) {
39589 return sprintf('<info>%s</info> version <comment>%s</comment>', $this->getName(), $this->getVersion());
39592 return '<info>Console Tool</info>';
39604 public function register($name)
39606 return $this->add(new Command($name));
39616 public function addCommands(array $commands)
39618 foreach ($commands as $command) {
39619 $this->add($command);
39634 public function add(Command $command)
39636 $command->setApplication($this);
39638 if (!$command->isEnabled()) {
39639 $command->setApplication(null);
39644 if (null === $command->getDefinition()) {
39645 throw new \LogicException(sprintf('Command class "%s" is not correctly initialized. You probably forgot to call the parent constructor.', get_class($command)));
39648 $this->commands[$command->getName()] = $command;
39650 foreach ($command->getAliases() as $alias) {
39651 $this->commands[$alias] = $command;
39668 public function get($name)
39670 if (!isset($this->commands[$name])) {
39671 throw new \InvalidArgumentException(sprintf('The command "%s" does not exist.', $name));
39674 $command = $this->commands[$name];
39676 if ($this->wantHelps) {
39677 $this->wantHelps = false;
39679 $helpCommand = $this->get('help');
39680 $helpCommand->setCommand($command);
39682 return $helpCommand;
39697 public function has($name)
39699 return isset($this->commands[$name]);
39709 public function getNamespaces()
39711 $namespaces = array();
39712 foreach ($this->commands as $command) {
39713 $namespaces[] = $this->extractNamespace($command->getName());
39715 foreach ($command->getAliases() as $alias) {
39716 $namespaces[] = $this->extractNamespace($alias);
39720 return array_values(array_unique(array_filter($namespaces)));
39732 public function findNamespace($namespace)
39734 $allNamespaces = $this->getNamespaces();
39735 $expr = preg_replace_callback('{([^:]+|)}', function ($matches) { return preg_quote($matches[1]).'[^:]*'; }, $namespace);
39736 $namespaces = preg_grep('{^'.$expr.'}', $allNamespaces);
39738 if (empty($namespaces)) {
39739 $message = sprintf('There are no commands defined in the "%s" namespace.', $namespace);
39741 if ($alternatives = $this->findAlternatives($namespace, $allNamespaces, array())) {
39742 if (1 == count($alternatives)) {
39743 $message .= "\n\nDid you mean this?\n ";
39745 $message .= "\n\nDid you mean one of these?\n ";
39748 $message .= implode("\n ", $alternatives);
39751 throw new \InvalidArgumentException($message);
39754 $exact = in_array($namespace, $namespaces, true);
39755 if (count($namespaces) > 1 && !$exact) {
39756 throw new \InvalidArgumentException(sprintf('The namespace "%s" is ambiguous (%s).', $namespace, $this->getAbbreviationSuggestions(array_values($namespaces))));
39759 return $exact ? $namespace : reset($namespaces);
39776 public function find($name)
39778 $allCommands = array_keys($this->commands);
39779 $expr = preg_replace_callback('{([^:]+|)}', function ($matches) { return preg_quote($matches[1]).'[^:]*'; }, $name);
39780 $commands = preg_grep('{^'.$expr.'}', $allCommands);
39782 if (empty($commands) || count(preg_grep('{^'.$expr.'$}', $commands)) < 1) {
39783 if (false !== $pos = strrpos($name, ':')) {
39785 $this->findNamespace(substr($name, 0, $pos));
39788 $message = sprintf('Command "%s" is not defined.', $name);
39790 if ($alternatives = $this->findAlternatives($name, $allCommands, array())) {
39791 if (1 == count($alternatives)) {
39792 $message .= "\n\nDid you mean this?\n ";
39794 $message .= "\n\nDid you mean one of these?\n ";
39796 $message .= implode("\n ", $alternatives);
39799 throw new \InvalidArgumentException($message);
39803 if (count($commands) > 1) {
39804 $commandList = $this->commands;
39805 $commands = array_filter($commands, function ($nameOrAlias) use ($commandList, $commands) {
39806 $commandName = $commandList[$nameOrAlias]->getName();
39808 return $commandName === $nameOrAlias || !in_array($commandName, $commands);
39812 $exact = in_array($name, $commands, true);
39813 if (count($commands) > 1 && !$exact) {
39814 $suggestions = $this->getAbbreviationSuggestions(array_values($commands));
39816 throw new \InvalidArgumentException(sprintf('Command "%s" is ambiguous (%s).', $name, $suggestions));
39819 return $this->get($exact ? $name : reset($commands));
39833 public function all($namespace = null)
39835 if (null === $namespace) {
39836 return $this->commands;
39839 $commands = array();
39840 foreach ($this->commands as $name => $command) {
39841 if ($namespace === $this->extractNamespace($name, substr_count($namespace, ':') + 1)) {
39842 $commands[$name] = $command;
39856 public static function getAbbreviations($names)
39858 $abbrevs = array();
39859 foreach ($names as $name) {
39860 for ($len = strlen($name); $len > 0; --$len) {
39861 $abbrev = substr($name, 0, $len);
39862 $abbrevs[$abbrev][] = $name;
39879 public function asText($namespace = null, $raw = false)
39881 $descriptor = new TextDescriptor();
39882 $output = new BufferedOutput(BufferedOutput::VERBOSITY_NORMAL, !$raw);
39883 $descriptor->describe($output, $this, array('namespace' => $namespace, 'raw_output' => true));
39885 return $output->fetch();
39898 public function asXml($namespace = null, $asDom = false)
39900 $descriptor = new XmlDescriptor();
39903 return $descriptor->getApplicationDocument($this, $namespace);
39906 $output = new BufferedOutput();
39907 $descriptor->describe($output, $this, array('namespace' => $namespace));
39909 return $output->fetch();
39918 public function renderException($e, $output)
39921 $title = sprintf(' [%s] ', get_class($e));
39923 $len = $this->stringWidth($title);
39925 $width = $this->getTerminalWidth() ? $this->getTerminalWidth() - 1 : PHP_INT_MAX;
39927 if (defined('HHVM_VERSION') && $width > 1 << 31) {
39930 $formatter = $output->getFormatter();
39932 foreach (preg_split('/\r?\n/', $e->getMessage()) as $line) {
39933 foreach ($this->splitStringByWidth($line, $width - 4) as $line) {
39935 $lineLength = $this->stringWidth(preg_replace('/
\e\[[^m]*m/', '', $formatter->format($line))) + 4;
39936 $lines[] = array($line, $lineLength);
39938 $len = max($lineLength, $len);
39942 $messages = array('', '');
39943 $messages[] = $emptyLine = $formatter->format(sprintf('<error>%s</error>', str_repeat(' ', $len)));
39944 $messages[] = $formatter->format(sprintf('<error>%s%s</error>', $title, str_repeat(' ', max(0, $len - $this->stringWidth($title)))));
39945 foreach ($lines as $line) {
39946 $messages[] = $formatter->format(sprintf('<error> %s %s</error>', $line[0], str_repeat(' ', $len - $line[1])));
39948 $messages[] = $emptyLine;
39952 $output->writeln($messages, OutputInterface::OUTPUT_RAW);
39954 if (OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) {
39955 $output->writeln('<comment>Exception trace:</comment>');
39958 $trace = $e->getTrace();
39959 array_unshift($trace, array(
39961 'file' => $e->getFile() != null ? $e->getFile() : 'n/a',
39962 'line' => $e->getLine() != null ? $e->getLine() : 'n/a',
39966 for ($i = 0, $count = count($trace); $i < $count; $i++) {
39967 $class = isset($trace[$i]['class']) ? $trace[$i]['class'] : '';
39968 $type = isset($trace[$i]['type']) ? $trace[$i]['type'] : '';
39969 $function = $trace[$i]['function'];
39970 $file = isset($trace[$i]['file']) ? $trace[$i]['file'] : 'n/a';
39971 $line = isset($trace[$i]['line']) ? $trace[$i]['line'] : 'n/a';
39973 $output->writeln(sprintf(' %s%s%s() at <info>%s:%s</info>', $class, $type, $function, $file, $line));
39976 $output->writeln("");
39977 $output->writeln("");
39979 } while ($e = $e->getPrevious());
39981 if (null !== $this->runningCommand) {
39982 $output->writeln(sprintf('<info>%s</info>', sprintf($this->runningCommand->getSynopsis(), $this->getName())));
39983 $output->writeln("");
39984 $output->writeln("");
39993 protected function getTerminalWidth()
39995 $dimensions = $this->getTerminalDimensions();
39997 return $dimensions[0];
40005 protected function getTerminalHeight()
40007 $dimensions = $this->getTerminalDimensions();
40009 return $dimensions[1];
40017 public function getTerminalDimensions()
40019 if ($this->terminalDimensions) {
40020 return $this->terminalDimensions;
40023 if (defined('PHP_WINDOWS_VERSION_BUILD')) {
40025 if (preg_match('/^(\d+)x\d+ \(\d+x(\d+)\)$/', trim(getenv('ANSICON')), $matches)) {
40026 return array((int) $matches[1], (int) $matches[2]);
40029 if (preg_match('/^(\d+)x(\d+)$/', $this->getConsoleMode(), $matches)) {
40030 return array((int) $matches[1], (int) $matches[2]);
40034 if ($sttyString = $this->getSttyColumns()) {
40036 if (preg_match('/rows.(\d+);.columns.(\d+);/i', $sttyString, $matches)) {
40037 return array((int) $matches[2], (int) $matches[1]);
40040 if (preg_match('/;.(\d+).rows;.(\d+).columns/i', $sttyString, $matches)) {
40041 return array((int) $matches[2], (int) $matches[1]);
40045 return array(null, null);
40058 public function setTerminalDimensions($width, $height)
40060 $this->terminalDimensions = array($width, $height);
40071 protected function configureIO(InputInterface $input, OutputInterface $output)
40073 if (true === $input->hasParameterOption(array('--ansi'))) {
40074 $output->setDecorated(true);
40075 } elseif (true === $input->hasParameterOption(array('--no-ansi'))) {
40076 $output->setDecorated(false);
40079 if (true === $input->hasParameterOption(array('--no-interaction', '-n'))) {
40080 $input->setInteractive(false);
40081 } elseif (function_exists('posix_isatty') && $this->getHelperSet()->has('question')) {
40082 $inputStream = $this->getHelperSet()->get('question')->getInputStream();
40083 if (!@posix_isatty($inputStream)) {
40084 $input->setInteractive(false);
40088 if (true === $input->hasParameterOption(array('--quiet', '-q'))) {
40089 $output->setVerbosity(OutputInterface::VERBOSITY_QUIET);
40091 if ($input->hasParameterOption('-vvv') || $input->hasParameterOption('--verbose=3') || $input->getParameterOption('--verbose') === 3) {
40092 $output->setVerbosity(OutputInterface::VERBOSITY_DEBUG);
40093 } elseif ($input->hasParameterOption('-vv') || $input->hasParameterOption('--verbose=2') || $input->getParameterOption('--verbose') === 2) {
40094 $output->setVerbosity(OutputInterface::VERBOSITY_VERY_VERBOSE);
40095 } elseif ($input->hasParameterOption('-v') || $input->hasParameterOption('--verbose=1') || $input->hasParameterOption('--verbose') || $input->getParameterOption('--verbose')) {
40096 $output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE);
40115 protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output)
40117 foreach ($command->getHelperSet() as $helper) {
40118 if ($helper instanceof InputAwareInterface) {
40119 $helper->setInput($input);
40123 if (null === $this->dispatcher) {
40124 return $command->run($input, $output);
40127 $event = new ConsoleCommandEvent($command, $input, $output);
40128 $this->dispatcher->dispatch(ConsoleEvents::COMMAND, $event);
40130 if ($event->commandShouldRun()) {
40132 $exitCode = $command->run($input, $output);
40133 } catch (\Exception $e) {
40134 $event = new ConsoleTerminateEvent($command, $input, $output, $e->getCode());
40135 $this->dispatcher->dispatch(ConsoleEvents::TERMINATE, $event);
40137 $event = new ConsoleExceptionEvent($command, $input, $output, $e, $event->getExitCode());
40138 $this->dispatcher->dispatch(ConsoleEvents::EXCEPTION, $event);
40140 throw $event->getException();
40143 $exitCode = ConsoleCommandEvent::RETURN_CODE_DISABLED;
40146 $event = new ConsoleTerminateEvent($command, $input, $output, $exitCode);
40147 $this->dispatcher->dispatch(ConsoleEvents::TERMINATE, $event);
40149 return $event->getExitCode();
40159 protected function getCommandName(InputInterface $input)
40161 return $input->getFirstArgument();
40169 protected function getDefaultInputDefinition()
40171 return new InputDefinition(array(
40172 new InputArgument('command', InputArgument::REQUIRED, 'The command to execute'),
40174 new InputOption('--help', '-h', InputOption::VALUE_NONE, 'Display this help message.'),
40175 new InputOption('--quiet', '-q', InputOption::VALUE_NONE, 'Do not output any message.'),
40176 new InputOption('--verbose', '-v|vv|vvv', InputOption::VALUE_NONE, 'Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug.'),
40177 new InputOption('--version', '-V', InputOption::VALUE_NONE, 'Display this application version.'),
40178 new InputOption('--ansi', '', InputOption::VALUE_NONE, 'Force ANSI output.'),
40179 new InputOption('--no-ansi', '', InputOption::VALUE_NONE, 'Disable ANSI output.'),
40180 new InputOption('--no-interaction', '-n', InputOption::VALUE_NONE, 'Do not ask any interactive question.'),
40189 protected function getDefaultCommands()
40191 return array(new HelpCommand(), new ListCommand());
40199 protected function getDefaultHelperSet()
40201 return new HelperSet(array(
40202 new FormatterHelper(),
40203 new DialogHelper(),
40204 new ProgressHelper(),
40206 new DebugFormatterHelper(),
40207 new ProcessHelper(),
40208 new QuestionHelper(),
40217 private function getSttyColumns()
40219 if (!function_exists('proc_open')) {
40223 $descriptorspec = array(1 => array('pipe', 'w'), 2 => array('pipe', 'w'));
40224 $process = proc_open('stty -a | grep columns', $descriptorspec, $pipes, null, null, array('suppress_errors' => true));
40225 if (is_resource($process)) {
40226 $info = stream_get_contents($pipes[1]);
40229 proc_close($process);
40240 private function getConsoleMode()
40242 if (!function_exists('proc_open')) {
40246 $descriptorspec = array(1 => array('pipe', 'w'), 2 => array('pipe', 'w'));
40247 $process = proc_open('mode CON', $descriptorspec, $pipes, null, null, array('suppress_errors' => true));
40248 if (is_resource($process)) {
40249 $info = stream_get_contents($pipes[1]);
40252 proc_close($process);
40254 if (preg_match('/--------+\r?\n.+?(\d+)\r?\n.+?(\d+)\r?\n/', $info, $matches)) {
40255 return $matches[2].'x'.$matches[1];
40267 private function getAbbreviationSuggestions($abbrevs)
40269 return sprintf('%s, %s%s', $abbrevs[0], $abbrevs[1], count($abbrevs) > 2 ? sprintf(' and %d more', count($abbrevs) - 2) : '');
40282 public function extractNamespace($name, $limit = null)
40284 $parts = explode(':', $name);
40287 return implode(':', null === $limit ? $parts : array_slice($parts, 0, $limit));
40299 private function findAlternatives($name, $collection)
40302 $alternatives = array();
40304 $collectionParts = array();
40305 foreach ($collection as $item) {
40306 $collectionParts[$item] = explode(':', $item);
40309 foreach (explode(':', $name) as $i => $subname) {
40310 foreach ($collectionParts as $collectionName => $parts) {
40311 $exists = isset($alternatives[$collectionName]);
40312 if (!isset($parts[$i]) && $exists) {
40313 $alternatives[$collectionName] += $threshold;
40315 } elseif (!isset($parts[$i])) {
40319 $lev = levenshtein($subname, $parts[$i]);
40320 if ($lev <= strlen($subname) / 3 || '' !== $subname && false !== strpos($parts[$i], $subname)) {
40321 $alternatives[$collectionName] = $exists ? $alternatives[$collectionName] + $lev : $lev;
40322 } elseif ($exists) {
40323 $alternatives[$collectionName] += $threshold;
40328 foreach ($collection as $item) {
40329 $lev = levenshtein($name, $item);
40330 if ($lev <= strlen($name) / 3 || false !== strpos($item, $name)) {
40331 $alternatives[$item] = isset($alternatives[$item]) ? $alternatives[$item] - $lev : $lev;
40335 $alternatives = array_filter($alternatives, function ($lev) use ($threshold) { return $lev < 2*$threshold; });
40336 asort($alternatives);
40338 return array_keys($alternatives);
40346 public function setDefaultCommand($commandName)
40348 $this->defaultCommand = $commandName;
40351 private function stringWidth($string)
40353 if (!function_exists('mb_strwidth')) {
40354 return strlen($string);
40357 if (false === $encoding = mb_detect_encoding($string)) {
40358 return strlen($string);
40361 return mb_strwidth($string, $encoding);
40364 private function splitStringByWidth($string, $width)
40370 if (!function_exists('mb_strwidth')) {
40371 return str_split($string, $width);
40374 if (false === $encoding = mb_detect_encoding($string)) {
40375 return str_split($string, $width);
40378 $utf8String = mb_convert_encoding($string, 'utf8', $encoding);
40381 foreach (preg_split('//u', $utf8String) as $char) {
40383 if (mb_strwidth($line.$char, 'utf8') <= $width) {
40388 $lines[] = str_pad($line, $width);
40391 if (strlen($line)) {
40392 $lines[] = count($lines) ? str_pad($line, $width) : $line;
40395 mb_convert_variables($encoding, 'utf8', $lines);
40411 namespace Symfony\Component\Console\Input;
40420 class InputArgument
40422 const REQUIRED = 1;
40423 const OPTIONAL = 2;
40424 const IS_ARRAY = 4;
40429 private $description;
40443 public function __construct($name, $mode = null, $description = '', $default = null)
40445 if (null === $mode) {
40446 $mode = self::OPTIONAL;
40447 } elseif (!is_int($mode) || $mode > 7 || $mode < 1) {
40448 throw new \InvalidArgumentException(sprintf('Argument mode "%s" is not valid.', $mode));
40451 $this->name = $name;
40452 $this->mode = $mode;
40453 $this->description = $description;
40455 $this->setDefault($default);
40463 public function getName()
40465 return $this->name;
40473 public function isRequired()
40475 return self::REQUIRED === (self::REQUIRED & $this->mode);
40483 public function isArray()
40485 return self::IS_ARRAY === (self::IS_ARRAY & $this->mode);
40495 public function setDefault($default = null)
40497 if (self::REQUIRED === $this->mode && null !== $default) {
40498 throw new \LogicException('Cannot set a default value except for InputArgument::OPTIONAL mode.');
40501 if ($this->isArray()) {
40502 if (null === $default) {
40503 $default = array();
40504 } elseif (!is_array($default)) {
40505 throw new \LogicException('A default value for an array argument must be an array.');
40509 $this->default = $default;
40517 public function getDefault()
40519 return $this->default;
40527 public function getDescription()
40529 return $this->description;
40543 namespace Symfony\Component\Console\Input;
40556 abstract class Input implements InputInterface
40561 protected $definition;
40562 protected $options = array();
40563 protected $arguments = array();
40564 protected $interactive = true;
40571 public function __construct(InputDefinition $definition = null)
40573 if (null === $definition) {
40574 $this->definition = new InputDefinition();
40576 $this->bind($definition);
40586 public function bind(InputDefinition $definition)
40588 $this->arguments = array();
40589 $this->options = array();
40590 $this->definition = $definition;
40598 abstract protected function parse();
40605 public function validate()
40607 if (count($this->arguments) < $this->definition->getArgumentRequiredCount()) {
40608 throw new \RuntimeException('Not enough arguments.');
40617 public function isInteractive()
40619 return $this->interactive;
40627 public function setInteractive($interactive)
40629 $this->interactive = (bool) $interactive;
40637 public function getArguments()
40639 return array_merge($this->definition->getArgumentDefaults(), $this->arguments);
40651 public function getArgument($name)
40653 if (!$this->definition->hasArgument($name)) {
40654 throw new \InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
40657 return isset($this->arguments[$name]) ? $this->arguments[$name] : $this->definition->getArgument($name)->getDefault();
40668 public function setArgument($name, $value)
40670 if (!$this->definition->hasArgument($name)) {
40671 throw new \InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
40674 $this->arguments[$name] = $value;
40684 public function hasArgument($name)
40686 return $this->definition->hasArgument($name);
40694 public function getOptions()
40696 return array_merge($this->definition->getOptionDefaults(), $this->options);
40708 public function getOption($name)
40710 if (!$this->definition->hasOption($name)) {
40711 throw new \InvalidArgumentException(sprintf('The "%s" option does not exist.', $name));
40714 return isset($this->options[$name]) ? $this->options[$name] : $this->definition->getOption($name)->getDefault();
40725 public function setOption($name, $value)
40727 if (!$this->definition->hasOption($name)) {
40728 throw new \InvalidArgumentException(sprintf('The "%s" option does not exist.', $name));
40731 $this->options[$name] = $value;
40741 public function hasOption($name)
40743 return $this->definition->hasOption($name);
40753 public function escapeToken($token)
40755 return preg_match('{^[\w-]+$}', $token) ? $token : escapeshellarg($token);
40769 namespace Symfony\Component\Console\Input;
40782 class StringInput extends ArgvInput
40784 const REGEX_STRING = '([^\s]+?)(?:\s|(?<!\\\\)"|(?<!\\\\)\'|$)';
40785 const REGEX_QUOTED_STRING = '(?:"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\')';
40797 public function __construct($input, InputDefinition $definition = null)
40799 parent::__construct(array(), null);
40801 $this->setTokens($this->tokenize($input));
40803 if (null !== $definition) {
40804 $this->bind($definition);
40817 private function tokenize($input)
40820 $length = strlen($input);
40822 while ($cursor < $length) {
40823 if (preg_match('/\s+/A', $input, $match, null, $cursor)) {
40824 } elseif (preg_match('/([^="\'\s]+?)(=?)('.self::REGEX_QUOTED_STRING.'+)/A', $input, $match, null, $cursor)) {
40825 $tokens[] = $match[1].$match[2].stripcslashes(str_replace(array('"\'', '\'"', '\'\'', '""'), '', substr($match[3], 1, strlen($match[3]) - 2)));
40826 } elseif (preg_match('/'.self::REGEX_QUOTED_STRING.'/A', $input, $match, null, $cursor)) {
40827 $tokens[] = stripcslashes(substr($match[0], 1, strlen($match[0]) - 2));
40828 } elseif (preg_match('/'.self::REGEX_STRING.'/A', $input, $match, null, $cursor)) {
40829 $tokens[] = stripcslashes($match[1]);
40832 throw new \InvalidArgumentException(sprintf('Unable to parse input near "... %s ..."', substr($input, $cursor, 10)));
40835 $cursor += strlen($match[0]);
40852 namespace Symfony\Component\Console\Input;
40863 const VALUE_NONE = 1;
40864 const VALUE_REQUIRED = 2;
40865 const VALUE_OPTIONAL = 4;
40866 const VALUE_IS_ARRAY = 8;
40872 private $description;
40887 public function __construct($name, $shortcut = null, $mode = null, $description = '', $default = null)
40889 if (0 === strpos($name, '--')) {
40890 $name = substr($name, 2);
40893 if (empty($name)) {
40894 throw new \InvalidArgumentException('An option name cannot be empty.');
40897 if (empty($shortcut)) {
40901 if (null !== $shortcut) {
40902 if (is_array($shortcut)) {
40903 $shortcut = implode('|', $shortcut);
40905 $shortcuts = preg_split('{(\|)-?}', ltrim($shortcut, '-'));
40906 $shortcuts = array_filter($shortcuts);
40907 $shortcut = implode('|', $shortcuts);
40909 if (empty($shortcut)) {
40910 throw new \InvalidArgumentException('An option shortcut cannot be empty.');
40914 if (null === $mode) {
40915 $mode = self::VALUE_NONE;
40916 } elseif (!is_int($mode) || $mode > 15 || $mode < 1) {
40917 throw new \InvalidArgumentException(sprintf('Option mode "%s" is not valid.', $mode));
40920 $this->name = $name;
40921 $this->shortcut = $shortcut;
40922 $this->mode = $mode;
40923 $this->description = $description;
40925 if ($this->isArray() && !$this->acceptValue()) {
40926 throw new \InvalidArgumentException('Impossible to have an option mode VALUE_IS_ARRAY if the option does not accept a value.');
40929 $this->setDefault($default);
40937 public function getShortcut()
40939 return $this->shortcut;
40947 public function getName()
40949 return $this->name;
40957 public function acceptValue()
40959 return $this->isValueRequired() || $this->isValueOptional();
40967 public function isValueRequired()
40969 return self::VALUE_REQUIRED === (self::VALUE_REQUIRED & $this->mode);
40977 public function isValueOptional()
40979 return self::VALUE_OPTIONAL === (self::VALUE_OPTIONAL & $this->mode);
40987 public function isArray()
40989 return self::VALUE_IS_ARRAY === (self::VALUE_IS_ARRAY & $this->mode);
40999 public function setDefault($default = null)
41001 if (self::VALUE_NONE === (self::VALUE_NONE & $this->mode) && null !== $default) {
41002 throw new \LogicException('Cannot set a default value when using InputOption::VALUE_NONE mode.');
41005 if ($this->isArray()) {
41006 if (null === $default) {
41007 $default = array();
41008 } elseif (!is_array($default)) {
41009 throw new \LogicException('A default value for an array option must be an array.');
41013 $this->default = $this->acceptValue() ? $default : false;
41021 public function getDefault()
41023 return $this->default;
41031 public function getDescription()
41033 return $this->description;
41043 public function equals(InputOption $option)
41045 return $option->getName() === $this->getName()
41046 && $option->getShortcut() === $this->getShortcut()
41047 && $option->getDefault() === $this->getDefault()
41048 && $option->isArray() === $this->isArray()
41049 && $option->isValueRequired() === $this->isValueRequired()
41050 && $option->isValueOptional() === $this->isValueOptional()
41065 namespace Symfony\Component\Console\Input;
41094 class ArgvInput extends Input
41107 public function __construct(array $argv = null, InputDefinition $definition = null)
41109 if (null === $argv) {
41110 $argv = $_SERVER['argv'];
41114 array_shift($argv);
41116 $this->tokens = $argv;
41118 parent::__construct($definition);
41121 protected function setTokens(array $tokens)
41123 $this->tokens = $tokens;
41129 protected function parse()
41131 $parseOptions = true;
41132 $this->parsed = $this->tokens;
41133 while (null !== $token = array_shift($this->parsed)) {
41134 if ($parseOptions && '' == $token) {
41135 $this->parseArgument($token);
41136 } elseif ($parseOptions && '--' == $token) {
41137 $parseOptions = false;
41138 } elseif ($parseOptions && 0 === strpos($token, '--')) {
41139 $this->parseLongOption($token);
41140 } elseif ($parseOptions && '-' === $token[0] && '-' !== $token) {
41141 $this->parseShortOption($token);
41143 $this->parseArgument($token);
41153 private function parseShortOption($token)
41155 $name = substr($token, 1);
41157 if (strlen($name) > 1) {
41158 if ($this->definition->hasShortcut($name[0]) && $this->definition->getOptionForShortcut($name[0])->acceptValue()) {
41160 $this->addShortOption($name[0], substr($name, 1));
41162 $this->parseShortOptionSet($name);
41165 $this->addShortOption($name, null);
41176 private function parseShortOptionSet($name)
41178 $len = strlen($name);
41179 for ($i = 0; $i < $len; $i++) {
41180 if (!$this->definition->hasShortcut($name[$i])) {
41181 throw new \RuntimeException(sprintf('The "-%s" option does not exist.', $name[$i]));
41184 $option = $this->definition->getOptionForShortcut($name[$i]);
41185 if ($option->acceptValue()) {
41186 $this->addLongOption($option->getName(), $i === $len - 1 ? null : substr($name, $i + 1));
41190 $this->addLongOption($option->getName(), null);
41200 private function parseLongOption($token)
41202 $name = substr($token, 2);
41204 if (false !== $pos = strpos($name, '=')) {
41205 $this->addLongOption(substr($name, 0, $pos), substr($name, $pos + 1));
41207 $this->addLongOption($name, null);
41218 private function parseArgument($token)
41220 $c = count($this->arguments);
41223 if ($this->definition->hasArgument($c)) {
41224 $arg = $this->definition->getArgument($c);
41225 $this->arguments[$arg->getName()] = $arg->isArray() ? array($token) : $token;
41228 } elseif ($this->definition->hasArgument($c - 1) && $this->definition->getArgument($c - 1)->isArray()) {
41229 $arg = $this->definition->getArgument($c - 1);
41230 $this->arguments[$arg->getName()][] = $token;
41234 throw new \RuntimeException('Too many arguments.');
41246 private function addShortOption($shortcut, $value)
41248 if (!$this->definition->hasShortcut($shortcut)) {
41249 throw new \RuntimeException(sprintf('The "-%s" option does not exist.', $shortcut));
41252 $this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value);
41263 private function addLongOption($name, $value)
41265 if (!$this->definition->hasOption($name)) {
41266 throw new \RuntimeException(sprintf('The "--%s" option does not exist.', $name));
41269 $option = $this->definition->getOption($name);
41272 if (false === $value) {
41276 if (null !== $value && !$option->acceptValue()) {
41277 throw new \RuntimeException(sprintf('The "--%s" option does not accept a value.', $name, $value));
41280 if (null === $value && $option->acceptValue() && count($this->parsed)) {
41283 $next = array_shift($this->parsed);
41284 if (isset($next[0]) && '-' !== $next[0]) {
41286 } elseif (empty($next)) {
41289 array_unshift($this->parsed, $next);
41293 if (null === $value) {
41294 if ($option->isValueRequired()) {
41295 throw new \RuntimeException(sprintf('The "--%s" option requires a value.', $name));
41298 if (!$option->isArray()) {
41299 $value = $option->isValueOptional() ? $option->getDefault() : true;
41303 if ($option->isArray()) {
41304 $this->options[$name][] = $value;
41306 $this->options[$name] = $value;
41315 public function getFirstArgument()
41317 foreach ($this->tokens as $token) {
41318 if ($token && '-' === $token[0]) {
41336 public function hasParameterOption($values)
41338 $values = (array) $values;
41340 foreach ($this->tokens as $token) {
41341 foreach ($values as $value) {
41342 if ($token === $value || 0 === strpos($token, $value.'=')) {
41362 public function getParameterOption($values, $default = false)
41364 $values = (array) $values;
41366 $tokens = $this->tokens;
41367 while ($token = array_shift($tokens)) {
41368 foreach ($values as $value) {
41369 if ($token === $value || 0 === strpos($token, $value.'=')) {
41370 if (false !== $pos = strpos($token, '=')) {
41371 return substr($token, $pos + 1);
41374 return array_shift($tokens);
41387 public function __toString()
41390 $tokens = array_map(function ($token) use ($self) {
41391 if (preg_match('{^(-[^=]+=)(.+)}', $token, $match)) {
41392 return $match[1].$self->escapeToken($match[2]);
41395 if ($token && $token[0] !== '-') {
41396 return $self->escapeToken($token);
41402 return implode(' ', $tokens);
41416 namespace Symfony\Component\Console\Input;
41423 interface InputInterface
41430 public function getFirstArgument();
41442 public function hasParameterOption($values);
41455 public function getParameterOption($values, $default = false);
41462 public function bind(InputDefinition $definition);
41471 public function validate();
41478 public function getArguments();
41487 public function getArgument($name);
41497 public function setArgument($name, $value);
41506 public function hasArgument($name);
41513 public function getOptions();
41522 public function getOption($name);
41532 public function setOption($name, $value);
41541 public function hasOption($name);
41548 public function isInteractive();
41555 public function setInteractive($interactive);
41568 namespace Symfony\Component\Console\Input;
41581 class ArrayInput extends Input
41583 private $parameters;
41593 public function __construct(array $parameters, InputDefinition $definition = null)
41595 $this->parameters = $parameters;
41597 parent::__construct($definition);
41605 public function getFirstArgument()
41607 foreach ($this->parameters as $key => $value) {
41608 if ($key && '-' === $key[0]) {
41626 public function hasParameterOption($values)
41628 $values = (array) $values;
41630 foreach ($this->parameters as $k => $v) {
41635 if (in_array($v, $values)) {
41654 public function getParameterOption($values, $default = false)
41656 $values = (array) $values;
41658 foreach ($this->parameters as $k => $v) {
41659 if (is_int($k) && in_array($v, $values)) {
41661 } elseif (in_array($k, $values)) {
41674 public function __toString()
41677 foreach ($this->parameters as $param => $val) {
41678 if ($param && '-' === $param[0]) {
41679 $params[] = $param.('' != $val ? '='.$this->escapeToken($val) : '');
41681 $params[] = $this->escapeToken($val);
41685 return implode(' ', $params);
41691 protected function parse()
41693 foreach ($this->parameters as $key => $value) {
41694 if (0 === strpos($key, '--')) {
41695 $this->addLongOption(substr($key, 2), $value);
41696 } elseif ('-' === $key[0]) {
41697 $this->addShortOption(substr($key, 1), $value);
41699 $this->addArgument($key, $value);
41712 private function addShortOption($shortcut, $value)
41714 if (!$this->definition->hasShortcut($shortcut)) {
41715 throw new \InvalidArgumentException(sprintf('The "-%s" option does not exist.', $shortcut));
41718 $this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value);
41730 private function addLongOption($name, $value)
41732 if (!$this->definition->hasOption($name)) {
41733 throw new \InvalidArgumentException(sprintf('The "--%s" option does not exist.', $name));
41736 $option = $this->definition->getOption($name);
41738 if (null === $value) {
41739 if ($option->isValueRequired()) {
41740 throw new \InvalidArgumentException(sprintf('The "--%s" option requires a value.', $name));
41743 $value = $option->isValueOptional() ? $option->getDefault() : true;
41746 $this->options[$name] = $value;
41757 private function addArgument($name, $value)
41759 if (!$this->definition->hasArgument($name)) {
41760 throw new \InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
41763 $this->arguments[$name] = $value;
41777 namespace Symfony\Component\Console\Input;
41785 interface InputAwareInterface
41792 public function setInput(InputInterface $input);
41805 namespace Symfony\Component\Console\Input;
41807 use Symfony\Component\Console\Descriptor\TextDescriptor;
41808 use Symfony\Component\Console\Descriptor\XmlDescriptor;
41809 use Symfony\Component\Console\Output\BufferedOutput;
41825 class InputDefinition
41827 private $arguments;
41828 private $requiredCount;
41829 private $hasAnArrayArgument = false;
41830 private $hasOptional;
41832 private $shortcuts;
41841 public function __construct(array $definition = array())
41843 $this->setDefinition($definition);
41853 public function setDefinition(array $definition)
41855 $arguments = array();
41856 $options = array();
41857 foreach ($definition as $item) {
41858 if ($item instanceof InputOption) {
41859 $options[] = $item;
41861 $arguments[] = $item;
41865 $this->setArguments($arguments);
41866 $this->setOptions($options);
41876 public function setArguments($arguments = array())
41878 $this->arguments = array();
41879 $this->requiredCount = 0;
41880 $this->hasOptional = false;
41881 $this->hasAnArrayArgument = false;
41882 $this->addArguments($arguments);
41892 public function addArguments($arguments = array())
41894 if (null !== $arguments) {
41895 foreach ($arguments as $argument) {
41896 $this->addArgument($argument);
41910 public function addArgument(InputArgument $argument)
41912 if (isset($this->arguments[$argument->getName()])) {
41913 throw new \LogicException(sprintf('An argument with name "%s" already exists.', $argument->getName()));
41916 if ($this->hasAnArrayArgument) {
41917 throw new \LogicException('Cannot add an argument after an array argument.');
41920 if ($argument->isRequired() && $this->hasOptional) {
41921 throw new \LogicException('Cannot add a required argument after an optional one.');
41924 if ($argument->isArray()) {
41925 $this->hasAnArrayArgument = true;
41928 if ($argument->isRequired()) {
41929 ++$this->requiredCount;
41931 $this->hasOptional = true;
41934 $this->arguments[$argument->getName()] = $argument;
41948 public function getArgument($name)
41950 if (!$this->hasArgument($name)) {
41951 throw new \InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
41954 $arguments = is_int($name) ? array_values($this->arguments) : $this->arguments;
41956 return $arguments[$name];
41968 public function hasArgument($name)
41970 $arguments = is_int($name) ? array_values($this->arguments) : $this->arguments;
41972 return isset($arguments[$name]);
41982 public function getArguments()
41984 return $this->arguments;
41992 public function getArgumentCount()
41994 return $this->hasAnArrayArgument ? PHP_INT_MAX : count($this->arguments);
42002 public function getArgumentRequiredCount()
42004 return $this->requiredCount;
42012 public function getArgumentDefaults()
42015 foreach ($this->arguments as $argument) {
42016 $values[$argument->getName()] = $argument->getDefault();
42029 public function setOptions($options = array())
42031 $this->options = array();
42032 $this->shortcuts = array();
42033 $this->addOptions($options);
42043 public function addOptions($options = array())
42045 foreach ($options as $option) {
42046 $this->addOption($option);
42059 public function addOption(InputOption $option)
42061 if (isset($this->options[$option->getName()]) && !$option->equals($this->options[$option->getName()])) {
42062 throw new \LogicException(sprintf('An option named "%s" already exists.', $option->getName()));
42065 if ($option->getShortcut()) {
42066 foreach (explode('|', $option->getShortcut()) as $shortcut) {
42067 if (isset($this->shortcuts[$shortcut]) && !$option->equals($this->options[$this->shortcuts[$shortcut]])) {
42068 throw new \LogicException(sprintf('An option with shortcut "%s" already exists.', $shortcut));
42073 $this->options[$option->getName()] = $option;
42074 if ($option->getShortcut()) {
42075 foreach (explode('|', $option->getShortcut()) as $shortcut) {
42076 $this->shortcuts[$shortcut] = $option->getName();
42092 public function getOption($name)
42094 if (!$this->hasOption($name)) {
42095 throw new \InvalidArgumentException(sprintf('The "--%s" option does not exist.', $name));
42098 return $this->options[$name];
42110 public function hasOption($name)
42112 return isset($this->options[$name]);
42122 public function getOptions()
42124 return $this->options;
42134 public function hasShortcut($name)
42136 return isset($this->shortcuts[$name]);
42146 public function getOptionForShortcut($shortcut)
42148 return $this->getOption($this->shortcutToName($shortcut));
42156 public function getOptionDefaults()
42159 foreach ($this->options as $option) {
42160 $values[$option->getName()] = $option->getDefault();
42175 private function shortcutToName($shortcut)
42177 if (!isset($this->shortcuts[$shortcut])) {
42178 throw new \InvalidArgumentException(sprintf('The "-%s" option does not exist.', $shortcut));
42181 return $this->shortcuts[$shortcut];
42189 public function getSynopsis()
42191 $elements = array();
42192 foreach ($this->getOptions() as $option) {
42193 $shortcut = $option->getShortcut() ? sprintf('-%s|', $option->getShortcut()) : '';
42194 $elements[] = sprintf('['.($option->isValueRequired() ? '%s--%s="..."' : ($option->isValueOptional() ? '%s--%s[="..."]' : '%s--%s')).']', $shortcut, $option->getName());
42197 foreach ($this->getArguments() as $argument) {
42198 $elements[] = sprintf($argument->isRequired() ? '%s' : '[%s]', $argument->getName().($argument->isArray() ? '1' : ''));
42200 if ($argument->isArray()) {
42201 $elements[] = sprintf('... [%sN]', $argument->getName());
42205 return implode(' ', $elements);
42215 public function asText()
42217 $descriptor = new TextDescriptor();
42218 $output = new BufferedOutput(BufferedOutput::VERBOSITY_NORMAL, true);
42219 $descriptor->describe($output, $this, array('raw_output' => true));
42221 return $output->fetch();
42233 public function asXml($asDom = false)
42235 $descriptor = new XmlDescriptor();
42238 return $descriptor->getInputDefinitionDocument($this);
42241 $output = new BufferedOutput();
42242 $descriptor->describe($output, $this);
42244 return $output->fetch();
42258 namespace Symfony\Component\Console;
42260 use Symfony\Component\Console\Input\StringInput;
42261 use Symfony\Component\Console\Output\ConsoleOutput;
42262 use Symfony\Component\Process\ProcessBuilder;
42263 use Symfony\Component\Process\PhpExecutableFinder;
42276 private $application;
42279 private $hasReadline;
42280 private $processIsolation = false;
42290 public function __construct(Application $application)
42292 $this->hasReadline = function_exists('readline');
42293 $this->application = $application;
42294 $this->history = getenv('HOME').'/.history_'.$application->getName();
42295 $this->output = new ConsoleOutput();
42301 public function run()
42303 $this->application->setAutoExit(false);
42304 $this->application->setCatchExceptions(true);
42306 if ($this->hasReadline) {
42307 readline_read_history($this->history);
42308 readline_completion_function(array($this, 'autocompleter'));
42311 $this->output->writeln($this->getHeader());
42313 if ($this->processIsolation) {
42314 $finder = new PhpExecutableFinder();
42315 $php = $finder->find();
42316 $this->output->writeln(<<<EOF
42317 <info>Running with process isolation, you should consider this:</info>
42318 * each command is executed as separate process,
42319 * commands don't support interactivity, all params must be passed explicitly,
42320 * commands output is not colorized.
42327 $command = $this->readline();
42329 if (false === $command) {
42330 $this->output->writeln("\n");
42335 if ($this->hasReadline) {
42336 readline_add_history($command);
42337 readline_write_history($this->history);
42340 if ($this->processIsolation) {
42341 $pb = new ProcessBuilder();
42345 ->add($_SERVER['argv'][0])
42347 ->inheritEnvironmentVariables(true)
42351 $output = $this->output;
42352 $process->run(function ($type, $data) use ($output) {
42353 $output->writeln($data);
42356 $ret = $process->getExitCode();
42358 $ret = $this->application->run(new StringInput($command), $this->output);
42362 $this->output->writeln(sprintf('<error>The command terminated with an error status (%s)</error>', $ret));
42372 protected function getHeader()
42376 Welcome to the <info>{$this->application->getName()}</info> shell (<comment>{$this->application->getVersion()}</comment>).
42378 At the prompt, type <comment>help</comment> for some help,
42379 or <comment>list</comment> to get a list of available commands.
42381 To exit the shell, type <comment>^D</comment>.
42391 protected function getPrompt()
42394 return $this->output->getFormatter()->format($this->application->getName().' > ');
42397 protected function getOutput()
42399 return $this->output;
42402 protected function getApplication()
42404 return $this->application;
42414 private function autocompleter($text)
42416 $info = readline_info();
42417 $text = substr($info['line_buffer'], 0, $info['end']);
42419 if ($info['point'] !== $info['end']) {
42424 if (false === strpos($text, ' ') || !$text) {
42425 return array_keys($this->application->all());
42430 $command = $this->application->find(substr($text, 0, strpos($text, ' ')));
42431 } catch (\Exception $e) {
42435 $list = array('--help');
42436 foreach ($command->getDefinition()->getOptions() as $option) {
42437 $list[] = '--'.$option->getName();
42448 private function readline()
42450 if ($this->hasReadline) {
42451 $line = readline($this->getPrompt());
42453 $this->output->write($this->getPrompt());
42454 $line = fgets(STDIN, 1024);
42455 $line = (!$line && strlen($line) == 0) ? false : rtrim($line);
42461 public function getProcessIsolation()
42463 return $this->processIsolation;
42466 public function setProcessIsolation($processIsolation)
42468 $this->processIsolation = (bool) $processIsolation;
42470 if ($this->processIsolation && !class_exists('Symfony\\Component\\Process\\Process')) {
42471 throw new \RuntimeException('Unable to isolate processes as the Symfony Process Component is not installed.');
42486 namespace Symfony\Component\Console\Question;
42493 class ChoiceQuestion extends Question
42496 private $multiselect = false;
42497 private $prompt = ' > ';
42498 private $errorMessage = 'Value "%s" is invalid';
42500 public function __construct($question, array $choices, $default = null)
42502 parent::__construct($question, $default);
42504 $this->choices = $choices;
42505 $this->setValidator($this->getDefaultValidator());
42506 $this->setAutocompleterValues(array_keys($choices));
42514 public function getChoices()
42516 return $this->choices;
42528 public function setMultiselect($multiselect)
42530 $this->multiselect = $multiselect;
42531 $this->setValidator($this->getDefaultValidator());
42541 public function getPrompt()
42543 return $this->prompt;
42553 public function setPrompt($prompt)
42555 $this->prompt = $prompt;
42569 public function setErrorMessage($errorMessage)
42571 $this->errorMessage = $errorMessage;
42572 $this->setValidator($this->getDefaultValidator());
42577 private function getDefaultValidator()
42579 $choices = $this->choices;
42580 $errorMessage = $this->errorMessage;
42581 $multiselect = $this->multiselect;
42583 return function ($selected) use ($choices, $errorMessage, $multiselect) {
42585 $selectedChoices = str_replace(' ', '', $selected);
42587 if ($multiselect) {
42589 if (!preg_match('/^[a-zA-Z0-9_-]+(?:,[a-zA-Z0-9_-]+)*$/', $selectedChoices, $matches)) {
42590 throw new \InvalidArgumentException(sprintf($errorMessage, $selected));
42592 $selectedChoices = explode(',', $selectedChoices);
42594 $selectedChoices = array($selected);
42597 $multiselectChoices = array();
42598 foreach ($selectedChoices as $value) {
42599 if (empty($choices[$value])) {
42600 throw new \InvalidArgumentException(sprintf($errorMessage, $value));
42602 array_push($multiselectChoices, $choices[$value]);
42605 if ($multiselect) {
42606 return $multiselectChoices;
42609 return $choices[$selected];
42624 namespace Symfony\Component\Console\Question;
42631 class ConfirmationQuestion extends Question
42633 public function __construct($question, $default = true)
42635 parent::__construct($question, (bool) $default);
42637 $this->setNormalizer($this->getDefaultNormalizer());
42640 private function getDefaultNormalizer()
42642 $default = $this->getDefault();
42644 return function ($answer) use ($default) {
42645 if (is_bool($answer)) {
42649 if (false === $default) {
42650 return $answer && 'y' === strtolower($answer[0]);
42653 return !$answer || 'y' === strtolower($answer[0]);
42668 namespace Symfony\Component\Console\Question;
42679 private $hidden = false;
42680 private $hiddenFallback = true;
42681 private $autocompleterValues;
42682 private $validator;
42684 private $normalizer;
42692 public function __construct($question, $default = null)
42694 $this->question = $question;
42695 $this->default = $default;
42703 public function getQuestion()
42705 return $this->question;
42713 public function getDefault()
42715 return $this->default;
42723 public function isHidden()
42725 return $this->hidden;
42737 public function setHidden($hidden)
42739 if ($this->autocompleterValues) {
42740 throw new \LogicException('A hidden question cannot use the autocompleter.');
42743 $this->hidden = (bool) $hidden;
42753 public function isHiddenFallback()
42755 return $this->hiddenFallback;
42765 public function setHiddenFallback($fallback)
42767 $this->hiddenFallback = (bool) $fallback;
42777 public function getAutocompleterValues()
42779 return $this->autocompleterValues;
42792 public function setAutocompleterValues($values)
42794 if (null !== $values && !is_array($values)) {
42795 if (!$values instanceof \Traversable || $values instanceof \Countable) {
42796 throw new \InvalidArgumentException('Autocompleter values can be either an array, `null` or an object implementing both `Countable` and `Traversable` interfaces.');
42800 if ($this->hidden) {
42801 throw new \LogicException('A hidden question cannot use the autocompleter.');
42804 $this->autocompleterValues = $values;
42816 public function setValidator($validator)
42818 $this->validator = $validator;
42828 public function getValidator()
42830 return $this->validator;
42844 public function setMaxAttempts($attempts)
42846 if (null !== $attempts && $attempts < 1) {
42847 throw new \InvalidArgumentException('Maximum number of attempts must be a positive value.');
42850 $this->attempts = $attempts;
42862 public function getMaxAttempts()
42864 return $this->attempts;
42876 public function setNormalizer($normalizer)
42878 $this->normalizer = $normalizer;
42890 public function getNormalizer()
42892 return $this->normalizer;
42906 namespace Symfony\Component\Console\Output;
42908 use Symfony\Component\Console\Formatter\OutputFormatterInterface;
42909 use Symfony\Component\Console\Formatter\OutputFormatter;
42926 abstract class Output implements OutputInterface
42928 private $verbosity;
42929 private $formatter;
42940 public function __construct($verbosity = self::VERBOSITY_NORMAL, $decorated = false, OutputFormatterInterface $formatter = null)
42942 $this->verbosity = null === $verbosity ? self::VERBOSITY_NORMAL : $verbosity;
42943 $this->formatter = $formatter ?: new OutputFormatter();
42944 $this->formatter->setDecorated($decorated);
42950 public function setFormatter(OutputFormatterInterface $formatter)
42952 $this->formatter = $formatter;
42958 public function getFormatter()
42960 return $this->formatter;
42966 public function setDecorated($decorated)
42968 $this->formatter->setDecorated($decorated);
42974 public function isDecorated()
42976 return $this->formatter->isDecorated();
42982 public function setVerbosity($level)
42984 $this->verbosity = (int) $level;
42990 public function getVerbosity()
42992 return $this->verbosity;
42995 public function isQuiet()
42997 return self::VERBOSITY_QUIET === $this->verbosity;
43000 public function isVerbose()
43002 return self::VERBOSITY_VERBOSE <= $this->verbosity;
43005 public function isVeryVerbose()
43007 return self::VERBOSITY_VERY_VERBOSE <= $this->verbosity;
43010 public function isDebug()
43012 return self::VERBOSITY_DEBUG <= $this->verbosity;
43018 public function writeln($messages, $type = self::OUTPUT_NORMAL)
43020 $this->write($messages, true, $type);
43026 public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL)
43028 if (self::VERBOSITY_QUIET === $this->verbosity) {
43032 $messages = (array) $messages;
43034 foreach ($messages as $message) {
43036 case OutputInterface::OUTPUT_NORMAL:
43037 $message = $this->formatter->format($message);
43039 case OutputInterface::OUTPUT_RAW:
43041 case OutputInterface::OUTPUT_PLAIN:
43042 $message = strip_tags($this->formatter->format($message));
43045 throw new \InvalidArgumentException(sprintf('Unknown output type given (%s)', $type));
43048 $this->doWrite($message, $newline);
43058 abstract protected function doWrite($message, $newline);
43071 namespace Symfony\Component\Console\Output;
43073 use Symfony\Component\Console\Formatter\OutputFormatter;
43074 use Symfony\Component\Console\Formatter\OutputFormatterInterface;
43086 class NullOutput implements OutputInterface
43091 public function setFormatter(OutputFormatterInterface $formatter)
43099 public function getFormatter()
43102 return new OutputFormatter();
43108 public function setDecorated($decorated)
43116 public function isDecorated()
43124 public function setVerbosity($level)
43132 public function getVerbosity()
43134 return self::VERBOSITY_QUIET;
43137 public function isQuiet()
43142 public function isVerbose()
43147 public function isVeryVerbose()
43152 public function isDebug()
43160 public function writeln($messages, $type = self::OUTPUT_NORMAL)
43168 public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL)
43184 namespace Symfony\Component\Console\Output;
43192 interface ConsoleOutputInterface extends OutputInterface
43199 public function getErrorOutput();
43206 public function setErrorOutput(OutputInterface $error);
43219 namespace Symfony\Component\Console\Output;
43221 use Symfony\Component\Console\Formatter\OutputFormatterInterface;
43238 class StreamOutput extends Output
43254 public function __construct($stream, $verbosity = self::VERBOSITY_NORMAL, $decorated = null, OutputFormatterInterface $formatter = null)
43256 if (!is_resource($stream) || 'stream' !== get_resource_type($stream)) {
43257 throw new \InvalidArgumentException('The StreamOutput class needs a stream as its first argument.');
43260 $this->stream = $stream;
43262 if (null === $decorated) {
43263 $decorated = $this->hasColorSupport();
43266 parent::__construct($verbosity, $decorated, $formatter);
43274 public function getStream()
43276 return $this->stream;
43282 protected function doWrite($message, $newline)
43284 if (false === @fwrite($this->stream, $message.($newline ? PHP_EOL : ''))) {
43286 throw new \RuntimeException('Unable to write output.');
43289 fflush($this->stream);
43302 protected function hasColorSupport()
43304 if (DIRECTORY_SEPARATOR == '\\') {
43305 return false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI');
43308 return function_exists('posix_isatty') && @posix_isatty($this->stream);
43322 namespace Symfony\Component\Console\Output;
43327 class BufferedOutput extends Output
43332 private $buffer = '';
43339 public function fetch()
43341 $content = $this->buffer;
43342 $this->buffer = '';
43350 protected function doWrite($message, $newline)
43352 $this->buffer .= $message;
43355 $this->buffer .= "\n";
43370 namespace Symfony\Component\Console\Output;
43372 use Symfony\Component\Console\Formatter\OutputFormatterInterface;
43381 interface OutputInterface
43383 const VERBOSITY_QUIET = 0;
43384 const VERBOSITY_NORMAL = 1;
43385 const VERBOSITY_VERBOSE = 2;
43386 const VERBOSITY_VERY_VERBOSE = 3;
43387 const VERBOSITY_DEBUG = 4;
43389 const OUTPUT_NORMAL = 0;
43390 const OUTPUT_RAW = 1;
43391 const OUTPUT_PLAIN = 2;
43404 public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL);
43416 public function writeln($messages, $type = self::OUTPUT_NORMAL);
43425 public function setVerbosity($level);
43434 public function getVerbosity();
43443 public function setDecorated($decorated);
43452 public function isDecorated();
43461 public function setFormatter(OutputFormatterInterface $formatter);
43470 public function getFormatter();
43483 namespace Symfony\Component\Console\Output;
43485 use Symfony\Component\Console\Formatter\OutputFormatterInterface;
43502 class ConsoleOutput extends StreamOutput implements ConsoleOutputInterface
43515 public function __construct($verbosity = self::VERBOSITY_NORMAL, $decorated = null, OutputFormatterInterface $formatter = null)
43517 $outputStream = 'php://stdout';
43518 if (!$this->hasStdoutSupport()) {
43519 $outputStream = 'php://output';
43522 parent::__construct(fopen($outputStream, 'w'), $verbosity, $decorated, $formatter);
43524 $this->stderr = new StreamOutput(fopen('php://stderr', 'w'), $verbosity, $decorated, $this->getFormatter());
43530 public function setDecorated($decorated)
43532 parent::setDecorated($decorated);
43533 $this->stderr->setDecorated($decorated);
43539 public function setFormatter(OutputFormatterInterface $formatter)
43541 parent::setFormatter($formatter);
43542 $this->stderr->setFormatter($formatter);
43548 public function setVerbosity($level)
43550 parent::setVerbosity($level);
43551 $this->stderr->setVerbosity($level);
43557 public function getErrorOutput()
43559 return $this->stderr;
43565 public function setErrorOutput(OutputInterface $error)
43567 $this->stderr = $error;
43580 protected function hasStdoutSupport()
43582 return ('OS400' != php_uname('s'));
43596 namespace Symfony\Component\Console\Logger;
43598 use Psr\Log\AbstractLogger;
43599 use Psr\Log\InvalidArgumentException;
43600 use Psr\Log\LogLevel;
43601 use Symfony\Component\Console\Output\OutputInterface;
43602 use Symfony\Component\Console\Output\ConsoleOutputInterface;
43610 class ConsoleLogger extends AbstractLogger
43612 const INFO = 'info';
43613 const ERROR = 'error';
43622 private $verbosityLevelMap = array(
43623 LogLevel::EMERGENCY => OutputInterface::VERBOSITY_NORMAL,
43624 LogLevel::ALERT => OutputInterface::VERBOSITY_NORMAL,
43625 LogLevel::CRITICAL => OutputInterface::VERBOSITY_NORMAL,
43626 LogLevel::ERROR => OutputInterface::VERBOSITY_NORMAL,
43627 LogLevel::WARNING => OutputInterface::VERBOSITY_NORMAL,
43628 LogLevel::NOTICE => OutputInterface::VERBOSITY_VERBOSE,
43629 LogLevel::INFO => OutputInterface::VERBOSITY_VERY_VERBOSE,
43630 LogLevel::DEBUG => OutputInterface::VERBOSITY_DEBUG,
43635 private $formatLevelMap = array(
43636 LogLevel::EMERGENCY => self::ERROR,
43637 LogLevel::ALERT => self::ERROR,
43638 LogLevel::CRITICAL => self::ERROR,
43639 LogLevel::ERROR => self::ERROR,
43640 LogLevel::WARNING => self::INFO,
43641 LogLevel::NOTICE => self::INFO,
43642 LogLevel::INFO => self::INFO,
43643 LogLevel::DEBUG => self::INFO,
43651 public function __construct(OutputInterface $output, array $verbosityLevelMap = array(), array $formatLevelMap = array())
43653 $this->output = $output;
43654 $this->verbosityLevelMap = $verbosityLevelMap + $this->verbosityLevelMap;
43655 $this->formatLevelMap = $formatLevelMap + $this->formatLevelMap;
43661 public function log($level, $message, array $context = array())
43663 if (!isset($this->verbosityLevelMap[$level])) {
43664 throw new InvalidArgumentException(sprintf('The log level "%s" does not exist.', $level));
43668 if ($this->formatLevelMap[$level] === self::ERROR && $this->output instanceof ConsoleOutputInterface) {
43669 $output = $this->output->getErrorOutput();
43671 $output = $this->output;
43674 if ($output->getVerbosity() >= $this->verbosityLevelMap[$level]) {
43675 $output->writeln(sprintf('<%1$s>[%2$s] %3$s</%1$s>', $this->formatLevelMap[$level], $level, $this->interpolate($message, $context)));
43687 private function interpolate($message, array $context)
43690 $replace = array();
43691 foreach ($context as $key => $val) {
43692 if (!is_array($val) && (!is_object($val) || method_exists($val, '__toString'))) {
43693 $replace[sprintf('{%s}', $key)] = $val;
43698 return strtr($message, $replace);
43712 namespace Symfony\Component\Console\Descriptor;
43714 use Symfony\Component\Console\Output\OutputInterface;
43721 interface DescriptorInterface
43730 public function describe(OutputInterface $output, $object, array $options = array());
43743 namespace Symfony\Component\Console\Descriptor;
43745 use Symfony\Component\Console\Application;
43746 use Symfony\Component\Console\Command\Command;
43747 use Symfony\Component\Console\Input\InputArgument;
43748 use Symfony\Component\Console\Input\InputDefinition;
43749 use Symfony\Component\Console\Input\InputOption;
43756 class TextDescriptor extends Descriptor
43761 protected function describeInputArgument(InputArgument $argument, array $options = array())
43763 if (null !== $argument->getDefault() && (!is_array($argument->getDefault()) || count($argument->getDefault()))) {
43764 $default = sprintf('<comment> (default: %s)</comment>', $this->formatDefaultValue($argument->getDefault()));
43769 $nameWidth = isset($options['name_width']) ? $options['name_width'] : strlen($argument->getName());
43771 $this->writeText(sprintf(" <info>%-${nameWidth}s</info> %s%s",
43772 $argument->getName(),
43773 str_replace("\n", "\n".str_repeat(' ', $nameWidth + 2), $argument->getDescription()),
43781 protected function describeInputOption(InputOption $option, array $options = array())
43783 if ($option->acceptValue() && null !== $option->getDefault() && (!is_array($option->getDefault()) || count($option->getDefault()))) {
43784 $default = sprintf('<comment> (default: %s)</comment>', $this->formatDefaultValue($option->getDefault()));
43789 $nameWidth = isset($options['name_width']) ? $options['name_width'] : strlen($option->getName());
43790 $nameWithShortcutWidth = $nameWidth - strlen($option->getName()) - 2;
43792 $this->writeText(sprintf(" <info>%s</info> %-${nameWithShortcutWidth}s%s%s%s",
43793 '--'.$option->getName(),
43794 $option->getShortcut() ? sprintf('(-%s) ', $option->getShortcut()) : '',
43795 str_replace("\n", "\n".str_repeat(' ', $nameWidth + 2), $option->getDescription()),
43797 $option->isArray() ? '<comment> (multiple values allowed)</comment>' : ''
43804 protected function describeInputDefinition(InputDefinition $definition, array $options = array())
43807 foreach ($definition->getOptions() as $option) {
43808 $nameLength = strlen($option->getName()) + 2;
43809 if ($option->getShortcut()) {
43810 $nameLength += strlen($option->getShortcut()) + 3;
43812 $nameWidth = max($nameWidth, $nameLength);
43814 foreach ($definition->getArguments() as $argument) {
43815 $nameWidth = max($nameWidth, strlen($argument->getName()));
43819 if ($definition->getArguments()) {
43820 $this->writeText('<comment>Arguments:</comment>', $options);
43821 $this->writeText("\n");
43822 foreach ($definition->getArguments() as $argument) {
43823 $this->describeInputArgument($argument, array_merge($options, array('name_width' => $nameWidth)));
43824 $this->writeText("\n");
43828 if ($definition->getArguments() && $definition->getOptions()) {
43829 $this->writeText("\n");
43832 if ($definition->getOptions()) {
43833 $this->writeText('<comment>Options:</comment>', $options);
43834 $this->writeText("\n");
43835 foreach ($definition->getOptions() as $option) {
43836 $this->describeInputOption($option, array_merge($options, array('name_width' => $nameWidth)));
43837 $this->writeText("\n");
43845 protected function describeCommand(Command $command, array $options = array())
43847 $command->getSynopsis();
43848 $command->mergeApplicationDefinition(false);
43850 $this->writeText('<comment>Usage:</comment>', $options);
43851 $this->writeText("\n");
43852 $this->writeText(' '.$command->getSynopsis(), $options);
43853 $this->writeText("\n");
43855 if (count($command->getAliases()) > 0) {
43856 $this->writeText("\n");
43857 $this->writeText('<comment>Aliases:</comment> <info>'.implode(', ', $command->getAliases()).'</info>', $options);
43860 if ($definition = $command->getNativeDefinition()) {
43861 $this->writeText("\n");
43862 $this->describeInputDefinition($definition, $options);
43865 $this->writeText("\n");
43867 if ($help = $command->getProcessedHelp()) {
43868 $this->writeText('<comment>Help:</comment>', $options);
43869 $this->writeText("\n");
43870 $this->writeText(' '.str_replace("\n", "\n ", $help), $options);
43871 $this->writeText("\n");
43878 protected function describeApplication(Application $application, array $options = array())
43880 $describedNamespace = isset($options['namespace']) ? $options['namespace'] : null;
43881 $description = new ApplicationDescription($application, $describedNamespace);
43883 if (isset($options['raw_text']) && $options['raw_text']) {
43884 $width = $this->getColumnWidth($description->getCommands());
43886 foreach ($description->getCommands() as $command) {
43887 $this->writeText(sprintf("%-${width}s %s", $command->getName(), $command->getDescription()), $options);
43888 $this->writeText("\n");
43891 if ('' != $help = $application->getHelp()) {
43892 $this->writeText("$help\n\n", $options);
43895 $this->writeText("<comment>Usage:</comment>\n", $options);
43896 $this->writeText(" [options] command [arguments]\n\n", $options);
43897 $this->writeText('<comment>Options:</comment>', $options);
43899 $inputOptions = $application->getDefinition()->getOptions();
43902 foreach ($inputOptions as $option) {
43903 $nameLength = strlen($option->getName()) + 2;
43904 if ($option->getShortcut()) {
43905 $nameLength += strlen($option->getShortcut()) + 3;
43907 $width = max($width, $nameLength);
43911 foreach ($inputOptions as $option) {
43912 $this->writeText("\n", $options);
43913 $this->describeInputOption($option, array_merge($options, array('name_width' => $width)));
43916 $this->writeText("\n\n", $options);
43918 $width = $this->getColumnWidth($description->getCommands());
43920 if ($describedNamespace) {
43921 $this->writeText(sprintf("<comment>Available commands for the \"%s\" namespace:</comment>", $describedNamespace), $options);
43923 $this->writeText('<comment>Available commands:</comment>', $options);
43927 foreach ($description->getNamespaces() as $namespace) {
43928 if (!$describedNamespace && ApplicationDescription::GLOBAL_NAMESPACE !== $namespace['id']) {
43929 $this->writeText("\n");
43930 $this->writeText('<comment>'.$namespace['id'].'</comment>', $options);
43933 foreach ($namespace['commands'] as $name) {
43934 $this->writeText("\n");
43935 $this->writeText(sprintf(" <info>%-${width}s</info> %s", $name, $description->getCommand($name)->getDescription()), $options);
43939 $this->writeText("\n");
43946 private function writeText($content, array $options = array())
43949 isset($options['raw_text']) && $options['raw_text'] ? strip_tags($content) : $content,
43950 isset($options['raw_output']) ? !$options['raw_output'] : true
43961 private function formatDefaultValue($default)
43963 if (PHP_VERSION_ID < 50400) {
43964 return str_replace('\/', '/', json_encode($default));
43967 return json_encode($default, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
43975 private function getColumnWidth(array $commands)
43978 foreach ($commands as $command) {
43979 $width = strlen($command->getName()) > $width ? strlen($command->getName()) : $width;
43996 namespace Symfony\Component\Console\Descriptor;
43998 use Symfony\Component\Console\Application;
43999 use Symfony\Component\Console\Command\Command;
44000 use Symfony\Component\Console\Input\InputArgument;
44001 use Symfony\Component\Console\Input\InputDefinition;
44002 use Symfony\Component\Console\Input\InputOption;
44009 class XmlDescriptor extends Descriptor
44016 public function getInputDefinitionDocument(InputDefinition $definition)
44018 $dom = new \DOMDocument('1.0', 'UTF-8');
44019 $dom->appendChild($definitionXML = $dom->createElement('definition'));
44021 $definitionXML->appendChild($argumentsXML = $dom->createElement('arguments'));
44022 foreach ($definition->getArguments() as $argument) {
44023 $this->appendDocument($argumentsXML, $this->getInputArgumentDocument($argument));
44026 $definitionXML->appendChild($optionsXML = $dom->createElement('options'));
44027 foreach ($definition->getOptions() as $option) {
44028 $this->appendDocument($optionsXML, $this->getInputOptionDocument($option));
44039 public function getCommandDocument(Command $command)
44041 $dom = new \DOMDocument('1.0', 'UTF-8');
44042 $dom->appendChild($commandXML = $dom->createElement('command'));
44044 $command->getSynopsis();
44045 $command->mergeApplicationDefinition(false);
44047 $commandXML->setAttribute('id', $command->getName());
44048 $commandXML->setAttribute('name', $command->getName());
44050 $commandXML->appendChild($usageXML = $dom->createElement('usage'));
44051 $usageXML->appendChild($dom->createTextNode(sprintf($command->getSynopsis(), '')));
44053 $commandXML->appendChild($descriptionXML = $dom->createElement('description'));
44054 $descriptionXML->appendChild($dom->createTextNode(str_replace("\n", "\n ", $command->getDescription())));
44056 $commandXML->appendChild($helpXML = $dom->createElement('help'));
44057 $helpXML->appendChild($dom->createTextNode(str_replace("\n", "\n ", $command->getProcessedHelp())));
44059 $commandXML->appendChild($aliasesXML = $dom->createElement('aliases'));
44060 foreach ($command->getAliases() as $alias) {
44061 $aliasesXML->appendChild($aliasXML = $dom->createElement('alias'));
44062 $aliasXML->appendChild($dom->createTextNode($alias));
44065 $definitionXML = $this->getInputDefinitionDocument($command->getNativeDefinition());
44066 $this->appendDocument($commandXML, $definitionXML->getElementsByTagName('definition')->item(0));
44077 public function getApplicationDocument(Application $application, $namespace = null)
44079 $dom = new \DOMDocument('1.0', 'UTF-8');
44080 $dom->appendChild($rootXml = $dom->createElement('symfony'));
44082 if ($application->getName() !== 'UNKNOWN') {
44083 $rootXml->setAttribute('name', $application->getName());
44084 if ($application->getVersion() !== 'UNKNOWN') {
44085 $rootXml->setAttribute('version', $application->getVersion());
44089 $rootXml->appendChild($commandsXML = $dom->createElement('commands'));
44091 $description = new ApplicationDescription($application, $namespace);
44094 $commandsXML->setAttribute('namespace', $namespace);
44097 foreach ($description->getCommands() as $command) {
44098 $this->appendDocument($commandsXML, $this->getCommandDocument($command));
44102 $rootXml->appendChild($namespacesXML = $dom->createElement('namespaces'));
44104 foreach ($description->getNamespaces() as $namespaceDescription) {
44105 $namespacesXML->appendChild($namespaceArrayXML = $dom->createElement('namespace'));
44106 $namespaceArrayXML->setAttribute('id', $namespaceDescription['id']);
44108 foreach ($namespaceDescription['commands'] as $name) {
44109 $namespaceArrayXML->appendChild($commandXML = $dom->createElement('command'));
44110 $commandXML->appendChild($dom->createTextNode($name));
44121 protected function describeInputArgument(InputArgument $argument, array $options = array())
44123 $this->writeDocument($this->getInputArgumentDocument($argument));
44129 protected function describeInputOption(InputOption $option, array $options = array())
44131 $this->writeDocument($this->getInputOptionDocument($option));
44137 protected function describeInputDefinition(InputDefinition $definition, array $options = array())
44139 $this->writeDocument($this->getInputDefinitionDocument($definition));
44145 protected function describeCommand(Command $command, array $options = array())
44147 $this->writeDocument($this->getCommandDocument($command));
44153 protected function describeApplication(Application $application, array $options = array())
44155 $this->writeDocument($this->getApplicationDocument($application, isset($options['namespace']) ? $options['namespace'] : null));
44164 private function appendDocument(\DOMNode $parentNode, \DOMNode $importedParent)
44166 foreach ($importedParent->childNodes as $childNode) {
44167 $parentNode->appendChild($parentNode->ownerDocument->importNode($childNode, true));
44178 private function writeDocument(\DOMDocument $dom)
44180 $dom->formatOutput = true;
44181 $this->write($dom->saveXML());
44189 private function getInputArgumentDocument(InputArgument $argument)
44191 $dom = new \DOMDocument('1.0', 'UTF-8');
44193 $dom->appendChild($objectXML = $dom->createElement('argument'));
44194 $objectXML->setAttribute('name', $argument->getName());
44195 $objectXML->setAttribute('is_required', $argument->isRequired() ? 1 : 0);
44196 $objectXML->setAttribute('is_array', $argument->isArray() ? 1 : 0);
44197 $objectXML->appendChild($descriptionXML = $dom->createElement('description'));
44198 $descriptionXML->appendChild($dom->createTextNode($argument->getDescription()));
44200 $objectXML->appendChild($defaultsXML = $dom->createElement('defaults'));
44201 $defaults = is_array($argument->getDefault()) ? $argument->getDefault() : (is_bool($argument->getDefault()) ? array(var_export($argument->getDefault(), true)) : ($argument->getDefault() ? array($argument->getDefault()) : array()));
44202 foreach ($defaults as $default) {
44203 $defaultsXML->appendChild($defaultXML = $dom->createElement('default'));
44204 $defaultXML->appendChild($dom->createTextNode($default));
44215 private function getInputOptionDocument(InputOption $option)
44217 $dom = new \DOMDocument('1.0', 'UTF-8');
44219 $dom->appendChild($objectXML = $dom->createElement('option'));
44220 $objectXML->setAttribute('name', '--'.$option->getName());
44221 $pos = strpos($option->getShortcut(), '|');
44222 if (false !== $pos) {
44223 $objectXML->setAttribute('shortcut', '-'.substr($option->getShortcut(), 0, $pos));
44224 $objectXML->setAttribute('shortcuts', '-'.implode('|-', explode('|', $option->getShortcut())));
44226 $objectXML->setAttribute('shortcut', $option->getShortcut() ? '-'.$option->getShortcut() : '');
44228 $objectXML->setAttribute('accept_value', $option->acceptValue() ? 1 : 0);
44229 $objectXML->setAttribute('is_value_required', $option->isValueRequired() ? 1 : 0);
44230 $objectXML->setAttribute('is_multiple', $option->isArray() ? 1 : 0);
44231 $objectXML->appendChild($descriptionXML = $dom->createElement('description'));
44232 $descriptionXML->appendChild($dom->createTextNode($option->getDescription()));
44234 if ($option->acceptValue()) {
44235 $defaults = is_array($option->getDefault()) ? $option->getDefault() : (is_bool($option->getDefault()) ? array(var_export($option->getDefault(), true)) : ($option->getDefault() ? array($option->getDefault()) : array()));
44236 $objectXML->appendChild($defaultsXML = $dom->createElement('defaults'));
44238 if (!empty($defaults)) {
44239 foreach ($defaults as $default) {
44240 $defaultsXML->appendChild($defaultXML = $dom->createElement('default'));
44241 $defaultXML->appendChild($dom->createTextNode($default));
44260 namespace Symfony\Component\Console\Descriptor;
44262 use Symfony\Component\Console\Application;
44263 use Symfony\Component\Console\Command\Command;
44264 use Symfony\Component\Console\Input\InputArgument;
44265 use Symfony\Component\Console\Input\InputDefinition;
44266 use Symfony\Component\Console\Input\InputOption;
44273 class MarkdownDescriptor extends Descriptor
44278 protected function describeInputArgument(InputArgument $argument, array $options = array())
44281 '**'.$argument->getName().':**'."\n\n"
44282 .'* Name: '.($argument->getName() ?: '<none>')."\n"
44283 .'* Is required: '.($argument->isRequired() ? 'yes' : 'no')."\n"
44284 .'* Is array: '.($argument->isArray() ? 'yes' : 'no')."\n"
44285 .'* Description: '.($argument->getDescription() ?: '<none>')."\n"
44286 .'* Default: `'.str_replace("\n", '', var_export($argument->getDefault(), true)).'`'
44293 protected function describeInputOption(InputOption $option, array $options = array())
44296 '**'.$option->getName().':**'."\n\n"
44297 .'* Name: `--'.$option->getName().'`'."\n"
44298 .'* Shortcut: '.($option->getShortcut() ? '`-'.implode('|-', explode('|', $option->getShortcut())).'`' : '<none>')."\n"
44299 .'* Accept value: '.($option->acceptValue() ? 'yes' : 'no')."\n"
44300 .'* Is value required: '.($option->isValueRequired() ? 'yes' : 'no')."\n"
44301 .'* Is multiple: '.($option->isArray() ? 'yes' : 'no')."\n"
44302 .'* Description: '.($option->getDescription() ?: '<none>')."\n"
44303 .'* Default: `'.str_replace("\n", '', var_export($option->getDefault(), true)).'`'
44310 protected function describeInputDefinition(InputDefinition $definition, array $options = array())
44312 if ($showArguments = count($definition->getArguments()) > 0) {
44313 $this->write('### Arguments:');
44314 foreach ($definition->getArguments() as $argument) {
44315 $this->write("\n\n");
44316 $this->write($this->describeInputArgument($argument));
44320 if (count($definition->getOptions()) > 0) {
44321 if ($showArguments) {
44322 $this->write("\n\n");
44325 $this->write('### Options:');
44326 foreach ($definition->getOptions() as $option) {
44327 $this->write("\n\n");
44328 $this->write($this->describeInputOption($option));
44336 protected function describeCommand(Command $command, array $options = array())
44338 $command->getSynopsis();
44339 $command->mergeApplicationDefinition(false);
44342 $command->getName()."\n"
44343 .str_repeat('-', strlen($command->getName()))."\n\n"
44344 .'* Description: '.($command->getDescription() ?: '<none>')."\n"
44345 .'* Usage: `'.$command->getSynopsis().'`'."\n"
44346 .'* Aliases: '.(count($command->getAliases()) ? '`'.implode('`, `', $command->getAliases()).'`' : '<none>')
44349 if ($help = $command->getProcessedHelp()) {
44350 $this->write("\n\n");
44351 $this->write($help);
44354 if ($definition = $command->getNativeDefinition()) {
44355 $this->write("\n\n");
44356 $this->describeInputDefinition($command->getNativeDefinition());
44363 protected function describeApplication(Application $application, array $options = array())
44365 $describedNamespace = isset($options['namespace']) ? $options['namespace'] : null;
44366 $description = new ApplicationDescription($application, $describedNamespace);
44368 $this->write($application->getName()."\n".str_repeat('=', strlen($application->getName())));
44370 foreach ($description->getNamespaces() as $namespace) {
44371 if (ApplicationDescription::GLOBAL_NAMESPACE !== $namespace['id']) {
44372 $this->write("\n\n");
44373 $this->write('**'.$namespace['id'].':**');
44376 $this->write("\n\n");
44377 $this->write(implode("\n", array_map(function ($commandName) {
44378 return '* '.$commandName;
44379 }, $namespace['commands'])));
44382 foreach ($description->getCommands() as $command) {
44383 $this->write("\n\n");
44384 $this->write($this->describeCommand($command));
44399 namespace Symfony\Component\Console\Descriptor;
44401 use Symfony\Component\Console\Application;
44402 use Symfony\Component\Console\Command\Command;
44407 class ApplicationDescription
44409 const GLOBAL_NAMESPACE = '_global';
44414 private $application;
44419 private $namespace;
44424 private $namespaces;
44442 public function __construct(Application $application, $namespace = null)
44444 $this->application = $application;
44445 $this->namespace = $namespace;
44451 public function getNamespaces()
44453 if (null === $this->namespaces) {
44454 $this->inspectApplication();
44457 return $this->namespaces;
44463 public function getCommands()
44465 if (null === $this->commands) {
44466 $this->inspectApplication();
44469 return $this->commands;
44479 public function getCommand($name)
44481 if (!isset($this->commands[$name]) && !isset($this->aliases[$name])) {
44482 throw new \InvalidArgumentException(sprintf('Command %s does not exist.', $name));
44485 return isset($this->commands[$name]) ? $this->commands[$name] : $this->aliases[$name];
44488 private function inspectApplication()
44490 $this->commands = array();
44491 $this->namespaces = array();
44493 $all = $this->application->all($this->namespace ? $this->application->findNamespace($this->namespace) : null);
44494 foreach ($this->sortCommands($all) as $namespace => $commands) {
44498 foreach ($commands as $name => $command) {
44499 if (!$command->getName()) {
44503 if ($command->getName() === $name) {
44504 $this->commands[$name] = $command;
44506 $this->aliases[$name] = $command;
44512 $this->namespaces[$namespace] = array('id' => $namespace, 'commands' => $names);
44521 private function sortCommands(array $commands)
44523 $namespacedCommands = array();
44524 foreach ($commands as $name => $command) {
44525 $key = $this->application->extractNamespace($name, 1);
44530 $namespacedCommands[$key][$name] = $command;
44532 ksort($namespacedCommands);
44534 foreach ($namespacedCommands as &$commands) {
44538 return $namespacedCommands;
44552 namespace Symfony\Component\Console\Descriptor;
44554 use Symfony\Component\Console\Application;
44555 use Symfony\Component\Console\Command\Command;
44556 use Symfony\Component\Console\Input\InputArgument;
44557 use Symfony\Component\Console\Input\InputDefinition;
44558 use Symfony\Component\Console\Input\InputOption;
44559 use Symfony\Component\Console\Output\OutputInterface;
44564 abstract class Descriptor implements DescriptorInterface
44574 public function describe(OutputInterface $output, $object, array $options = array())
44576 $this->output = $output;
44579 case $object instanceof InputArgument:
44580 $this->describeInputArgument($object, $options);
44582 case $object instanceof InputOption:
44583 $this->describeInputOption($object, $options);
44585 case $object instanceof InputDefinition:
44586 $this->describeInputDefinition($object, $options);
44588 case $object instanceof Command:
44589 $this->describeCommand($object, $options);
44591 case $object instanceof Application:
44592 $this->describeApplication($object, $options);
44595 throw new \InvalidArgumentException(sprintf('Object of type "%s" is not describable.', get_class($object)));
44605 protected function write($content, $decorated = false)
44607 $this->output->write($content, false, $decorated ? OutputInterface::OUTPUT_NORMAL : OutputInterface::OUTPUT_RAW);
44618 abstract protected function describeInputArgument(InputArgument $argument, array $options = array());
44628 abstract protected function describeInputOption(InputOption $option, array $options = array());
44638 abstract protected function describeInputDefinition(InputDefinition $definition, array $options = array());
44648 abstract protected function describeCommand(Command $command, array $options = array());
44658 abstract protected function describeApplication(Application $application, array $options = array());
44671 namespace Symfony\Component\Console\Descriptor;
44673 use Symfony\Component\Console\Application;
44674 use Symfony\Component\Console\Command\Command;
44675 use Symfony\Component\Console\Input\InputArgument;
44676 use Symfony\Component\Console\Input\InputDefinition;
44677 use Symfony\Component\Console\Input\InputOption;
44684 class JsonDescriptor extends Descriptor
44689 protected function describeInputArgument(InputArgument $argument, array $options = array())
44691 $this->writeData($this->getInputArgumentData($argument), $options);
44697 protected function describeInputOption(InputOption $option, array $options = array())
44699 $this->writeData($this->getInputOptionData($option), $options);
44705 protected function describeInputDefinition(InputDefinition $definition, array $options = array())
44707 $this->writeData($this->getInputDefinitionData($definition), $options);
44713 protected function describeCommand(Command $command, array $options = array())
44715 $this->writeData($this->getCommandData($command), $options);
44721 protected function describeApplication(Application $application, array $options = array())
44723 $describedNamespace = isset($options['namespace']) ? $options['namespace'] : null;
44724 $description = new ApplicationDescription($application, $describedNamespace);
44725 $commands = array();
44727 foreach ($description->getCommands() as $command) {
44728 $commands[] = $this->getCommandData($command);
44731 $data = $describedNamespace
44732 ? array('commands' => $commands, 'namespace' => $describedNamespace)
44733 : array('commands' => $commands, 'namespaces' => array_values($description->getNamespaces()));
44735 $this->writeData($data, $options);
44746 private function writeData(array $data, array $options)
44748 $this->write(json_encode($data, isset($options['json_encoding']) ? $options['json_encoding'] : 0));
44756 private function getInputArgumentData(InputArgument $argument)
44759 'name' => $argument->getName(),
44760 'is_required' => $argument->isRequired(),
44761 'is_array' => $argument->isArray(),
44762 'description' => $argument->getDescription(),
44763 'default' => $argument->getDefault(),
44772 private function getInputOptionData(InputOption $option)
44775 'name' => '--'.$option->getName(),
44776 'shortcut' => $option->getShortcut() ? '-'.implode('|-', explode('|', $option->getShortcut())) : '',
44777 'accept_value' => $option->acceptValue(),
44778 'is_value_required' => $option->isValueRequired(),
44779 'is_multiple' => $option->isArray(),
44780 'description' => $option->getDescription(),
44781 'default' => $option->getDefault(),
44790 private function getInputDefinitionData(InputDefinition $definition)
44792 $inputArguments = array();
44793 foreach ($definition->getArguments() as $name => $argument) {
44794 $inputArguments[$name] = $this->getInputArgumentData($argument);
44797 $inputOptions = array();
44798 foreach ($definition->getOptions() as $name => $option) {
44799 $inputOptions[$name] = $this->getInputOptionData($option);
44802 return array('arguments' => $inputArguments, 'options' => $inputOptions);
44810 private function getCommandData(Command $command)
44812 $command->getSynopsis();
44813 $command->mergeApplicationDefinition(false);
44816 'name' => $command->getName(),
44817 'usage' => $command->getSynopsis(),
44818 'description' => $command->getDescription(),
44819 'help' => $command->getProcessedHelp(),
44820 'aliases' => $command->getAliases(),
44821 'definition' => $this->getInputDefinitionData($command->getNativeDefinition()),
44836 namespace Symfony\Component\Console\Helper;
44845 interface HelperInterface
44854 public function setHelperSet(HelperSet $helperSet = null);
44863 public function getHelperSet();
44872 public function getName();
44885 namespace Symfony\Component\Console\Helper;
44887 use Symfony\Component\Console\Output\OutputInterface;
44888 use Symfony\Component\Console\Output\NullOutput;
44898 class TableHelper extends Helper
44900 const LAYOUT_DEFAULT = 0;
44901 const LAYOUT_BORDERLESS = 1;
44902 const LAYOUT_COMPACT = 2;
44909 public function __construct()
44911 $this->table = new Table(new NullOutput());
44923 public function setLayout($layout)
44926 case self::LAYOUT_BORDERLESS:
44927 $this->table->setStyle('borderless');
44930 case self::LAYOUT_COMPACT:
44931 $this->table->setStyle('compact');
44934 case self::LAYOUT_DEFAULT:
44935 $this->table->setStyle('default');
44939 throw new \InvalidArgumentException(sprintf('Invalid table layout "%s".', $layout));
44946 public function setHeaders(array $headers)
44948 $this->table->setHeaders($headers);
44953 public function setRows(array $rows)
44955 $this->table->setRows($rows);
44960 public function addRows(array $rows)
44962 $this->table->addRows($rows);
44967 public function addRow(array $row)
44969 $this->table->addRow($row);
44974 public function setRow($column, array $row)
44976 $this->table->setRow($column, $row);
44988 public function setPaddingChar($paddingChar)
44990 $this->table->getStyle()->setPaddingChar($paddingChar);
45002 public function setHorizontalBorderChar($horizontalBorderChar)
45004 $this->table->getStyle()->setHorizontalBorderChar($horizontalBorderChar);
45016 public function setVerticalBorderChar($verticalBorderChar)
45018 $this->table->getStyle()->setVerticalBorderChar($verticalBorderChar);
45030 public function setCrossingChar($crossingChar)
45032 $this->table->getStyle()->setCrossingChar($crossingChar);
45044 public function setCellHeaderFormat($cellHeaderFormat)
45046 $this->table->getStyle()->setCellHeaderFormat($cellHeaderFormat);
45058 public function setCellRowFormat($cellRowFormat)
45060 $this->table->getStyle()->setCellHeaderFormat($cellRowFormat);
45072 public function setCellRowContentFormat($cellRowContentFormat)
45074 $this->table->getStyle()->setCellRowContentFormat($cellRowContentFormat);
45086 public function setBorderFormat($borderFormat)
45088 $this->table->getStyle()->setBorderFormat($borderFormat);
45100 public function setPadType($padType)
45102 $this->table->getStyle()->setPadType($padType);
45121 public function render(OutputInterface $output)
45123 $p = new \ReflectionProperty($this->table, 'output');
45124 $p->setAccessible(true);
45125 $p->setValue($this->table, $output);
45127 $this->table->render();
45133 public function getName()
45149 namespace Symfony\Component\Console\Helper;
45151 use Symfony\Component\Console\Output\OutputInterface;
45166 private $headers = array();
45173 private $rows = array();
45180 private $columnWidths = array();
45187 private $numberOfColumns;
45199 private static $styles;
45201 public function __construct(OutputInterface $output)
45203 $this->output = $output;
45205 if (!self::$styles) {
45206 self::$styles = self::initStyles();
45209 $this->setStyle('default');
45218 public static function setStyleDefinition($name, TableStyle $style)
45220 if (!self::$styles) {
45221 self::$styles = self::initStyles();
45224 self::$styles[$name] = $style;
45234 public static function getStyleDefinition($name)
45236 if (!self::$styles) {
45237 self::$styles = self::initStyles();
45240 if (!self::$styles[$name]) {
45241 throw new \InvalidArgumentException(sprintf('Style "%s" is not defined.', $name));
45244 return self::$styles[$name];
45254 public function setStyle($name)
45256 if ($name instanceof TableStyle) {
45257 $this->style = $name;
45258 } elseif (isset(self::$styles[$name])) {
45259 $this->style = self::$styles[$name];
45261 throw new \InvalidArgumentException(sprintf('Style "%s" is not defined.', $name));
45272 public function getStyle()
45274 return $this->style;
45277 public function setHeaders(array $headers)
45279 $this->headers = array_values($headers);
45284 public function setRows(array $rows)
45286 $this->rows = array();
45288 return $this->addRows($rows);
45291 public function addRows(array $rows)
45293 foreach ($rows as $row) {
45294 $this->addRow($row);
45300 public function addRow($row)
45302 if ($row instanceof TableSeparator) {
45303 $this->rows[] = $row;
45308 if (!is_array($row)) {
45309 throw new \InvalidArgumentException('A row must be an array or a TableSeparator instance.');
45312 $this->rows[] = array_values($row);
45314 $keys = array_keys($this->rows);
45315 $rowKey = array_pop($keys);
45317 foreach ($row as $key => $cellValue) {
45318 if (!strstr($cellValue, "\n")) {
45322 $lines = explode("\n", $cellValue);
45323 $this->rows[$rowKey][$key] = $lines[0];
45326 foreach ($lines as $lineKey => $line) {
45327 $nextRowKey = $rowKey + $lineKey + 1;
45329 if (isset($this->rows[$nextRowKey])) {
45330 $this->rows[$nextRowKey][$key] = $line;
45332 $this->rows[$nextRowKey] = array($key => $line);
45340 public function setRow($column, array $row)
45342 $this->rows[$column] = $row;
45359 public function render()
45361 $this->renderRowSeparator();
45362 $this->renderRow($this->headers, $this->style->getCellHeaderFormat());
45363 if (!empty($this->headers)) {
45364 $this->renderRowSeparator();
45366 foreach ($this->rows as $row) {
45367 if ($row instanceof TableSeparator) {
45368 $this->renderRowSeparator();
45370 $this->renderRow($row, $this->style->getCellRowFormat());
45373 if (!empty($this->rows)) {
45374 $this->renderRowSeparator();
45385 private function renderRowSeparator()
45387 if (0 === $count = $this->getNumberOfColumns()) {
45391 if (!$this->style->getHorizontalBorderChar() && !$this->style->getCrossingChar()) {
45395 $markup = $this->style->getCrossingChar();
45396 for ($column = 0; $column < $count; $column++) {
45397 $markup .= str_repeat($this->style->getHorizontalBorderChar(), $this->getColumnWidth($column)).$this->style->getCrossingChar();
45400 $this->output->writeln(sprintf($this->style->getBorderFormat(), $markup));
45406 private function renderColumnSeparator()
45408 $this->output->write(sprintf($this->style->getBorderFormat(), $this->style->getVerticalBorderChar()));
45419 private function renderRow(array $row, $cellFormat)
45425 $this->renderColumnSeparator();
45426 for ($column = 0, $count = $this->getNumberOfColumns(); $column < $count; $column++) {
45427 $this->renderCell($row, $column, $cellFormat);
45428 $this->renderColumnSeparator();
45430 $this->output->writeln('');
45440 private function renderCell(array $row, $column, $cellFormat)
45442 $cell = isset($row[$column]) ? $row[$column] : '';
45443 $width = $this->getColumnWidth($column);
45446 if (function_exists('mb_strlen') && false !== $encoding = mb_detect_encoding($cell)) {
45447 $width += strlen($cell) - mb_strlen($cell, $encoding);
45450 $width += Helper::strlen($cell) - Helper::strlenWithoutDecoration($this->output->getFormatter(), $cell);
45452 $content = sprintf($this->style->getCellRowContentFormat(), $cell);
45454 $this->output->write(sprintf($cellFormat, str_pad($content, $width, $this->style->getPaddingChar(), $this->style->getPadType())));
45462 private function getNumberOfColumns()
45464 if (null !== $this->numberOfColumns) {
45465 return $this->numberOfColumns;
45468 $columns = array(count($this->headers));
45469 foreach ($this->rows as $row) {
45470 $columns[] = count($row);
45473 return $this->numberOfColumns = max($columns);
45483 private function getColumnWidth($column)
45485 if (isset($this->columnWidths[$column])) {
45486 return $this->columnWidths[$column];
45489 $lengths = array($this->getCellWidth($this->headers, $column));
45490 foreach ($this->rows as $row) {
45491 if ($row instanceof TableSeparator) {
45495 $lengths[] = $this->getCellWidth($row, $column);
45498 return $this->columnWidths[$column] = max($lengths) + strlen($this->style->getCellRowContentFormat()) - 2;
45509 private function getCellWidth(array $row, $column)
45511 return isset($row[$column]) ? Helper::strlenWithoutDecoration($this->output->getFormatter(), $row[$column]) : 0;
45517 private function cleanup()
45519 $this->columnWidths = array();
45520 $this->numberOfColumns = null;
45523 private static function initStyles()
45525 $borderless = new TableStyle();
45527 ->setHorizontalBorderChar('=')
45528 ->setVerticalBorderChar(' ')
45529 ->setCrossingChar(' ')
45532 $compact = new TableStyle();
45534 ->setHorizontalBorderChar('')
45535 ->setVerticalBorderChar(' ')
45536 ->setCrossingChar('')
45537 ->setCellRowContentFormat('%s')
45541 'default' => new TableStyle(),
45542 'borderless' => $borderless,
45543 'compact' => $compact,
45558 namespace Symfony\Component\Console\Helper;
45560 use Symfony\Component\Console\Output\OutputInterface;
45571 private $barWidth = 28;
45573 private $emptyBarChar = '-';
45574 private $progressChar = '>';
45575 private $format = null;
45576 private $redrawFreq = 1;
45584 private $startTime;
45585 private $stepWidth;
45586 private $percent = 0.0;
45587 private $lastMessagesLength = 0;
45588 private $formatLineCount;
45590 private $overwrite = true;
45592 private static $formatters;
45593 private static $formats;
45601 public function __construct(OutputInterface $output, $max = 0)
45603 $this->output = $output;
45604 $this->setMaxSteps($max);
45606 if (!$this->output->isDecorated()) {
45608 $this->overwrite = false;
45610 if ($this->max > 10) {
45612 $this->setRedrawFrequency($max / 10);
45616 $this->setFormat($this->determineBestFormat());
45618 $this->startTime = time();
45629 public static function setPlaceholderFormatterDefinition($name, $callable)
45631 if (!self::$formatters) {
45632 self::$formatters = self::initPlaceholderFormatters();
45635 self::$formatters[$name] = $callable;
45645 public static function getPlaceholderFormatterDefinition($name)
45647 if (!self::$formatters) {
45648 self::$formatters = self::initPlaceholderFormatters();
45651 return isset(self::$formatters[$name]) ? self::$formatters[$name] : null;
45662 public static function setFormatDefinition($name, $format)
45664 if (!self::$formats) {
45665 self::$formats = self::initFormats();
45668 self::$formats[$name] = $format;
45678 public static function getFormatDefinition($name)
45680 if (!self::$formats) {
45681 self::$formats = self::initFormats();
45684 return isset(self::$formats[$name]) ? self::$formats[$name] : null;
45687 public function setMessage($message, $name = 'message')
45689 $this->messages[$name] = $message;
45692 public function getMessage($name = 'message')
45694 return $this->messages[$name];
45702 public function getStartTime()
45704 return $this->startTime;
45712 public function getMaxSteps()
45724 public function getStep()
45726 return $this->getProgress();
45734 public function getProgress()
45736 return $this->step;
45746 public function getStepWidth()
45748 return $this->stepWidth;
45756 public function getProgressPercent()
45758 return $this->percent;
45766 public function setBarWidth($size)
45768 $this->barWidth = (int) $size;
45776 public function getBarWidth()
45778 return $this->barWidth;
45786 public function setBarCharacter($char)
45788 $this->barChar = $char;
45796 public function getBarCharacter()
45798 if (null === $this->barChar) {
45799 return $this->max ? '=' : $this->emptyBarChar;
45802 return $this->barChar;
45810 public function setEmptyBarCharacter($char)
45812 $this->emptyBarChar = $char;
45820 public function getEmptyBarCharacter()
45822 return $this->emptyBarChar;
45830 public function setProgressCharacter($char)
45832 $this->progressChar = $char;
45840 public function getProgressCharacter()
45842 return $this->progressChar;
45850 public function setFormat($format)
45853 if (!$this->max && null !== self::getFormatDefinition($format.'_nomax')) {
45854 $this->format = self::getFormatDefinition($format.'_nomax');
45855 } elseif (null !== self::getFormatDefinition($format)) {
45856 $this->format = self::getFormatDefinition($format);
45858 $this->format = $format;
45861 $this->formatLineCount = substr_count($this->format, "\n");
45869 public function setRedrawFrequency($freq)
45871 $this->redrawFreq = (int) $freq;
45879 public function start($max = null)
45881 $this->startTime = time();
45883 $this->percent = 0.0;
45885 if (null !== $max) {
45886 $this->setMaxSteps($max);
45899 public function advance($step = 1)
45901 $this->setProgress($this->step + $step);
45913 public function setCurrent($step)
45915 $this->setProgress($step);
45923 public function setOverwrite($overwrite)
45925 $this->overwrite = (bool) $overwrite;
45935 public function setProgress($step)
45937 $step = (int) $step;
45938 if ($step < $this->step) {
45939 throw new \LogicException('You can\'t regress the progress bar.');
45942 if ($this->max && $step > $this->max) {
45943 $this->max = $step;
45946 $prevPeriod = intval($this->step / $this->redrawFreq);
45947 $currPeriod = intval($step / $this->redrawFreq);
45948 $this->step = $step;
45949 $this->percent = $this->max ? (float) $this->step / $this->max : 0;
45950 if ($prevPeriod !== $currPeriod || $this->max === $step) {
45958 public function finish()
45961 $this->max = $this->step;
45964 if ($this->step === $this->max && !$this->overwrite) {
45969 $this->setProgress($this->max);
45975 public function display()
45977 if (OutputInterface::VERBOSITY_QUIET === $this->output->getVerbosity()) {
45983 $output = $this->output;
45984 $messages = $this->messages;
45985 $this->overwrite(preg_replace_callback("{%([a-z\-_]+)(?:\:([^%]+))?%}i", function ($matches) use ($self, $output, $messages) {
45986 if ($formatter = $self::getPlaceholderFormatterDefinition($matches[1])) {
45987 $text = call_user_func($formatter, $self, $output);
45988 } elseif (isset($messages[$matches[1]])) {
45989 $text = $messages[$matches[1]];
45991 return $matches[0];
45994 if (isset($matches[2])) {
45995 $text = sprintf('%'.$matches[2], $text);
45999 }, $this->format));
46009 public function clear()
46011 if (!$this->overwrite) {
46015 $this->overwrite(str_repeat("\n", $this->formatLineCount));
46023 private function setMaxSteps($max)
46025 $this->max = max(0, (int) $max);
46026 $this->stepWidth = $this->max ? Helper::strlen($this->max) : 4;
46034 private function overwrite($message)
46036 $lines = explode("\n", $message);
46039 if (null !== $this->lastMessagesLength) {
46040 foreach ($lines as $i => $line) {
46041 if ($this->lastMessagesLength > Helper::strlenWithoutDecoration($this->output->getFormatter(), $line)) {
46042 $lines[$i] = str_pad($line, $this->lastMessagesLength, "\x20", STR_PAD_RIGHT);
46047 if ($this->overwrite) {
46049 $this->output->write("\x0D");
46050 } elseif ($this->step > 0) {
46052 $this->output->writeln('');
46055 if ($this->formatLineCount) {
46056 $this->output->write(sprintf("\033[%dA", $this->formatLineCount));
46058 $this->output->write(implode("\n", $lines));
46060 $this->lastMessagesLength = 0;
46061 foreach ($lines as $line) {
46062 $len = Helper::strlenWithoutDecoration($this->output->getFormatter(), $line);
46063 if ($len > $this->lastMessagesLength) {
46064 $this->lastMessagesLength = $len;
46069 private function determineBestFormat()
46071 switch ($this->output->getVerbosity()) {
46073 case OutputInterface::VERBOSITY_VERBOSE:
46074 return $this->max ? 'verbose' : 'verbose_nomax';
46075 case OutputInterface::VERBOSITY_VERY_VERBOSE:
46076 return $this->max ? 'very_verbose' : 'very_verbose_nomax';
46077 case OutputInterface::VERBOSITY_DEBUG:
46078 return $this->max ? 'debug' : 'debug_nomax';
46080 return $this->max ? 'normal' : 'normal_nomax';
46084 private static function initPlaceholderFormatters()
46087 'bar' => function (ProgressBar $bar, OutputInterface $output) {
46088 $completeBars = floor($bar->getMaxSteps() > 0 ? $bar->getProgressPercent() * $bar->getBarWidth() : $bar->getProgress() % $bar->getBarWidth());
46089 $display = str_repeat($bar->getBarCharacter(), $completeBars);
46090 if ($completeBars < $bar->getBarWidth()) {
46091 $emptyBars = $bar->getBarWidth() - $completeBars - Helper::strlenWithoutDecoration($output->getFormatter(), $bar->getProgressCharacter());
46092 $display .= $bar->getProgressCharacter().str_repeat($bar->getEmptyBarCharacter(), $emptyBars);
46097 'elapsed' => function (ProgressBar $bar) {
46098 return Helper::formatTime(time() - $bar->getStartTime());
46100 'remaining' => function (ProgressBar $bar) {
46101 if (!$bar->getMaxSteps()) {
46102 throw new \LogicException('Unable to display the remaining time if the maximum number of steps is not set.');
46105 if (!$bar->getProgress()) {
46108 $remaining = round((time() - $bar->getStartTime()) / $bar->getProgress() * ($bar->getMaxSteps() - $bar->getProgress()));
46111 return Helper::formatTime($remaining);
46113 'estimated' => function (ProgressBar $bar) {
46114 if (!$bar->getMaxSteps()) {
46115 throw new \LogicException('Unable to display the estimated time if the maximum number of steps is not set.');
46118 if (!$bar->getProgress()) {
46121 $estimated = round((time() - $bar->getStartTime()) / $bar->getProgress() * $bar->getMaxSteps());
46124 return Helper::formatTime($estimated);
46126 'memory' => function (ProgressBar $bar) {
46127 return Helper::formatMemory(memory_get_usage(true));
46129 'current' => function (ProgressBar $bar) {
46130 return str_pad($bar->getProgress(), $bar->getStepWidth(), ' ', STR_PAD_LEFT);
46132 'max' => function (ProgressBar $bar) {
46133 return $bar->getMaxSteps();
46135 'percent' => function (ProgressBar $bar) {
46136 return floor($bar->getProgressPercent() * 100);
46141 private static function initFormats()
46144 'normal' => ' %current%/%max% [%bar%] %percent:3s%%',
46145 'normal_nomax' => ' %current% [%bar%]',
46147 'verbose' => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%',
46148 'verbose_nomax' => ' %current% [%bar%] %elapsed:6s%',
46150 'very_verbose' => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s%',
46151 'very_verbose_nomax' => ' %current% [%bar%] %elapsed:6s%',
46153 'debug' => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s% %memory:6s%',
46154 'debug_nomax' => ' %current% [%bar%] %elapsed:6s% %memory:6s%',
46169 namespace Symfony\Component\Console\Helper;
46171 use Symfony\Component\Console\Descriptor\DescriptorInterface;
46172 use Symfony\Component\Console\Descriptor\JsonDescriptor;
46173 use Symfony\Component\Console\Descriptor\MarkdownDescriptor;
46174 use Symfony\Component\Console\Descriptor\TextDescriptor;
46175 use Symfony\Component\Console\Descriptor\XmlDescriptor;
46176 use Symfony\Component\Console\Output\OutputInterface;
46183 class DescriptorHelper extends Helper
46188 private $descriptors = array();
46193 public function __construct()
46196 ->register('txt', new TextDescriptor())
46197 ->register('xml', new XmlDescriptor())
46198 ->register('json', new JsonDescriptor())
46199 ->register('md', new MarkdownDescriptor())
46216 public function describe(OutputInterface $output, $object, array $options = array())
46218 $options = array_merge(array(
46219 'raw_text' => false,
46223 if (!isset($this->descriptors[$options['format']])) {
46224 throw new \InvalidArgumentException(sprintf('Unsupported format "%s".', $options['format']));
46227 $descriptor = $this->descriptors[$options['format']];
46228 $descriptor->describe($output, $object, $options);
46239 public function register($format, DescriptorInterface $descriptor)
46241 $this->descriptors[$format] = $descriptor;
46249 public function getName()
46251 return 'descriptor';
46265 namespace Symfony\Component\Console\Helper;
46267 use Symfony\Component\Console\Formatter\OutputFormatter;
46274 class FormatterHelper extends Helper
46285 public function formatSection($section, $message, $style = 'info')
46287 return sprintf('<%s>[%s]</%s> %s', $style, $section, $style, $message);
46299 public function formatBlock($messages, $style, $large = false)
46301 $messages = (array) $messages;
46305 foreach ($messages as $message) {
46306 $message = OutputFormatter::escape($message);
46307 $lines[] = sprintf($large ? ' %s ' : ' %s ', $message);
46308 $len = max($this->strlen($message) + ($large ? 4 : 2), $len);
46311 $messages = $large ? array(str_repeat(' ', $len)) : array();
46312 foreach ($lines as $line) {
46313 $messages[] = $line.str_repeat(' ', $len - $this->strlen($line));
46316 $messages[] = str_repeat(' ', $len);
46319 foreach ($messages as &$message) {
46320 $message = sprintf('<%s>%s</%s>', $style, $message, $style);
46323 return implode("\n", $messages);
46329 public function getName()
46331 return 'formatter';
46345 namespace Symfony\Component\Console\Helper;
46354 class DebugFormatterHelper extends Helper
46356 private $colors = array('black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white');
46357 private $started = array();
46358 private $count = -1;
46369 public function start($id, $message, $prefix = 'RUN')
46371 $this->started[$id] = array('border' => ++$this->count % count($this->colors));
46373 return sprintf("%s<bg=blue;fg=white> %s </> <fg=blue>%s</>\n", $this->getBorder($id), $prefix, $message);
46387 public function progress($id, $buffer, $error = false, $prefix = 'OUT', $errorPrefix = 'ERR')
46392 if (isset($this->started[$id]['out'])) {
46394 unset($this->started[$id]['out']);
46396 if (!isset($this->started[$id]['err'])) {
46397 $message .= sprintf("%s<bg=red;fg=white> %s </> ", $this->getBorder($id), $errorPrefix);
46398 $this->started[$id]['err'] = true;
46401 $message .= str_replace("\n", sprintf("\n%s<bg=red;fg=white> %s </> ", $this->getBorder($id), $errorPrefix), $buffer);
46403 if (isset($this->started[$id]['err'])) {
46405 unset($this->started[$id]['err']);
46407 if (!isset($this->started[$id]['out'])) {
46408 $message .= sprintf("%s<bg=green;fg=white> %s </> ", $this->getBorder($id), $prefix);
46409 $this->started[$id]['out'] = true;
46412 $message .= str_replace("\n", sprintf("\n%s<bg=green;fg=white> %s </> ", $this->getBorder($id), $prefix), $buffer);
46428 public function stop($id, $message, $successful, $prefix = 'RES')
46430 $trailingEOL = isset($this->started[$id]['out']) || isset($this->started[$id]['err']) ? "\n" : '';
46433 return sprintf("%s%s<bg=green;fg=white> %s </> <fg=green>%s</>\n", $trailingEOL, $this->getBorder($id), $prefix, $message);
46436 $message = sprintf("%s%s<bg=red;fg=white> %s </> <fg=red>%s</>\n", $trailingEOL, $this->getBorder($id), $prefix, $message);
46438 unset($this->started[$id]['out'], $this->started[$id]['err']);
46448 private function getBorder($id)
46450 return sprintf('<bg=%s> </>', $this->colors[$this->started[$id]['border']]);
46456 public function getName()
46458 return 'debug_formatter';
46472 namespace Symfony\Component\Console\Helper;
46474 use Symfony\Component\Console\Output\OutputInterface;
46475 use Symfony\Component\Process\Exception\ProcessFailedException;
46476 use Symfony\Component\Process\Process;
46477 use Symfony\Component\Process\ProcessBuilder;
46484 class ProcessHelper extends Helper
46498 public function run(OutputInterface $output, $cmd, $error = null, $callback = null, $verbosity = OutputInterface::VERBOSITY_VERY_VERBOSE)
46500 $formatter = $this->getHelperSet()->get('debug_formatter');
46502 if (is_array($cmd)) {
46503 $process = ProcessBuilder::create($cmd)->getProcess();
46504 } elseif ($cmd instanceof Process) {
46507 $process = new Process($cmd);
46510 if ($verbosity <= $output->getVerbosity()) {
46511 $output->write($formatter->start(spl_object_hash($process), $this->escapeString($process->getCommandLine())));
46514 if ($output->isDebug()) {
46515 $callback = $this->wrapCallback($output, $process, $callback);
46518 $process->run($callback);
46520 if ($verbosity <= $output->getVerbosity()) {
46521 $message = $process->isSuccessful() ? 'Command ran successfully' : sprintf('%s Command did not run successfully', $process->getExitCode());
46522 $output->write($formatter->stop(spl_object_hash($process), $message, $process->isSuccessful()));
46525 if (!$process->isSuccessful() && null !== $error) {
46526 $output->writeln(sprintf('<error>%s</error>', $this->escapeString($error)));
46550 public function mustRun(OutputInterface $output, $cmd, $error = null, $callback = null)
46552 $process = $this->run($output, $cmd, $error, $callback);
46554 if (!$process->isSuccessful()) {
46555 throw new ProcessFailedException($process);
46570 public function wrapCallback(OutputInterface $output, Process $process, $callback = null)
46572 $formatter = $this->getHelperSet()->get('debug_formatter');
46576 return function ($type, $buffer) use ($output, $process, $callback, $formatter, $that) {
46577 $output->write($formatter->progress(spl_object_hash($process), $that->escapeString($buffer), Process::ERR === $type));
46579 if (null !== $callback) {
46580 call_user_func($callback, $type, $buffer);
46590 public function escapeString($str)
46592 return str_replace('<', '\\<', $str);
46598 public function getName()
46614 namespace Symfony\Component\Console\Helper;
46616 use Symfony\Component\Console\Command\Command;
46623 class HelperSet implements \IteratorAggregate
46625 private $helpers = array();
46633 public function __construct(array $helpers = array())
46635 foreach ($helpers as $alias => $helper) {
46636 $this->set($helper, is_int($alias) ? null : $alias);
46646 public function set(HelperInterface $helper, $alias = null)
46648 $this->helpers[$helper->getName()] = $helper;
46649 if (null !== $alias) {
46650 $this->helpers[$alias] = $helper;
46653 $helper->setHelperSet($this);
46663 public function has($name)
46665 return isset($this->helpers[$name]);
46677 public function get($name)
46679 if (!$this->has($name)) {
46680 throw new \InvalidArgumentException(sprintf('The helper "%s" is not defined.', $name));
46683 return $this->helpers[$name];
46691 public function setCommand(Command $command = null)
46693 $this->command = $command;
46701 public function getCommand()
46703 return $this->command;
46706 public function getIterator()
46708 return new \ArrayIterator($this->helpers);
46722 namespace Symfony\Component\Console\Helper;
46724 use Symfony\Component\Console\Output\OutputInterface;
46725 use Symfony\Component\Console\Formatter\OutputFormatterStyle;
46735 class DialogHelper extends InputAwareHelper
46737 private $inputStream;
46738 private static $shell;
46739 private static $stty;
46756 public function select(OutputInterface $output, $question, $choices, $default = null, $attempts = false, $errorMessage = 'Value "%s" is invalid', $multiselect = false)
46758 $width = max(array_map('strlen', array_keys($choices)));
46760 $messages = (array) $question;
46761 foreach ($choices as $key => $value) {
46762 $messages[] = sprintf(" [<info>%-${width}s</info>] %s", $key, $value);
46765 $output->writeln($messages);
46767 $result = $this->askAndValidate($output, '> ', function ($picked) use ($choices, $errorMessage, $multiselect) {
46769 $selectedChoices = str_replace(" ", "", $picked);
46771 if ($multiselect) {
46773 if (!preg_match('/^[a-zA-Z0-9_-]+(?:,[a-zA-Z0-9_-]+)*$/', $selectedChoices, $matches)) {
46774 throw new \InvalidArgumentException(sprintf($errorMessage, $picked));
46776 $selectedChoices = explode(",", $selectedChoices);
46778 $selectedChoices = array($picked);
46781 $multiselectChoices = array();
46783 foreach ($selectedChoices as $value) {
46784 if (empty($choices[$value])) {
46785 throw new \InvalidArgumentException(sprintf($errorMessage, $value));
46787 array_push($multiselectChoices, $value);
46790 if ($multiselect) {
46791 return $multiselectChoices;
46795 }, $attempts, $default);
46812 public function ask(OutputInterface $output, $question, $default = null, array $autocomplete = null)
46814 if ($this->input && !$this->input->isInteractive()) {
46818 $output->write($question);
46820 $inputStream = $this->inputStream ?: STDIN;
46822 if (null === $autocomplete || !$this->hasSttyAvailable()) {
46823 $ret = fgets($inputStream, 4096);
46824 if (false === $ret) {
46825 throw new \RuntimeException('Aborted');
46833 $matches = $autocomplete;
46834 $numMatches = count($matches);
46836 $sttyMode = shell_exec('stty -g');
46839 shell_exec('stty -icanon -echo');
46842 $output->getFormatter()->setStyle('hl', new OutputFormatterStyle('black', 'white'));
46845 while (!feof($inputStream)) {
46846 $c = fread($inputStream, 1);
46849 if ("\177" === $c) {
46850 if (0 === $numMatches && 0 !== $i) {
46853 $output->write("\033[1D");
46858 $matches = $autocomplete;
46859 $numMatches = count($matches);
46865 $ret = substr($ret, 0, $i);
46866 } elseif ("\033" === $c) {
46868 $c .= fread($inputStream, 2);
46871 if (isset($c[2]) && ('A' === $c[2] || 'B' === $c[2])) {
46872 if ('A' === $c[2] && -1 === $ofs) {
46876 if (0 === $numMatches) {
46880 $ofs += ('A' === $c[2]) ? -1 : 1;
46881 $ofs = ($numMatches + $ofs) % $numMatches;
46883 } elseif (ord($c) < 32) {
46884 if ("\t" === $c || "\n" === $c) {
46885 if ($numMatches > 0 && -1 !== $ofs) {
46886 $ret = $matches[$ofs];
46888 $output->write(substr($ret, $i));
46893 $output->write($c);
46902 $output->write($c);
46909 foreach ($autocomplete as $value) {
46911 if (0 === strpos($value, $ret) && $i !== strlen($value)) {
46912 $matches[$numMatches++] = $value;
46918 $output->write("\033[K");
46920 if ($numMatches > 0 && -1 !== $ofs) {
46922 $output->write("\0337");
46924 $output->write('<hl>'.substr($matches[$ofs], $i).'</hl>');
46926 $output->write("\0338");
46931 shell_exec(sprintf('stty %s', $sttyMode));
46934 return strlen($ret) > 0 ? $ret : $default;
46948 public function askConfirmation(OutputInterface $output, $question, $default = true)
46951 while ($answer && !in_array(strtolower($answer[0]), array('y', 'n'))) {
46952 $answer = $this->ask($output, $question);
46955 if (false === $default) {
46956 return $answer && 'y' == strtolower($answer[0]);
46959 return !$answer || 'y' == strtolower($answer[0]);
46973 public function askHiddenResponse(OutputInterface $output, $question, $fallback = true)
46975 if (defined('PHP_WINDOWS_VERSION_BUILD')) {
46976 $exe = __DIR__.'/../Resources/bin/hiddeninput.exe';
46979 if ('phar:' === substr(__FILE__, 0, 5)) {
46980 $tmpExe = sys_get_temp_dir().'/hiddeninput.exe';
46981 copy($exe, $tmpExe);
46985 $output->write($question);
46986 $value = rtrim(shell_exec($exe));
46987 $output->writeln('');
46989 if (isset($tmpExe)) {
46996 if ($this->hasSttyAvailable()) {
46997 $output->write($question);
46999 $sttyMode = shell_exec('stty -g');
47001 shell_exec('stty -echo');
47002 $value = fgets($this->inputStream ?: STDIN, 4096);
47003 shell_exec(sprintf('stty %s', $sttyMode));
47005 if (false === $value) {
47006 throw new \RuntimeException('Aborted');
47009 $value = trim($value);
47010 $output->writeln('');
47015 if (false !== $shell = $this->getShell()) {
47016 $output->write($question);
47017 $readCmd = $shell === 'csh' ? 'set mypassword = $<' : 'read -r mypassword';
47018 $command = sprintf("/usr/bin/env %s -c 'stty -echo; %s; stty echo; echo \$mypassword'", $shell, $readCmd);
47019 $value = rtrim(shell_exec($command));
47020 $output->writeln('');
47026 return $this->ask($output, $question);
47029 throw new \RuntimeException('Unable to hide the response');
47050 public function askAndValidate(OutputInterface $output, $question, $validator, $attempts = false, $default = null, array $autocomplete = null)
47054 $interviewer = function () use ($output, $question, $default, $autocomplete, $that) {
47055 return $that->ask($output, $question, $default, $autocomplete);
47058 return $this->validateAttempts($interviewer, $output, $validator, $attempts);
47080 public function askHiddenResponseAndValidate(OutputInterface $output, $question, $validator, $attempts = false, $fallback = true)
47084 $interviewer = function () use ($output, $question, $fallback, $that) {
47085 return $that->askHiddenResponse($output, $question, $fallback);
47088 return $this->validateAttempts($interviewer, $output, $validator, $attempts);
47098 public function setInputStream($stream)
47100 $this->inputStream = $stream;
47108 public function getInputStream()
47110 return $this->inputStream;
47116 public function getName()
47126 private function getShell()
47128 if (null !== self::$shell) {
47129 return self::$shell;
47132 self::$shell = false;
47134 if (file_exists('/usr/bin/env')) {
47136 $test = "/usr/bin/env %s -c 'echo OK' 2> /dev/null";
47137 foreach (array('bash', 'zsh', 'ksh', 'csh') as $sh) {
47138 if ('OK' === rtrim(shell_exec(sprintf($test, $sh)))) {
47139 self::$shell = $sh;
47145 return self::$shell;
47148 private function hasSttyAvailable()
47150 if (null !== self::$stty) {
47151 return self::$stty;
47154 exec('stty 2>&1', $output, $exitcode);
47156 return self::$stty = $exitcode === 0;
47171 private function validateAttempts($interviewer, OutputInterface $output, $validator, $attempts)
47174 while (false === $attempts || $attempts--) {
47175 if (null !== $error) {
47176 $output->writeln($this->getHelperSet()->get('formatter')->formatBlock($error->getMessage(), 'error'));
47180 return call_user_func($validator, $interviewer());
47181 } catch (\Exception $error) {
47199 namespace Symfony\Component\Console\Helper;
47201 use Symfony\Component\Console\Input\InputInterface;
47202 use Symfony\Component\Console\Output\OutputInterface;
47203 use Symfony\Component\Console\Formatter\OutputFormatterStyle;
47204 use Symfony\Component\Console\Question\Question;
47205 use Symfony\Component\Console\Question\ChoiceQuestion;
47212 class QuestionHelper extends Helper
47214 private $inputStream;
47215 private static $shell;
47216 private static $stty;
47229 public function ask(InputInterface $input, OutputInterface $output, Question $question)
47231 if (!$input->isInteractive()) {
47232 return $question->getDefault();
47235 if (!$question->getValidator()) {
47236 return $this->doAsk($output, $question);
47241 $interviewer = function () use ($output, $question, $that) {
47242 return $that->doAsk($output, $question);
47245 return $this->validateAttempts($interviewer, $output, $question);
47257 public function setInputStream($stream)
47259 if (!is_resource($stream)) {
47260 throw new \InvalidArgumentException('Input stream must be a valid resource.');
47263 $this->inputStream = $stream;
47271 public function getInputStream()
47273 return $this->inputStream;
47279 public function getName()
47297 public function doAsk(OutputInterface $output, Question $question)
47299 $inputStream = $this->inputStream ?: STDIN;
47301 $message = $question->getQuestion();
47302 if ($question instanceof ChoiceQuestion) {
47303 $width = max(array_map('strlen', array_keys($question->getChoices())));
47305 $messages = (array) $question->getQuestion();
47306 foreach ($question->getChoices() as $key => $value) {
47307 $messages[] = sprintf(" [<info>%-${width}s</info>] %s", $key, $value);
47310 $output->writeln($messages);
47312 $message = $question->getPrompt();
47315 $output->write($message);
47317 $autocomplete = $question->getAutocompleterValues();
47318 if (null === $autocomplete || !$this->hasSttyAvailable()) {
47320 if ($question->isHidden()) {
47322 $ret = trim($this->getHiddenResponse($output, $inputStream));
47323 } catch (\RuntimeException $e) {
47324 if (!$question->isHiddenFallback()) {
47330 if (false === $ret) {
47331 $ret = fgets($inputStream, 4096);
47332 if (false === $ret) {
47333 throw new \RuntimeException('Aborted');
47338 $ret = trim($this->autocomplete($output, $question, $inputStream));
47341 $ret = strlen($ret) > 0 ? $ret : $question->getDefault();
47343 if ($normalizer = $question->getNormalizer()) {
47344 return $normalizer($ret);
47358 private function autocomplete(OutputInterface $output, Question $question, $inputStream)
47360 $autocomplete = $question->getAutocompleterValues();
47365 $matches = $autocomplete;
47366 $numMatches = count($matches);
47368 $sttyMode = shell_exec('stty -g');
47371 shell_exec('stty -icanon -echo');
47374 $output->getFormatter()->setStyle('hl', new OutputFormatterStyle('black', 'white'));
47377 while (!feof($inputStream)) {
47378 $c = fread($inputStream, 1);
47381 if ("\177" === $c) {
47382 if (0 === $numMatches && 0 !== $i) {
47385 $output->write("\033[1D");
47390 $matches = $autocomplete;
47391 $numMatches = count($matches);
47397 $ret = substr($ret, 0, $i);
47398 } elseif ("\033" === $c) {
47400 $c .= fread($inputStream, 2);
47403 if (isset($c[2]) && ('A' === $c[2] || 'B' === $c[2])) {
47404 if ('A' === $c[2] && -1 === $ofs) {
47408 if (0 === $numMatches) {
47412 $ofs += ('A' === $c[2]) ? -1 : 1;
47413 $ofs = ($numMatches + $ofs) % $numMatches;
47415 } elseif (ord($c) < 32) {
47416 if ("\t" === $c || "\n" === $c) {
47417 if ($numMatches > 0 && -1 !== $ofs) {
47418 $ret = $matches[$ofs];
47420 $output->write(substr($ret, $i));
47425 $output->write($c);
47434 $output->write($c);
47441 foreach ($autocomplete as $value) {
47443 if (0 === strpos($value, $ret) && $i !== strlen($value)) {
47444 $matches[$numMatches++] = $value;
47450 $output->write("\033[K");
47452 if ($numMatches > 0 && -1 !== $ofs) {
47454 $output->write("\0337");
47456 $output->write('<hl>'.substr($matches[$ofs], $i).'</hl>');
47458 $output->write("\0338");
47463 shell_exec(sprintf('stty %s', $sttyMode));
47477 private function getHiddenResponse(OutputInterface $output, $inputStream)
47479 if (defined('PHP_WINDOWS_VERSION_BUILD')) {
47480 $exe = __DIR__.'/../Resources/bin/hiddeninput.exe';
47483 if ('phar:' === substr(__FILE__, 0, 5)) {
47484 $tmpExe = sys_get_temp_dir().'/hiddeninput.exe';
47485 copy($exe, $tmpExe);
47489 $value = rtrim(shell_exec($exe));
47490 $output->writeln('');
47492 if (isset($tmpExe)) {
47499 if ($this->hasSttyAvailable()) {
47500 $sttyMode = shell_exec('stty -g');
47502 shell_exec('stty -echo');
47503 $value = fgets($inputStream, 4096);
47504 shell_exec(sprintf('stty %s', $sttyMode));
47506 if (false === $value) {
47507 throw new \RuntimeException('Aborted');
47510 $value = trim($value);
47511 $output->writeln('');
47516 if (false !== $shell = $this->getShell()) {
47517 $readCmd = $shell === 'csh' ? 'set mypassword = $<' : 'read -r mypassword';
47518 $command = sprintf("/usr/bin/env %s -c 'stty -echo; %s; stty echo; echo \$mypassword'", $shell, $readCmd);
47519 $value = rtrim(shell_exec($command));
47520 $output->writeln('');
47525 throw new \RuntimeException('Unable to hide the response.');
47539 private function validateAttempts($interviewer, OutputInterface $output, Question $question)
47542 $attempts = $question->getMaxAttempts();
47543 while (null === $attempts || $attempts--) {
47544 if (null !== $error) {
47545 $output->writeln($this->getHelperSet()->get('formatter')->formatBlock($error->getMessage(), 'error'));
47549 return call_user_func($question->getValidator(), $interviewer());
47550 } catch (\Exception $error) {
47562 private function getShell()
47564 if (null !== self::$shell) {
47565 return self::$shell;
47568 self::$shell = false;
47570 if (file_exists('/usr/bin/env')) {
47572 $test = "/usr/bin/env %s -c 'echo OK' 2> /dev/null";
47573 foreach (array('bash', 'zsh', 'ksh', 'csh') as $sh) {
47574 if ('OK' === rtrim(shell_exec(sprintf($test, $sh)))) {
47575 self::$shell = $sh;
47581 return self::$shell;
47589 private function hasSttyAvailable()
47591 if (null !== self::$stty) {
47592 return self::$stty;
47595 exec('stty 2>&1', $output, $exitcode);
47597 return self::$stty = $exitcode === 0;
47611 namespace Symfony\Component\Console\Helper;
47618 class TableSeparator
47632 namespace Symfony\Component\Console\Helper;
47634 use Symfony\Component\Console\Formatter\OutputFormatterInterface;
47641 abstract class Helper implements HelperInterface
47643 protected $helperSet = null;
47650 public function setHelperSet(HelperSet $helperSet = null)
47652 $this->helperSet = $helperSet;
47660 public function getHelperSet()
47662 return $this->helperSet;
47672 public static function strlen($string)
47674 if (!function_exists('mb_strwidth')) {
47675 return strlen($string);
47678 if (false === $encoding = mb_detect_encoding($string)) {
47679 return strlen($string);
47682 return mb_strwidth($string, $encoding);
47685 public static function formatTime($secs)
47687 static $timeFormats = array(
47688 array(0, '< 1 sec'),
47690 array(59, 'secs', 1),
47691 array(60, '1 min'),
47692 array(3600, 'mins', 60),
47693 array(5400, '1 hr'),
47694 array(86400, 'hrs', 3600),
47695 array(129600, '1 day'),
47696 array(604800, 'days', 86400),
47699 foreach ($timeFormats as $format) {
47700 if ($secs >= $format[0]) {
47704 if (2 == count($format)) {
47708 return ceil($secs / $format[2]).' '.$format[1];
47712 public static function formatMemory($memory)
47714 if ($memory >= 1024 * 1024 * 1024) {
47715 return sprintf('%.1f GiB', $memory / 1024 / 1024 / 1024);
47718 if ($memory >= 1024 * 1024) {
47719 return sprintf('%.1f MiB', $memory / 1024 / 1024);
47722 if ($memory >= 1024) {
47723 return sprintf('%d KiB', $memory / 1024);
47726 return sprintf('%d B', $memory);
47729 public static function strlenWithoutDecoration(OutputFormatterInterface $formatter, $string)
47731 $isDecorated = $formatter->isDecorated();
47732 $formatter->setDecorated(false);
47734 $string = $formatter->format($string);
47736 $string = preg_replace("/\033\[[^m]*m/", '', $string);
47737 $formatter->setDecorated($isDecorated);
47739 return self::strlen($string);
47753 namespace Symfony\Component\Console\Helper;
47755 use Symfony\Component\Console\Output\NullOutput;
47756 use Symfony\Component\Console\Output\OutputInterface;
47766 class ProgressHelper extends Helper
47768 const FORMAT_QUIET = ' %percent%%';
47769 const FORMAT_NORMAL = ' %current%/%max% [%bar%] %percent%%';
47770 const FORMAT_VERBOSE = ' %current%/%max% [%bar%] %percent%% Elapsed: %elapsed%';
47771 const FORMAT_QUIET_NOMAX = ' %current%';
47772 const FORMAT_NORMAL_NOMAX = ' %current% [%bar%]';
47773 const FORMAT_VERBOSE_NOMAX = ' %current% [%bar%] Elapsed: %elapsed%';
47776 private $barWidth = 28;
47777 private $barChar = '=';
47778 private $emptyBarChar = '-';
47779 private $progressChar = '>';
47780 private $format = null;
47781 private $redrawFreq = 1;
47783 private $lastMessagesLength;
47784 private $barCharOriginal;
47810 private $startTime;
47817 private $defaultFormatVars = array(
47830 private $formatVars;
47837 private $widths = array(
47849 private $timeFormats = array(
47852 array(59, 'secs', 1),
47853 array(60, '1 min'),
47854 array(3600, 'mins', 60),
47855 array(5400, '1 hr'),
47856 array(86400, 'hrs', 3600),
47857 array(129600, '1 day'),
47858 array(604800, 'days', 86400),
47866 public function setBarWidth($size)
47868 $this->barWidth = (int) $size;
47876 public function setBarCharacter($char)
47878 $this->barChar = $char;
47886 public function setEmptyBarCharacter($char)
47888 $this->emptyBarChar = $char;
47896 public function setProgressCharacter($char)
47898 $this->progressChar = $char;
47906 public function setFormat($format)
47908 $this->format = $format;
47916 public function setRedrawFrequency($freq)
47918 $this->redrawFreq = (int) $freq;
47927 public function start(OutputInterface $output, $max = null)
47929 $this->startTime = time();
47930 $this->current = 0;
47931 $this->max = (int) $max;
47934 $this->output = $output->isDecorated() ? $output : new NullOutput();
47935 $this->lastMessagesLength = 0;
47936 $this->barCharOriginal = '';
47938 if (null === $this->format) {
47939 switch ($output->getVerbosity()) {
47940 case OutputInterface::VERBOSITY_QUIET:
47941 $this->format = self::FORMAT_QUIET_NOMAX;
47942 if ($this->max > 0) {
47943 $this->format = self::FORMAT_QUIET;
47946 case OutputInterface::VERBOSITY_VERBOSE:
47947 case OutputInterface::VERBOSITY_VERY_VERBOSE:
47948 case OutputInterface::VERBOSITY_DEBUG:
47949 $this->format = self::FORMAT_VERBOSE_NOMAX;
47950 if ($this->max > 0) {
47951 $this->format = self::FORMAT_VERBOSE;
47955 $this->format = self::FORMAT_NORMAL_NOMAX;
47956 if ($this->max > 0) {
47957 $this->format = self::FORMAT_NORMAL;
47963 $this->initialize();
47974 public function advance($step = 1, $redraw = false)
47976 $this->setCurrent($this->current + $step, $redraw);
47987 public function setCurrent($current, $redraw = false)
47989 if (null === $this->startTime) {
47990 throw new \LogicException('You must start the progress bar before calling setCurrent().');
47993 $current = (int) $current;
47995 if ($current < $this->current) {
47996 throw new \LogicException('You can\'t regress the progress bar');
47999 if (0 === $this->current) {
48003 $prevPeriod = intval($this->current / $this->redrawFreq);
48005 $this->current = $current;
48007 $currPeriod = intval($this->current / $this->redrawFreq);
48008 if ($redraw || $prevPeriod !== $currPeriod || $this->max === $this->current) {
48020 public function display($finish = false)
48022 if (null === $this->startTime) {
48023 throw new \LogicException('You must start the progress bar before calling display().');
48026 $message = $this->format;
48027 foreach ($this->generate($finish) as $name => $value) {
48028 $message = str_replace("%{$name}%", $value, $message);
48030 $this->overwrite($this->output, $message);
48040 public function clear()
48042 $this->overwrite($this->output, '');
48048 public function finish()
48050 if (null === $this->startTime) {
48051 throw new \LogicException('You must start the progress bar before calling finish().');
48054 if (null !== $this->startTime) {
48056 $this->barChar = $this->barCharOriginal;
48057 $this->display(true);
48059 $this->startTime = null;
48060 $this->output->writeln('');
48061 $this->output = null;
48068 private function initialize()
48070 $this->formatVars = array();
48071 foreach ($this->defaultFormatVars as $var) {
48072 if (false !== strpos($this->format, "%{$var}%")) {
48073 $this->formatVars[$var] = true;
48077 if ($this->max > 0) {
48078 $this->widths['max'] = $this->strlen($this->max);
48079 $this->widths['current'] = $this->widths['max'];
48081 $this->barCharOriginal = $this->barChar;
48082 $this->barChar = $this->emptyBarChar;
48093 private function generate($finish = false)
48097 if ($this->max > 0) {
48098 $percent = (float) $this->current / $this->max;
48101 if (isset($this->formatVars['bar'])) {
48104 if ($this->max > 0) {
48105 $completeBars = floor($percent * $this->barWidth);
48108 $completeBars = floor($this->current % $this->barWidth);
48110 $completeBars = $this->barWidth;
48114 $emptyBars = $this->barWidth - $completeBars - $this->strlen($this->progressChar);
48115 $bar = str_repeat($this->barChar, $completeBars);
48116 if ($completeBars < $this->barWidth) {
48117 $bar .= $this->progressChar;
48118 $bar .= str_repeat($this->emptyBarChar, $emptyBars);
48121 $vars['bar'] = $bar;
48124 if (isset($this->formatVars['elapsed'])) {
48125 $elapsed = time() - $this->startTime;
48126 $vars['elapsed'] = str_pad($this->humaneTime($elapsed), $this->widths['elapsed'], ' ', STR_PAD_LEFT);
48129 if (isset($this->formatVars['current'])) {
48130 $vars['current'] = str_pad($this->current, $this->widths['current'], ' ', STR_PAD_LEFT);
48133 if (isset($this->formatVars['max'])) {
48134 $vars['max'] = $this->max;
48137 if (isset($this->formatVars['percent'])) {
48138 $vars['percent'] = str_pad(floor($percent * 100), $this->widths['percent'], ' ', STR_PAD_LEFT);
48151 private function humaneTime($secs)
48154 foreach ($this->timeFormats as $format) {
48155 if ($secs < $format[0]) {
48156 if (count($format) == 2) {
48157 $text = $format[1];
48160 $text = ceil($secs / $format[2]).' '.$format[1];
48175 private function overwrite(OutputInterface $output, $message)
48177 $length = $this->strlen($message);
48180 if (null !== $this->lastMessagesLength && $this->lastMessagesLength > $length) {
48181 $message = str_pad($message, $this->lastMessagesLength, "\x20", STR_PAD_RIGHT);
48185 $output->write("\x0D");
48186 $output->write($message);
48188 $this->lastMessagesLength = $this->strlen($message);
48194 public function getName()
48210 namespace Symfony\Component\Console\Helper;
48212 use Symfony\Component\Console\Input\InputInterface;
48213 use Symfony\Component\Console\Input\InputAwareInterface;
48220 abstract class InputAwareHelper extends Helper implements InputAwareInterface
48227 public function setInput(InputInterface $input)
48229 $this->input = $input;
48243 namespace Symfony\Component\Console\Helper;
48253 private $paddingChar = ' ';
48254 private $horizontalBorderChar = '-';
48255 private $verticalBorderChar = '|';
48256 private $crossingChar = '+';
48257 private $cellHeaderFormat = '<info>%s</info>';
48258 private $cellRowFormat = '%s';
48259 private $cellRowContentFormat = ' %s ';
48260 private $borderFormat = '%s';
48261 private $padType = STR_PAD_RIGHT;
48270 public function setPaddingChar($paddingChar)
48272 if (!$paddingChar) {
48273 throw new \LogicException('The padding char must not be empty');
48276 $this->paddingChar = $paddingChar;
48286 public function getPaddingChar()
48288 return $this->paddingChar;
48298 public function setHorizontalBorderChar($horizontalBorderChar)
48300 $this->horizontalBorderChar = $horizontalBorderChar;
48310 public function getHorizontalBorderChar()
48312 return $this->horizontalBorderChar;
48322 public function setVerticalBorderChar($verticalBorderChar)
48324 $this->verticalBorderChar = $verticalBorderChar;
48334 public function getVerticalBorderChar()
48336 return $this->verticalBorderChar;
48346 public function setCrossingChar($crossingChar)
48348 $this->crossingChar = $crossingChar;
48358 public function getCrossingChar()
48360 return $this->crossingChar;
48370 public function setCellHeaderFormat($cellHeaderFormat)
48372 $this->cellHeaderFormat = $cellHeaderFormat;
48382 public function getCellHeaderFormat()
48384 return $this->cellHeaderFormat;
48394 public function setCellRowFormat($cellRowFormat)
48396 $this->cellRowFormat = $cellRowFormat;
48406 public function getCellRowFormat()
48408 return $this->cellRowFormat;
48418 public function setCellRowContentFormat($cellRowContentFormat)
48420 $this->cellRowContentFormat = $cellRowContentFormat;
48430 public function getCellRowContentFormat()
48432 return $this->cellRowContentFormat;
48442 public function setBorderFormat($borderFormat)
48444 $this->borderFormat = $borderFormat;
48454 public function getBorderFormat()
48456 return $this->borderFormat;
48466 public function setPadType($padType)
48468 $this->padType = $padType;
48478 public function getPadType()
48480 return $this->padType;
48494 namespace Symfony\Component\Console\Event;
48496 use Symfony\Component\Console\Command\Command;
48497 use Symfony\Component\Console\Input\InputInterface;
48498 use Symfony\Component\Console\Output\OutputInterface;
48499 use Symfony\Component\EventDispatcher\Event;
48506 class ConsoleEvent extends Event
48508 protected $command;
48513 public function __construct(Command $command, InputInterface $input, OutputInterface $output)
48515 $this->command = $command;
48516 $this->input = $input;
48517 $this->output = $output;
48525 public function getCommand()
48527 return $this->command;
48535 public function getInput()
48537 return $this->input;
48545 public function getOutput()
48547 return $this->output;
48561 namespace Symfony\Component\Console\Event;
48563 use Symfony\Component\Console\Command\Command;
48564 use Symfony\Component\Console\Input\InputInterface;
48565 use Symfony\Component\Console\Output\OutputInterface;
48572 class ConsoleTerminateEvent extends ConsoleEvent
48581 public function __construct(Command $command, InputInterface $input, OutputInterface $output, $exitCode)
48583 parent::__construct($command, $input, $output);
48585 $this->setExitCode($exitCode);
48593 public function setExitCode($exitCode)
48595 $this->exitCode = (int) $exitCode;
48603 public function getExitCode()
48605 return $this->exitCode;
48619 namespace Symfony\Component\Console\Event;
48621 use Symfony\Component\Console\Command\Command;
48622 use Symfony\Component\Console\Input\InputInterface;
48623 use Symfony\Component\Console\Output\OutputInterface;
48630 class ConsoleExceptionEvent extends ConsoleEvent
48632 private $exception;
48635 public function __construct(Command $command, InputInterface $input, OutputInterface $output, \Exception $exception, $exitCode)
48637 parent::__construct($command, $input, $output);
48639 $this->setException($exception);
48640 $this->exitCode = (int) $exitCode;
48648 public function getException()
48650 return $this->exception;
48660 public function setException(\Exception $exception)
48662 $this->exception = $exception;
48670 public function getExitCode()
48672 return $this->exitCode;
48686 namespace Symfony\Component\Console\Event;
48693 class ConsoleCommandEvent extends ConsoleEvent
48698 const RETURN_CODE_DISABLED = 113;
48705 private $commandShouldRun = true;
48712 public function disableCommand()
48714 return $this->commandShouldRun = false;
48722 public function enableCommand()
48724 return $this->commandShouldRun = true;
48732 public function commandShouldRun()
48734 return $this->commandShouldRun;
48748 namespace Symfony\Component\Console;
48755 final class ConsoleEvents
48769 const COMMAND = 'console.command';
48782 const TERMINATE = 'console.terminate';
48796 const EXCEPTION = 'console.exception';
48809 namespace Symfony\Component\Finder;
48811 use Symfony\Component\Finder\Adapter\AdapterInterface;
48812 use Symfony\Component\Finder\Adapter\GnuFindAdapter;
48813 use Symfony\Component\Finder\Adapter\BsdFindAdapter;
48814 use Symfony\Component\Finder\Adapter\PhpAdapter;
48815 use Symfony\Component\Finder\Exception\ExceptionInterface;
48832 class Finder implements \IteratorAggregate, \Countable
48834 const IGNORE_VCS_FILES = 1;
48835 const IGNORE_DOT_FILES = 2;
48838 private $names = array();
48839 private $notNames = array();
48840 private $exclude = array();
48841 private $filters = array();
48842 private $depths = array();
48843 private $sizes = array();
48844 private $followLinks = false;
48845 private $sort = false;
48846 private $ignore = 0;
48847 private $dirs = array();
48848 private $dates = array();
48849 private $iterators = array();
48850 private $contains = array();
48851 private $notContains = array();
48852 private $adapters = array();
48853 private $paths = array();
48854 private $notPaths = array();
48855 private $ignoreUnreadableDirs = false;
48857 private static $vcsPatterns = array('.svn', '_svn', 'CVS', '_darcs', '.arch-params', '.monotone', '.bzr', '.git', '.hg');
48862 public function __construct()
48864 $this->ignore = static::IGNORE_VCS_FILES | static::IGNORE_DOT_FILES;
48867 ->addAdapter(new GnuFindAdapter())
48868 ->addAdapter(new BsdFindAdapter())
48869 ->addAdapter(new PhpAdapter(), -50)
48870 ->setAdapter('php')
48881 public static function create()
48883 return new static();
48894 public function addAdapter(AdapterInterface $adapter, $priority = 0)
48896 $this->adapters[$adapter->getName()] = array(
48897 'adapter' => $adapter,
48898 'priority' => $priority,
48899 'selected' => false,
48902 return $this->sortAdapters();
48910 public function useBestAdapter()
48912 $this->resetAdapterSelection();
48914 return $this->sortAdapters();
48926 public function setAdapter($name)
48928 if (!isset($this->adapters[$name])) {
48929 throw new \InvalidArgumentException(sprintf('Adapter "%s" does not exist.', $name));
48932 $this->resetAdapterSelection();
48933 $this->adapters[$name]['selected'] = true;
48935 return $this->sortAdapters();
48943 public function removeAdapters()
48945 $this->adapters = array();
48955 public function getAdapters()
48957 return array_values(array_map(function (array $adapter) {
48958 return $adapter['adapter'];
48959 }, $this->adapters));
48969 public function directories()
48971 $this->mode = Iterator\FileTypeFilterIterator::ONLY_DIRECTORIES;
48983 public function files()
48985 $this->mode = Iterator\FileTypeFilterIterator::ONLY_FILES;
49007 public function depth($level)
49009 $this->depths[] = new Comparator\NumberComparator($level);
49034 public function date($date)
49036 $this->dates[] = new Comparator\DateComparator($date);
49058 public function name($pattern)
49060 $this->names[] = $pattern;
49076 public function notName($pattern)
49078 $this->notNames[] = $pattern;
49097 public function contains($pattern)
49099 $this->contains[] = $pattern;
49118 public function notContains($pattern)
49120 $this->notContains[] = $pattern;
49141 public function path($pattern)
49143 $this->paths[] = $pattern;
49164 public function notPath($pattern)
49166 $this->notPaths[] = $pattern;
49187 public function size($size)
49189 $this->sizes[] = new Comparator\NumberComparator($size);
49205 public function exclude($dirs)
49207 $this->exclude = array_merge($this->exclude, (array) $dirs);
49223 public function ignoreDotFiles($ignoreDotFiles)
49225 if ($ignoreDotFiles) {
49226 $this->ignore = $this->ignore | static::IGNORE_DOT_FILES;
49228 $this->ignore = $this->ignore & ~static::IGNORE_DOT_FILES;
49245 public function ignoreVCS($ignoreVCS)
49248 $this->ignore = $this->ignore | static::IGNORE_VCS_FILES;
49250 $this->ignore = $this->ignore & ~static::IGNORE_VCS_FILES;
49263 public static function addVCSPattern($pattern)
49265 foreach ((array) $pattern as $p) {
49266 self::$vcsPatterns[] = $p;
49269 self::$vcsPatterns = array_unique(self::$vcsPatterns);
49287 public function sort(\Closure $closure)
49289 $this->sort = $closure;
49305 public function sortByName()
49307 $this->sort = Iterator\SortableIterator::SORT_BY_NAME;
49323 public function sortByType()
49325 $this->sort = Iterator\SortableIterator::SORT_BY_TYPE;
49343 public function sortByAccessedTime()
49345 $this->sort = Iterator\SortableIterator::SORT_BY_ACCESSED_TIME;
49365 public function sortByChangedTime()
49367 $this->sort = Iterator\SortableIterator::SORT_BY_CHANGED_TIME;
49385 public function sortByModifiedTime()
49387 $this->sort = Iterator\SortableIterator::SORT_BY_MODIFIED_TIME;
49406 public function filter(\Closure $closure)
49408 $this->filters[] = $closure;
49420 public function followLinks()
49422 $this->followLinks = true;
49436 public function ignoreUnreadableDirs($ignore = true)
49438 $this->ignoreUnreadableDirs = (bool) $ignore;
49454 public function in($dirs)
49456 $resolvedDirs = array();
49458 foreach ((array) $dirs as $dir) {
49459 if (is_dir($dir)) {
49460 $resolvedDirs[] = $dir;
49461 } elseif ($glob = glob($dir, GLOB_BRACE | GLOB_ONLYDIR)) {
49462 $resolvedDirs = array_merge($resolvedDirs, $glob);
49464 throw new \InvalidArgumentException(sprintf('The "%s" directory does not exist.', $dir));
49468 $this->dirs = array_merge($this->dirs, $resolvedDirs);
49482 public function getIterator()
49484 if (0 === count($this->dirs) && 0 === count($this->iterators)) {
49485 throw new \LogicException('You must call one of in() or append() methods before iterating over a Finder.');
49488 if (1 === count($this->dirs) && 0 === count($this->iterators)) {
49489 return $this->searchInDirectory($this->dirs[0]);
49492 $iterator = new \AppendIterator();
49493 foreach ($this->dirs as $dir) {
49494 $iterator->append($this->searchInDirectory($dir));
49497 foreach ($this->iterators as $it) {
49498 $iterator->append($it);
49515 public function append($iterator)
49517 if ($iterator instanceof \IteratorAggregate) {
49518 $this->iterators[] = $iterator->getIterator();
49519 } elseif ($iterator instanceof \Iterator) {
49520 $this->iterators[] = $iterator;
49521 } elseif ($iterator instanceof \Traversable || is_array($iterator)) {
49522 $it = new \ArrayIterator();
49523 foreach ($iterator as $file) {
49524 $it->append($file instanceof \SplFileInfo ? $file : new \SplFileInfo($file));
49526 $this->iterators[] = $it;
49528 throw new \InvalidArgumentException('Finder::append() method wrong argument type.');
49539 public function count()
49541 return iterator_count($this->getIterator());
49547 private function sortAdapters()
49549 uasort($this->adapters, function (array $a, array $b) {
49550 if ($a['selected'] || $b['selected']) {
49551 return $a['selected'] ? -1 : 1;
49554 return $a['priority'] > $b['priority'] ? -1 : 1;
49567 private function searchInDirectory($dir)
49569 if (static::IGNORE_VCS_FILES === (static::IGNORE_VCS_FILES & $this->ignore)) {
49570 $this->exclude = array_merge($this->exclude, self::$vcsPatterns);
49573 if (static::IGNORE_DOT_FILES === (static::IGNORE_DOT_FILES & $this->ignore)) {
49574 $this->notPaths[] = '#(^|/)\..+(/|$)#';
49577 foreach ($this->adapters as $adapter) {
49578 if ($adapter['adapter']->isSupported()) {
49581 ->buildAdapter($adapter['adapter'])
49582 ->searchInDirectory($dir);
49583 } catch (ExceptionInterface $e) {
49588 throw new \RuntimeException('No supported adapter found.');
49596 private function buildAdapter(AdapterInterface $adapter)
49599 ->setFollowLinks($this->followLinks)
49600 ->setDepths($this->depths)
49601 ->setMode($this->mode)
49602 ->setExclude($this->exclude)
49603 ->setNames($this->names)
49604 ->setNotNames($this->notNames)
49605 ->setContains($this->contains)
49606 ->setNotContains($this->notContains)
49607 ->setSizes($this->sizes)
49608 ->setDates($this->dates)
49609 ->setFilters($this->filters)
49610 ->setSort($this->sort)
49611 ->setPath($this->paths)
49612 ->setNotPath($this->notPaths)
49613 ->ignoreUnreadableDirs($this->ignoreUnreadableDirs);
49619 private function resetAdapterSelection()
49621 $this->adapters = array_map(function (array $properties) {
49622 $properties['selected'] = false;
49624 return $properties;
49625 }, $this->adapters);
49639 namespace Symfony\Component\Finder\Shell;
49654 private $bits = array();
49659 private $labels = array();
49664 private $errorHandler;
49671 public function __construct(Command $parent = null)
49673 $this->parent = $parent;
49681 public function __toString()
49683 return $this->join();
49693 public static function create(Command $parent = null)
49695 return new self($parent);
49705 public static function escape($input)
49707 return escapeshellcmd($input);
49717 public static function quote($input)
49719 return escapeshellarg($input);
49729 public function add($bit)
49731 $this->bits[] = $bit;
49743 public function top($bit)
49745 array_unshift($this->bits, $bit);
49747 foreach ($this->labels as $label => $index) {
49748 $this->labels[$label] += 1;
49761 public function arg($arg)
49763 $this->bits[] = self::quote($arg);
49775 public function cmd($esc)
49777 $this->bits[] = self::escape($esc);
49791 public function ins($label)
49793 if (isset($this->labels[$label])) {
49794 throw new \RuntimeException(sprintf('Label "%s" already exists.', $label));
49797 $this->bits[] = self::create($this);
49798 $this->labels[$label] = count($this->bits)-1;
49800 return $this->bits[$this->labels[$label]];
49812 public function get($label)
49814 if (!isset($this->labels[$label])) {
49815 throw new \RuntimeException(sprintf('Label "%s" does not exist.', $label));
49818 return $this->bits[$this->labels[$label]];
49828 public function end()
49830 if (null === $this->parent) {
49831 throw new \RuntimeException('Calling end on root command doesn\'t make sense.');
49834 return $this->parent;
49842 public function length()
49844 return count($this->bits);
49852 public function setErrorHandler(\Closure $errorHandler)
49854 $this->errorHandler = $errorHandler;
49862 public function getErrorHandler()
49864 return $this->errorHandler;
49874 public function execute()
49876 if (null === $this->errorHandler) {
49877 exec($this->join(), $output);
49879 $process = proc_open($this->join(), array(0 => array('pipe', 'r'), 1 => array('pipe', 'w'), 2 => array('pipe', 'w')), $pipes);
49880 $output = preg_split('~(\r\n|\r|\n)~', stream_get_contents($pipes[1]), -1, PREG_SPLIT_NO_EMPTY);
49882 if ($error = stream_get_contents($pipes[2])) {
49883 call_user_func($this->errorHandler, $error);
49886 proc_close($process);
49889 return $output ?: array();
49897 public function join()
49899 return implode(' ', array_filter(
49900 array_map(function ($bit) {
49901 return $bit instanceof Command ? $bit->join() : ($bit ?: null);
49903 function ($bit) { return null !== $bit; }
49915 public function addAtIndex($bit, $index)
49917 array_splice($this->bits, $index, 0, $bit);
49933 namespace Symfony\Component\Finder\Shell;
49940 const TYPE_UNIX = 1;
49941 const TYPE_DARWIN = 2;
49942 const TYPE_CYGWIN = 3;
49943 const TYPE_WINDOWS = 4;
49944 const TYPE_BSD = 5;
49956 public function getType()
49958 if (null === $this->type) {
49959 $this->type = $this->guessType();
49962 return $this->type;
49972 public function testCommand($command)
49974 if (!function_exists('exec')) {
49979 $testCommand = 'which ';
49980 if (self::TYPE_WINDOWS === $this->type) {
49981 $testCommand = 'where ';
49984 $command = escapeshellcmd($command);
49986 exec($testCommand.$command, $output, $code);
49988 return 0 === $code && count($output) > 0;
49996 private function guessType()
49998 $os = strtolower(PHP_OS);
50000 if (false !== strpos($os, 'cygwin')) {
50001 return self::TYPE_CYGWIN;
50004 if (false !== strpos($os, 'darwin')) {
50005 return self::TYPE_DARWIN;
50008 if (false !== strpos($os, 'bsd')) {
50009 return self::TYPE_BSD;
50012 if (0 === strpos($os, 'win')) {
50013 return self::TYPE_WINDOWS;
50016 return self::TYPE_UNIX;
50030 namespace Symfony\Component\Finder\Expression;
50035 class Regex implements ValueInterface
50037 const START_FLAG = '^';
50038 const END_FLAG = '$';
50039 const BOUNDARY = '~';
50040 const JOKER = '.*';
50041 const ESCAPING = '\\';
50056 private $startFlag;
50066 private $startJoker;
50080 public static function create($expr)
50082 if (preg_match('/^(.{3,}?)([imsxuADU]*)$/', $expr, $m)) {
50083 $start = substr($m[1], 0, 1);
50084 $end = substr($m[1], -1);
50087 ($start === $end && !preg_match('/[*?[:alnum:] \\\\]/', $start))
50088 || ($start === '{' && $end === '}')
50089 || ($start === '(' && $end === ')')
50091 return new self(substr($m[1], 1, -1), $m[2], $end);
50095 throw new \InvalidArgumentException('Given expression is not a regex.');
50103 public function __construct($pattern, $options = '', $delimiter = null)
50105 if (null !== $delimiter) {
50107 $pattern = str_replace('\\'.$delimiter, $delimiter, $pattern);
50110 $this->parsePattern($pattern);
50111 $this->options = $options;
50117 public function __toString()
50119 return $this->render();
50125 public function render()
50127 return self::BOUNDARY
50128 .$this->renderPattern()
50136 public function renderPattern()
50138 return ($this->startFlag ? self::START_FLAG : '')
50139 .($this->startJoker ? self::JOKER : '')
50140 .str_replace(self::BOUNDARY, '\\'.self::BOUNDARY, $this->pattern)
50141 .($this->endJoker ? self::JOKER : '')
50142 .($this->endFlag ? self::END_FLAG : '');
50148 public function isCaseSensitive()
50150 return !$this->hasOption('i');
50156 public function getType()
50158 return Expression::TYPE_REGEX;
50164 public function prepend($expr)
50166 $this->pattern = $expr.$this->pattern;
50174 public function append($expr)
50176 $this->pattern .= $expr;
50186 public function hasOption($option)
50188 return false !== strpos($this->options, $option);
50196 public function addOption($option)
50198 if (!$this->hasOption($option)) {
50199 $this->options .= $option;
50210 public function removeOption($option)
50212 $this->options = str_replace($option, '', $this->options);
50222 public function setStartFlag($startFlag)
50224 $this->startFlag = $startFlag;
50232 public function hasStartFlag()
50234 return $this->startFlag;
50242 public function setEndFlag($endFlag)
50244 $this->endFlag = (bool) $endFlag;
50252 public function hasEndFlag()
50254 return $this->endFlag;
50262 public function setStartJoker($startJoker)
50264 $this->startJoker = $startJoker;
50272 public function hasStartJoker()
50274 return $this->startJoker;
50282 public function setEndJoker($endJoker)
50284 $this->endJoker = (bool) $endJoker;
50292 public function hasEndJoker()
50294 return $this->endJoker;
50302 public function replaceJokers($replacement)
50304 $replace = function ($subject) use ($replacement) {
50305 $subject = $subject[0];
50306 $replace = 0 === substr_count($subject, '\\') % 2;
50308 return $replace ? str_replace('.', $replacement, $subject) : $subject;
50311 $this->pattern = preg_replace_callback('~[\\\\]*\\.~', $replace, $this->pattern);
50319 private function parsePattern($pattern)
50321 if ($this->startFlag = self::START_FLAG === substr($pattern, 0, 1)) {
50322 $pattern = substr($pattern, 1);
50325 if ($this->startJoker = self::JOKER === substr($pattern, 0, 2)) {
50326 $pattern = substr($pattern, 2);
50329 if ($this->endFlag = (self::END_FLAG === substr($pattern, -1) && self::ESCAPING !== substr($pattern, -2, -1))) {
50330 $pattern = substr($pattern, 0, -1);
50333 if ($this->endJoker = (self::JOKER === substr($pattern, -2) && self::ESCAPING !== substr($pattern, -3, -2))) {
50334 $pattern = substr($pattern, 0, -2);
50337 $this->pattern = $pattern;
50351 namespace Symfony\Component\Finder\Expression;
50356 class Glob implements ValueInterface
50366 public function __construct($pattern)
50368 $this->pattern = $pattern;
50374 public function render()
50376 return $this->pattern;
50382 public function renderPattern()
50384 return $this->pattern;
50390 public function getType()
50392 return Expression::TYPE_GLOB;
50398 public function isCaseSensitive()
50406 public function prepend($expr)
50408 $this->pattern = $expr.$this->pattern;
50416 public function append($expr)
50418 $this->pattern .= $expr;
50428 public function isExpandable()
50430 return false !== strpos($this->pattern, '{')
50431 && false !== strpos($this->pattern, '}');
50440 public function toRegex($strictLeadingDot = true, $strictWildcardSlash = true)
50446 $sizeGlob = strlen($this->pattern);
50447 for ($i = 0; $i < $sizeGlob; $i++) {
50448 $car = $this->pattern[$i];
50450 if ($strictLeadingDot && '.' !== $car) {
50451 $regex .= '(?=[^\.])';
50454 $firstByte = false;
50457 if ('/' === $car) {
50461 if ('.' === $car || '(' === $car || ')' === $car || '|' === $car || '+' === $car || '^' === $car || '$' === $car) {
50462 $regex .= "\\$car";
50463 } elseif ('*' === $car) {
50464 $regex .= $escaping ? '\\*' : ($strictWildcardSlash ? '[^/]*' : '.*');
50465 } elseif ('?' === $car) {
50466 $regex .= $escaping ? '\\?' : ($strictWildcardSlash ? '[^/]' : '.');
50467 } elseif ('{' === $car) {
50468 $regex .= $escaping ? '\\{' : '(';
50472 } elseif ('}' === $car && $inCurlies) {
50473 $regex .= $escaping ? '}' : ')';
50477 } elseif (',' === $car && $inCurlies) {
50478 $regex .= $escaping ? ',' : '|';
50479 } elseif ('\\' === $car) {
50494 return new Regex('^'.$regex.'$');
50508 namespace Symfony\Component\Finder\Expression;
50513 class Expression implements ValueInterface
50515 const TYPE_REGEX = 1;
50516 const TYPE_GLOB = 2;
50528 public static function create($expr)
50530 return new self($expr);
50536 public function __construct($expr)
50539 $this->value = Regex::create($expr);
50540 } catch (\InvalidArgumentException $e) {
50541 $this->value = new Glob($expr);
50548 public function __toString()
50550 return $this->render();
50556 public function render()
50558 return $this->value->render();
50564 public function renderPattern()
50566 return $this->value->renderPattern();
50572 public function isCaseSensitive()
50574 return $this->value->isCaseSensitive();
50580 public function getType()
50582 return $this->value->getType();
50588 public function prepend($expr)
50590 $this->value->prepend($expr);
50598 public function append($expr)
50600 $this->value->append($expr);
50608 public function isRegex()
50610 return self::TYPE_REGEX === $this->value->getType();
50616 public function isGlob()
50618 return self::TYPE_GLOB === $this->value->getType();
50626 public function getGlob()
50628 if (self::TYPE_GLOB !== $this->value->getType()) {
50629 throw new \LogicException('Regex can\'t be transformed to glob.');
50632 return $this->value;
50638 public function getRegex()
50640 return self::TYPE_REGEX === $this->value->getType() ? $this->value : $this->value->toRegex();
50654 namespace Symfony\Component\Finder\Expression;
50659 interface ValueInterface
50666 public function render();
50673 public function renderPattern();
50680 public function isCaseSensitive();
50687 public function getType();
50694 public function prepend($expr);
50701 public function append($expr);
50714 namespace Symfony\Component\Finder\Adapter;
50719 interface AdapterInterface
50726 public function setFollowLinks($followLinks);
50733 public function setMode($mode);
50740 public function setExclude(array $exclude);
50747 public function setDepths(array $depths);
50754 public function setNames(array $names);
50761 public function setNotNames(array $notNames);
50768 public function setContains(array $contains);
50775 public function setNotContains(array $notContains);
50782 public function setSizes(array $sizes);
50789 public function setDates(array $dates);
50796 public function setFilters(array $filters);
50803 public function setSort($sort);
50810 public function setPath(array $paths);
50817 public function setNotPath(array $notPaths);
50824 public function ignoreUnreadableDirs($ignore = true);
50831 public function searchInDirectory($dir);
50838 public function isSupported();
50845 public function getName();
50858 namespace Symfony\Component\Finder\Adapter;
50860 use Symfony\Component\Finder\Shell\Shell;
50861 use Symfony\Component\Finder\Shell\Command;
50862 use Symfony\Component\Finder\Iterator\SortableIterator;
50863 use Symfony\Component\Finder\Expression\Expression;
50870 class BsdFindAdapter extends AbstractFindAdapter
50875 public function getName()
50883 protected function canBeUsed()
50885 return in_array($this->shell->getType(), array(Shell::TYPE_BSD, Shell::TYPE_DARWIN)) && parent::canBeUsed();
50891 protected function buildFormatSorting(Command $command, $sort)
50894 case SortableIterator::SORT_BY_NAME:
50895 $command->ins('sort')->add('| sort');
50898 case SortableIterator::SORT_BY_TYPE:
50901 case SortableIterator::SORT_BY_ACCESSED_TIME:
50904 case SortableIterator::SORT_BY_CHANGED_TIME:
50907 case SortableIterator::SORT_BY_MODIFIED_TIME:
50911 throw new \InvalidArgumentException(sprintf('Unknown sort options: %s.', $sort));
50915 ->add('-print0 | xargs -0 stat -f')
50916 ->arg($format.'%t%N')
50917 ->add('| sort | cut -f 2');
50923 protected function buildFindCommand(Command $command, $dir)
50925 parent::buildFindCommand($command, $dir)->addAtIndex('-E', 1);
50933 protected function buildContentFiltering(Command $command, array $contains, $not = false)
50935 foreach ($contains as $contain) {
50936 $expr = Expression::create($contain);
50940 ->add('| grep -v \'^$\'')
50941 ->add('| xargs -I{} grep -I')
50942 ->add($expr->isCaseSensitive() ? null : '-i')
50943 ->add($not ? '-L' : '-l')
50944 ->add('-Ee')->arg($expr->renderPattern())
50961 namespace Symfony\Component\Finder\Adapter;
50963 use Symfony\Component\Finder\Shell\Shell;
50964 use Symfony\Component\Finder\Shell\Command;
50965 use Symfony\Component\Finder\Iterator\SortableIterator;
50966 use Symfony\Component\Finder\Expression\Expression;
50973 class GnuFindAdapter extends AbstractFindAdapter
50978 public function getName()
50986 protected function buildFormatSorting(Command $command, $sort)
50989 case SortableIterator::SORT_BY_NAME:
50990 $command->ins('sort')->add('| sort');
50993 case SortableIterator::SORT_BY_TYPE:
50996 case SortableIterator::SORT_BY_ACCESSED_TIME:
50999 case SortableIterator::SORT_BY_CHANGED_TIME:
51002 case SortableIterator::SORT_BY_MODIFIED_TIME:
51006 throw new \InvalidArgumentException(sprintf('Unknown sort options: %s.', $sort));
51012 ->arg($format.' %h/%f\\n')
51013 ->add('| sort | cut')
51022 protected function canBeUsed()
51024 return $this->shell->getType() === Shell::TYPE_UNIX && parent::canBeUsed();
51030 protected function buildFindCommand(Command $command, $dir)
51032 return parent::buildFindCommand($command, $dir)->add('-regextype posix-extended');
51038 protected function buildContentFiltering(Command $command, array $contains, $not = false)
51040 foreach ($contains as $contain) {
51041 $expr = Expression::create($contain);
51045 ->add('| xargs -I{} -r grep -I')
51046 ->add($expr->isCaseSensitive() ? null : '-i')
51047 ->add($not ? '-L' : '-l')
51048 ->add('-Ee')->arg($expr->renderPattern())
51065 namespace Symfony\Component\Finder\Adapter;
51072 abstract class AbstractAdapter implements AdapterInterface
51074 protected $followLinks = false;
51075 protected $mode = 0;
51076 protected $minDepth = 0;
51077 protected $maxDepth = PHP_INT_MAX;
51078 protected $exclude = array();
51079 protected $names = array();
51080 protected $notNames = array();
51081 protected $contains = array();
51082 protected $notContains = array();
51083 protected $sizes = array();
51084 protected $dates = array();
51085 protected $filters = array();
51086 protected $sort = false;
51087 protected $paths = array();
51088 protected $notPaths = array();
51089 protected $ignoreUnreadableDirs = false;
51091 private static $areSupported = array();
51096 public function isSupported()
51098 $name = $this->getName();
51100 if (!array_key_exists($name, self::$areSupported)) {
51101 self::$areSupported[$name] = $this->canBeUsed();
51104 return self::$areSupported[$name];
51110 public function setFollowLinks($followLinks)
51112 $this->followLinks = $followLinks;
51120 public function setMode($mode)
51122 $this->mode = $mode;
51130 public function setDepths(array $depths)
51132 $this->minDepth = 0;
51133 $this->maxDepth = PHP_INT_MAX;
51135 foreach ($depths as $comparator) {
51136 switch ($comparator->getOperator()) {
51138 $this->minDepth = $comparator->getTarget() + 1;
51141 $this->minDepth = $comparator->getTarget();
51144 $this->maxDepth = $comparator->getTarget() - 1;
51147 $this->maxDepth = $comparator->getTarget();
51150 $this->minDepth = $this->maxDepth = $comparator->getTarget();
51160 public function setExclude(array $exclude)
51162 $this->exclude = $exclude;
51170 public function setNames(array $names)
51172 $this->names = $names;
51180 public function setNotNames(array $notNames)
51182 $this->notNames = $notNames;
51190 public function setContains(array $contains)
51192 $this->contains = $contains;
51200 public function setNotContains(array $notContains)
51202 $this->notContains = $notContains;
51210 public function setSizes(array $sizes)
51212 $this->sizes = $sizes;
51220 public function setDates(array $dates)
51222 $this->dates = $dates;
51230 public function setFilters(array $filters)
51232 $this->filters = $filters;
51240 public function setSort($sort)
51242 $this->sort = $sort;
51250 public function setPath(array $paths)
51252 $this->paths = $paths;
51260 public function setNotPath(array $notPaths)
51262 $this->notPaths = $notPaths;
51270 public function ignoreUnreadableDirs($ignore = true)
51272 $this->ignoreUnreadableDirs = (bool) $ignore;
51288 abstract protected function canBeUsed();
51301 namespace Symfony\Component\Finder\Adapter;
51303 use Symfony\Component\Finder\Exception\AccessDeniedException;
51304 use Symfony\Component\Finder\Iterator;
51305 use Symfony\Component\Finder\Shell\Shell;
51306 use Symfony\Component\Finder\Expression\Expression;
51307 use Symfony\Component\Finder\Shell\Command;
51308 use Symfony\Component\Finder\Comparator\NumberComparator;
51309 use Symfony\Component\Finder\Comparator\DateComparator;
51316 abstract class AbstractFindAdapter extends AbstractAdapter
51326 public function __construct()
51328 $this->shell = new Shell();
51334 public function searchInDirectory($dir)
51337 $dir = realpath($dir);
51340 if (Iterator\FileTypeFilterIterator::ONLY_DIRECTORIES === $this->mode && ($this->contains || $this->notContains)) {
51341 return new Iterator\FilePathsIterator(array(), $dir);
51344 $command = Command::create();
51345 $find = $this->buildFindCommand($command, $dir);
51347 if ($this->followLinks) {
51348 $find->add('-follow');
51351 $find->add('-mindepth')->add($this->minDepth + 1);
51353 if (PHP_INT_MAX !== $this->maxDepth) {
51354 $find->add('-maxdepth')->add($this->maxDepth + 1);
51357 if (Iterator\FileTypeFilterIterator::ONLY_DIRECTORIES === $this->mode) {
51358 $find->add('-type d');
51359 } elseif (Iterator\FileTypeFilterIterator::ONLY_FILES === $this->mode) {
51360 $find->add('-type f');
51363 $this->buildNamesFiltering($find, $this->names);
51364 $this->buildNamesFiltering($find, $this->notNames, true);
51365 $this->buildPathsFiltering($find, $dir, $this->paths);
51366 $this->buildPathsFiltering($find, $dir, $this->notPaths, true);
51367 $this->buildSizesFiltering($find, $this->sizes);
51368 $this->buildDatesFiltering($find, $this->dates);
51370 $useGrep = $this->shell->testCommand('grep') && $this->shell->testCommand('xargs');
51371 $useSort = is_int($this->sort) && $this->shell->testCommand('sort') && $this->shell->testCommand('cut');
51373 if ($useGrep && ($this->contains || $this->notContains)) {
51374 $grep = $command->ins('grep');
51375 $this->buildContentFiltering($grep, $this->contains);
51376 $this->buildContentFiltering($grep, $this->notContains, true);
51380 $this->buildSorting($command, $this->sort);
51383 $command->setErrorHandler(
51384 $this->ignoreUnreadableDirs
51386 ? function ($stderr) { return; }
51387 : function ($stderr) { throw new AccessDeniedException($stderr); }
51390 $paths = $this->shell->testCommand('uniq') ? $command->add('| uniq')->execute() : array_unique($command->execute());
51391 $iterator = new Iterator\FilePathsIterator($paths, $dir);
51393 if ($this->exclude) {
51394 $iterator = new Iterator\ExcludeDirectoryFilterIterator($iterator, $this->exclude);
51397 if (!$useGrep && ($this->contains || $this->notContains)) {
51398 $iterator = new Iterator\FilecontentFilterIterator($iterator, $this->contains, $this->notContains);
51401 if ($this->filters) {
51402 $iterator = new Iterator\CustomFilterIterator($iterator, $this->filters);
51405 if (!$useSort && $this->sort) {
51406 $iteratorAggregate = new Iterator\SortableIterator($iterator, $this->sort);
51407 $iterator = $iteratorAggregate->getIterator();
51416 protected function canBeUsed()
51418 return $this->shell->testCommand('find');
51427 protected function buildFindCommand(Command $command, $dir)
51441 private function buildNamesFiltering(Command $command, array $names, $not = false)
51443 if (0 === count($names)) {
51447 $command->add($not ? '-not' : null)->cmd('(');
51449 foreach ($names as $i => $name) {
51450 $expr = Expression::create($name);
51453 if ($expr->isGlob() && $expr->getGlob()->isExpandable()) {
51454 $expr = Expression::create($expr->getGlob()->toRegex(false));
51460 if ($expr->isRegex()) {
51461 $regex = $expr->getRegex();
51462 $regex->prepend($regex->hasStartFlag() ? '/' : '/[^/]*')
51463 ->setStartFlag(false)
51464 ->setStartJoker(true)
51465 ->replaceJokers('[^/]');
51466 if (!$regex->hasEndFlag() || $regex->hasEndJoker()) {
51467 $regex->setEndJoker(false)->append('[^/]*');
51472 ->add($i > 0 ? '-or' : null)
51473 ->add($expr->isRegex()
51474 ? ($expr->isCaseSensitive() ? '-regex' : '-iregex')
51475 : ($expr->isCaseSensitive() ? '-name' : '-iname')
51477 ->arg($expr->renderPattern());
51480 $command->cmd(')');
51489 private function buildPathsFiltering(Command $command, $dir, array $paths, $not = false)
51491 if (0 === count($paths)) {
51495 $command->add($not ? '-not' : null)->cmd('(');
51497 foreach ($paths as $i => $path) {
51498 $expr = Expression::create($path);
51501 if ($expr->isGlob() && $expr->getGlob()->isExpandable()) {
51502 $expr = Expression::create($expr->getGlob()->toRegex(false));
51506 if ($expr->isRegex()) {
51507 $regex = $expr->getRegex();
51508 $regex->prepend($regex->hasStartFlag() ? preg_quote($dir).DIRECTORY_SEPARATOR : '.*')->setEndJoker(!$regex->hasEndFlag());
51510 $expr->prepend('*')->append('*');
51514 ->add($i > 0 ? '-or' : null)
51515 ->add($expr->isRegex()
51516 ? ($expr->isCaseSensitive() ? '-regex' : '-iregex')
51517 : ($expr->isCaseSensitive() ? '-path' : '-ipath')
51519 ->arg($expr->renderPattern());
51522 $command->cmd(')');
51529 private function buildSizesFiltering(Command $command, array $sizes)
51531 foreach ($sizes as $i => $size) {
51532 $command->add($i > 0 ? '-and' : null);
51534 switch ($size->getOperator()) {
51536 $command->add('-size -'.($size->getTarget() + 1).'c');
51539 $command->add('-size +'.($size->getTarget() - 1).'c');
51542 $command->add('-size +'.$size->getTarget().'c');
51545 $command->add('-size -'.$size->getTarget().'c');
51546 $command->add('-size +'.$size->getTarget().'c');
51550 $command->add('-size -'.$size->getTarget().'c');
51559 private function buildDatesFiltering(Command $command, array $dates)
51561 foreach ($dates as $i => $date) {
51562 $command->add($i > 0 ? '-and' : null);
51564 $mins = (int) round((time()-$date->getTarget()) / 60);
51568 $command->add(' -mmin -0');
51573 switch ($date->getOperator()) {
51575 $command->add('-mmin +'.($mins - 1));
51578 $command->add('-mmin -'.($mins + 1));
51581 $command->add('-mmin -'.$mins);
51584 $command->add('-mmin +'.$mins.' -or -mmin -'.$mins);
51588 $command->add('-mmin +'.$mins);
51599 private function buildSorting(Command $command, $sort)
51601 $this->buildFormatSorting($command, $sort);
51608 abstract protected function buildFormatSorting(Command $command, $sort);
51615 abstract protected function buildContentFiltering(Command $command, array $contains, $not = false);
51628 namespace Symfony\Component\Finder\Adapter;
51630 use Symfony\Component\Finder\Iterator;
51637 class PhpAdapter extends AbstractAdapter
51642 public function searchInDirectory($dir)
51644 $flags = \RecursiveDirectoryIterator::SKIP_DOTS;
51646 if ($this->followLinks) {
51647 $flags |= \RecursiveDirectoryIterator::FOLLOW_SYMLINKS;
51650 $iterator = new \RecursiveIteratorIterator(
51651 new Iterator\RecursiveDirectoryIterator($dir, $flags, $this->ignoreUnreadableDirs),
51652 \RecursiveIteratorIterator::SELF_FIRST
51655 if ($this->minDepth > 0 || $this->maxDepth < PHP_INT_MAX) {
51656 $iterator = new Iterator\DepthRangeFilterIterator($iterator, $this->minDepth, $this->maxDepth);
51660 $iterator = new Iterator\FileTypeFilterIterator($iterator, $this->mode);
51663 if ($this->exclude) {
51664 $iterator = new Iterator\ExcludeDirectoryFilterIterator($iterator, $this->exclude);
51667 if ($this->names || $this->notNames) {
51668 $iterator = new Iterator\FilenameFilterIterator($iterator, $this->names, $this->notNames);
51671 if ($this->contains || $this->notContains) {
51672 $iterator = new Iterator\FilecontentFilterIterator($iterator, $this->contains, $this->notContains);
51675 if ($this->sizes) {
51676 $iterator = new Iterator\SizeRangeFilterIterator($iterator, $this->sizes);
51679 if ($this->dates) {
51680 $iterator = new Iterator\DateRangeFilterIterator($iterator, $this->dates);
51683 if ($this->filters) {
51684 $iterator = new Iterator\CustomFilterIterator($iterator, $this->filters);
51688 $iteratorAggregate = new Iterator\SortableIterator($iterator, $this->sort);
51689 $iterator = $iteratorAggregate->getIterator();
51692 if ($this->paths || $this->notPaths) {
51693 $iterator = new Iterator\PathFilterIterator($iterator, $this->paths, $this->notPaths);
51702 public function getName()
51710 protected function canBeUsed()
51726 namespace Symfony\Component\Finder;
51761 public static function toRegex($glob, $strictLeadingDot = true, $strictWildcardSlash = true)
51767 $sizeGlob = strlen($glob);
51768 for ($i = 0; $i < $sizeGlob; $i++) {
51771 if ($strictLeadingDot && '.' !== $car) {
51772 $regex .= '(?=[^\.])';
51775 $firstByte = false;
51778 if ('/' === $car) {
51782 if ('.' === $car || '(' === $car || ')' === $car || '|' === $car || '+' === $car || '^' === $car || '$' === $car) {
51783 $regex .= "\\$car";
51784 } elseif ('*' === $car) {
51785 $regex .= $escaping ? '\\*' : ($strictWildcardSlash ? '[^/]*' : '.*');
51786 } elseif ('?' === $car) {
51787 $regex .= $escaping ? '\\?' : ($strictWildcardSlash ? '[^/]' : '.');
51788 } elseif ('{' === $car) {
51789 $regex .= $escaping ? '\\{' : '(';
51793 } elseif ('}' === $car && $inCurlies) {
51794 $regex .= $escaping ? '}' : ')';
51798 } elseif (',' === $car && $inCurlies) {
51799 $regex .= $escaping ? ',' : '|';
51800 } elseif ('\\' === $car) {
51815 return '#^'.$regex.'$#';
51829 namespace Symfony\Component\Finder\Iterator;
51836 class DepthRangeFilterIterator extends FilterIterator
51838 private $minDepth = 0;
51847 public function __construct(\RecursiveIteratorIterator $iterator, $minDepth = 0, $maxDepth = PHP_INT_MAX)
51849 $this->minDepth = $minDepth;
51850 $iterator->setMaxDepth(PHP_INT_MAX === $maxDepth ? -1 : $maxDepth);
51852 parent::__construct($iterator);
51860 public function accept()
51862 return $this->getInnerIterator()->getDepth() >= $this->minDepth;
51876 namespace Symfony\Component\Finder\Iterator;
51883 class SortableIterator implements \IteratorAggregate
51885 const SORT_BY_NAME = 1;
51886 const SORT_BY_TYPE = 2;
51887 const SORT_BY_ACCESSED_TIME = 3;
51888 const SORT_BY_CHANGED_TIME = 4;
51889 const SORT_BY_MODIFIED_TIME = 5;
51902 public function __construct(\Traversable $iterator, $sort)
51904 $this->iterator = $iterator;
51906 if (self::SORT_BY_NAME === $sort) {
51907 $this->sort = function ($a, $b) {
51908 return strcmp($a->getRealpath(), $b->getRealpath());
51910 } elseif (self::SORT_BY_TYPE === $sort) {
51911 $this->sort = function ($a, $b) {
51912 if ($a->isDir() && $b->isFile()) {
51914 } elseif ($a->isFile() && $b->isDir()) {
51918 return strcmp($a->getRealpath(), $b->getRealpath());
51920 } elseif (self::SORT_BY_ACCESSED_TIME === $sort) {
51921 $this->sort = function ($a, $b) {
51922 return ($a->getATime() - $b->getATime());
51924 } elseif (self::SORT_BY_CHANGED_TIME === $sort) {
51925 $this->sort = function ($a, $b) {
51926 return ($a->getCTime() - $b->getCTime());
51928 } elseif (self::SORT_BY_MODIFIED_TIME === $sort) {
51929 $this->sort = function ($a, $b) {
51930 return ($a->getMTime() - $b->getMTime());
51932 } elseif (is_callable($sort)) {
51933 $this->sort = $sort;
51935 throw new \InvalidArgumentException('The SortableIterator takes a PHP callable or a valid built-in sort algorithm as an argument.');
51939 public function getIterator()
51941 $array = iterator_to_array($this->iterator, true);
51942 uasort($array, $this->sort);
51944 return new \ArrayIterator($array);
51958 namespace Symfony\Component\Finder\Iterator;
51966 class FilecontentFilterIterator extends MultiplePcreFilterIterator
51973 public function accept()
51975 if (!$this->matchRegexps && !$this->noMatchRegexps) {
51979 $fileinfo = $this->current();
51981 if ($fileinfo->isDir() || !$fileinfo->isReadable()) {
51985 $content = $fileinfo->getContents();
51991 foreach ($this->noMatchRegexps as $regex) {
51992 if (preg_match($regex, $content)) {
51999 if ($this->matchRegexps) {
52001 foreach ($this->matchRegexps as $regex) {
52002 if (preg_match($regex, $content)) {
52018 protected function toRegex($str)
52020 return $this->isRegex($str) ? $str : '/'.preg_quote($str, '/').'/';
52034 namespace Symfony\Component\Finder\Iterator;
52036 use Symfony\Component\Finder\SplFileInfo;
52043 class FilePathsIterator extends \ArrayIterator
52053 private $baseDirLength;
52063 private $subPathname;
52074 public function __construct(array $paths, $baseDir)
52076 $this->baseDir = $baseDir;
52077 $this->baseDirLength = strlen($baseDir);
52079 parent::__construct($paths);
52088 public function __call($name, array $arguments)
52090 return call_user_func_array(array($this->current(), $name), $arguments);
52098 public function current()
52100 return $this->current;
52106 public function key()
52108 return $this->current->getPathname();
52111 public function next()
52114 $this->buildProperties();
52117 public function rewind()
52120 $this->buildProperties();
52126 public function getSubPath()
52128 return $this->subPath;
52134 public function getSubPathname()
52136 return $this->subPathname;
52139 private function buildProperties()
52141 $absolutePath = parent::current();
52143 if ($this->baseDir === substr($absolutePath, 0, $this->baseDirLength)) {
52144 $this->subPathname = ltrim(substr($absolutePath, $this->baseDirLength), '/\\');
52145 $dir = dirname($this->subPathname);
52146 $this->subPath = '.' === $dir ? '' : $dir;
52148 $this->subPath = $this->subPathname = '';
52151 $this->current = new SplFileInfo(parent::current(), $this->subPath, $this->subPathname);
52165 namespace Symfony\Component\Finder\Iterator;
52167 use Symfony\Component\Finder\Comparator\NumberComparator;
52174 class SizeRangeFilterIterator extends FilterIterator
52176 private $comparators = array();
52184 public function __construct(\Iterator $iterator, array $comparators)
52186 $this->comparators = $comparators;
52188 parent::__construct($iterator);
52196 public function accept()
52198 $fileinfo = $this->current();
52199 if (!$fileinfo->isFile()) {
52203 $filesize = $fileinfo->getSize();
52204 foreach ($this->comparators as $compare) {
52205 if (!$compare->test($filesize)) {
52224 namespace Symfony\Component\Finder\Iterator;
52231 class ExcludeDirectoryFilterIterator extends FilterIterator
52233 private $patterns = array();
52241 public function __construct(\Iterator $iterator, array $directories)
52243 foreach ($directories as $directory) {
52244 $this->patterns[] = '#(^|/)'.preg_quote($directory, '#').'(/|$)#';
52247 parent::__construct($iterator);
52255 public function accept()
52257 $path = $this->isDir() ? $this->current()->getRelativePathname() : $this->current()->getRelativePath();
52258 $path = strtr($path, '\\', '/');
52259 foreach ($this->patterns as $pattern) {
52260 if (preg_match($pattern, $path)) {
52279 namespace Symfony\Component\Finder\Iterator;
52288 abstract class FilterIterator extends \FilterIterator
52296 public function rewind()
52299 while ($iterator instanceof \OuterIterator) {
52300 $innerIterator = $iterator->getInnerIterator();
52302 if ($innerIterator instanceof RecursiveDirectoryIterator) {
52303 if ($innerIterator->isRewindable()) {
52304 $innerIterator->next();
52305 $innerIterator->rewind();
52307 } elseif ($iterator->getInnerIterator() instanceof \FilesystemIterator) {
52308 $iterator->getInnerIterator()->next();
52309 $iterator->getInnerIterator()->rewind();
52311 $iterator = $iterator->getInnerIterator();
52328 namespace Symfony\Component\Finder\Iterator;
52330 use Symfony\Component\Finder\Expression\Expression;
52337 abstract class MultiplePcreFilterIterator extends FilterIterator
52339 protected $matchRegexps = array();
52340 protected $noMatchRegexps = array();
52349 public function __construct(\Iterator $iterator, array $matchPatterns, array $noMatchPatterns)
52351 foreach ($matchPatterns as $pattern) {
52352 $this->matchRegexps[] = $this->toRegex($pattern);
52355 foreach ($noMatchPatterns as $pattern) {
52356 $this->noMatchRegexps[] = $this->toRegex($pattern);
52359 parent::__construct($iterator);
52369 protected function isRegex($str)
52371 return Expression::create($str)->isRegex();
52381 abstract protected function toRegex($str);
52394 namespace Symfony\Component\Finder\Iterator;
52404 class CustomFilterIterator extends FilterIterator
52406 private $filters = array();
52416 public function __construct(\Iterator $iterator, array $filters)
52418 foreach ($filters as $filter) {
52419 if (!is_callable($filter)) {
52420 throw new \InvalidArgumentException('Invalid PHP callback.');
52423 $this->filters = $filters;
52425 parent::__construct($iterator);
52433 public function accept()
52435 $fileinfo = $this->current();
52437 foreach ($this->filters as $filter) {
52438 if (false === call_user_func($filter, $fileinfo)) {
52457 namespace Symfony\Component\Finder\Iterator;
52459 use Symfony\Component\Finder\Expression\Expression;
52466 class FilenameFilterIterator extends MultiplePcreFilterIterator
52473 public function accept()
52475 $filename = $this->current()->getFilename();
52478 foreach ($this->noMatchRegexps as $regex) {
52479 if (preg_match($regex, $filename)) {
52486 if ($this->matchRegexps) {
52488 foreach ($this->matchRegexps as $regex) {
52489 if (preg_match($regex, $filename)) {
52508 protected function toRegex($str)
52510 return Expression::create($str)->getRegex()->render();
52524 namespace Symfony\Component\Finder\Iterator;
52526 use Symfony\Component\Finder\Comparator\DateComparator;
52533 class DateRangeFilterIterator extends FilterIterator
52535 private $comparators = array();
52543 public function __construct(\Iterator $iterator, array $comparators)
52545 $this->comparators = $comparators;
52547 parent::__construct($iterator);
52555 public function accept()
52557 $fileinfo = $this->current();
52559 if (!file_exists($fileinfo->getRealPath())) {
52563 $filedate = $fileinfo->getMTime();
52564 foreach ($this->comparators as $compare) {
52565 if (!$compare->test($filedate)) {
52584 namespace Symfony\Component\Finder\Iterator;
52591 class FileTypeFilterIterator extends FilterIterator
52593 const ONLY_FILES = 1;
52594 const ONLY_DIRECTORIES = 2;
52604 public function __construct(\Iterator $iterator, $mode)
52606 $this->mode = $mode;
52608 parent::__construct($iterator);
52616 public function accept()
52618 $fileinfo = $this->current();
52619 if (self::ONLY_DIRECTORIES === (self::ONLY_DIRECTORIES & $this->mode) && $fileinfo->isFile()) {
52621 } elseif (self::ONLY_FILES === (self::ONLY_FILES & $this->mode) && $fileinfo->isDir()) {
52639 namespace Symfony\Component\Finder\Iterator;
52641 use Symfony\Component\Finder\Exception\AccessDeniedException;
52642 use Symfony\Component\Finder\SplFileInfo;
52649 class RecursiveDirectoryIterator extends \RecursiveDirectoryIterator
52654 private $ignoreUnreadableDirs;
52659 private $rewindable;
52670 public function __construct($path, $flags, $ignoreUnreadableDirs = false)
52672 if ($flags & (self::CURRENT_AS_PATHNAME | self::CURRENT_AS_SELF)) {
52673 throw new \RuntimeException('This iterator only support returning current as fileinfo.');
52676 parent::__construct($path, $flags);
52677 $this->ignoreUnreadableDirs = $ignoreUnreadableDirs;
52685 public function current()
52687 return new SplFileInfo(parent::current()->getPathname(), $this->getSubPath(), $this->getSubPathname());
52695 public function getChildren()
52698 $children = parent::getChildren();
52700 if ($children instanceof self) {
52702 $children->ignoreUnreadableDirs = $this->ignoreUnreadableDirs;
52706 } catch (\UnexpectedValueException $e) {
52707 if ($this->ignoreUnreadableDirs) {
52709 return new \RecursiveArrayIterator(array());
52711 throw new AccessDeniedException($e->getMessage(), $e->getCode(), $e);
52719 public function rewind()
52721 if (false === $this->isRewindable()) {
52736 public function isRewindable()
52738 if (null !== $this->rewindable) {
52739 return $this->rewindable;
52742 if (false !== $stream = @opendir($this->getPath())) {
52743 $infos = stream_get_meta_data($stream);
52746 if ($infos['seekable']) {
52747 return $this->rewindable = true;
52751 return $this->rewindable = false;
52765 namespace Symfony\Component\Finder\Iterator;
52773 class PathFilterIterator extends MultiplePcreFilterIterator
52780 public function accept()
52782 $filename = $this->current()->getRelativePathname();
52784 if (defined('PHP_WINDOWS_VERSION_MAJOR')) {
52785 $filename = strtr($filename, '\\', '/');
52789 foreach ($this->noMatchRegexps as $regex) {
52790 if (preg_match($regex, $filename)) {
52797 if ($this->matchRegexps) {
52799 foreach ($this->matchRegexps as $regex) {
52800 if (preg_match($regex, $filename)) {
52823 protected function toRegex($str)
52825 return $this->isRegex($str) ? $str : '/'.preg_quote($str, '/').'/';
52839 namespace Symfony\Component\Finder\Comparator;
52846 class DateComparator extends Comparator
52855 public function __construct($test)
52857 if (!preg_match('#^\s*(==|!=|[<>]=?|after|since|before|until)?\s*(.+?)\s*$#i', $test, $matches)) {
52858 throw new \InvalidArgumentException(sprintf('Don\'t understand "%s" as a date test.', $test));
52862 $date = new \DateTime($matches[2]);
52863 $target = $date->format('U');
52864 } catch (\Exception $e) {
52865 throw new \InvalidArgumentException(sprintf('"%s" is not a valid date.', $matches[2]));
52868 $operator = isset($matches[1]) ? $matches[1] : '==';
52869 if ('since' === $operator || 'after' === $operator) {
52873 if ('until' === $operator || 'before' === $operator) {
52877 $this->setOperator($operator);
52878 $this->setTarget($target);
52892 namespace Symfony\Component\Finder\Comparator;
52916 class NumberComparator extends Comparator
52925 public function __construct($test)
52927 if (!preg_match('#^\s*(==|!=|[<>]=?)?\s*([0-9\.]+)\s*([kmg]i?)?\s*$#i', $test, $matches)) {
52928 throw new \InvalidArgumentException(sprintf('Don\'t understand "%s" as a number test.', $test));
52931 $target = $matches[2];
52932 if (!is_numeric($target)) {
52933 throw new \InvalidArgumentException(sprintf('Invalid number "%s".', $target));
52935 if (isset($matches[3])) {
52937 switch (strtolower($matches[3])) {
52945 $target *= 1000000;
52948 $target *= 1024*1024;
52951 $target *= 1000000000;
52954 $target *= 1024*1024*1024;
52959 $this->setTarget($target);
52960 $this->setOperator(isset($matches[1]) ? $matches[1] : '==');
52974 namespace Symfony\Component\Finder\Comparator;
52984 private $operator = '==';
52991 public function getTarget()
52993 return $this->target;
53001 public function setTarget($target)
53003 $this->target = $target;
53011 public function getOperator()
53013 return $this->operator;
53023 public function setOperator($operator)
53029 if (!in_array($operator, array('>', '<', '>=', '<=', '==', '!='))) {
53030 throw new \InvalidArgumentException(sprintf('Invalid operator "%s".', $operator));
53033 $this->operator = $operator;
53043 public function test($test)
53045 switch ($this->operator) {
53047 return $test > $this->target;
53049 return $test >= $this->target;
53051 return $test < $this->target;
53053 return $test <= $this->target;
53055 return $test != $this->target;
53058 return $test == $this->target;
53072 namespace Symfony\Component\Finder\Exception;
53077 class AccessDeniedException extends \UnexpectedValueException
53091 namespace Symfony\Component\Finder\Exception;
53096 interface ExceptionInterface
53101 public function getAdapter();
53114 namespace Symfony\Component\Finder\Exception;
53119 class OperationNotPermitedException extends AdapterFailureException
53133 namespace Symfony\Component\Finder\Exception;
53135 use Symfony\Component\Finder\Adapter\AdapterInterface;
53142 class AdapterFailureException extends \RuntimeException implements ExceptionInterface
53154 public function __construct(AdapterInterface $adapter, $message = null, \Exception $previous = null)
53156 $this->adapter = $adapter;
53157 parent::__construct($message ?: 'Search failed with "'.$adapter->getName().'" adapter.', $previous);
53163 public function getAdapter()
53165 return $this->adapter;
53179 namespace Symfony\Component\Finder\Exception;
53181 use Symfony\Component\Finder\Adapter\AdapterInterface;
53182 use Symfony\Component\Finder\Shell\Command;
53187 class ShellCommandFailureException extends AdapterFailureException
53199 public function __construct(AdapterInterface $adapter, Command $command, \Exception $previous = null)
53201 $this->command = $command;
53202 parent::__construct($adapter, 'Shell command failed: "'.$command->join().'".', $previous);
53208 public function getCommand()
53210 return $this->command;
53224 namespace Symfony\Component\Finder;
53231 class SplFileInfo extends \SplFileInfo
53233 private $relativePath;
53234 private $relativePathname;
53243 public function __construct($file, $relativePath, $relativePathname)
53245 parent::__construct($file);
53246 $this->relativePath = $relativePath;
53247 $this->relativePathname = $relativePathname;
53255 public function getRelativePath()
53257 return $this->relativePath;
53265 public function getRelativePathname()
53267 return $this->relativePathname;
53277 public function getContents()
53279 $level = error_reporting(0);
53280 $content = file_get_contents($this->getPathname());
53281 error_reporting($level);
53282 if (false === $content) {
53283 $error = error_get_last();
53284 throw new \RuntimeException($error['message']);
53301 namespace Seld\JsonLint;
53317 namespace Seld\JsonLint;
53335 const DETECT_KEY_CONFLICTS = 1;
53336 const ALLOW_DUPLICATE_KEYS = 2;
53337 const PARSE_TO_ASSOC = 4;
53346 private $symbols = array(
53352 'JSONNullLiteral' => 7,
53354 'JSONBooleanLiteral' => 9,
53360 'JSONObject' => 15,
53364 'JSONMemberList' => 19,
53365 'JSONMember' => 20,
53370 'JSONElementList' => 25,
53375 private $terminals_ = array(
53391 private $productions_ = array(
53416 private $table = array(array(3 => 5, 4 => array(1,12), 5 => 6, 6 => array(1,13), 7 => 3, 8 => array(1,9), 9 => 4, 10 => array(1,10), 11 => array(1,11), 12 => 1, 13 => 2, 15 => 7, 16 => 8, 17 => array(1,14), 23 => array(1,15)), array( 1 => array(3)), array( 14 => array(1,16)), array( 14 => array(2,7), 18 => array(2,7), 22 => array(2,7), 24 => array(2,7)), array( 14 => array(2,8), 18 => array(2,8), 22 => array(2,8), 24 => array(2,8)), array( 14 => array(2,9), 18 => array(2,9), 22 => array(2,9), 24 => array(2,9)), array( 14 => array(2,10), 18 => array(2,10), 22 => array(2,10), 24 => array(2,10)), array( 14 => array(2,11), 18 => array(2,11), 22 => array(2,11), 24 => array(2,11)), array( 14 => array(2,12), 18 => array(2,12), 22 => array(2,12), 24 => array(2,12)), array( 14 => array(2,3), 18 => array(2,3), 22 => array(2,3), 24 => array(2,3)), array( 14 => array(2,4), 18 => array(2,4), 22 => array(2,4), 24 => array(2,4)), array( 14 => array(2,5), 18 => array(2,5), 22 => array(2,5), 24 => array(2,5)), array( 14 => array(2,1), 18 => array(2,1), 21 => array(2,1), 22 => array(2,1), 24 => array(2,1)), array( 14 => array(2,2), 18 => array(2,2), 22 => array(2,2), 24 => array(2,2)), array( 3 => 20, 4 => array(1,12), 18 => array(1,17), 19 => 18, 20 => 19 ), array( 3 => 5, 4 => array(1,12), 5 => 6, 6 => array(1,13), 7 => 3, 8 => array(1,9), 9 => 4, 10 => array(1,10), 11 => array(1,11), 13 => 23, 15 => 7, 16 => 8, 17 => array(1,14), 23 => array(1,15), 24 => array(1,21), 25 => 22 ), array( 1 => array(2,6)), array( 14 => array(2,13), 18 => array(2,13), 22 => array(2,13), 24 => array(2,13)), array( 18 => array(1,24), 22 => array(1,25)), array( 18 => array(2,16), 22 => array(2,16)), array( 21 => array(1,26)), array( 14 => array(2,18), 18 => array(2,18), 22 => array(2,18), 24 => array(2,18)), array( 22 => array(1,28), 24 => array(1,27)), array( 22 => array(2,20), 24 => array(2,20)), array( 14 => array(2,14), 18 => array(2,14), 22 => array(2,14), 24 => array(2,14)), array( 3 => 20, 4 => array(1,12), 20 => 29 ), array( 3 => 5, 4 => array(1,12), 5 => 6, 6 => array(1,13), 7 => 3, 8 => array(1,9), 9 => 4, 10 => array(1,10), 11 => array(1,11), 13 => 30, 15 => 7, 16 => 8, 17 => array(1,14), 23 => array(1,15)), array( 14 => array(2,19), 18 => array(2,19), 22 => array(2,19), 24 => array(2,19)), array( 3 => 5, 4 => array(1,12), 5 => 6, 6 => array(1,13), 7 => 3, 8 => array(1,9), 9 => 4, 10 => array(1,10), 11 => array(1,11), 13 => 31, 15 => 7, 16 => 8, 17 => array(1,14), 23 => array(1,15)), array( 18 => array(2,17), 22 => array(2,17)), array( 18 => array(2,15), 22 => array(2,15)), array( 22 => array(2,21), 24 => array(2,21)),
53419 private $defaultActions = array(
53427 public function lint($input)
53430 $this->parse($input);
53431 } catch (ParsingException $e) {
53441 public function parse($input, $flags = 0)
53443 $this->failOnBOM($input);
53445 $this->flags = $flags;
53447 $this->stack = array(0);
53448 $this->vstack = array(null);
53449 $this->lstack = array();
53458 $this->lexer = new Lexer();
53459 $this->lexer->setInput($input);
53461 $yyloc = $this->lexer->yylloc;
53462 $this->lstack[] = $yyloc;
53465 $preErrorSymbol = null;
53470 $yyval = new stdClass;
53479 $state = $this->stack[count($this->stack)-1];
53482 if (isset($this->defaultActions[$state])) {
53483 $action = $this->defaultActions[$state];
53485 if ($symbol == null) {
53486 $symbol = $this->lex();
53489 $action = isset($this->table[$state][$symbol]) ? $this->table[$state][$symbol] : false;
53493 if (!$action || !$action[0]) {
53494 if (!$recovering) {
53496 $expected = array();
53497 foreach ($this->table[$state] as $p => $ignore) {
53498 if (isset($this->terminals_[$p]) && $p > 2) {
53499 $expected[] = "'" . $this->terminals_[$p] . "'";
53504 if (in_array("'STRING'", $expected) && in_array(substr($this->lexer->match, 0, 1), array('"', "'"))) {
53505 $message = "Invalid string";
53506 if ("'" === substr($this->lexer->match, 0, 1)) {
53507 $message .= ", it appears you used single quotes instead of double quotes";
53508 } elseif (preg_match('{".+?(\\\\[^"bfnrt/\\\\u])}', $this->lexer->getUpcomingInput(), $match)) {
53509 $message .= ", it appears you have an unescaped backslash at: ".$match[1];
53510 } elseif (preg_match('{"(?:[^"]+|\\\\")*$}m', $this->lexer->getUpcomingInput())) {
53511 $message .= ", it appears you forgot to terminated the string, or attempted to write a multiline string which is invalid";
53515 $errStr = 'Parse error on line ' . ($yylineno+1) . ":\n";
53516 $errStr .= $this->lexer->showPosition() . "\n";
53518 $errStr .= $message;
53520 $errStr .= (count($expected) > 1) ? "Expected one of: " : "Expected: ";
53521 $errStr .= implode(', ', $expected);
53524 if (',' === substr(trim($this->lexer->getPastInput()), -1)) {
53525 $errStr .= " - It appears you have an extra trailing comma";
53528 $this->parseError($errStr, array(
53529 'text' => $this->lexer->match,
53530 'token' => !empty($this->terminals_[$symbol]) ? $this->terminals_[$symbol] : $symbol,
53531 'line' => $this->lexer->yylineno,
53533 'expected' => $expected,
53538 if ($recovering == 3) {
53539 if ($symbol == $EOF) {
53540 throw new ParsingException($errStr ?: 'Parsing halted.');
53544 $yyleng = $this->lexer->yyleng;
53545 $yytext = $this->lexer->yytext;
53546 $yylineno = $this->lexer->yylineno;
53547 $yyloc = $this->lexer->yylloc;
53548 $symbol = $this->lex();
53554 if (array_key_exists($TERROR, $this->table[$state])) {
53558 throw new ParsingException($errStr ?: 'Parsing halted.');
53560 $this->popStack(1);
53561 $state = $this->stack[count($this->stack)-1];
53564 $preErrorSymbol = $symbol;
53566 $state = $this->stack[count($this->stack)-1];
53567 $action = isset($this->table[$state][$TERROR]) ? $this->table[$state][$TERROR] : false;
53572 if (is_array($action[0]) && count($action) > 1) {
53573 throw new ParsingException('Parse Error: multiple actions possible at state: ' . $state . ', token: ' . $symbol);
53576 switch ($action[0]) {
53578 $this->stack[] = $symbol;
53579 $this->vstack[] = $this->lexer->yytext;
53580 $this->lstack[] = $this->lexer->yylloc;
53581 $this->stack[] = $action[1];
53583 if (!$preErrorSymbol) {
53584 $yyleng = $this->lexer->yyleng;
53585 $yytext = $this->lexer->yytext;
53586 $yylineno = $this->lexer->yylineno;
53587 $yyloc = $this->lexer->yylloc;
53588 if ($recovering > 0) {
53592 $symbol = $preErrorSymbol;
53593 $preErrorSymbol = null;
53598 $len = $this->productions_[$action[1]][1];
53601 $yyval->token = $this->vstack[count($this->vstack) - $len];
53603 $yyval->store = array(
53604 'first_line' => $this->lstack[count($this->lstack) - ($len ?: 1)]['first_line'],
53605 'last_line' => $this->lstack[count($this->lstack) - 1]['last_line'],
53606 'first_column' => $this->lstack[count($this->lstack) - ($len ?: 1)]['first_column'],
53607 'last_column' => $this->lstack[count($this->lstack) - 1]['last_column'],
53609 $r = $this->performAction($yyval, $yytext, $yyleng, $yylineno, $action[1], $this->vstack, $this->lstack);
53611 if (!$r instanceof Undefined) {
53616 $this->popStack($len);
53619 $this->stack[] = $this->productions_[$action[1]][0];
53620 $this->vstack[] = $yyval->token;
53621 $this->lstack[] = $yyval->store;
53622 $newState = $this->table[$this->stack[count($this->stack)-2]][$this->stack[count($this->stack)-1]];
53623 $this->stack[] = $newState;
53635 protected function parseError($str, $hash)
53637 throw new ParsingException($str, $hash);
53643 private function performAction(stdClass $yyval, $yytext, $yyleng, $yylineno, $yystate, &$tokens)
53646 $len = count($tokens) - 1;
53647 switch ($yystate) {
53649 $yytext = preg_replace_callback('{(?:\\\\["bfnrt/\\\\]|\\\\u[a-fA-F0-9]{4})}', array($this, 'stringInterpolation'), $yytext);
53650 $yyval->token = $yytext;
53653 if (strpos($yytext, 'e') !== false || strpos($yytext, 'E') !== false) {
53654 $yyval->token = floatval($yytext);
53656 $yyval->token = strpos($yytext, '.') === false ? intval($yytext) : floatval($yytext);
53660 $yyval->token = null;
53663 $yyval->token = true;
53666 $yyval->token = false;
53669 return $yyval->token = $tokens[$len-1];
53671 if ($this->flags & self::PARSE_TO_ASSOC) {
53672 $yyval->token = array();
53674 $yyval->token = new stdClass;
53678 $yyval->token = $tokens[$len-1];
53681 $yyval->token = array($tokens[$len-2], $tokens[$len]);
53684 $property = $tokens[$len][0] === '' ? '_empty_' : $tokens[$len][0];
53685 if ($this->flags & self::PARSE_TO_ASSOC) {
53686 $yyval->token = array();
53687 $yyval->token[$property] = $tokens[$len][1];
53689 $yyval->token = new stdClass;
53690 $yyval->token->$property = $tokens[$len][1];
53694 if ($this->flags & self::PARSE_TO_ASSOC) {
53695 $yyval->token =& $tokens[$len-2];
53696 $key = $tokens[$len][0];
53697 if (($this->flags & self::DETECT_KEY_CONFLICTS) && isset($tokens[$len-2][$key])) {
53698 $errStr = 'Parse error on line ' . ($yylineno+1) . ":\n";
53699 $errStr .= $this->lexer->showPosition() . "\n";
53700 $errStr .= "Duplicate key: ".$tokens[$len][0];
53701 throw new ParsingException($errStr);
53702 } elseif (($this->flags & self::ALLOW_DUPLICATE_KEYS) && isset($tokens[$len-2][$key])) {
53703 $duplicateCount = 1;
53705 $duplicateKey = $key . '.' . $duplicateCount++;
53706 } while (isset($tokens[$len-2][$duplicateKey]));
53707 $key = $duplicateKey;
53709 $tokens[$len-2][$key] = $tokens[$len][1];
53711 $yyval->token = $tokens[$len-2];
53712 $key = $tokens[$len][0] === '' ? '_empty_' : $tokens[$len][0];
53713 if (($this->flags & self::DETECT_KEY_CONFLICTS) && isset($tokens[$len-2]->{$key})) {
53714 $errStr = 'Parse error on line ' . ($yylineno+1) . ":\n";
53715 $errStr .= $this->lexer->showPosition() . "\n";
53716 $errStr .= "Duplicate key: ".$tokens[$len][0];
53717 throw new ParsingException($errStr);
53718 } elseif (($this->flags & self::ALLOW_DUPLICATE_KEYS) && isset($tokens[$len-2]->{$key})) {
53719 $duplicateCount = 1;
53721 $duplicateKey = $key . '.' . $duplicateCount++;
53722 } while (isset($tokens[$len-2]->$duplicateKey));
53723 $key = $duplicateKey;
53725 $tokens[$len-2]->$key = $tokens[$len][1];
53729 $yyval->token = array();
53732 $yyval->token = $tokens[$len-1];
53735 $yyval->token = array($tokens[$len]);
53738 $tokens[$len-2][] = $tokens[$len];
53739 $yyval->token = $tokens[$len-2];
53743 return new Undefined();
53746 private function stringInterpolation($match)
53748 switch ($match[0]) {
53766 return html_entity_decode('&#x'.ltrim(substr($match[0], 2), '0').';', 0, 'UTF-8');
53770 private function popStack($n)
53772 $this->stack = array_slice($this->stack, 0, - (2 * $n));
53773 $this->vstack = array_slice($this->vstack, 0, - $n);
53774 $this->lstack = array_slice($this->lstack, 0, - $n);
53777 private function lex()
53779 $token = $this->lexer->lex() ?: 1;
53781 if (!is_numeric($token)) {
53782 $token = isset($this->symbols[$token]) ? $this->symbols[$token] : $token;
53788 private function failOnBOM($input)
53791 $bom = "\xEF\xBB\xBF";
53793 if (substr($input, 0, 3) === $bom) {
53794 $this->parseError("BOM detected, make sure your input does not include a Unicode Byte-Order-Mark", array());
53809 namespace Seld\JsonLint;
53819 private $rules = array(
53821 1 => '/^-?([0-9]|[1-9][0-9]+)(\.[0-9]+)?([eE][+-]?[0-9]+)?\b/',
53822 2 => '{^"(\\\\["bfnrt/\\\\]|\\\\u[a-fA-F0-9]{4}|[^\0-\x09\x0a-\x1f\\\\"])*"}',
53830 10 => '/^false\b/',
53836 private $conditions = array(
53837 "INITIAL" => array(
53838 "rules" => array(0,1,2,3,4,5,6,7,8,9,10,11,12,13),
53839 "inclusive" => true,
53843 private $conditionStack;
53855 public function lex()
53857 $r = $this->next();
53858 if (!$r instanceof Undefined) {
53862 return $this->lex();
53865 public function setInput($input)
53867 $this->input = $input;
53868 $this->more = false;
53869 $this->done = false;
53870 $this->yylineno = $this->yyleng = 0;
53871 $this->yytext = $this->matched = $this->match = '';
53872 $this->conditionStack = array('INITIAL');
53873 $this->yylloc = array('first_line' => 1, 'first_column' => 0, 'last_line' => 1, 'last_column' => 0);
53878 public function showPosition()
53880 $pre = str_replace("\n", '', $this->getPastInput());
53881 $c = str_repeat('-', max(0, strlen($pre) - 1));
53883 return $pre . str_replace("\n", '', $this->getUpcomingInput()) . "\n" . $c . "^";
53886 public function getPastInput()
53888 $past = substr($this->matched, 0, strlen($this->matched) - strlen($this->match));
53890 return (strlen($past) > 20 ? '...' : '') . substr($past, -20);
53893 public function getUpcomingInput()
53895 $next = $this->match;
53896 if (strlen($next) < 20) {
53897 $next .= substr($this->input, 0, 20 - strlen($next));
53900 return substr($next, 0, 20) . (strlen($next) > 20 ? '...' : '');
53903 protected function parseError($str, $hash)
53905 throw new \Exception($str);
53908 private function next()
53913 if (!$this->input) {
53914 $this->done = true;
53922 if (!$this->more) {
53923 $this->yytext = '';
53927 $rules = $this->getCurrentRules();
53928 $rulesLen = count($rules);
53930 for ($i=0; $i < $rulesLen; $i++) {
53931 if (preg_match($this->rules[$rules[$i]], $this->input, $match)) {
53932 preg_match_all('/\n.*/', $match[0], $lines);
53933 $lines = $lines[0];
53935 $this->yylineno += count($lines);
53938 $this->yylloc = array(
53939 'first_line' => $this->yylloc['last_line'],
53940 'last_line' => $this->yylineno+1,
53941 'first_column' => $this->yylloc['last_column'],
53942 'last_column' => $lines ? strlen($lines[count($lines) - 1]) - 1 : $this->yylloc['last_column'] + strlen($match[0]),
53944 $this->yytext .= $match[0];
53945 $this->match .= $match[0];
53946 $this->yyleng = strlen($this->yytext);
53947 $this->more = false;
53948 $this->input = substr($this->input, strlen($match[0]));
53949 $this->matched .= $match[0];
53950 $token = $this->performAction($rules[$i], $this->conditionStack[count($this->conditionStack)-1]);
53955 return new Undefined();
53959 if ($this->input === "") {
53964 'Lexical error on line ' . ($this->yylineno+1) . ". Unrecognized text.\n" . $this->showPosition(),
53968 'line' => $this->yylineno,
53973 private function getCurrentRules()
53975 return $this->conditions[$this->conditionStack[count($this->conditionStack)-1]]['rules'];
53978 private function performAction($avoiding_name_collisions, $YY_START)
53980 switch ($avoiding_name_collisions) {
53987 $this->yytext = substr($this->yytext, 1, $this->yyleng-2);
54026 namespace Seld\JsonLint;
54028 class ParsingException extends \Exception
54030 protected $details;
54032 public function __construct($message, $details = array())
54034 $this->details = $details;
54035 parent::__construct($message);
54038 public function getDetails()
54040 return $this->details;
54052 namespace JsonSchema;
54054 use JsonSchema\Uri\Retrievers\UriRetrieverInterface;
54055 use JsonSchema\Uri\UriRetriever;
54072 protected static $depth = 0;
54078 public static $maxDepth = 7;
54083 protected $uriRetriever = null;
54088 public function __construct($retriever = null)
54090 $this->uriRetriever = $retriever;
54100 public function fetchRef($ref, $sourceUri)
54102 $retriever = $this->getUriRetriever();
54103 $jsonSchema = $retriever->retrieve($ref, $sourceUri);
54104 $this->resolve($jsonSchema);
54106 return $jsonSchema;
54115 public function getUriRetriever()
54117 if (is_null($this->uriRetriever)) {
54118 $this->setUriRetriever(new UriRetriever);
54121 return $this->uriRetriever;
54138 public function resolve($schema, $sourceUri = null)
54140 if (self::$depth > self::$maxDepth) {
54145 if (! is_object($schema)) {
54150 if (null === $sourceUri && ! empty($schema->id)) {
54151 $sourceUri = $schema->id;
54155 $this->resolveRef($schema, $sourceUri);
54159 foreach (array('additionalItems', 'additionalProperties', 'extends', 'items') as $propertyName) {
54160 $this->resolveProperty($schema, $propertyName, $sourceUri);
54166 foreach (array('disallow', 'extends', 'items', 'type', 'allOf', 'anyOf', 'oneOf') as $propertyName) {
54167 $this->resolveArrayOfSchemas($schema, $propertyName, $sourceUri);
54171 foreach (array('dependencies', 'patternProperties', 'properties') as $propertyName) {
54172 $this->resolveObjectOfSchemas($schema, $propertyName, $sourceUri);
54186 public function resolveArrayOfSchemas($schema, $propertyName, $sourceUri)
54188 if (! isset($schema->$propertyName) || ! is_array($schema->$propertyName)) {
54192 foreach ($schema->$propertyName as $possiblySchema) {
54193 $this->resolve($possiblySchema, $sourceUri);
54205 public function resolveObjectOfSchemas($schema, $propertyName, $sourceUri)
54207 if (! isset($schema->$propertyName) || ! is_object($schema->$propertyName)) {
54211 foreach (get_object_vars($schema->$propertyName) as $possiblySchema) {
54212 $this->resolve($possiblySchema, $sourceUri);
54224 public function resolveProperty($schema, $propertyName, $sourceUri)
54226 if (! isset($schema->$propertyName)) {
54230 $this->resolve($schema->$propertyName, $sourceUri);
54241 public function resolveRef($schema, $sourceUri)
54245 if (empty($schema->$ref)) {
54249 $refSchema = $this->fetchRef($schema->$ref, $sourceUri);
54250 unset($schema->$ref);
54253 foreach (get_object_vars($refSchema) as $prop => $value) {
54254 $schema->$prop = $value;
54264 public function setUriRetriever(UriRetriever $retriever)
54266 $this->uriRetriever = $retriever;
54280 namespace JsonSchema\Constraints;
54282 use JsonSchema\Exception\InvalidArgumentException;
54283 use JsonSchema\Uri\UriResolver;
54291 class Undefined extends Constraint
54296 public function check($value, $schema = null, $path = null, $i = null)
54298 if (is_null($schema)) {
54302 if (!is_object($schema)) {
54303 throw new InvalidArgumentException(
54304 'Given schema must be an object in ' . $path
54305 . ' but is a ' . gettype($schema)
54309 $i = is_null($i) ? "" : $i;
54310 $path = $this->incrementPath($path, $i);
54313 $this->validateCommonProperties($value, $schema, $path);
54316 $this->validateOfProperties($value, $schema, $path);
54319 $this->validateTypes($value, $schema, $path, $i);
54330 public function validateTypes($value, $schema = null, $path = null, $i = null)
54333 if (is_array($value)) {
54334 $this->checkArray($value, $schema, $path, $i);
54338 if (is_object($value) && (isset($schema->properties) || isset($schema->patternProperties))) {
54339 $this->checkObject(
54341 isset($schema->properties) ? $schema->properties : null,
54343 isset($schema->additionalProperties) ? $schema->additionalProperties : null,
54344 isset($schema->patternProperties) ? $schema->patternProperties : null
54349 if (is_string($value)) {
54350 $this->checkString($value, $schema, $path, $i);
54354 if (is_numeric($value)) {
54355 $this->checkNumber($value, $schema, $path, $i);
54359 if (isset($schema->enum)) {
54360 $this->checkEnum($value, $schema, $path, $i);
54372 protected function validateCommonProperties($value, $schema = null, $path = null, $i = "")
54375 if (isset($schema->extends)) {
54376 if (is_string($schema->extends)) {
54377 $schema->extends = $this->validateUri($schema, $schema->extends);
54379 if (is_array($schema->extends)) {
54380 foreach ($schema->extends as $extends) {
54381 $this->checkUndefined($value, $extends, $path, $i);
54384 $this->checkUndefined($value, $schema->extends, $path, $i);
54389 if (is_object($value)) {
54391 if (!($value instanceof Undefined) && isset($schema->required) && is_array($schema->required) ) {
54393 foreach ($schema->required as $required) {
54394 if (!property_exists($value, $required)) {
54395 $this->addError($path, "the property " . $required . " is required");
54398 } else if (isset($schema->required) && !is_array($schema->required)) {
54400 if ( $schema->required && $value instanceof Undefined) {
54401 $this->addError($path, "is missing and it is required");
54407 if (!($value instanceof Undefined)) {
54408 $this->checkType($value, $schema, $path);
54412 if (isset($schema->disallow)) {
54413 $initErrors = $this->getErrors();
54415 $typeSchema = new \stdClass();
54416 $typeSchema->type = $schema->disallow;
54417 $this->checkType($value, $typeSchema, $path);
54420 if (count($this->getErrors()) == count($initErrors)) {
54421 $this->addError($path, "disallowed value was matched");
54423 $this->errors = $initErrors;
54427 if (isset($schema->not)) {
54428 $initErrors = $this->getErrors();
54429 $this->checkUndefined($value, $schema->not, $path, $i);
54432 if (count($this->getErrors()) == count($initErrors)) {
54433 $this->addError($path, "matched a schema which it should not");
54435 $this->errors = $initErrors;
54440 if (is_object($value)) {
54441 if (isset($schema->minProperties)) {
54442 if (count(get_object_vars($value)) < $schema->minProperties) {
54443 $this->addError($path, "must contain a minimum of " . $schema->minProperties . " properties");
54446 if (isset($schema->maxProperties)) {
54447 if (count(get_object_vars($value)) > $schema->maxProperties) {
54448 $this->addError($path, "must contain no more than " . $schema->maxProperties . " properties");
54454 if (is_object($value) && isset($schema->dependencies)) {
54455 $this->validateDependencies($value, $schema->dependencies, $path);
54467 protected function validateOfProperties($value, $schema, $path, $i = "")
54470 if ($value instanceof Undefined) {
54474 if (isset($schema->allOf)) {
54476 foreach ($schema->allOf as $allOf) {
54477 $initErrors = $this->getErrors();
54478 $this->checkUndefined($value, $allOf, $path, $i);
54479 $isValid = $isValid && (count($this->getErrors()) == count($initErrors));
54482 $this->addError($path, "failed to match all schemas");
54486 if (isset($schema->anyOf)) {
54488 $startErrors = $this->getErrors();
54489 foreach ($schema->anyOf as $anyOf) {
54490 $initErrors = $this->getErrors();
54491 $this->checkUndefined($value, $anyOf, $path, $i);
54492 if ($isValid = (count($this->getErrors()) == count($initErrors))) {
54497 $this->addError($path, "failed to match at least one schema");
54499 $this->errors = $startErrors;
54503 if (isset($schema->oneOf)) {
54504 $allErrors = array();
54505 $matchedSchemas = 0;
54506 $startErrors = $this->getErrors();
54507 foreach ($schema->oneOf as $oneOf) {
54508 $this->errors = array();
54509 $this->checkUndefined($value, $oneOf, $path, $i);
54510 if (count($this->getErrors()) == 0) {
54513 $allErrors = array_merge($allErrors, array_values($this->getErrors()));
54515 if ($matchedSchemas !== 1) {
54520 'property' => $path,
54521 'message' => "failed to match exactly one schema"
54527 $this->errors = $startErrors;
54540 protected function validateDependencies($value, $dependencies, $path, $i = "")
54542 foreach ($dependencies as $key => $dependency) {
54543 if (property_exists($value, $key)) {
54544 if (is_string($dependency)) {
54546 if (!property_exists($value, $dependency)) {
54547 $this->addError($path, "$key depends on $dependency and $dependency is missing");
54549 } else if (is_array($dependency)) {
54551 foreach ($dependency as $d) {
54552 if (!property_exists($value, $d)) {
54553 $this->addError($path, "$key depends on $d and $d is missing");
54556 } else if (is_object($dependency)) {
54558 $this->checkUndefined($value, $dependency, $path, $i);
54564 protected function validateUri($schema, $schemaUri = null)
54566 $resolver = new UriResolver();
54567 $retriever = $this->getUriRetriever();
54569 $jsonSchema = null;
54570 if ($resolver->isValid($schemaUri)) {
54571 $schemaId = property_exists($schema, 'id') ? $schema->id : null;
54572 $jsonSchema = $retriever->retrieve($schemaId, $schemaUri);
54575 return $jsonSchema;
54587 namespace JsonSchema\Constraints;
54589 use JsonSchema\Exception\InvalidArgumentException;
54590 use UnexpectedValueException as StandardUnexpectedValueException;
54598 class Type extends Constraint
54603 static $wording = array(
54604 'integer' => 'an integer',
54605 'number' => 'a number',
54606 'boolean' => 'a boolean',
54607 'object' => 'an object',
54608 'array' => 'an array',
54609 'string' => 'a string',
54610 'null' => 'a null',
54618 public function check($value = null, $schema = null, $path = null, $i = null)
54620 $type = isset($schema->type) ? $schema->type : null;
54623 if (is_array($type)) {
54625 $validatedOneType = false;
54627 foreach ($type as $tp) {
54628 $validator = new Type($this->checkMode);
54629 $subSchema = new \stdClass();
54630 $subSchema->type = $tp;
54631 $validator->check($value, $subSchema, $path, null);
54632 $error = $validator->getErrors();
54634 if (!count($error)) {
54635 $validatedOneType = true;
54642 if (!$validatedOneType) {
54643 return $this->addErrors($errors);
54645 } elseif (is_object($type)) {
54646 $this->checkUndefined($value, $type, $path);
54648 $isValid = $this->validateType($value, $type);
54651 if ($isValid === false) {
54652 if (!isset(self::$wording[$type])) {
54653 throw new StandardUnexpectedValueException(
54655 "No wording for %s available, expected wordings are: [%s]",
54656 var_export($type, true),
54657 implode(', ', array_filter(self::$wording)))
54660 $this->addError($path, gettype($value) . " value found, but " . self::$wording[$type] . " is required");
54674 protected function validateType($value, $type)
54681 if ('integer' === $type) {
54682 return is_int($value);
54685 if ('number' === $type) {
54686 return is_numeric($value) && !is_string($value);
54689 if ('boolean' === $type) {
54690 return is_bool($value);
54693 if ('object' === $type) {
54694 return is_object($value);
54698 if ('array' === $type) {
54699 return is_array($value);
54702 if ('string' === $type) {
54703 return is_string($value);
54706 if ('null' === $type) {
54707 return is_null($value);
54710 if ('any' === $type) {
54714 throw new InvalidArgumentException((is_object($value) ? 'object' : $value) . ' is an invalid type for ' . $type);
54725 namespace JsonSchema\Constraints;
54727 use JsonSchema\Exception\InvalidArgumentException;
54735 class Schema extends Constraint
54740 public function check($element, $schema = null, $path = null, $i = null)
54742 if ($schema !== null) {
54744 $this->checkUndefined($element, $schema, '', '');
54745 } elseif (property_exists($element, $this->inlineSchemaProperty)) {
54747 $this->checkUndefined($element, $element->{$this->inlineSchemaProperty}, '', '');
54749 throw new InvalidArgumentException('no schema found to verify against');
54761 namespace JsonSchema\Constraints;
54769 class Number extends Constraint
54774 public function check($element, $schema = null, $path = null, $i = null)
54777 if (isset($schema->exclusiveMinimum)) {
54778 if (isset($schema->minimum)) {
54779 if ($schema->exclusiveMinimum && $element === $schema->minimum) {
54780 $this->addError($path, "must have a minimum value greater than boundary value of " . $schema->minimum);
54781 } else if ($element < $schema->minimum) {
54782 $this->addError($path, "must have a minimum value of " . $schema->minimum);
54785 $this->addError($path, "use of exclusiveMinimum requires presence of minimum");
54787 } else if (isset($schema->minimum) && $element < $schema->minimum) {
54788 $this->addError($path, "must have a minimum value of " . $schema->minimum);
54792 if (isset($schema->exclusiveMaximum)) {
54793 if (isset($schema->maximum)) {
54794 if ($schema->exclusiveMaximum && $element === $schema->maximum) {
54795 $this->addError($path, "must have a maximum value less than boundary value of " . $schema->maximum);
54796 } else if ($element > $schema->maximum) {
54797 $this->addError($path, "must have a maximum value of " . $schema->maximum);
54800 $this->addError($path, "use of exclusiveMaximum requires presence of maximum");
54802 } else if (isset($schema->maximum) && $element > $schema->maximum) {
54803 $this->addError($path, "must have a maximum value of " . $schema->maximum);
54807 if (isset($schema->divisibleBy) && $this->fmod($element, $schema->divisibleBy) != 0) {
54808 $this->addError($path, "is not divisible by " . $schema->divisibleBy);
54812 if (isset($schema->multipleOf) && $this->fmod($element, $schema->multipleOf) != 0) {
54813 $this->addError($path, "must be a multiple of " . $schema->multipleOf);
54816 $this->checkFormat($element, $schema, $path, $i);
54819 private function fmod($number1, $number2)
54821 $modulus = fmod($number1, $number2);
54822 $precision = abs(0.0000000001);
54823 $diff = (float)($modulus - $number2);
54825 if (-$precision < $diff && $diff < $precision) {
54829 $decimals1 = mb_strpos($number1, ".") ? mb_strlen($number1) - mb_strpos($number1, ".") - 1 : 0;
54830 $decimals2 = mb_strpos($number2, ".") ? mb_strlen($number2) - mb_strpos($number2, ".") - 1 : 0;
54832 return (float)round($modulus, max($decimals1, $decimals2));
54844 namespace JsonSchema\Constraints;
54852 class Object extends Constraint
54857 function check($element, $definition = null, $path = null, $additionalProp = null, $patternProperties = null)
54859 if ($element instanceof Undefined) {
54863 $matches = array();
54864 if ($patternProperties) {
54865 $matches = $this->validatePatternProperties($element, $path, $patternProperties);
54870 $this->validateDefinition($element, $definition, $path);
54874 $this->validateElement($element, $matches, $definition, $path, $additionalProp);
54877 public function validatePatternProperties($element, $path, $patternProperties)
54879 $matches = array();
54880 foreach ($patternProperties as $pregex => $schema) {
54882 if (@preg_match('/'. $pregex . '/', '') === false) {
54883 $this->addError($path, 'The pattern "' . $pregex . '" is invalid');
54886 foreach ($element as $i => $value) {
54887 if (preg_match('/' . $pregex . '/', $i)) {
54889 $this->checkUndefined($value, $schema ? : new \stdClass(), $path, $i);
54905 public function validateElement($element, $matches, $objectDefinition = null, $path = null, $additionalProp = null)
54907 foreach ($element as $i => $value) {
54909 $property = $this->getProperty($element, $i, new Undefined());
54910 $definition = $this->getProperty($objectDefinition, $i);
54913 if (!in_array($i, $matches) && $additionalProp === false && $this->inlineSchemaProperty !== $i && !$definition) {
54914 $this->addError($path, "The property " . $i . " is not defined and the definition does not allow additional properties");
54918 if (!in_array($i, $matches) && $additionalProp && !$definition) {
54919 if ($additionalProp === true) {
54920 $this->checkUndefined($value, null, $path, $i);
54922 $this->checkUndefined($value, $additionalProp, $path, $i);
54927 $require = $this->getProperty($definition, 'requires');
54928 if ($require && !$this->getProperty($element, $require)) {
54929 $this->addError($path, "the presence of the property " . $i . " requires that " . $require . " also be present");
54932 if (!$definition) {
54934 $this->checkUndefined($value, new \stdClass(), $path, $i);
54946 public function validateDefinition($element, $objectDefinition = null, $path = null)
54948 foreach ($objectDefinition as $i => $value) {
54949 $property = $this->getProperty($element, $i, new Undefined());
54950 $definition = $this->getProperty($objectDefinition, $i);
54951 $this->checkUndefined($property, $definition, $path, $i);
54964 protected function getProperty($element, $property, $fallback = null)
54966 if (is_array($element) ) {
54967 return array_key_exists($property, $element) ? $element[$property] : $fallback;
54968 } elseif (is_object($element)) {
54969 return property_exists($element, $property) ? $element->$property : $fallback;
54983 namespace JsonSchema\Constraints;
54990 interface ConstraintInterface
54997 public function getErrors();
55004 public function addErrors(array $errors);
55012 public function addError($path, $message);
55019 public function isValid();
55030 public function check($value, $schema = null, $path = null, $i = null);
55040 namespace JsonSchema\Constraints;
55042 use JsonSchema\Uri\UriRetriever;
55050 abstract class Constraint implements ConstraintInterface
55052 protected $checkMode = self::CHECK_MODE_NORMAL;
55053 protected $uriRetriever;
55054 protected $errors = array();
55055 protected $inlineSchemaProperty = '$schema';
55057 const CHECK_MODE_NORMAL = 1;
55058 const CHECK_MODE_TYPE_CAST = 2;
55064 public function __construct($checkMode = self::CHECK_MODE_NORMAL, UriRetriever $uriRetriever = null)
55066 $this->checkMode = $checkMode;
55067 $this->uriRetriever = $uriRetriever;
55073 public function getUriRetriever()
55075 if (is_null($this->uriRetriever))
55077 $this->setUriRetriever(new UriRetriever);
55080 return $this->uriRetriever;
55086 public function setUriRetriever(UriRetriever $uriRetriever)
55088 $this->uriRetriever = $uriRetriever;
55094 public function addError($path, $message)
55096 $this->errors[] = array(
55097 'property' => $path,
55098 'message' => $message
55105 public function addErrors(array $errors)
55107 $this->errors = array_merge($this->errors, $errors);
55113 public function getErrors()
55115 return $this->errors;
55121 public function isValid()
55123 return !$this->getErrors();
55130 public function reset()
55132 $this->errors = array();
55143 protected function incrementPath($path, $i)
55145 if ($path !== '') {
55147 $path .= '[' . $i . ']';
55148 } elseif ($i == '') {
55168 protected function checkArray($value, $schema = null, $path = null, $i = null)
55170 $validator = new Collection($this->checkMode, $this->uriRetriever);
55171 $validator->check($value, $schema, $path, $i);
55173 $this->addErrors($validator->getErrors());
55185 protected function checkObject($value, $schema = null, $path = null, $i = null, $patternProperties = null)
55187 $validator = new Object($this->checkMode, $this->uriRetriever);
55188 $validator->check($value, $schema, $path, $i, $patternProperties);
55190 $this->addErrors($validator->getErrors());
55201 protected function checkType($value, $schema = null, $path = null, $i = null)
55203 $validator = new Type($this->checkMode, $this->uriRetriever);
55204 $validator->check($value, $schema, $path, $i);
55206 $this->addErrors($validator->getErrors());
55217 protected function checkUndefined($value, $schema = null, $path = null, $i = null)
55219 $validator = new Undefined($this->checkMode, $this->uriRetriever);
55220 $validator->check($value, $schema, $path, $i);
55222 $this->addErrors($validator->getErrors());
55233 protected function checkString($value, $schema = null, $path = null, $i = null)
55235 $validator = new String($this->checkMode, $this->uriRetriever);
55236 $validator->check($value, $schema, $path, $i);
55238 $this->addErrors($validator->getErrors());
55249 protected function checkNumber($value, $schema = null, $path = null, $i = null)
55251 $validator = new Number($this->checkMode, $this->uriRetriever);
55252 $validator->check($value, $schema, $path, $i);
55254 $this->addErrors($validator->getErrors());
55265 protected function checkEnum($value, $schema = null, $path = null, $i = null)
55267 $validator = new Enum($this->checkMode, $this->uriRetriever);
55268 $validator->check($value, $schema, $path, $i);
55270 $this->addErrors($validator->getErrors());
55273 protected function checkFormat($value, $schema = null, $path = null, $i = null)
55275 $validator = new Format($this->checkMode, $this->uriRetriever);
55276 $validator->check($value, $schema, $path, $i);
55278 $this->addErrors($validator->getErrors());
55285 protected function retrieveUri($uri)
55287 if (null === $this->uriRetriever) {
55288 $this->setUriRetriever(new UriRetriever);
55290 $jsonSchema = $this->uriRetriever->retrieve($uri);
55292 return $jsonSchema;
55304 namespace JsonSchema\Constraints;
55312 class Collection extends Constraint
55317 public function check($value, $schema = null, $path = null, $i = null)
55320 if (isset($schema->minItems) && count($value) < $schema->minItems) {
55321 $this->addError($path, "There must be a minimum of " . $schema->minItems . " in the array");
55325 if (isset($schema->maxItems) && count($value) > $schema->maxItems) {
55326 $this->addError($path, "There must be a maximum of " . $schema->maxItems . " in the array");
55330 if (isset($schema->uniqueItems)) {
55332 if (is_array($value) && count($value)) {
55333 $unique = array_map(function($e) { return var_export($e, true); }, $value);
55335 if (count(array_unique($unique)) != count($value)) {
55336 $this->addError($path, "There are no duplicates allowed in the array");
55341 if (isset($schema->items)) {
55342 $this->validateItems($value, $schema, $path, $i);
55354 protected function validateItems($value, $schema = null, $path = null, $i = null)
55356 if (is_object($schema->items)) {
55358 foreach ($value as $k => $v) {
55359 $initErrors = $this->getErrors();
55362 $this->checkUndefined($v, $schema->items, $path, $k);
55365 if (count($initErrors) < count($this->getErrors()) && (isset($schema->additionalItems) && $schema->additionalItems !== false)) {
55366 $secondErrors = $this->getErrors();
55367 $this->checkUndefined($v, $schema->additionalItems, $path, $k);
55371 if (isset($secondErrors) && count($secondErrors) < count($this->getErrors())) {
55372 $this->errors = $secondErrors;
55373 } else if (isset($secondErrors) && count($secondErrors) === count($this->getErrors())) {
55374 $this->errors = $initErrors;
55379 foreach ($value as $k => $v) {
55380 if (array_key_exists($k, $schema->items)) {
55381 $this->checkUndefined($v, $schema->items[$k], $path, $k);
55384 if (property_exists($schema, 'additionalItems')) {
55385 if ($schema->additionalItems !== false) {
55386 $this->checkUndefined($v, $schema->additionalItems, $path, $k);
55389 $path, 'The item ' . $i . '[' . $k . '] is not defined and the definition does not allow additional items');
55393 $this->checkUndefined($v, new \stdClass(), $path, $k);
55399 if(count($value) > 0) {
55400 for ($k = count($value); $k < count($schema->items); $k++) {
55401 $this->checkUndefined(new Undefined(), $schema->items[$k], $path, $k);
55415 namespace JsonSchema\Constraints;
55423 class Enum extends Constraint
55428 public function check($element, $schema = null, $path = null, $i = null)
55431 if ($element instanceof Undefined && (!isset($schema->required) || !$schema->required)) {
55435 foreach ($schema->enum as $enum) {
55436 if ((gettype($element) === gettype($enum)) && ($element == $enum)) {
55441 $this->addError($path, "does not have a value in the enumeration " . print_r($schema->enum, true));
55452 namespace JsonSchema\Constraints;
55460 class Format extends Constraint
55465 public function check($element, $schema = null, $path = null, $i = null)
55467 if (!isset($schema->format)) {
55471 switch ($schema->format) {
55473 if (!$date = $this->validateDateTime($element, 'Y-m-d')) {
55474 $this->addError($path, sprintf('Invalid date %s, expected format YYYY-MM-DD', json_encode($element)));
55479 if (!$this->validateDateTime($element, 'H:i:s')) {
55480 $this->addError($path, sprintf('Invalid time %s, expected format hh:mm:ss', json_encode($element)));
55485 if (!$this->validateDateTime($element, 'Y-m-d\TH:i:s\Z') &&
55486 !$this->validateDateTime($element, 'Y-m-d\TH:i:s.u\Z') &&
55487 !$this->validateDateTime($element, 'Y-m-d\TH:i:sP') &&
55488 !$this->validateDateTime($element, 'Y-m-d\TH:i:sO')
55490 $this->addError($path, sprintf('Invalid date-time %s, expected format YYYY-MM-DDThh:mm:ssZ or YYYY-MM-DDThh:mm:ss+hh:mm', json_encode($element)));
55494 case 'utc-millisec':
55495 if (!$this->validateDateTime($element, 'U')) {
55496 $this->addError($path, sprintf('Invalid time %s, expected integer of milliseconds since Epoch', json_encode($element)));
55501 if (!$this->validateRegex($element)) {
55502 $this->addError($path, 'Invalid regex format ' . $element);
55507 if (!$this->validateColor($element)) {
55508 $this->addError($path, "Invalid color");
55513 if (!$this->validateStyle($element)) {
55514 $this->addError($path, "Invalid style");
55519 if (!$this->validatePhone($element)) {
55520 $this->addError($path, "Invalid phone number");
55525 if (null === filter_var($element, FILTER_VALIDATE_URL, FILTER_NULL_ON_FAILURE)) {
55526 $this->addError($path, "Invalid URL format");
55531 if (null === filter_var($element, FILTER_VALIDATE_EMAIL, FILTER_NULL_ON_FAILURE)) {
55532 $this->addError($path, "Invalid email");
55538 if (null === filter_var($element, FILTER_VALIDATE_IP, FILTER_NULL_ON_FAILURE | FILTER_FLAG_IPV4)) {
55539 $this->addError($path, "Invalid IP address");
55544 if (null === filter_var($element, FILTER_VALIDATE_IP, FILTER_NULL_ON_FAILURE | FILTER_FLAG_IPV6)) {
55545 $this->addError($path, "Invalid IP address");
55551 if (!$this->validateHostname($element)) {
55552 $this->addError($path, "Invalid hostname");
55557 $this->addError($path, "Unknown format: " . json_encode($schema->format));
55562 protected function validateDateTime($datetime, $format)
55564 $dt = \DateTime::createFromFormat($format, $datetime);
55570 return $datetime === $dt->format($format);
55573 protected function validateRegex($regex)
55575 return false !== @preg_match('/' . $regex . '/', '');
55578 protected function validateColor($color)
55580 if (in_array(strtolower($color), array('aqua', 'black', 'blue', 'fuchsia',
55581 'gray', 'green', 'lime', 'maroon', 'navy', 'olive', 'orange', 'purple',
55582 'red', 'silver', 'teal', 'white', 'yellow'))) {
55586 return preg_match('/^#([a-f0-9]{3}|[a-f0-9]{6})$/i', $color);
55589 protected function validateStyle($style)
55591 $properties = explode(';', rtrim($style, ';'));
55592 $invalidEntries = preg_grep('/^\s*[-a-z]+\s*:\s*.+$/i', $properties, PREG_GREP_INVERT);
55594 return empty($invalidEntries);
55597 protected function validatePhone($phone)
55599 return preg_match('/^\+?(\(\d{3}\)|\d{3}) \d{3} \d{4}$/', $phone);
55602 protected function validateHostname($host)
55604 return preg_match('/^[_a-z]+\.([_a-z]+\.?)+$/i', $host);
55616 namespace JsonSchema\Constraints;
55624 class String extends Constraint
55629 public function check($element, $schema = null, $path = null, $i = null)
55632 if (isset($schema->maxLength) && $this->strlen($element) > $schema->maxLength) {
55633 $this->addError($path, "must be at most " . $schema->maxLength . " characters long");
55637 if (isset($schema->minLength) && $this->strlen($element) < $schema->minLength) {
55638 $this->addError($path, "must be at least " . $schema->minLength . " characters long");
55642 if (isset($schema->pattern) && !preg_match('#' . str_replace('#', '\\#', $schema->pattern) . '#', $element)) {
55643 $this->addError($path, "does not match the regex pattern " . $schema->pattern);
55646 $this->checkFormat($element, $schema, $path, $i);
55649 private function strlen($string)
55651 if (extension_loaded('mbstring')) {
55652 return mb_strlen($string, mb_detect_encoding($string));
55654 return strlen($string);
55667 namespace JsonSchema\Exception;
55672 class UriResolverException extends \RuntimeException
55683 namespace JsonSchema\Exception;
55688 class ResourceNotFoundException extends \RuntimeException
55699 namespace JsonSchema\Exception;
55704 class InvalidSchemaMediaTypeException extends \RuntimeException
55715 namespace JsonSchema\Exception;
55720 class InvalidSourceUriException extends InvalidArgumentException
55732 namespace JsonSchema\Exception;
55737 class JsonDecodingException extends \RuntimeException
55739 public function __construct($code = JSON_ERROR_NONE, \Exception $previous = null)
55742 case JSON_ERROR_DEPTH:
55743 $message = 'The maximum stack depth has been exceeded';
55745 case JSON_ERROR_STATE_MISMATCH:
55746 $message = 'Invalid or malformed JSON';
55748 case JSON_ERROR_CTRL_CHAR:
55749 $message = 'Control character error, possibly incorrectly encoded';
55751 case JSON_ERROR_UTF8:
55752 $message = 'Malformed UTF-8 characters, possibly incorrectly encoded';
55754 case JSON_ERROR_SYNTAX:
55755 $message = 'JSON syntax is malformed';
55758 $message = 'Syntax error';
55760 parent::__construct($message, $code, $previous);
55771 namespace JsonSchema\Exception;
55776 class InvalidArgumentException extends \InvalidArgumentException
55787 namespace JsonSchema\Uri;
55789 use JsonSchema\Exception\UriResolverException;
55804 public function parse($uri)
55806 preg_match('|^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?|', $uri, $match);
55808 $components = array();
55809 if (5 < count($match)) {
55810 $components = array(
55811 'scheme' => $match[2],
55812 'authority' => $match[4],
55813 'path' => $match[5]
55816 if (7 < count($match)) {
55817 $components['query'] = $match[7];
55819 if (9 < count($match)) {
55820 $components['fragment'] = $match[9];
55823 return $components;
55832 public function generate(array $components)
55834 $uri = $components['scheme'] . '://'
55835 . $components['authority']
55836 . $components['path'];
55838 if (array_key_exists('query', $components)) {
55839 $uri .= $components['query'];
55841 if (array_key_exists('fragment', $components)) {
55842 $uri .= '#' . $components['fragment'];
55855 public function resolve($uri, $baseUri = null)
55861 $components = $this->parse($uri);
55862 $path = $components['path'];
55864 if (! empty($components['scheme'])) {
55867 $baseComponents = $this->parse($baseUri);
55868 $basePath = $baseComponents['path'];
55870 $baseComponents['path'] = self::combineRelativePathWithBasePath($path, $basePath);
55871 if (isset($components['fragment'])) {
55872 $baseComponents['fragment'] = $components['fragment'];
55875 return $this->generate($baseComponents);
55886 public static function combineRelativePathWithBasePath($relativePath, $basePath)
55888 $relativePath = self::normalizePath($relativePath);
55889 if ($relativePath == '') {
55892 if ($relativePath{0} == '/') {
55893 return $relativePath;
55896 $basePathSegments = self::getPathSegments($basePath);
55898 preg_match('|^/?(\.\./(?:\./)*)*|', $relativePath, $match);
55899 $numLevelUp = strlen($match[0]) /3 + 1;
55900 if ($numLevelUp >= count($basePathSegments)) {
55901 throw new UriResolverException(sprintf("Unable to resolve URI '%s' from base '%s'", $relativePath, $basePath));
55903 $basePathSegments = array_slice($basePathSegments, 0, -$numLevelUp);
55904 $path = preg_replace('|^/?(\.\./(\./)*)*|', '', $relativePath);
55906 return implode('/', $basePathSegments) . '/' . $path;
55915 private static function normalizePath($path)
55917 $path = preg_replace('|((?<!\.)\./)*|', '', $path);
55918 $path = preg_replace('|//|', '/', $path);
55926 private static function getPathSegments($path) {
55928 return explode('/', $path);
55935 public function isValid($uri)
55937 $components = $this->parse($uri);
55939 return !empty($components);
55951 namespace JsonSchema\Uri;
55953 use JsonSchema\Uri\Retrievers\FileGetContents;
55954 use JsonSchema\Uri\Retrievers\UriRetrieverInterface;
55955 use JsonSchema\Validator;
55956 use JsonSchema\Exception\InvalidSchemaMediaTypeException;
55957 use JsonSchema\Exception\JsonDecodingException;
55966 protected $uriRetriever = null;
55973 public function confirmMediaType($uriRetriever, $uri)
55975 $contentType = $uriRetriever->getContentType();
55977 if (is_null($contentType)) {
55982 if (Validator::SCHEMA_MEDIA_TYPE === $contentType) {
55986 if (substr($uri, 0, 23) == 'http://json-schema.org/') {
55991 throw new InvalidSchemaMediaTypeException(sprintf('Media type %s expected', Validator::SCHEMA_MEDIA_TYPE));
56002 public function getUriRetriever()
56004 if (is_null($this->uriRetriever)) {
56005 $this->setUriRetriever(new FileGetContents);
56008 return $this->uriRetriever;
56024 public function resolvePointer($jsonSchema, $uri)
56026 $resolver = new UriResolver();
56027 $parsed = $resolver->parse($uri);
56028 if (empty($parsed['fragment'])) {
56029 return $jsonSchema;
56032 $path = explode('/', $parsed['fragment']);
56034 $pathElement = array_shift($path);
56035 if (! empty($pathElement)) {
56036 $pathElement = str_replace('~1', '/', $pathElement);
56037 $pathElement = str_replace('~0', '~', $pathElement);
56038 if (! empty($jsonSchema->$pathElement)) {
56039 $jsonSchema = $jsonSchema->$pathElement;
56041 throw new \JsonSchema\Exception\ResourceNotFoundException(
56042 'Fragment "' . $parsed['fragment'] . '" not found'
56047 if (! is_object($jsonSchema)) {
56048 throw new \JsonSchema\Exception\ResourceNotFoundException(
56049 'Fragment part "' . $pathElement . '" is no object '
56056 return $jsonSchema;
56066 public function retrieve($uri, $baseUri = null)
56068 $resolver = new UriResolver();
56069 $resolvedUri = $fetchUri = $resolver->resolve($uri, $baseUri);
56072 $arParts = $resolver->parse($resolvedUri);
56073 if (isset($arParts['fragment'])) {
56074 unset($arParts['fragment']);
56075 $fetchUri = $resolver->generate($arParts);
56078 $jsonSchema = $this->loadSchema($fetchUri);
56081 $jsonSchema = $this->resolvePointer($jsonSchema, $resolvedUri);
56082 $jsonSchema->id = $resolvedUri;
56084 return $jsonSchema;
56095 protected function loadSchema($fetchUri)
56097 if (isset($this->schemaCache[$fetchUri])) {
56098 return $this->schemaCache[$fetchUri];
56101 $uriRetriever = $this->getUriRetriever();
56102 $contents = $this->uriRetriever->retrieve($fetchUri);
56103 $this->confirmMediaType($uriRetriever, $fetchUri);
56104 $jsonSchema = json_decode($contents);
56106 if (JSON_ERROR_NONE < $error = json_last_error()) {
56107 throw new JsonDecodingException($error);
56110 $this->schemaCache[$fetchUri] = $jsonSchema;
56111 return $jsonSchema;
56120 public function setUriRetriever(UriRetrieverInterface $uriRetriever)
56122 $this->uriRetriever = $uriRetriever;
56133 public function parse($uri)
56135 preg_match('|^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?|', $uri, $match);
56137 $components = array();
56138 if (5 < count($match)) {
56139 $components = array(
56140 'scheme' => $match[2],
56141 'authority' => $match[4],
56142 'path' => $match[5]
56146 if (7 < count($match)) {
56147 $components['query'] = $match[7];
56150 if (9 < count($match)) {
56151 $components['fragment'] = $match[9];
56154 return $components;
56163 public function generate(array $components)
56165 $uri = $components['scheme'] . '://'
56166 . $components['authority']
56167 . $components['path'];
56169 if (array_key_exists('query', $components)) {
56170 $uri .= $components['query'];
56173 if (array_key_exists('fragment', $components)) {
56174 $uri .= $components['fragment'];
56187 public function resolve($uri, $baseUri = null)
56189 $components = $this->parse($uri);
56190 $path = $components['path'];
56192 if ((array_key_exists('scheme', $components)) && ('http' === $components['scheme'])) {
56196 $baseComponents = $this->parse($baseUri);
56197 $basePath = $baseComponents['path'];
56199 $baseComponents['path'] = self::combineRelativePathWithBasePath($path, $basePath);
56201 return $this->generate($baseComponents);
56212 private static function combineRelativePathWithBasePath($relativePath, $basePath)
56214 $relativePath = self::normalizePath($relativePath);
56215 $basePathSegments = self::getPathSegments($basePath);
56217 preg_match('|^/?(\.\./(?:\./)*)*|', $relativePath, $match);
56218 $numLevelUp = strlen($match[0]) /3 + 1;
56219 if ($numLevelUp >= count($basePathSegments)) {
56220 throw new \JsonSchema\Exception\UriResolverException(sprintf("Unable to resolve URI '%s' from base '%s'", $relativePath, $basePath));
56223 $basePathSegments = array_slice($basePathSegments, 0, -$numLevelUp);
56224 $path = preg_replace('|^/?(\.\./(\./)*)*|', '', $relativePath);
56226 return implode('/', $basePathSegments) . '/' . $path;
56235 private static function normalizePath($path)
56237 $path = preg_replace('|((?<!\.)\./)*|', '', $path);
56238 $path = preg_replace('|//|', '/', $path);
56246 private static function getPathSegments($path)
56248 return explode('/', $path);
56255 public function isValid($uri)
56257 $components = $this->parse($uri);
56259 return !empty($components);
56267 namespace JsonSchema\Uri\Retrievers;
56274 abstract class AbstractRetriever implements UriRetrieverInterface
56280 protected $contentType;
56286 public function getContentType()
56288 return $this->contentType;
56300 namespace JsonSchema\Uri\Retrievers;
56302 use JsonSchema\Exception\ResourceNotFoundException;
56303 use JsonSchema\Validator;
56310 class FileGetContents extends AbstractRetriever
56312 protected $messageBody;
56318 public function retrieve($uri)
56320 $context = stream_context_create(array(
56323 'header' => "Accept: " . Validator::SCHEMA_MEDIA_TYPE
56326 $response = file_get_contents($uri);
56327 if (false === $response) {
56328 throw new ResourceNotFoundException('JSON schema not found at ' . $uri);
56330 if ($response == ''
56331 && substr($uri, 0, 7) == 'file://' && substr($uri, -1) == '/'
56333 throw new ResourceNotFoundException('JSON schema not found at ' . $uri);
56336 $this->messageBody = $response;
56337 if (! empty($http_response_header)) {
56338 $this->fetchContentType($http_response_header);
56341 $this->contentType = null;
56344 return $this->messageBody;
56351 private function fetchContentType(array $headers)
56353 foreach ($headers as $header) {
56354 if ($this->contentType = self::getContentTypeMatchInHeader($header)) {
56366 protected static function getContentTypeMatchInHeader($header)
56368 if (0 < preg_match("/Content-Type:(\V*)/ims", $header, $match)) {
56369 return trim($match[1]);
56382 namespace JsonSchema\Uri\Retrievers;
56389 interface UriRetrieverInterface
56397 public function retrieve($uri);
56403 public function getContentType();
56413 namespace JsonSchema\Uri\Retrievers;
56415 use JsonSchema\Validator;
56422 class Curl extends AbstractRetriever
56424 protected $messageBody;
56426 public function __construct()
56428 if (!function_exists('curl_init')) {
56429 throw new \RuntimeException("cURL not installed");
56437 public function retrieve($uri)
56441 curl_setopt($ch, CURLOPT_URL, $uri);
56442 curl_setopt($ch, CURLOPT_HEADER, true);
56443 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
56444 curl_setopt($ch, CURLOPT_HTTPHEADER, array('Accept: ' . Validator::SCHEMA_MEDIA_TYPE));
56446 $response = curl_exec($ch);
56447 if (false === $response) {
56448 throw new \JsonSchema\Exception\ResourceNotFoundException('JSON schema not found');
56451 $this->fetchMessageBody($response);
56452 $this->fetchContentType($response);
56456 return $this->messageBody;
56462 private function fetchMessageBody($response)
56464 preg_match("/(?:\r\n){2}(.*)$/ms", $response, $match);
56465 $this->messageBody = $match[1];
56472 protected function fetchContentType($response)
56474 if (0 < preg_match("/Content-Type:(\V*)/ims", $response, $match)) {
56475 $this->contentType = trim($match[1]);
56484 namespace JsonSchema\Uri\Retrievers;
56486 use JsonSchema\Validator;
56487 use JsonSchema\Uri\Retrievers\UriRetrieverInterface;
56501 class PredefinedArray extends AbstractRetriever
56515 public function __construct(array $schemas, $contentType = Validator::SCHEMA_MEDIA_TYPE)
56517 $this->schemas = $schemas;
56518 $this->contentType = $contentType;
56525 public function retrieve($uri)
56527 if (!array_key_exists($uri, $this->schemas)) {
56528 throw new \JsonSchema\Exception\ResourceNotFoundException(sprintf(
56529 'The JSON schema "%s" was not found.',
56534 return $this->schemas[$uri];
56545 namespace JsonSchema;
56547 use JsonSchema\Constraints\Schema;
56548 use JsonSchema\Constraints\Constraint;
56550 use JsonSchema\Exception\InvalidSchemaMediaTypeException;
56551 use JsonSchema\Exception\JsonDecodingException;
56553 use JsonSchema\Uri\Retrievers\UriRetrieverInterface;
56562 class Validator extends Constraint
56564 const SCHEMA_MEDIA_TYPE = 'application/schema+json';
56573 public function check($value, $schema = null, $path = null, $i = null)
56575 $validator = new Schema($this->checkMode, $this->uriRetriever);
56576 $validator->check($value, $schema);
56578 $this->addErrors(array_unique($validator->getErrors(), SORT_REGULAR));
56585 require_once __DIR__ . '/composer' . '/autoload_real.php';
56587 return ComposerAutoloaderInit4fedf8bd1e8f37fa27ee584fd8eec2a5::getLoader();
56592 $vendorDir = dirname(dirname(__FILE__));
56593 $baseDir = dirname($vendorDir);
56596 'Symfony\\Component\\Process\\' => array($vendorDir . '/symfony/process'),
56597 'Symfony\\Component\\Finder\\' => array($vendorDir . '/symfony/finder'),
56598 'Symfony\\Component\\Console\\' => array($vendorDir . '/symfony/console'),
56599 'JsonSchema' => array($vendorDir . '/justinrainbow/json-schema/src'),
56600 'Composer' => array($baseDir . '/src'),
56606 $vendorDir = dirname(dirname(__FILE__));
56607 $baseDir = dirname($vendorDir);
56610 'Seld\\JsonLint\\' => array($vendorDir . '/seld/jsonlint/src/Seld/JsonLint'),
56616 $vendorDir = dirname(dirname(__FILE__));
56617 $baseDir = dirname($vendorDir);
56625 class ComposerAutoloaderInit4fedf8bd1e8f37fa27ee584fd8eec2a5
56627 private static $loader;
56629 public static function loadClassLoader($class)
56631 if ('Composer\Autoload\ClassLoader' === $class) {
56632 require __DIR__ . '/ClassLoader.php';
56636 public static function getLoader()
56638 if (null !== self::$loader) {
56639 return self::$loader;
56642 spl_autoload_register(array('ComposerAutoloaderInit4fedf8bd1e8f37fa27ee584fd8eec2a5', 'loadClassLoader'), true, true);
56643 self::$loader = $loader = new \Composer\Autoload\ClassLoader();
56644 spl_autoload_unregister(array('ComposerAutoloaderInit4fedf8bd1e8f37fa27ee584fd8eec2a5', 'loadClassLoader'));
56646 $vendorDir = dirname(__DIR__);
56647 $baseDir = dirname($vendorDir);
56649 $map = require __DIR__ . '/autoload_namespaces.php';
56650 foreach ($map as $namespace => $path) {
56651 $loader->set($namespace, $path);
56654 $map = require __DIR__ . '/autoload_psr4.php';
56655 foreach ($map as $namespace => $path) {
56656 $loader->setPsr4($namespace, $path);
56659 $classMap = require __DIR__ . '/autoload_classmap.php';
56661 $loader->addClassMap($classMap);
56664 $loader->register(true);
56673 $vendorDir = dirname(dirname(__FILE__));
56674 $baseDir = dirname($vendorDir);
56677 $vendorDir . '/phpunit/phpunit-mock-objects',
56678 $vendorDir . '/phpunit/php-timer',
56679 $vendorDir . '/phpunit/php-token-stream',
56680 $vendorDir . '/phpunit/php-file-iterator',
56681 $vendorDir . '/phpunit/php-text-template',
56682 $vendorDir . '/phpunit/php-code-coverage',
56683 $vendorDir . '/phpunit/phpunit',
56684 $vendorDir . '/symfony/yaml',
56698 namespace Composer\Autoload;
56731 private $prefixLengthsPsr4 = array();
56732 private $prefixDirsPsr4 = array();
56733 private $fallbackDirsPsr4 = array();
56736 private $prefixesPsr0 = array();
56737 private $fallbackDirsPsr0 = array();
56739 private $useIncludePath = false;
56740 private $classMap = array();
56742 public function getPrefixes()
56744 return call_user_func_array('array_merge', $this->prefixesPsr0);
56747 public function getPrefixesPsr4()
56749 return $this->prefixDirsPsr4;
56752 public function getFallbackDirs()
56754 return $this->fallbackDirsPsr0;
56757 public function getFallbackDirsPsr4()
56759 return $this->fallbackDirsPsr4;
56762 public function getClassMap()
56764 return $this->classMap;
56770 public function addClassMap(array $classMap)
56772 if ($this->classMap) {
56773 $this->classMap = array_merge($this->classMap, $classMap);
56775 $this->classMap = $classMap;
56787 public function add($prefix, $paths, $prepend = false)
56791 $this->fallbackDirsPsr0 = array_merge(
56793 $this->fallbackDirsPsr0
56796 $this->fallbackDirsPsr0 = array_merge(
56797 $this->fallbackDirsPsr0,
56805 $first = $prefix[0];
56806 if (!isset($this->prefixesPsr0[$first][$prefix])) {
56807 $this->prefixesPsr0[$first][$prefix] = (array) $paths;
56812 $this->prefixesPsr0[$first][$prefix] = array_merge(
56814 $this->prefixesPsr0[$first][$prefix]
56817 $this->prefixesPsr0[$first][$prefix] = array_merge(
56818 $this->prefixesPsr0[$first][$prefix],
56832 public function addPsr4($prefix, $paths, $prepend = false)
56837 $this->fallbackDirsPsr4 = array_merge(
56839 $this->fallbackDirsPsr4
56842 $this->fallbackDirsPsr4 = array_merge(
56843 $this->fallbackDirsPsr4,
56847 } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
56849 $length = strlen($prefix);
56850 if ('\\' !== $prefix[$length - 1]) {
56851 throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
56853 $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
56854 $this->prefixDirsPsr4[$prefix] = (array) $paths;
56855 } elseif ($prepend) {
56857 $this->prefixDirsPsr4[$prefix] = array_merge(
56859 $this->prefixDirsPsr4[$prefix]
56863 $this->prefixDirsPsr4[$prefix] = array_merge(
56864 $this->prefixDirsPsr4[$prefix],
56877 public function set($prefix, $paths)
56880 $this->fallbackDirsPsr0 = (array) $paths;
56882 $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
56893 public function setPsr4($prefix, $paths) {
56895 $this->fallbackDirsPsr4 = (array) $paths;
56897 $length = strlen($prefix);
56898 if ('\\' !== $prefix[$length - 1]) {
56899 throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
56901 $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
56902 $this->prefixDirsPsr4[$prefix] = (array) $paths;
56911 public function setUseIncludePath($useIncludePath)
56913 $this->useIncludePath = $useIncludePath;
56922 public function getUseIncludePath()
56924 return $this->useIncludePath;
56932 public function register($prepend = false)
56934 spl_autoload_register(array($this, 'loadClass'), true, $prepend);
56940 public function unregister()
56942 spl_autoload_unregister(array($this, 'loadClass'));
56951 public function loadClass($class)
56953 if ($file = $this->findFile($class)) {
56967 public function findFile($class)
56970 if ('\\' == $class[0]) {
56971 $class = substr($class, 1);
56975 if (isset($this->classMap[$class])) {
56976 return $this->classMap[$class];
56980 $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . '.php';
56982 $first = $class[0];
56983 if (isset($this->prefixLengthsPsr4[$first])) {
56984 foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) {
56985 if (0 === strpos($class, $prefix)) {
56986 foreach ($this->prefixDirsPsr4[$prefix] as $dir) {
56987 if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
56996 foreach ($this->fallbackDirsPsr4 as $dir) {
56997 if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
57003 if (false !== $pos = strrpos($class, '\\')) {
57005 $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
57006 . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
57009 $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . '.php';
57012 if (isset($this->prefixesPsr0[$first])) {
57013 foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
57014 if (0 === strpos($class, $prefix)) {
57015 foreach ($dirs as $dir) {
57016 if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
57025 foreach ($this->fallbackDirsPsr0 as $dir) {
57026 if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
57032 if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
57037 return $this->classMap[$class] = false;
57042 if (PHP_SAPI !== 'cli') {
57043 echo 'Warning: Composer should be invoked via the CLI version of PHP, not the '.PHP_SAPI.' SAPI'.PHP_EOL;
57046 require __DIR__.'/../src/bootstrap.php';
57048 use Composer\Console\Application;
57050 error_reporting(-1);
57052 if (function_exists('ini_set')) {
57053 @ini_set('display_errors', 1);
57055 $memoryInBytes = function ($value) {
57056 $unit = strtolower(substr($value, -1, 1));
57057 $value = (int) $value;
57061 // no break (cumulative multiplier)
57064 // no break (cumulative multiplier)
57072 $memoryLimit = trim(ini_get('memory_limit'));
57073 // Increase memory_limit if it is lower than 512M
57074 if ($memoryLimit != -1 && $memoryInBytes($memoryLimit) < 512 * 1024 * 1024) {
57075 @ini_set('memory_limit', '512M');
57077 unset($memoryInBytes, $memoryLimit);
57080 // run the command application
57081 $application = new Application();
57082 $application->run();
57084 Copyright (c) 2011 Nils Adermann, Jordi Boggiano
57086 Permission is hereby granted, free of charge, to any person obtaining a copy
57087 of this software and associated documentation files (the "Software"), to deal
57088 in the Software without restriction, including without limitation the rights
57089 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
57090 copies of the Software, and to permit persons to whom the Software is furnished
57091 to do so, subject to the following conditions:
57093 The above copyright notice and this permission notice shall be included in all
57094 copies or substantial portions of the Software.
57096 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
57097 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
57098 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
57099 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
57100 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
57101 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
57104 \b.ãØi¥¦§ùP
\95ó[UR
\88ËÄi»
\ 2\0\0\0GBMB