Initial commit: apps.supersam24.ru

This commit is contained in:
root committed 2026-10-22 00:04:27 +03:00
commit e955e1da36
6566 files changed
+1271095

No files matched your search

+445
View File
@@ -0,0 +1,445 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Autoload;
/**
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
*
* $loader = new \Composer\Autoload\ClassLoader();
*
* // register classes with namespaces
* $loader->add('Symfony\Component', __DIR__.'/component');
* $loader->add('Symfony', __DIR__.'/framework');
*
* // activate the autoloader
* $loader->register();
*
* // to enable searching the include path (eg. for PEAR packages)
* $loader->setUseIncludePath(true);
*
* In this example, if you try to use a class in the Symfony\Component
* namespace or one of its children (Symfony\Component\Console for instance),
* the autoloader will first look for the class under the component/
* directory, and it will then fallback to the framework/ directory if not
* found before giving up.
*
* This class is loosely based on the Symfony UniversalClassLoader.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
* @see http://www.php-fig.org/psr/psr-0/
* @see http://www.php-fig.org/psr/psr-4/
*/
class ClassLoader
{
// PSR-4
private $prefixLengthsPsr4 = array();
private $prefixDirsPsr4 = array();
private $fallbackDirsPsr4 = array();
// PSR-0
private $prefixesPsr0 = array();
private $fallbackDirsPsr0 = array();
private $useIncludePath = false;
private $classMap = array();
private $classMapAuthoritative = false;
private $missingClasses = array();
private $apcuPrefix;
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
return call_user_func_array('array_merge', $this->prefixesPsr0);
}
return array();
}
public function getPrefixesPsr4()
{
return $this->prefixDirsPsr4;
}
public function getFallbackDirs()
{
return $this->fallbackDirsPsr0;
}
public function getFallbackDirsPsr4()
{
return $this->fallbackDirsPsr4;
}
public function getClassMap()
{
return $this->classMap;
}
/**
* @param array $classMap Class to filename map
*/
public function addClassMap(array $classMap)
{
if ($this->classMap) {
$this->classMap = array_merge($this->classMap, $classMap);
} else {
$this->classMap = $classMap;
}
}
/**
* Registers a set of PSR-0 directories for a given prefix, either
* appending or prepending to the ones previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
*/
public function add($prefix, $paths, $prepend = false)
{
if (!$prefix) {
if ($prepend) {
$this->fallbackDirsPsr0 = array_merge(
(array) $paths,
$this->fallbackDirsPsr0
);
} else {
$this->fallbackDirsPsr0 = array_merge(
$this->fallbackDirsPsr0,
(array) $paths
);
}
return;
}
$first = $prefix[0];
if (!isset($this->prefixesPsr0[$first][$prefix])) {
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
return;
}
if ($prepend) {
$this->prefixesPsr0[$first][$prefix] = array_merge(
(array) $paths,
$this->prefixesPsr0[$first][$prefix]
);
} else {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$this->prefixesPsr0[$first][$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-4 directories for a given namespace, either
* appending or prepending to the ones previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories
*
* @throws \InvalidArgumentException
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
if (!$prefix) {
// Register directories for the root namespace.
if ($prepend) {
$this->fallbackDirsPsr4 = array_merge(
(array) $paths,
$this->fallbackDirsPsr4
);
} else {
$this->fallbackDirsPsr4 = array_merge(
$this->fallbackDirsPsr4,
(array) $paths
);
}
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
// Register directories for a new namespace.
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
} elseif ($prepend) {
// Prepend directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
(array) $paths,
$this->prefixDirsPsr4[$prefix]
);
} else {
// Append directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$this->prefixDirsPsr4[$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-0 directories for a given prefix,
* replacing any others previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 base directories
*/
public function set($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr0 = (array) $paths;
} else {
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
}
}
/**
* Registers a set of PSR-4 directories for a given namespace,
* replacing any others previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
*
* @throws \InvalidArgumentException
*/
public function setPsr4($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr4 = (array) $paths;
} else {
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
}
}
/**
* Turns on searching the include path for class files.
*
* @param bool $useIncludePath
*/
public function setUseIncludePath($useIncludePath)
{
$this->useIncludePath = $useIncludePath;
}
/**
* Can be used to check if the autoloader uses the include path to check
* for classes.
*
* @return bool
*/
public function getUseIncludePath()
{
return $this->useIncludePath;
}
/**
* Turns off searching the prefix and fallback directories for classes
* that have not been registered with the class map.
*
* @param bool $classMapAuthoritative
*/
public function setClassMapAuthoritative($classMapAuthoritative)
{
$this->classMapAuthoritative = $classMapAuthoritative;
}
/**
* Should class lookup fail if not found in the current class map?
*
* @return bool
*/
public function isClassMapAuthoritative()
{
return $this->classMapAuthoritative;
}
/**
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
*
* @param string|null $apcuPrefix
*/
public function setApcuPrefix($apcuPrefix)
{
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
}
/**
* The APCu prefix in use, or null if APCu caching is not enabled.
*
* @return string|null
*/
public function getApcuPrefix()
{
return $this->apcuPrefix;
}
/**
* Registers this instance as an autoloader.
*
* @param bool $prepend Whether to prepend the autoloader or not
*/
public function register($prepend = false)
{
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
}
/**
* Unregisters this instance as an autoloader.
*/
public function unregister()
{
spl_autoload_unregister(array($this, 'loadClass'));
}
/**
* Loads the given class or interface.
*
* @param string $class The name of the class
* @return bool|null True if loaded, null otherwise
*/
public function loadClass($class)
{
if ($file = $this->findFile($class)) {
includeFile($file);
return true;
}
}
/**
* Finds the path to the file where the class is defined.
*
* @param string $class The name of the class
*
* @return string|false The path if found, false otherwise
*/
public function findFile($class)
{
// class map lookup
if (isset($this->classMap[$class])) {
return $this->classMap[$class];
}
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
return false;
}
if (null !== $this->apcuPrefix) {
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
if ($hit) {
return $file;
}
}
$file = $this->findFileWithExtension($class, '.php');
// Search for Hack files if we are running on HHVM
if (false === $file && defined('HHVM_VERSION')) {
$file = $this->findFileWithExtension($class, '.hh');
}
if (null !== $this->apcuPrefix) {
apcu_add($this->apcuPrefix.$class, $file);
}
if (false === $file) {
// Remember that this class does not exist.
$this->missingClasses[$class] = true;
}
return $file;
}
private function findFileWithExtension($class, $ext)
{
// PSR-4 lookup
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
$first = $class[0];
if (isset($this->prefixLengthsPsr4[$first])) {
$subPath = $class;
while (false !== $lastPos = strrpos($subPath, '\\')) {
$subPath = substr($subPath, 0, $lastPos);
$search = $subPath . '\\';
if (isset($this->prefixDirsPsr4[$search])) {
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
foreach ($this->prefixDirsPsr4[$search] as $dir) {
if (file_exists($file = $dir . $pathEnd)) {
return $file;
}
}
}
}
}
// PSR-4 fallback dirs
foreach ($this->fallbackDirsPsr4 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
return $file;
}
}
// PSR-0 lookup
if (false !== $pos = strrpos($class, '\\')) {
// namespaced class name
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
} else {
// PEAR-like class name
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
}
if (isset($this->prefixesPsr0[$first])) {
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
if (0 === strpos($class, $prefix)) {
foreach ($dirs as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
}
}
}
// PSR-0 fallback dirs
foreach ($this->fallbackDirsPsr0 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
// PSR-0 include paths.
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
return $file;
}
return false;
}
}
/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*/
function includeFile($file)
{
include $file;
}
+21
View File
@@ -0,0 +1,21 @@
Copyright (c) Nils Adermann, Jordi Boggiano
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+18
View File
@@ -0,0 +1,18 @@
<?php
// autoload_classmap.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'Dompdf\\Cpdf' => $vendorDir . '/dompdf/dompdf/lib/Cpdf.php',
'HTML5_Data' => $vendorDir . '/dompdf/dompdf/lib/html5lib/Data.php',
'HTML5_InputStream' => $vendorDir . '/dompdf/dompdf/lib/html5lib/InputStream.php',
'HTML5_Parser' => $vendorDir . '/dompdf/dompdf/lib/html5lib/Parser.php',
'HTML5_Tokenizer' => $vendorDir . '/dompdf/dompdf/lib/html5lib/Tokenizer.php',
'HTML5_TreeBuilder' => $vendorDir . '/dompdf/dompdf/lib/html5lib/TreeBuilder.php',
'JsonException' => $vendorDir . '/symfony/polyfill-php73/Resources/stubs/JsonException.php',
'Mobile_Detect' => $vendorDir . '/mobiledetect/mobiledetectlib/Mobile_Detect.php',
'SqlFormatter' => $vendorDir . '/jdorn/sql-formatter/lib/SqlFormatter.php',
);
+87
View File
@@ -0,0 +1,87 @@
<?php
// autoload_files.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php',
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
'0d59ee240a4cd96ddbb4ff164fccea4d' => $vendorDir . '/symfony/polyfill-php73/bootstrap.php',
'34122c0574b76bf21c9a8db62b5b9cf3' => $vendorDir . '/cakephp/chronos/src/carbon_compat.php',
'cf97c57bfe0f23854afd2f3818abb7a0' => $vendorDir . '/zendframework/zend-diactoros/src/functions/create_uploaded_file.php',
'9bf37a3d0dad93e29cb4e1b1bfab04e9' => $vendorDir . '/zendframework/zend-diactoros/src/functions/marshal_headers_from_sapi.php',
'ce70dccb4bcc2efc6e94d2ee526e6972' => $vendorDir . '/zendframework/zend-diactoros/src/functions/marshal_method_from_sapi.php',
'f86420df471f14d568bfcb71e271b523' => $vendorDir . '/zendframework/zend-diactoros/src/functions/marshal_protocol_version_from_sapi.php',
'b87481e008a3700344428ae089e7f9e5' => $vendorDir . '/zendframework/zend-diactoros/src/functions/marshal_uri_from_sapi.php',
'0b0974a5566a1077e4f2e111341112c1' => $vendorDir . '/zendframework/zend-diactoros/src/functions/normalize_server.php',
'1ca3bc274755662169f9629d5412a1da' => $vendorDir . '/zendframework/zend-diactoros/src/functions/normalize_uploaded_files.php',
'40360c0b9b437e69bcbb7f1349ce029e' => $vendorDir . '/zendframework/zend-diactoros/src/functions/parse_cookie_header.php',
'c720f792236cd163ece8049879166850' => $vendorDir . '/cakephp/cakephp/src/Core/functions.php',
'ede59e3a405fb689cd1cebb7bb1db3fb' => $vendorDir . '/cakephp/cakephp/src/Collection/functions.php',
'90236b492da7ca2983a2ad6e33e4152e' => $vendorDir . '/cakephp/cakephp/src/I18n/functions.php',
'b1fc73705e1bec51cd2b20a32cf1c60a' => $vendorDir . '/cakephp/cakephp/src/Utility/bootstrap.php',
'6124b4c8570aa390c21fafd04a26c69f' => $vendorDir . '/myclabs/deep-copy/src/DeepCopy/deep_copy.php',
'5255c38a0faeba867671b61dfda6d864' => $vendorDir . '/paragonie/random_compat/lib/random.php',
'667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php',
'abede361264e2ae69ec1eee813a101af' => $vendorDir . '/markbaker/complex/classes/src/functions/abs.php',
'21a5860fbef5be28db5ddfbc3cca67c4' => $vendorDir . '/markbaker/complex/classes/src/functions/acos.php',
'1546e3f9d127f2a9bb2d1b6c31c26ef1' => $vendorDir . '/markbaker/complex/classes/src/functions/acosh.php',
'd2516f7f4fba5ea5905f494b4a8262e0' => $vendorDir . '/markbaker/complex/classes/src/functions/acot.php',
'4511163d560956219b96882c0980b65e' => $vendorDir . '/markbaker/complex/classes/src/functions/acoth.php',
'c361f5616dc2a8da4fa3e137077cd4ea' => $vendorDir . '/markbaker/complex/classes/src/functions/acsc.php',
'02d68920fc98da71991ce569c91df0f6' => $vendorDir . '/markbaker/complex/classes/src/functions/acsch.php',
'88e19525eae308b4a6aa3419364875d3' => $vendorDir . '/markbaker/complex/classes/src/functions/argument.php',
'60e8e2d0827b58bfc904f13957e51849' => $vendorDir . '/markbaker/complex/classes/src/functions/asec.php',
'13d2f040713999eab66c359b4d79871d' => $vendorDir . '/markbaker/complex/classes/src/functions/asech.php',
'838ab38beb32c68a79d3cd2c007d5a04' => $vendorDir . '/markbaker/complex/classes/src/functions/asin.php',
'bb28eccd0f8f008333a1b3c163d604ac' => $vendorDir . '/markbaker/complex/classes/src/functions/asinh.php',
'9e483de83558c98f7d3feaa402c78cb3' => $vendorDir . '/markbaker/complex/classes/src/functions/atan.php',
'36b74b5b765ded91ee58c8ee3c0e85e3' => $vendorDir . '/markbaker/complex/classes/src/functions/atanh.php',
'05c15ee9510da7fd6bf6136f436500c0' => $vendorDir . '/markbaker/complex/classes/src/functions/conjugate.php',
'd3208dfbce2505e370788f9f22f6785f' => $vendorDir . '/markbaker/complex/classes/src/functions/cos.php',
'141cf1fb3a3046f8b64534b0ebab33ca' => $vendorDir . '/markbaker/complex/classes/src/functions/cosh.php',
'be660df75fd0dbe7fa7c03b7434b3294' => $vendorDir . '/markbaker/complex/classes/src/functions/cot.php',
'01e31ea298a51bc9e91517e3ce6b9e76' => $vendorDir . '/markbaker/complex/classes/src/functions/coth.php',
'803ddd97f7b1da68982a7b087c3476f6' => $vendorDir . '/markbaker/complex/classes/src/functions/csc.php',
'3001cdfd101ec3c32da34ee43c2e149b' => $vendorDir . '/markbaker/complex/classes/src/functions/csch.php',
'77b2d7629ef2a93fabb8c56754a91051' => $vendorDir . '/markbaker/complex/classes/src/functions/exp.php',
'4a4471296dec796c21d4f4b6552396a9' => $vendorDir . '/markbaker/complex/classes/src/functions/inverse.php',
'c3e9897e1744b88deb56fcdc39d34d85' => $vendorDir . '/markbaker/complex/classes/src/functions/ln.php',
'a83cacf2de942cff288de15a83afd26d' => $vendorDir . '/markbaker/complex/classes/src/functions/log2.php',
'6a861dacc9ee2f3061241d4c7772fa21' => $vendorDir . '/markbaker/complex/classes/src/functions/log10.php',
'4d2522d968c8ba78d6c13548a1b4200e' => $vendorDir . '/markbaker/complex/classes/src/functions/negative.php',
'fd587ca933fc0447fa5ab4843bdd97f7' => $vendorDir . '/markbaker/complex/classes/src/functions/pow.php',
'383ef01c62028fc78cd4388082fce3c2' => $vendorDir . '/markbaker/complex/classes/src/functions/rho.php',
'150fbd1b95029dc47292da97ecab9375' => $vendorDir . '/markbaker/complex/classes/src/functions/sec.php',
'549abd9bae174286d660bdaa07407c68' => $vendorDir . '/markbaker/complex/classes/src/functions/sech.php',
'6bfbf5eaea6b17a0ed85cb21ba80370c' => $vendorDir . '/markbaker/complex/classes/src/functions/sin.php',
'22efe13f1a497b8e199540ae2d9dc59c' => $vendorDir . '/markbaker/complex/classes/src/functions/sinh.php',
'e90135ab8e787795a509ed7147de207d' => $vendorDir . '/markbaker/complex/classes/src/functions/sqrt.php',
'bb0a7923ffc6a90919cd64ec54ff06bc' => $vendorDir . '/markbaker/complex/classes/src/functions/tan.php',
'2d302f32ce0fd4e433dd91c5bb404a28' => $vendorDir . '/markbaker/complex/classes/src/functions/tanh.php',
'24dd4658a952171a4ee79218c4f9fd06' => $vendorDir . '/markbaker/complex/classes/src/functions/theta.php',
'e49b7876281d6f5bc39536dde96d1f4a' => $vendorDir . '/markbaker/complex/classes/src/operations/add.php',
'47596e02b43cd6da7700134fd08f88cf' => $vendorDir . '/markbaker/complex/classes/src/operations/subtract.php',
'883af48563631547925fa4c3b48ead07' => $vendorDir . '/markbaker/complex/classes/src/operations/multiply.php',
'f190e3308e6ca23234a2875edc985c03' => $vendorDir . '/markbaker/complex/classes/src/operations/divideby.php',
'ac9e33ce6841aa5bf5d16d465a2f03a7' => $vendorDir . '/markbaker/complex/classes/src/operations/divideinto.php',
'9d8e013a5160a09477beb8e44f8ae97b' => $vendorDir . '/markbaker/matrix/classes/src/functions/adjoint.php',
'6e78d1bdea6248d6aa117229efae50f2' => $vendorDir . '/markbaker/matrix/classes/src/functions/antidiagonal.php',
'4623d87924d94f5412fe5afbf1cef31d' => $vendorDir . '/markbaker/matrix/classes/src/functions/cofactors.php',
'901fd1f6950a637ca85f66b701a45e13' => $vendorDir . '/markbaker/matrix/classes/src/functions/determinant.php',
'83057abc0e4acc99ba80154ee5d02a49' => $vendorDir . '/markbaker/matrix/classes/src/functions/diagonal.php',
'07b7fd7a434451149b4fd477fca0ce06' => $vendorDir . '/markbaker/matrix/classes/src/functions/identity.php',
'c8d43b340583e07ae89f2a3baef2cf89' => $vendorDir . '/markbaker/matrix/classes/src/functions/inverse.php',
'499bb10ed7a3aee2ba4c09a31a85e8d1' => $vendorDir . '/markbaker/matrix/classes/src/functions/minors.php',
'1cad2e6414d652e8b1c64e8967f6f37d' => $vendorDir . '/markbaker/matrix/classes/src/functions/trace.php',
'95a7f134ac17161d07def442b3b737e8' => $vendorDir . '/markbaker/matrix/classes/src/functions/transpose.php',
'b3a6bc628377118d4b4b8ba08d1eb949' => $vendorDir . '/markbaker/matrix/classes/src/operations/add.php',
'5fef6d0e407f3f8887266dfa4a6c534c' => $vendorDir . '/markbaker/matrix/classes/src/operations/directsum.php',
'684ba247e1385946e3babdaa054119de' => $vendorDir . '/markbaker/matrix/classes/src/operations/subtract.php',
'aa53dcba601214d17ad405b7c291b7e8' => $vendorDir . '/markbaker/matrix/classes/src/operations/multiply.php',
'75c79eb1b25749b05a47976f32b0d8a2' => $vendorDir . '/markbaker/matrix/classes/src/operations/divideby.php',
'6ab8ad87a734f276a6bcd5a0fe1289be' => $vendorDir . '/markbaker/matrix/classes/src/operations/divideinto.php',
'801c31d8ed748cfa537fa45402288c95' => $vendorDir . '/psy/psysh/src/functions.php',
);
+15
View File
@@ -0,0 +1,15 @@
<?php
// autoload_namespaces.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'josegonzalez\\Dotenv' => array($vendorDir . '/josegonzalez/dotenv/src', $vendorDir . '/josegonzalez/dotenv/tests'),
'Umpirsky\\' => array($vendorDir . '/umpirsky/twig-php-function/src'),
'Twig_' => array($vendorDir . '/twig/twig/lib'),
'Sabberworm\\CSS' => array($vendorDir . '/sabberworm/php-css-parser/lib'),
'Detection' => array($vendorDir . '/mobiledetect/mobiledetectlib/namespaced'),
'Aptoma' => array($vendorDir . '/aptoma/twig-markdown/src'),
);
+68
View File
@@ -0,0 +1,68 @@
<?php
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'setasign\\Fpdi\\' => array($vendorDir . '/setasign/fpdi/src'),
'Zend\\Diactoros\\' => array($vendorDir . '/zendframework/zend-diactoros/src'),
'XdgBaseDir\\' => array($vendorDir . '/dnoegel/php-xdg-base-dir/src'),
'WyriHaximus\\TwigView\\' => array($vendorDir . '/wyrihaximus/twig-view/src'),
'Twig\\' => array($vendorDir . '/twig/twig/src'),
'Symfony\\Polyfill\\Php73\\' => array($vendorDir . '/symfony/polyfill-php73'),
'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'),
'Symfony\\Polyfill\\Ctype\\' => array($vendorDir . '/symfony/polyfill-ctype'),
'Symfony\\Contracts\\Service\\' => array($vendorDir . '/symfony/service-contracts'),
'Symfony\\Component\\Yaml\\' => array($vendorDir . '/symfony/yaml'),
'Symfony\\Component\\VarDumper\\' => array($vendorDir . '/symfony/var-dumper'),
'Symfony\\Component\\Process\\' => array($vendorDir . '/symfony/process'),
'Symfony\\Component\\Finder\\' => array($vendorDir . '/symfony/finder'),
'Symfony\\Component\\Filesystem\\' => array($vendorDir . '/symfony/filesystem'),
'Symfony\\Component\\Console\\' => array($vendorDir . '/symfony/console'),
'Symfony\\Component\\Config\\' => array($vendorDir . '/symfony/config'),
'Svg\\' => array($vendorDir . '/phenx/php-svg-lib/src/Svg'),
'Seld\\PharUtils\\' => array($vendorDir . '/seld/phar-utils/src'),
'Seld\\JsonLint\\' => array($vendorDir . '/seld/jsonlint/src/Seld/JsonLint'),
'Psy\\' => array($vendorDir . '/psy/psysh/src'),
'Psr\\SimpleCache\\' => array($vendorDir . '/psr/simple-cache/src'),
'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'),
'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src'),
'Psr\\Container\\' => array($vendorDir . '/psr/container/src'),
'PhpParser\\' => array($vendorDir . '/nikic/php-parser/lib/PhpParser'),
'PhpOffice\\PhpSpreadsheet\\' => array($vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet'),
'Phinx\\' => array($vendorDir . '/robmorgan/phinx/src/Phinx'),
'ParseCsv\\' => array($vendorDir . '/parsecsv/php-parsecsv/src'),
'PHPMailer\\PHPMailer\\' => array($vendorDir . '/phpmailer/phpmailer/src'),
'Mpdf\\' => array($vendorDir . '/mpdf/mpdf/src'),
'Migrations\\' => array($vendorDir . '/cakephp/migrations/src'),
'Matrix\\' => array($vendorDir . '/markbaker/matrix/classes/src'),
'M1\\Env\\' => array($vendorDir . '/m1/env/src'),
'JsonSchema\\' => array($vendorDir . '/justinrainbow/json-schema/src/JsonSchema'),
'Jasny\\Twig\\' => array($vendorDir . '/jasny/twig-extensions/src'),
'JakubOnderka\\PhpConsoleHighlighter\\' => array($vendorDir . '/jakub-onderka/php-console-highlighter/src'),
'JakubOnderka\\PhpConsoleColor\\' => array($vendorDir . '/jakub-onderka/php-console-color/src'),
'FontLib\\' => array($vendorDir . '/phenx/php-font-lib/src/FontLib'),
'Dompdf\\' => array($vendorDir . '/dompdf/dompdf/src'),
'DeepCopy\\' => array($vendorDir . '/myclabs/deep-copy/src/DeepCopy'),
'DebugKit\\Test\\Fixture\\' => array($vendorDir . '/cakephp/debug_kit/tests/Fixture'),
'DebugKit\\' => array($vendorDir . '/cakephp/debug_kit/src'),
'Composer\\XdebugHandler\\' => array($vendorDir . '/composer/xdebug-handler/src'),
'Composer\\Spdx\\' => array($vendorDir . '/composer/spdx-licenses/src'),
'Composer\\Semver\\' => array($vendorDir . '/composer/semver/src'),
'Composer\\CaBundle\\' => array($vendorDir . '/composer/ca-bundle/src'),
'Composer\\' => array($vendorDir . '/composer/composer/src/Composer'),
'Complex\\' => array($vendorDir . '/markbaker/complex/classes/src'),
'Cake\\Test\\' => array($vendorDir . '/cakephp/cakephp/tests'),
'Cake\\Composer\\' => array($vendorDir . '/cakephp/plugin-installer/src'),
'Cake\\Chronos\\' => array($vendorDir . '/cakephp/chronos/src'),
'Cake\\' => array($vendorDir . '/cakephp/cakephp/src'),
'CakePHP\\' => array($vendorDir . '/cakephp/cakephp-codesniffer/CakePHP'),
'Bake\\' => array($vendorDir . '/cakephp/bake/src'),
'Aura\\Intl\\' => array($vendorDir . '/aura/intl/src'),
'App\\Test\\' => array($baseDir . '/tests'),
'App\\' => array($baseDir . '/src'),
'Ajgl\\Twig\\Extension\\' => array($vendorDir . '/ajgl/breakpoint-twig-extension/src'),
'' => array($vendorDir . '/asm89/twig-cache-extension/lib'),
);
+70
View File
@@ -0,0 +1,70 @@
<?php
// autoload_real.php @generated by Composer
class ComposerAutoloaderInit89ba59be1ed926d2e0969daf36fcbd1f
{
private static $loader;
public static function loadClassLoader($class)
{
if ('Composer\Autoload\ClassLoader' === $class) {
require __DIR__ . '/ClassLoader.php';
}
}
public static function getLoader()
{
if (null !== self::$loader) {
return self::$loader;
}
spl_autoload_register(array('ComposerAutoloaderInit89ba59be1ed926d2e0969daf36fcbd1f', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
spl_autoload_unregister(array('ComposerAutoloaderInit89ba59be1ed926d2e0969daf36fcbd1f', 'loadClassLoader'));
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
if ($useStaticLoader) {
require_once __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInit89ba59be1ed926d2e0969daf36fcbd1f::getInitializer($loader));
} else {
$map = require __DIR__ . '/autoload_namespaces.php';
foreach ($map as $namespace => $path) {
$loader->set($namespace, $path);
}
$map = require __DIR__ . '/autoload_psr4.php';
foreach ($map as $namespace => $path) {
$loader->setPsr4($namespace, $path);
}
$classMap = require __DIR__ . '/autoload_classmap.php';
if ($classMap) {
$loader->addClassMap($classMap);
}
}
$loader->register(true);
if ($useStaticLoader) {
$includeFiles = Composer\Autoload\ComposerStaticInit89ba59be1ed926d2e0969daf36fcbd1f::$files;
} else {
$includeFiles = require __DIR__ . '/autoload_files.php';
}
foreach ($includeFiles as $fileIdentifier => $file) {
composerRequire89ba59be1ed926d2e0969daf36fcbd1f($fileIdentifier, $file);
}
return $loader;
}
}
function composerRequire89ba59be1ed926d2e0969daf36fcbd1f($fileIdentifier, $file)
{
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
require $file;
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
}
}
+501
View File
@@ -0,0 +1,501 @@
<?php
// autoload_static.php @generated by Composer
namespace Composer\Autoload;
class ComposerStaticInit89ba59be1ed926d2e0969daf36fcbd1f
{
public static $files = array (
'320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php',
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
'0d59ee240a4cd96ddbb4ff164fccea4d' => __DIR__ . '/..' . '/symfony/polyfill-php73/bootstrap.php',
'34122c0574b76bf21c9a8db62b5b9cf3' => __DIR__ . '/..' . '/cakephp/chronos/src/carbon_compat.php',
'cf97c57bfe0f23854afd2f3818abb7a0' => __DIR__ . '/..' . '/zendframework/zend-diactoros/src/functions/create_uploaded_file.php',
'9bf37a3d0dad93e29cb4e1b1bfab04e9' => __DIR__ . '/..' . '/zendframework/zend-diactoros/src/functions/marshal_headers_from_sapi.php',
'ce70dccb4bcc2efc6e94d2ee526e6972' => __DIR__ . '/..' . '/zendframework/zend-diactoros/src/functions/marshal_method_from_sapi.php',
'f86420df471f14d568bfcb71e271b523' => __DIR__ . '/..' . '/zendframework/zend-diactoros/src/functions/marshal_protocol_version_from_sapi.php',
'b87481e008a3700344428ae089e7f9e5' => __DIR__ . '/..' . '/zendframework/zend-diactoros/src/functions/marshal_uri_from_sapi.php',
'0b0974a5566a1077e4f2e111341112c1' => __DIR__ . '/..' . '/zendframework/zend-diactoros/src/functions/normalize_server.php',
'1ca3bc274755662169f9629d5412a1da' => __DIR__ . '/..' . '/zendframework/zend-diactoros/src/functions/normalize_uploaded_files.php',
'40360c0b9b437e69bcbb7f1349ce029e' => __DIR__ . '/..' . '/zendframework/zend-diactoros/src/functions/parse_cookie_header.php',
'c720f792236cd163ece8049879166850' => __DIR__ . '/..' . '/cakephp/cakephp/src/Core/functions.php',
'ede59e3a405fb689cd1cebb7bb1db3fb' => __DIR__ . '/..' . '/cakephp/cakephp/src/Collection/functions.php',
'90236b492da7ca2983a2ad6e33e4152e' => __DIR__ . '/..' . '/cakephp/cakephp/src/I18n/functions.php',
'b1fc73705e1bec51cd2b20a32cf1c60a' => __DIR__ . '/..' . '/cakephp/cakephp/src/Utility/bootstrap.php',
'6124b4c8570aa390c21fafd04a26c69f' => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy/deep_copy.php',
'5255c38a0faeba867671b61dfda6d864' => __DIR__ . '/..' . '/paragonie/random_compat/lib/random.php',
'667aeda72477189d0494fecd327c3641' => __DIR__ . '/..' . '/symfony/var-dumper/Resources/functions/dump.php',
'abede361264e2ae69ec1eee813a101af' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/abs.php',
'21a5860fbef5be28db5ddfbc3cca67c4' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/acos.php',
'1546e3f9d127f2a9bb2d1b6c31c26ef1' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/acosh.php',
'd2516f7f4fba5ea5905f494b4a8262e0' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/acot.php',
'4511163d560956219b96882c0980b65e' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/acoth.php',
'c361f5616dc2a8da4fa3e137077cd4ea' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/acsc.php',
'02d68920fc98da71991ce569c91df0f6' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/acsch.php',
'88e19525eae308b4a6aa3419364875d3' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/argument.php',
'60e8e2d0827b58bfc904f13957e51849' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/asec.php',
'13d2f040713999eab66c359b4d79871d' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/asech.php',
'838ab38beb32c68a79d3cd2c007d5a04' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/asin.php',
'bb28eccd0f8f008333a1b3c163d604ac' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/asinh.php',
'9e483de83558c98f7d3feaa402c78cb3' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/atan.php',
'36b74b5b765ded91ee58c8ee3c0e85e3' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/atanh.php',
'05c15ee9510da7fd6bf6136f436500c0' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/conjugate.php',
'd3208dfbce2505e370788f9f22f6785f' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/cos.php',
'141cf1fb3a3046f8b64534b0ebab33ca' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/cosh.php',
'be660df75fd0dbe7fa7c03b7434b3294' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/cot.php',
'01e31ea298a51bc9e91517e3ce6b9e76' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/coth.php',
'803ddd97f7b1da68982a7b087c3476f6' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/csc.php',
'3001cdfd101ec3c32da34ee43c2e149b' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/csch.php',
'77b2d7629ef2a93fabb8c56754a91051' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/exp.php',
'4a4471296dec796c21d4f4b6552396a9' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/inverse.php',
'c3e9897e1744b88deb56fcdc39d34d85' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/ln.php',
'a83cacf2de942cff288de15a83afd26d' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/log2.php',
'6a861dacc9ee2f3061241d4c7772fa21' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/log10.php',
'4d2522d968c8ba78d6c13548a1b4200e' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/negative.php',
'fd587ca933fc0447fa5ab4843bdd97f7' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/pow.php',
'383ef01c62028fc78cd4388082fce3c2' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/rho.php',
'150fbd1b95029dc47292da97ecab9375' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/sec.php',
'549abd9bae174286d660bdaa07407c68' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/sech.php',
'6bfbf5eaea6b17a0ed85cb21ba80370c' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/sin.php',
'22efe13f1a497b8e199540ae2d9dc59c' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/sinh.php',
'e90135ab8e787795a509ed7147de207d' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/sqrt.php',
'bb0a7923ffc6a90919cd64ec54ff06bc' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/tan.php',
'2d302f32ce0fd4e433dd91c5bb404a28' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/tanh.php',
'24dd4658a952171a4ee79218c4f9fd06' => __DIR__ . '/..' . '/markbaker/complex/classes/src/functions/theta.php',
'e49b7876281d6f5bc39536dde96d1f4a' => __DIR__ . '/..' . '/markbaker/complex/classes/src/operations/add.php',
'47596e02b43cd6da7700134fd08f88cf' => __DIR__ . '/..' . '/markbaker/complex/classes/src/operations/subtract.php',
'883af48563631547925fa4c3b48ead07' => __DIR__ . '/..' . '/markbaker/complex/classes/src/operations/multiply.php',
'f190e3308e6ca23234a2875edc985c03' => __DIR__ . '/..' . '/markbaker/complex/classes/src/operations/divideby.php',
'ac9e33ce6841aa5bf5d16d465a2f03a7' => __DIR__ . '/..' . '/markbaker/complex/classes/src/operations/divideinto.php',
'9d8e013a5160a09477beb8e44f8ae97b' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/functions/adjoint.php',
'6e78d1bdea6248d6aa117229efae50f2' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/functions/antidiagonal.php',
'4623d87924d94f5412fe5afbf1cef31d' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/functions/cofactors.php',
'901fd1f6950a637ca85f66b701a45e13' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/functions/determinant.php',
'83057abc0e4acc99ba80154ee5d02a49' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/functions/diagonal.php',
'07b7fd7a434451149b4fd477fca0ce06' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/functions/identity.php',
'c8d43b340583e07ae89f2a3baef2cf89' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/functions/inverse.php',
'499bb10ed7a3aee2ba4c09a31a85e8d1' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/functions/minors.php',
'1cad2e6414d652e8b1c64e8967f6f37d' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/functions/trace.php',
'95a7f134ac17161d07def442b3b737e8' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/functions/transpose.php',
'b3a6bc628377118d4b4b8ba08d1eb949' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/operations/add.php',
'5fef6d0e407f3f8887266dfa4a6c534c' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/operations/directsum.php',
'684ba247e1385946e3babdaa054119de' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/operations/subtract.php',
'aa53dcba601214d17ad405b7c291b7e8' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/operations/multiply.php',
'75c79eb1b25749b05a47976f32b0d8a2' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/operations/divideby.php',
'6ab8ad87a734f276a6bcd5a0fe1289be' => __DIR__ . '/..' . '/markbaker/matrix/classes/src/operations/divideinto.php',
'801c31d8ed748cfa537fa45402288c95' => __DIR__ . '/..' . '/psy/psysh/src/functions.php',
);
public static $prefixLengthsPsr4 = array (
's' =>
array (
'setasign\\Fpdi\\' => 14,
),
'Z' =>
array (
'Zend\\Diactoros\\' => 15,
),
'X' =>
array (
'XdgBaseDir\\' => 11,
),
'W' =>
array (
'WyriHaximus\\TwigView\\' => 21,
),
'T' =>
array (
'Twig\\' => 5,
),
'S' =>
array (
'Symfony\\Polyfill\\Php73\\' => 23,
'Symfony\\Polyfill\\Mbstring\\' => 26,
'Symfony\\Polyfill\\Ctype\\' => 23,
'Symfony\\Contracts\\Service\\' => 26,
'Symfony\\Component\\Yaml\\' => 23,
'Symfony\\Component\\VarDumper\\' => 28,
'Symfony\\Component\\Process\\' => 26,
'Symfony\\Component\\Finder\\' => 25,
'Symfony\\Component\\Filesystem\\' => 29,
'Symfony\\Component\\Console\\' => 26,
'Symfony\\Component\\Config\\' => 25,
'Svg\\' => 4,
'Seld\\PharUtils\\' => 15,
'Seld\\JsonLint\\' => 14,
),
'P' =>
array (
'Psy\\' => 4,
'Psr\\SimpleCache\\' => 16,
'Psr\\Log\\' => 8,
'Psr\\Http\\Message\\' => 17,
'Psr\\Container\\' => 14,
'PhpParser\\' => 10,
'PhpOffice\\PhpSpreadsheet\\' => 25,
'Phinx\\' => 6,
'ParseCsv\\' => 9,
'PHPMailer\\PHPMailer\\' => 20,
),
'M' =>
array (
'Mpdf\\' => 5,
'Migrations\\' => 11,
'Matrix\\' => 7,
'M1\\Env\\' => 7,
),
'J' =>
array (
'JsonSchema\\' => 11,
'Jasny\\Twig\\' => 11,
'JakubOnderka\\PhpConsoleHighlighter\\' => 35,
'JakubOnderka\\PhpConsoleColor\\' => 29,
),
'F' =>
array (
'FontLib\\' => 8,
),
'D' =>
array (
'Dompdf\\' => 7,
'DeepCopy\\' => 9,
'DebugKit\\Test\\Fixture\\' => 22,
'DebugKit\\' => 9,
),
'C' =>
array (
'Composer\\XdebugHandler\\' => 23,
'Composer\\Spdx\\' => 14,
'Composer\\Semver\\' => 16,
'Composer\\CaBundle\\' => 18,
'Composer\\' => 9,
'Complex\\' => 8,
'Cake\\Test\\' => 10,
'Cake\\Composer\\' => 14,
'Cake\\Chronos\\' => 13,
'Cake\\' => 5,
'CakePHP\\' => 8,
),
'B' =>
array (
'Bake\\' => 5,
),
'A' =>
array (
'Aura\\Intl\\' => 10,
'App\\Test\\' => 9,
'App\\' => 4,
'Ajgl\\Twig\\Extension\\' => 20,
),
);
public static $prefixDirsPsr4 = array (
'setasign\\Fpdi\\' =>
array (
0 => __DIR__ . '/..' . '/setasign/fpdi/src',
),
'Zend\\Diactoros\\' =>
array (
0 => __DIR__ . '/..' . '/zendframework/zend-diactoros/src',
),
'XdgBaseDir\\' =>
array (
0 => __DIR__ . '/..' . '/dnoegel/php-xdg-base-dir/src',
),
'WyriHaximus\\TwigView\\' =>
array (
0 => __DIR__ . '/..' . '/wyrihaximus/twig-view/src',
),
'Twig\\' =>
array (
0 => __DIR__ . '/..' . '/twig/twig/src',
),
'Symfony\\Polyfill\\Php73\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/polyfill-php73',
),
'Symfony\\Polyfill\\Mbstring\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/polyfill-mbstring',
),
'Symfony\\Polyfill\\Ctype\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/polyfill-ctype',
),
'Symfony\\Contracts\\Service\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/service-contracts',
),
'Symfony\\Component\\Yaml\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/yaml',
),
'Symfony\\Component\\VarDumper\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/var-dumper',
),
'Symfony\\Component\\Process\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/process',
),
'Symfony\\Component\\Finder\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/finder',
),
'Symfony\\Component\\Filesystem\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/filesystem',
),
'Symfony\\Component\\Console\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/console',
),
'Symfony\\Component\\Config\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/config',
),
'Svg\\' =>
array (
0 => __DIR__ . '/..' . '/phenx/php-svg-lib/src/Svg',
),
'Seld\\PharUtils\\' =>
array (
0 => __DIR__ . '/..' . '/seld/phar-utils/src',
),
'Seld\\JsonLint\\' =>
array (
0 => __DIR__ . '/..' . '/seld/jsonlint/src/Seld/JsonLint',
),
'Psy\\' =>
array (
0 => __DIR__ . '/..' . '/psy/psysh/src',
),
'Psr\\SimpleCache\\' =>
array (
0 => __DIR__ . '/..' . '/psr/simple-cache/src',
),
'Psr\\Log\\' =>
array (
0 => __DIR__ . '/..' . '/psr/log/Psr/Log',
),
'Psr\\Http\\Message\\' =>
array (
0 => __DIR__ . '/..' . '/psr/http-message/src',
),
'Psr\\Container\\' =>
array (
0 => __DIR__ . '/..' . '/psr/container/src',
),
'PhpParser\\' =>
array (
0 => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser',
),
'PhpOffice\\PhpSpreadsheet\\' =>
array (
0 => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet',
),
'Phinx\\' =>
array (
0 => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx',
),
'ParseCsv\\' =>
array (
0 => __DIR__ . '/..' . '/parsecsv/php-parsecsv/src',
),
'PHPMailer\\PHPMailer\\' =>
array (
0 => __DIR__ . '/..' . '/phpmailer/phpmailer/src',
),
'Mpdf\\' =>
array (
0 => __DIR__ . '/..' . '/mpdf/mpdf/src',
),
'Migrations\\' =>
array (
0 => __DIR__ . '/..' . '/cakephp/migrations/src',
),
'Matrix\\' =>
array (
0 => __DIR__ . '/..' . '/markbaker/matrix/classes/src',
),
'M1\\Env\\' =>
array (
0 => __DIR__ . '/..' . '/m1/env/src',
),
'JsonSchema\\' =>
array (
0 => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema',
),
'Jasny\\Twig\\' =>
array (
0 => __DIR__ . '/..' . '/jasny/twig-extensions/src',
),
'JakubOnderka\\PhpConsoleHighlighter\\' =>
array (
0 => __DIR__ . '/..' . '/jakub-onderka/php-console-highlighter/src',
),
'JakubOnderka\\PhpConsoleColor\\' =>
array (
0 => __DIR__ . '/..' . '/jakub-onderka/php-console-color/src',
),
'FontLib\\' =>
array (
0 => __DIR__ . '/..' . '/phenx/php-font-lib/src/FontLib',
),
'Dompdf\\' =>
array (
0 => __DIR__ . '/..' . '/dompdf/dompdf/src',
),
'DeepCopy\\' =>
array (
0 => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy',
),
'DebugKit\\Test\\Fixture\\' =>
array (
0 => __DIR__ . '/..' . '/cakephp/debug_kit/tests/Fixture',
),
'DebugKit\\' =>
array (
0 => __DIR__ . '/..' . '/cakephp/debug_kit/src',
),
'Composer\\XdebugHandler\\' =>
array (
0 => __DIR__ . '/..' . '/composer/xdebug-handler/src',
),
'Composer\\Spdx\\' =>
array (
0 => __DIR__ . '/..' . '/composer/spdx-licenses/src',
),
'Composer\\Semver\\' =>
array (
0 => __DIR__ . '/..' . '/composer/semver/src',
),
'Composer\\CaBundle\\' =>
array (
0 => __DIR__ . '/..' . '/composer/ca-bundle/src',
),
'Composer\\' =>
array (
0 => __DIR__ . '/..' . '/composer/composer/src/Composer',
),
'Complex\\' =>
array (
0 => __DIR__ . '/..' . '/markbaker/complex/classes/src',
),
'Cake\\Test\\' =>
array (
0 => __DIR__ . '/..' . '/cakephp/cakephp/tests',
),
'Cake\\Composer\\' =>
array (
0 => __DIR__ . '/..' . '/cakephp/plugin-installer/src',
),
'Cake\\Chronos\\' =>
array (
0 => __DIR__ . '/..' . '/cakephp/chronos/src',
),
'Cake\\' =>
array (
0 => __DIR__ . '/..' . '/cakephp/cakephp/src',
),
'CakePHP\\' =>
array (
0 => __DIR__ . '/..' . '/cakephp/cakephp-codesniffer/CakePHP',
),
'Bake\\' =>
array (
0 => __DIR__ . '/..' . '/cakephp/bake/src',
),
'Aura\\Intl\\' =>
array (
0 => __DIR__ . '/..' . '/aura/intl/src',
),
'App\\Test\\' =>
array (
0 => __DIR__ . '/../..' . '/tests',
),
'App\\' =>
array (
0 => __DIR__ . '/../..' . '/src',
),
'Ajgl\\Twig\\Extension\\' =>
array (
0 => __DIR__ . '/..' . '/ajgl/breakpoint-twig-extension/src',
),
);
public static $fallbackDirsPsr4 = array (
0 => __DIR__ . '/..' . '/asm89/twig-cache-extension/lib',
);
public static $prefixesPsr0 = array (
'j' =>
array (
'josegonzalez\\Dotenv' =>
array (
0 => __DIR__ . '/..' . '/josegonzalez/dotenv/src',
1 => __DIR__ . '/..' . '/josegonzalez/dotenv/tests',
),
),
'U' =>
array (
'Umpirsky\\' =>
array (
0 => __DIR__ . '/..' . '/umpirsky/twig-php-function/src',
),
),
'T' =>
array (
'Twig_' =>
array (
0 => __DIR__ . '/..' . '/twig/twig/lib',
),
),
'S' =>
array (
'Sabberworm\\CSS' =>
array (
0 => __DIR__ . '/..' . '/sabberworm/php-css-parser/lib',
),
),
'D' =>
array (
'Detection' =>
array (
0 => __DIR__ . '/..' . '/mobiledetect/mobiledetectlib/namespaced',
),
),
'A' =>
array (
'Aptoma' =>
array (
0 => __DIR__ . '/..' . '/aptoma/twig-markdown/src',
),
),
);
public static $classMap = array (
'Dompdf\\Cpdf' => __DIR__ . '/..' . '/dompdf/dompdf/lib/Cpdf.php',
'HTML5_Data' => __DIR__ . '/..' . '/dompdf/dompdf/lib/html5lib/Data.php',
'HTML5_InputStream' => __DIR__ . '/..' . '/dompdf/dompdf/lib/html5lib/InputStream.php',
'HTML5_Parser' => __DIR__ . '/..' . '/dompdf/dompdf/lib/html5lib/Parser.php',
'HTML5_Tokenizer' => __DIR__ . '/..' . '/dompdf/dompdf/lib/html5lib/Tokenizer.php',
'HTML5_TreeBuilder' => __DIR__ . '/..' . '/dompdf/dompdf/lib/html5lib/TreeBuilder.php',
'JsonException' => __DIR__ . '/..' . '/symfony/polyfill-php73/Resources/stubs/JsonException.php',
'Mobile_Detect' => __DIR__ . '/..' . '/mobiledetect/mobiledetectlib/Mobile_Detect.php',
'SqlFormatter' => __DIR__ . '/..' . '/jdorn/sql-formatter/lib/SqlFormatter.php',
);
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInit89ba59be1ed926d2e0969daf36fcbd1f::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInit89ba59be1ed926d2e0969daf36fcbd1f::$prefixDirsPsr4;
$loader->fallbackDirsPsr4 = ComposerStaticInit89ba59be1ed926d2e0969daf36fcbd1f::$fallbackDirsPsr4;
$loader->prefixesPsr0 = ComposerStaticInit89ba59be1ed926d2e0969daf36fcbd1f::$prefixesPsr0;
$loader->classMap = ComposerStaticInit89ba59be1ed926d2e0969daf36fcbd1f::$classMap;
}, null, ClassLoader::class);
}
}
+19
View File
@@ -0,0 +1,19 @@
Copyright (C) 2016 Composer
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+85
View File
@@ -0,0 +1,85 @@
composer/ca-bundle
==================
Small utility library that lets you find a path to the system CA bundle,
and includes a fallback to the Mozilla CA bundle.
Originally written as part of [composer/composer](https://github.com/composer/composer),
now extracted and made available as a stand-alone library.
Installation
------------
Install the latest version with:
```bash
$ composer require composer/ca-bundle
```
Requirements
------------
* PHP 5.3.2 is required but using the latest version of PHP is highly recommended.
Basic usage
-----------
### `Composer\CaBundle\CaBundle`
- `CaBundle::getSystemCaRootBundlePath()`: Returns the system CA bundle path, or a path to the bundled one as fallback
- `CaBundle::getBundledCaBundlePath()`: Returns the path to the bundled CA file
- `CaBundle::validateCaFile($filename)`: Validates a CA file using openssl_x509_parse only if it is safe to use
- `CaBundle::isOpensslParseSafe()`: Test if it is safe to use the PHP function openssl_x509_parse()
- `CaBundle::reset()`: Resets the static caches
#### To use with curl
```php
$curl = curl_init("https://example.org/");
$caPathOrFile = \Composer\CaBundle\CaBundle::getSystemCaRootBundlePath();
if (is_dir($caPathOrFile)) {
curl_setopt($curl, CURLOPT_CAPATH, $caPathOrFile);
} else {
curl_setopt($curl, CURLOPT_CAINFO, $caPathOrFile);
}
$result = curl_exec($curl);
```
#### To use with php streams
```php
$opts = array(
'http' => array(
'method' => "GET"
)
);
$caPathOrFile = \Composer\CaBundle\CaBundle::getSystemCaRootBundlePath();
if (is_dir($caPathOrFile)) {
$opts['ssl']['capath'] = $caPathOrFile;
} else {
$opts['ssl']['cafile'] = $caPathOrFile;
}
$context = stream_context_create($opts);
$result = file_get_contents('https://example.com', false, $context);
```
#### To use with Guzzle
```php
$client = new \GuzzleHttp\Client([
\GuzzleHttp\RequestOptions::VERIFY => \Composer\CaBundle\CaBundle::getSystemCaRootBundlePath()
]);
```
License
-------
composer/ca-bundle is licensed under the MIT License, see the LICENSE file for details.
+54
View File
@@ -0,0 +1,54 @@
{
"name": "composer/ca-bundle",
"description": "Lets you find a path to the system CA bundle, and includes a fallback to the Mozilla CA bundle.",
"type": "library",
"license": "MIT",
"keywords": [
"cabundle",
"cacert",
"certificate",
"ssl",
"tls"
],
"authors": [
{
"name": "Jordi Boggiano",
"email": "j.boggiano@seld.be",
"homepage": "http://seld.be"
}
],
"support": {
"irc": "irc://irc.freenode.org/composer",
"issues": "https://github.com/composer/ca-bundle/issues"
},
"require": {
"ext-openssl": "*",
"ext-pcre": "*",
"php": "^5.3.2 || ^7.0 || ^8.0"
},
"require-dev": {
"phpunit/phpunit": "^4.8.35 || ^5.7 || 6.5 - 8",
"psr/log": "^1.0",
"symfony/process": "^2.5 || ^3.0 || ^4.0 || ^5.0"
},
"autoload": {
"psr-4": {
"Composer\\CaBundle\\": "src"
}
},
"autoload-dev": {
"psr-4": {
"Composer\\CaBundle\\": "tests"
}
},
"extra": {
"branch-alias": {
"dev-master": "1.x-dev"
}
},
"config": {
"platform": {
"php": "5.3.9"
}
}
}
File diff suppressed because it is too large. Load diff
+324
View File
@@ -0,0 +1,324 @@
<?php
/*
* This file is part of composer/ca-bundle.
*
* (c) Composer <https://github.com/composer>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Composer\CaBundle;
use Psr\Log\LoggerInterface;
use Symfony\Component\Process\PhpProcess;
/**
* @author Chris Smith <chris@cs278.org>
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
class CaBundle
{
private static $caPath;
private static $caFileValidity = array();
private static $useOpensslParse;
/**
* Returns the system CA bundle path, or a path to the bundled one
*
* This method was adapted from Sslurp.
* https://github.com/EvanDotPro/Sslurp
*
* (c) Evan Coury <me@evancoury.com>
*
* For the full copyright and license information, please see below:
*
* Copyright (c) 2013, Evan Coury
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @param LoggerInterface $logger optional logger for information about which CA files were loaded
* @return string path to a CA bundle file or directory
*/
public static function getSystemCaRootBundlePath(LoggerInterface $logger = null)
{
if (self::$caPath !== null) {
return self::$caPath;
}
$caBundlePaths = array();
// If SSL_CERT_FILE env variable points to a valid certificate/bundle, use that.
// This mimics how OpenSSL uses the SSL_CERT_FILE env variable.
$caBundlePaths[] = self::getEnvVariable('SSL_CERT_FILE');
// If SSL_CERT_DIR env variable points to a valid certificate/bundle, use that.
// This mimics how OpenSSL uses the SSL_CERT_FILE env variable.
$caBundlePaths[] = self::getEnvVariable('SSL_CERT_DIR');
$caBundlePaths[] = ini_get('openssl.cafile');
$caBundlePaths[] = ini_get('openssl.capath');
$otherLocations = array(
'/etc/pki/tls/certs/ca-bundle.crt', // Fedora, RHEL, CentOS (ca-certificates package)
'/etc/ssl/certs/ca-certificates.crt', // Debian, Ubuntu, Gentoo, Arch Linux (ca-certificates package)
'/etc/ssl/ca-bundle.pem', // SUSE, openSUSE (ca-certificates package)
'/usr/local/share/certs/ca-root-nss.crt', // FreeBSD (ca_root_nss_package)
'/usr/ssl/certs/ca-bundle.crt', // Cygwin
'/opt/local/share/curl/curl-ca-bundle.crt', // OS X macports, curl-ca-bundle package
'/usr/local/share/curl/curl-ca-bundle.crt', // Default cURL CA bunde path (without --with-ca-bundle option)
'/usr/share/ssl/certs/ca-bundle.crt', // Really old RedHat?
'/etc/ssl/cert.pem', // OpenBSD
'/usr/local/etc/ssl/cert.pem', // FreeBSD 10.x
'/usr/local/etc/openssl/cert.pem', // OS X homebrew, openssl package
'/usr/local/etc/openssl@1.1/cert.pem', // OS X homebrew, openssl@1.1 package
);
foreach($otherLocations as $location) {
$otherLocations[] = dirname($location);
}
$caBundlePaths = array_merge($caBundlePaths, $otherLocations);
foreach ($caBundlePaths as $caBundle) {
if (self::caFileUsable($caBundle, $logger)) {
return self::$caPath = $caBundle;
}
if (self::caDirUsable($caBundle)) {
return self::$caPath = $caBundle;
}
}
return self::$caPath = static::getBundledCaBundlePath(); // Bundled CA file, last resort
}
/**
* Returns the path to the bundled CA file
*
* In case you don't want to trust the user or the system, you can use this directly
*
* @return string path to a CA bundle file
*/
public static function getBundledCaBundlePath()
{
$caBundleFile = __DIR__.'/../res/cacert.pem';
// cURL does not understand 'phar://' paths
// see https://github.com/composer/ca-bundle/issues/10
if (0 === strpos($caBundleFile, 'phar://')) {
file_put_contents(
$tempCaBundleFile = tempnam(sys_get_temp_dir(), 'openssl-ca-bundle-'),
file_get_contents($caBundleFile)
);
register_shutdown_function(function() use ($tempCaBundleFile) {
@unlink($tempCaBundleFile);
});
$caBundleFile = $tempCaBundleFile;
}
return $caBundleFile;
}
/**
* Validates a CA file using opensl_x509_parse only if it is safe to use
*
* @param string $filename
* @param LoggerInterface $logger optional logger for information about which CA files were loaded
*
* @return bool
*/
public static function validateCaFile($filename, LoggerInterface $logger = null)
{
static $warned = false;
if (isset(self::$caFileValidity[$filename])) {
return self::$caFileValidity[$filename];
}
$contents = file_get_contents($filename);
// assume the CA is valid if php is vulnerable to
// https://www.sektioneins.de/advisories/advisory-012013-php-openssl_x509_parse-memory-corruption-vulnerability.html
if (!static::isOpensslParseSafe()) {
if (!$warned && $logger) {
$logger->warning(sprintf(
'Your version of PHP, %s, is affected by CVE-2013-6420 and cannot safely perform certificate validation, we strongly suggest you upgrade.',
PHP_VERSION
));
$warned = true;
}
$isValid = !empty($contents);
} else {
$isValid = (bool) openssl_x509_parse($contents);
}
if ($logger) {
$logger->debug('Checked CA file '.realpath($filename).': '.($isValid ? 'valid' : 'invalid'));
}
return self::$caFileValidity[$filename] = $isValid;
}
/**
* Test if it is safe to use the PHP function openssl_x509_parse().
*
* This checks if OpenSSL extensions is vulnerable to remote code execution
* via the exploit documented as CVE-2013-6420.
*
* @return bool
*/
public static function isOpensslParseSafe()
{
if (null !== self::$useOpensslParse) {
return self::$useOpensslParse;
}
if (PHP_VERSION_ID >= 50600) {
return self::$useOpensslParse = true;
}
// Vulnerable:
// PHP 5.3.0 - PHP 5.3.27
// PHP 5.4.0 - PHP 5.4.22
// PHP 5.5.0 - PHP 5.5.6
if (
(PHP_VERSION_ID < 50400 && PHP_VERSION_ID >= 50328)
|| (PHP_VERSION_ID < 50500 && PHP_VERSION_ID >= 50423)
|| (PHP_VERSION_ID < 50600 && PHP_VERSION_ID >= 50507)
) {
// This version of PHP has the fix for CVE-2013-6420 applied.
return self::$useOpensslParse = true;
}
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
// Windows is probably insecure in this case.
return self::$useOpensslParse = false;
}
$compareDistroVersionPrefix = function ($prefix, $fixedVersion) {
$regex = '{^'.preg_quote($prefix).'([0-9]+)$}';
if (preg_match($regex, PHP_VERSION, $m)) {
return ((int) $m[1]) >= $fixedVersion;
}
return false;
};
// Hard coded list of PHP distributions with the fix backported.
if (
$compareDistroVersionPrefix('5.3.3-7+squeeze', 18) // Debian 6 (Squeeze)
|| $compareDistroVersionPrefix('5.4.4-14+deb7u', 7) // Debian 7 (Wheezy)
|| $compareDistroVersionPrefix('5.3.10-1ubuntu3.', 9) // Ubuntu 12.04 (Precise)
) {
return self::$useOpensslParse = true;
}
// Symfony Process component is missing so we assume it is unsafe at this point
if (!class_exists('Symfony\Component\Process\PhpProcess')) {
return self::$useOpensslParse = false;
}
// This is where things get crazy, because distros backport security
// fixes the chances are on NIX systems the fix has been applied but
// it's not possible to verify that from the PHP version.
//
// To verify exec a new PHP process and run the issue testcase with
// known safe input that replicates the bug.
// Based on testcase in https://github.com/php/php-src/commit/c1224573c773b6845e83505f717fbf820fc18415
// changes in https://github.com/php/php-src/commit/76a7fd893b7d6101300cc656058704a73254d593
$cert = 'LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVwRENDQTR5Z0F3SUJBZ0lKQUp6dThyNnU2ZUJjTUEwR0NTcUdTSWIzRFFFQkJRVUFNSUhETVFzd0NRWUQKVlFRR0V3SkVSVEVjTUJvR0ExVUVDQXdUVG05eVpISm9aV2x1TFZkbGMzUm1ZV3hsYmpFUU1BNEdBMVVFQnd3SApTOE9Ed3Jac2JqRVVNQklHQTFVRUNnd0xVMlZyZEdsdmJrVnBibk14SHpBZEJnTlZCQXNNRmsxaGJHbGphVzkxCmN5QkRaWEowSUZObFkzUnBiMjR4SVRBZkJnTlZCQU1NR0cxaGJHbGphVzkxY3k1elpXdDBhVzl1WldsdWN5NWsKWlRFcU1DZ0dDU3FHU0liM0RRRUpBUlliYzNSbFptRnVMbVZ6YzJWeVFITmxhM1JwYjI1bGFXNXpMbVJsTUhVWQpaREU1TnpBd01UQXhNREF3TURBd1dnQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBCkFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUEKQUFBQUFBQVhEVEUwTVRFeU9ERXhNemt6TlZvd2djTXhDekFKQmdOVkJBWVRBa1JGTVJ3d0dnWURWUVFJREJOTwpiM0prY21obGFXNHRWMlZ6ZEdaaGJHVnVNUkF3RGdZRFZRUUhEQWRMdzRQQ3RteHVNUlF3RWdZRFZRUUtEQXRUClpXdDBhVzl1UldsdWN6RWZNQjBHQTFVRUN3d1dUV0ZzYVdOcGIzVnpJRU5sY25RZ1UyVmpkR2x2YmpFaE1COEcKQTFVRUF3d1liV0ZzYVdOcGIzVnpMbk5sYTNScGIyNWxhVzV6TG1SbE1Tb3dLQVlKS29aSWh2Y05BUWtCRmh0egpkR1ZtWVc0dVpYTnpaWEpBYzJWcmRHbHZibVZwYm5NdVpHVXdnZ0VpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElCCkR3QXdnZ0VLQW9JQkFRRERBZjNobDdKWTBYY0ZuaXlFSnBTU0RxbjBPcUJyNlFQNjV1c0pQUnQvOFBhRG9xQnUKd0VZVC9OYSs2ZnNnUGpDMHVLOURaZ1dnMnRIV1dvYW5TYmxBTW96NVBINlorUzRTSFJaN2UyZERJalBqZGhqaAowbUxnMlVNTzV5cDBWNzk3R2dzOWxOdDZKUmZIODFNTjJvYlhXczROdHp0TE11RDZlZ3FwcjhkRGJyMzRhT3M4CnBrZHVpNVVhd1Raa3N5NXBMUEhxNWNNaEZHbTA2djY1Q0xvMFYyUGQ5K0tBb2tQclBjTjVLTEtlYno3bUxwazYKU01lRVhPS1A0aWRFcXh5UTdPN2ZCdUhNZWRzUWh1K3ByWTNzaTNCVXlLZlF0UDVDWm5YMmJwMHdLSHhYMTJEWAoxbmZGSXQ5RGJHdkhUY3lPdU4rblpMUEJtM3ZXeG50eUlJdlZBZ01CQUFHalFqQkFNQWtHQTFVZEV3UUNNQUF3CkVRWUpZSVpJQVliNFFnRUJCQVFEQWdlQU1Bc0dBMVVkRHdRRUF3SUZvREFUQmdOVkhTVUVEREFLQmdnckJnRUYKQlFjREFqQU5CZ2txaGtpRzl3MEJBUVVGQUFPQ0FRRUFHMGZaWVlDVGJkajFYWWMrMVNub2FQUit2SThDOENhRAo4KzBVWWhkbnlVNGdnYTBCQWNEclk5ZTk0ZUVBdTZacXljRjZGakxxWFhkQWJvcHBXb2NyNlQ2R0QxeDMzQ2tsClZBcnpHL0t4UW9oR0QySmVxa2hJTWxEb214SE83a2EzOStPYThpMnZXTFZ5alU4QVp2V01BcnVIYTRFRU55RzcKbFcyQWFnYUZLRkNyOVRuWFRmcmR4R1ZFYnY3S1ZRNmJkaGc1cDVTanBXSDErTXEwM3VSM1pYUEJZZHlWODMxOQpvMGxWajFLRkkyRENML2xpV2lzSlJvb2YrMWNSMzVDdGQwd1lCY3BCNlRac2xNY09QbDc2ZHdLd0pnZUpvMlFnClpzZm1jMnZDMS9xT2xOdU5xLzBUenprVkd2OEVUVDNDZ2FVK1VYZTRYT1Z2a2NjZWJKbjJkZz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K';
$script = <<<'EOT'
error_reporting(-1);
$info = openssl_x509_parse(base64_decode('%s'));
var_dump(PHP_VERSION, $info['issuer']['emailAddress'], $info['validFrom_time_t']);
EOT;
$script = '<'."?php\n".sprintf($script, $cert);
try {
$process = new PhpProcess($script);
$process->mustRun();
} catch (\Exception $e) {
// In the case of any exceptions just accept it is not possible to
// determine the safety of openssl_x509_parse and bail out.
return self::$useOpensslParse = false;
}
$output = preg_split('{\r?\n}', trim($process->getOutput()));
$errorOutput = trim($process->getErrorOutput());
if (
count($output) === 3
&& $output[0] === sprintf('string(%d) "%s"', strlen(PHP_VERSION), PHP_VERSION)
&& $output[1] === 'string(27) "stefan.esser@sektioneins.de"'
&& $output[2] === 'int(-1)'
&& preg_match('{openssl_x509_parse\(\): illegal (?:ASN1 data type for|length in) timestamp in - on line \d+}', $errorOutput)
) {
// This PHP has the fix backported probably by a distro security team.
return self::$useOpensslParse = true;
}
return self::$useOpensslParse = false;
}
/**
* Resets the static caches
*/
public static function reset()
{
self::$caFileValidity = array();
self::$caPath = null;
self::$useOpensslParse = null;
}
private static function getEnvVariable($name)
{
if (isset($_SERVER[$name])) {
return (string) $_SERVER[$name];
}
if (PHP_SAPI === 'cli' && ($value = getenv($name)) !== false && $value !== null) {
return (string) $value;
}
return false;
}
private static function caFileUsable($certFile, LoggerInterface $logger = null)
{
return $certFile && @is_file($certFile) && @is_readable($certFile) && static::validateCaFile($certFile, $logger);
}
private static function caDirUsable($certDir)
{
return $certDir && @is_dir($certDir) && @is_readable($certDir) && glob($certDir . '/*');
}
}
+11
View File
@@ -0,0 +1,11 @@
root = true
[*]
charset = utf-8
indent_size = 4
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
[*.yml]
indent_size = 2
+17
View File
@@ -0,0 +1,17 @@
# Auto-detect text files, ensure they use LF.
* text=auto eol=lf
# These files are always considered text and should use LF.
# See core.whitespace @ http://git-scm.com/docs/git-config for whitespace flags.
*.php text eol=lf whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent,tabwidth=4 diff=php
*.json text eol=lf whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent,tabwidth=4
*.test text eol=lf whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent,tabwidth=4
*.yml text eol=lf whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent,tabwidth=2
# Exclude non-essential files from dist
/tests export-ignore
.github export-ignore
.php_cs export-ignore
.travis.yml export-ignore
appveyor.yml export-ignore
phpunit.xml.dist export-ignore
+11
View File
@@ -0,0 +1,11 @@
/.settings
/.project
/.buildpath
/composer.phar
/vendor
/nbproject
phpunit.xml
.vagrant
Vagrantfile
.idea
.php_cs.cache
+855
View File
@@ -0,0 +1,855 @@
### [1.9.3] 2020-02-04
* Fixed GitHub deprecation of access_token query parameter, now using Authorization header
### [1.9.2] 2020-01-14
* Fixed minor git driver bugs
* Fixed schema validation for version field to allow dev-* versions too
* Fixed external processes' output being formatted even though it should not
* Fixed issue with path repositories when trying to install feature branches
### [1.9.1] 2019-11-01
* Fixed various credential handling issues with gitlab and github
* Fixed credentials being present in git remotes in Composer cache and vendor directory when not using SSH keys
* Fixed `composer why` not listing replacers as a reason something is present
* Fixed various PHP 7.4 compatibility issues
* Fixed root warnings always present in Docker containers, setting COMPOSER_ALLOW_SUPERUSER is not necessary anymore
* Fixed GitHub access tokens leaking into debug-verbosity output
* Fixed several edge case issues detecting GitHub, Bitbucket and GitLab repository types
* Fixed Composer asking if you want to use a composer.json in a parent directory when ran in non-interactive mode
* Fixed classmap autoloading issue finding classes located within a few non-PHP context blocks (?>...<?php)
### [1.9.0] 2019-08-02
* Breaking: artifact repositories with URLs containing port numbers and requiring authentication now require you to configure http-basic auth for the `host:port` pair explicitly
* Added a `--no-cache` flag available on all commands to run with the cache disabled
* Added PHP_BINARY as env var pointing to the PHP process when executing Composer scripts as shell scripts
* Added a `use-github-api` config option which can set the `no-api` flag on all GitHub VCS repositories declared
* Added a static helper you can preprend to a script to avoid process timeouts, `"Composer\\Config::disableProcessTimeout"`
* Added Event::getOriginatingEvent to retrieve an event's original event when a script handler forwards to another one
* Added support for autoloading directly from a phar file
* Fixed loading order of plugins to always initialize them in order of dependencies
* Fixed various network-mount related issues
* Fixed --ignore-platform-reqs not ignoring conflict rules against platform packages
### [1.8.6] 2019-06-11
* Fixed handling of backslash-escapes handling in composer.json when using the require command
* Fixed create-project not following classmap-authoritative and apcu-autoloader config values
* Fixed HHVM version warning showing up in some cases when it was not in use
### [1.8.5] 2019-04-09
* HHVM 4.0 is no longer compatible with Composer. Please use PHP instead going forward.
* Added forward compatibility with upcoming 2.0 changes
* Fixed support for PHP 7.3-style heredoc/nowdoc syntax changes in autoload generation
* Fixed require command usage when combined with --ignore-platform-reqs
* Fixed and cleaned up various Windows junctions handling issues
### [1.8.4] 2019-02-11
* Fixed long standing solver bug leading to odd solving issues in edge cases, see #7946
* Fixed HHVM support for upcoming releases
* Fixed unix proxy for binaries to be POSIX compatible instead of breaking some shells
* Fixed invalid deprecation warning for composer-plugin-api
* Fixed edge case issues with Windows junctions when working with path repositories
### [1.8.3] 2019-01-30
* Fixed regression when executing partial updates
### [1.8.2] 2019-01-29
* Fixed invalid deprecation warning for ext-pdo_mysql and similar
* Updated to latest xdebug-handler
### [1.8.1] 2019-01-29
* Deprecated support for non-standard package names (anything with uppercase, or no / in it). Make sure to follow the warnings if you see any to avoid problems in 2.0.
* Fixed some packages missing from the autoloader config when installing with --no-dev
* Fixed support for cloning GitLab repos using OAuth tokens instead of SSH keys
* Fixed metapackage installs/updates missing from output
* Fixed --with-dependencies / --with-all-dependencies not updating some packages in some edge cases
* Fixed compatibility with Symfony 4.2 deprecations
* Fixed temp dir not being cleaned up on download error while archiving packages
* Updated to latest ca-bundle
### [1.8.0] 2018-12-03
* Changed `post-package-install` / `post-package-update` event to be fired *after* the lock file has been updated as opposed to before
* Added support for removing packages using a wildcard with the `remove` command, e.g. `composer remove foo/*`
* Added `chat` to the list of `support` channels you can list in composer.json
* Added signal handling on require command to restore the composer.json in case of abort
* Added `--ignore` to `outdated` command to pass one or more packages that you do not want to be listed
* Added `--no-dev` to `check-platform-reqs` command to skip dev requirements even if they are installed
* Added support for running plugin commands from sub-directories within a project much like other Composer commands
* Added support for running Composer via phpdbg
* Added `lib-imagick` platform package
* Fixed validate command always checking for disabled checks when used with `--strict`
### [1.7.3] 2018-11-01
* Fixed handling of replace/conflict rules. This may affect dependency resolution in some edge cases.
* Fixed Bitbucket API support and migrated all calls to API v2 as v1 is deprecated
* Fixed support for lib-openssl 1.1.1 having only lowercase algorithm names
* Fixed escaping of URLs in Perforce and Svn drivers
* Fixed `show` command not respecting `--path` when a single package name was given
* Fixed regression in 1.7.2's handling of metapackages
### [1.7.2] 2018-08-16
* Fixed reporting of authentication/rate limiting issues for GitHub API access
* Fixed `create-project` not checking the checking the latest commit out when a cache was already present
* Fixed reporting of errors when `global` command can not switch the working directory
* Fixed PHP 5.3 JSON encoding issues with complex unicode character sequences
* Updated to latest ca-bundle and xdebug-handler projects, see related changelogs
### [1.7.1] 2018-08-07
* Fixed issue autoloading plugins in require-dev in some conditions
* Fixed handling of SSL to repo.packagist.org on very old PHP versions
### [1.7.0] 2018-08-03
* Added the overridden platform config's PHP version in the `diagnose` command output
* Fixed --no-plugins not being respected in a few commands
* Fixed 1.7.0-RC regression in output showing <warn> instead of proper colors
* Fixed 1.7.0-RC regression in output missing "Loading from cache" output on package install
### [1.7.0-RC] 2018-07-24
* Changed default repository URL from packagist.org to repo.packagist.org, this might affect people with strict firewall rules
* Changed output from Updating to Downgrading when performing package downgrades, this might affect anything parsing output
* Several minor performance improvements
* Added basic authentication support for mercurial repos
* Added explicit `i` and `u` aliases for the `install` and `update` commands
* Added support for `show` command to output json format with --tree
* Added support for {glob,braces} support in the path repository's path argument
* Added support in `status` command for showing diffs in vendor dir even for packages installed as dist/zip archives
* Added `--remove-vcs` flag to `create-project` command to avoid prompting for keeping VCS files
* Added `--no-secure-http` flag to `create-project` command to bypass https (use at your own risk)
* Added `pre-command-run` event that lets plugins modify arguments
* Added RemoteFilesystem::getRemoteContents extension point
* Fixed setting scripts via `config` command
### [1.6.5] 2018-05-04
* Fixed regression in 1.6.4 causing strange update behaviors with dev packages
* Fixed regression in 1.6.4 color support detection for Windows
* Fixed issues dealing with broken symlinks when switching branches and using path repositories
* Fixed JSON schema for package repositories
* Fixed issues on computers set to Turkish locale
* Fixed classmap parsing of files using short-open-tags when they are disabled in php
### [1.6.4] 2018-04-13
* Security fixes in some edge case scenarios, recommended update for all users
* Fixed regression in version guessing of path repositories
* Fixed removing aliased packages from the repository, which might resolve some odd update bugs
* Fixed updating of package URLs for GitLab
* Fixed run-script --list failing when script handlers were defined
* Fixed init command not respecting the current php version when selecting package versions
* Fixed handling of uppercase package names in why/why-not commands
* Fixed exclude-from-classmap symlink handling
* Fixed filesystem permissions of PEAR binaries
* Improved performance of subversion repos
* Other minor fixes
### [1.6.3] 2018-01-31
* Fixed GitLab downloads failing in some edge cases
* Fixed ctrl-C handling during create-project
* Fixed GitHub VCS repositories not prompting for a token in some conditions
* Fixed SPDX license identifiers being case sensitive
* Fixed and clarified a few dependency resolution error reporting strings
* Fixed SVN commit log fetching in verbose mode when using private repositories
### [1.6.2] 2018-01-05
* Fixed more autoloader regressions
* Fixed support for updating dist refs in gitlab URLs
### [1.6.1] 2018-01-04
* Fixed upgrade regression due to some autoloader cleanups
* Fixed some overly loose version constraints
### [1.6.0] 2018-01-04
* Added support for SPDX license identifiers v3.0, deprecates GPL/LGPL/AGPL identifiers, which should now have a `-only` or `-or-later` suffix added.
* Added support for COMPOSER_MEMORY_LIMIT env var to make Composer set the PHP memory limit explicitly
* Added support for simple strings for the `bin`
* Fixed `check-platform-reqs` bug in version checking
### [1.6.0-RC] 2017-12-19
* Improved performance of installs and updates from git clones when checking out known commits
* Added `check-platform-reqs` command that checks that your PHP and extensions versions match the platform requirements of the installed packages
* Added `--with-all-dependencies` to the `update` and `require` commands which updates all dependencies of the listed packages, including those that are direct root requirements
* Added `scripts-descriptions` key to composer.json to customize the description and document your custom commands
* Added support for the uppercase NO_PROXY env var
* Added support for COMPOSER_DEFAULT_{AUTHOR,LICENSE,EMAIL,VENDOR} env vars to pre-populate init command values
* Added support for local fossil repositories
* Added suggestions for alternative spellings when entering packages in `init` and `require` commands and nothing can be found
* Fixed installed.json data to be sorted alphabetically by package name
* Fixed compatibility with Symfony 4.x components that Composer uses
### [1.5.6] - 2017-12-18
* Fixed root package version guessed when a tag is checked out
* Fixed support for GitLab repos hosted on non-standard ports
* Fixed regression in require command when requiring unstable packages, part 3
### [1.5.5] - 2017-12-01
* Fixed regression in require command when requiring unstable packages, part 2
### [1.5.4] - 2017-12-01
* Fixed regression in require command when requiring unstable packages
### [1.5.3] - 2017-11-30
* Fixed require/remove commands reverting the composer.json change when a non-solver-related error occurs
* Fixed GitLabDriver to support installations of GitLab not at the root of the domain
* Fixed create-project not following the optimize-autoloader flag of the root package
* Fixed Authorization header being forwarded across domains after a redirect
* Improved some error messages for clarity
### [1.5.2] - 2017-09-11
* Fixed GitLabDriver looping endlessly in some conditions
* Fixed GitLabDriver support for unauthenticated requests
* Fixed GitLab zip downloads not triggering credentials prompt if unauthenticated
* Fixed path repository support of COMPOSER_ROOT_VERSION, it now applies to all path repos within the same git repository
* Fixed path repository handling of copies to avoid copying VCS files and others
* Fixed sub-directory call to ignore list and create-project commands as well as calls to Composer using --working-dir
* Fixed invalid warning appearing when calling `remove` on an non-stable package
### [1.5.1] - 2017-08-09
* Fixed regression in GitLabDriver with repos containing >100 branches or tags
* Fixed sub-directory call support to respect the COMPOSER env var
### [1.5.0] - 2017-08-08
* Changed the package install order to ensure that plugins are always installed as soon as possible
* Added ability to call composer from within sub-directories of a project
* Added support for GitLab API v4
* Added support for GitLab sub-groups
* Added some more rules to composer validate
* Added support for reading the `USER` env when guessing the username in `composer init`
* Added warning when uncompressing files with the same name but difference cases on case insensitive filesystems
* Added `htaccess-protect` option / `COMPOSER_HTACCESS_PROTECT` env var to disable the .htaccess creation in home dir (defaults to true)
* Improved `clear-cache` command
* Minor improvements/fixes and many documentation updates
### [1.4.3] - 2017-08-06
* Fixed GitLab URLs
* Fixed root package version detection using latest git versions
* Fixed inconsistencies in date format in composer.lock when installing from source
* Fixed Mercurial support regression
* Fixed exclude-from-classmap not being applied when autoloading files for Composer plugins
* Fixed exclude-from-classmap being ignored when cwd has the wrong case on case insensitive filesystems
* Fixed several other minor issues
### [1.4.2] - 2017-05-17
* Fixed Bitbucket API handler parsing old deleted branches in hg repos
* Fixed regression in gitlab downloads
* Fixed output inconsistencies
* Fixed unicode handling in `init` command for author names
* Fixed useless warning when doing partial updates/removes on packages that are not currently installed
* Fixed xdebug disabling issue when combined with disable_functions and allow_url_fopen CLI overrides
### [1.4.1] - 2017-03-10
* Fixed `apcu-autoloader` config option being ignored in `dump-autoload` command
* Fixed json validation not allowing boolean for trunk-path, branches-path and tags-path in svn repos
* Fixed json validation not allowing repository URLs without scheme
### [1.4.0] - 2017-03-08
* Improved memory usage of dependency solver
* Added `--format json` option to the `outdated` and `show` command to get machine readable package listings
* Added `--ignore-filters` flag to `archive` command to bypass the .gitignore and co
* Added support for `outdated` output without ansi colors
* Added support for Bitbucket API v2
* Changed the require command to follow minimum-stability / prefer-stable values when picking a version
* Fixed regression when using composer in a Mercurial repository
### [1.3.3] - 2017-03-08
* **Capifony users beware**: This release has output format tweaks that mess up capifony interactive mode, see #6233
* Improved baseline psr-4 autoloader performance for projects with many nested namespaces configured
* Fixed issues with gitlab API access when the token had insufficient permissions
* Fixed some HHVM strict type issues
* Fixed version guessing of headless git checkouts in some conditions
* Fixed compatibility with subversion 1.8
* Fixed version guessing not working with svn/hg
* Fixed script/exec errors not being output correctly
* Fixed PEAR repository bug with pear.php.net
### [1.3.2] - 2017-01-27
* Added `COMPOSER_BINARY` env var that is defined within the scope of a Composer run automatically with the path to the phar file
* Fixed create-project ending in a detached HEAD when installing aliased packages
* Fixed composer show not returning non-zero exit code when the package does not exist
* Fixed `@composer` handling in scripts when --working-dir is used together with it
* Fixed private-GitLab handling of repos with dashes in them
### [1.3.1] - 2017-01-07
* Fixed dist downloads from Bitbucket
* Fixed some regressions related to xdebug disabling
* Fixed `--minor-only` flag in `outdated` command
* Fixed handling of config.platform.php which did not replace other php-* package's versions
### [1.3.0] - 2016-12-24
* Fixed handling of annotated git tags vs lightweight tags leading to useless updates sometimes
* Fixed ext-xdebug not being require-able anymore due to automatic xdebug disabling
* Fixed case insensitivity of remove command
### [1.3.0-RC] - 2016-12-11
* Added workaround for xdebug performance impact by restarting PHP without xdebug automatically in case it is enabled
* Added `--minor-only` to the `outdated` command to only show updates to minor versions and ignore new major versions
* Added `--apcu-autoloader` to the `update`/`install` commands and `--apcu` to `dump-autoload` to enable an APCu-caching autoloader, which can be more efficient than --classmap-authoritative if you attempt to autoload many classes that do not exist, or if you can not use authoritative classmaps for some reason
* Added summary of operations to be executed before they run, and made execution output more compact
* Added `php-debug` and `php-zts` virtual platform packages
* Added `gitlab-token` auth config for GitLab private tokens
* Added `--strict` to the `outdated` command to return a non-zero exit code when there are outdated packages
* Added ability to call php scripts using the current php interpreter (instead of finding php in PATH by default) in script handlers via `@php ...`
* Added `COMPOSER_ALLOW_XDEBUG` env var to circumvent the xdebug-disabling behavior
* Added `COMPOSER_MIRROR_PATH_REPOS` env var to force mirroring of path repositories vs symlinking
* Added `COMPOSER_DEV_MODE` env var that is set by Composer to forward the dev mode to script handlers
* Fixed support for git 2.11
* Fixed output from zip and rar leaking out when an error occurred
* Removed `hash` from composer.lock, only `content-hash` is now used which should reduce conflicts
* Minor fixes and performance improvements
### [1.2.4] - 2016-12-06
* Fixed regression in output handling of scripts from 1.2.3
* Fixed support for LibreSSL detection as lib-openssl
* Fixed issue with Zend Guard in the autoloader bootstrapping
* Fixed support for loading partial provider repositories
### [1.2.3] - 2016-12-01
* Fixed bug in HgDriver failing to identify BitBucket repositories
* Fixed support for loading partial provider repositories
### [1.2.2] - 2016-11-03
* Fixed selection of packages based on stability to be independent from package repository order
* Fixed POST_DEPENDENCIES_SOLVING not containing some operations in edge cases
* Fixed issue handling GitLab URLs containing dots and other special characters
* Fixed issue on Windows when running composer at the root of a drive
* Minor fixes
### [1.2.1] - 2016-09-12
* Fixed edge case issues with the static autoloader
* Minor fixes
### [1.2.0] - 2016-07-19
* Security: Fixed [httpoxy](https://httpoxy.org/) vulnerability
* Fixed `home` command to avoid rogue output on unix
* Fixed output of git clones to clearly state when clones are from cache
* (from 1.2 RC) Fixed ext-network-ipv6 to be php-ipv6
### [1.2.0-RC] - 2016-07-04
* Added caching of git repositories if you have git 2.3+ installed. Repositories will now be cached once and then cloned from local cache so subsequent installs should be faster
* Added detection of HEAD changes to the `status` command. If you `git checkout X` in a vendor directory for example it will tell you that it is not at the version that was installed
* Added a virtual `php-ipv6` extension to require PHP compiled with IPv6 support
* Added `--no-suggest` to `install` and `update` commands to skip output of suggestions at the end
* Added `--type` to the `search` command to restrict to a given package type
* Added fossil support as alternative to git/svn/.. for package downloads
* Improved BitBucket OAuth support
* Added support for blocking cache operations using COMPOSER_CACHE_DIR=/dev/null (or NUL on windows)
* Added support for using declare(strict_types=1) in plugins
* Added `--prefer-stable` and `--prefer-lowest` to the `require` command
* Added `--no-scripts` to the `require` and `remove` commands
* Added `_comment` top level key to the schema to endorse using it as a place to store comments (it can be a string or array of strings)
* Added support for justinrainbow/json-schema 2.0
* Fixed binaries not being re-installed if deleted by users or the bin-dir changes. `update` and `install` will now re-install them
* Many minor UX and docs improvements
### [1.1.3] - 2016-06-26
* Fixed bitbucket oauth instructions
* Fixed version parsing issue
* Fixed handling of bad proxies that modify JSON content on the fly
### [1.1.2] - 2016-05-31
* Fixed degraded mode issue when accessing packagist.org
* Fixed GitHub access_token being added on subsequent requests in case of redirections
* Fixed exclude-from-classmap not working in some circumstances
* Fixed openssl warning preventing the use of config command for disabling tls
### [1.1.1] - 2016-05-17
* Fixed regression in handling of #reference which made it update every time
* Fixed dev platform requirements being required even in --no-dev install from a lock file
* Fixed parsing of extension versions that do not follow valid numbers, we now try to parse x.y.z and ignore the rest
* Fixed exact constraints warnings appearing for 0.x versions
* Fixed regression in the `remove` command
### [1.1.0] - 2016-05-10
* Added fallback to SSH for https bitbucket URLs
* Added BaseCommand::isProxyCommand that can be overridden to mark a command as being a mere proxy, which helps avoid duplicate warnings etc on composer startup
* Fixed archiving generating long paths in zip files on Windows
### [1.1.0-RC] - 2016-04-29
* Added ability for plugins to register their own composer commands
* Optimized the autoloader initialization using static loading on PHP 5.6 and above, this reduces the load time for large classmaps to almost nothing
* Added `--latest` to `show` command to show the latest version available of your dependencies
* Added `--outdated` to `show` command an `composer outdated` alias for it, to show only packages in need of update
* Added `--direct` to `show` and `outdated` commands to show only your direct dependencies in the listing
* Added support for editing all top-level properties (name, minimum-stability, ...) as well as extra values via the `config` command
* Added abandoned state warning to the `show` and `outdated` commands when listing latest packages
* Added support for `~/` and `$HOME/` in the path repository paths
* Added support for wildcards in the `show` command package filter, e.g. `composer show seld/*`
* Added ability to call composer itself from scripts via `@composer ...`
* Added untracked files detection to the `status` command
* Added warning to `validate` command when using exact-version requires
* Added warning once per domain when accessing insecure URLs with secure-http disabled
* Added a dependency on composer/ca-bundle (extracted CA bundle management to a standalone lib)
* Added support for empty directories when archiving to tar
* Added an `init` event for plugins to react to, which occurs right after a Composer instance is fully initialized
* Added many new detections of problems in the `why-not`/`prohibits` command to figure out why something does not get installed in the expected version
* Added a deprecation notice for script event listeners that use legacy script classes
* Fixed abandoned state not showing up if you had a package installed before it was marked abandoned
* Fixed --no-dev updates creating an incomplete lock file, everything is now always resolved on update
* Fixed partial updates in case the vendor dir was not up to date with the lock file
### [1.0.3] - 2016-04-29
* Security: Fixed possible command injection from the env vars into our sudo detection
* Fixed interactive authentication with gitlab
* Fixed class name replacement in plugins
* Fixed classmap generation mistakenly detecting anonymous classes
* Fixed auto-detection of stability flags in complex constraints like `2.0-dev || ^1.5`
* Fixed content-length handling when redirecting to very small responses
### [1.0.2] - 2016-04-21
* Fixed regression in 1.0.1 on systems with mbstring.func_overload enabled
* Fixed regression in 1.0.1 that made dev packages update to the latest reference even if not whitelisted in a partial update
* Fixed init command ignoring the COMPOSER env var for choosing the json file name
* Fixed error reporting bug when the dependency resolution fails
* Fixed handling of `$` sign in composer config command in some cases it could corrupt the json file
### [1.0.1] - 2016-04-18
* Fixed URL updating when a package's URL changes, composer.lock now contains the right URL including correct reference
* Fixed URL updating of the origin git remote as well for packages installed as git clone
* Fixed binary .bat files generated from linux being incompatible with windows cmd
* Fixed handling of paths with trailing slashes in path repository
* Fixed create-project not using platform config when selecting a package
* Fixed self-update not showing the channel it uses to perform the update
* Fixed file downloads not failing loudly when the content does not match the Content-Length header
* Fixed secure-http detecting some malformed URLs as insecure
* Updated CA bundle
### [1.0.0] - 2016-04-05
* Added support for bitbucket-oauth configuration
* Added warning when running composer as super user, set COMPOSER_ALLOW_SUPERUSER=1 to hide the warning if you really must
* Added PluginManager::getGlobalComposer getter to retrieve the global instance (which can be null!)
* Fixed dependency solver error reporting in many cases it now shows you proper errors instead of just saying a package does not exist
* Fixed output of failed downloads appearing as 100% done instead of Failed
* Fixed handling of empty directories when archiving, they are not skipped anymore
* Fixed installation of broken plugins corrupting the vendor state when combined with symlinked path repositories
### [1.0.0-beta2] - 2016-03-27
* Break: The `install` command now turns into an `update` command automatically if you have no composer.lock. This was done only half-way before which caused inconsistencies
* Break: By default the `remove` command now removes dependencies as well, and --update-with-dependencies is deprecated. Use --no-update-with-dependencies to get old behavior
* Added support for update channels in `self-update`. All users will now update to stable builds by default. Run `self-update` with `--snapshot`, `--preview` or `--stable` to switch between update channels.
* Added support for SSL_CERT_DIR env var and openssl.capath ini value
* Added some conflict detection in `why-not` command
* Added suggestion of root package's suggests in `create-project` command
* Fixed `create-project` ignoring --ignore-platform-reqs when choosing a version of the package
* Fixed `search` command in a directory without composer.json
* Fixed path repository handling of symlinks on windows
* Fixed PEAR repo handling to prefer HTTPS mirrors over HTTP ones
* Fixed handling of Path env var on Windows, only PATH was accepted before
* Small error reporting and docs improvements
### [1.0.0-beta1] - 2016-03-03
* Break: By default we now disable any non-secure protocols (http, git, svn). This may lead to issues if you rely on those. See `secure-http` config option.
* Break: `show` / `list` command now only show installed packages by default. An `--all` option is added to show all packages.
* Added VCS repo support for the GitLab API, see also `gitlab-oauth` and `gitlab-domains` config options
* Added `prohibits` / `why-not` command to show what blocks an upgrade to a given package:version pair
* Added --tree / -t to the `show` command to see all your installed packages in a tree view
* Added --interactive / -i to the `update` command, which lets you pick packages to update interactively
* Added `exec` command to run binaries while having bin-dir in the PATH for convenience
* Added --root-reqs to the `update` command to update only your direct, first degree dependencies
* Added `cafile` and `capath` config options to control HTTPS certificate authority
* Added pubkey verification of composer.phar when running self-update
* Added possibility to configure per-package `preferred-install` types for more flexibility between prefer-source and prefer-dist
* Added unpushed-changes detection when updating dependencies and in the `status` command
* Added COMPOSER_AUTH env var that lets you pass a json configuration like the auth.json file
* Added `secure-http` and `disable-tls` config options to control HTTPS/HTTP
* Added warning when Xdebug is enabled as it reduces performance quite a bit, hide it with COMPOSER_DISABLE_XDEBUG_WARN=1 if you must
* Added duplicate key detection when loading composer.json
* Added `sort-packages` config option to force sorting of the requirements when using the `require` command
* Added support for the XDG Base Directory spec on linux
* Added XzDownloader for xz file support
* Fixed SSL support to fully verify peers in all PHP versions, unsecure HTTP is also disabled by default
* Fixed stashing and cleaning up of untracked files when updating packages
* Fixed plugins being enabled after installation even when --no-plugins
* Many small bug fixes and additions
### [1.0.0-alpha11] - 2015-11-14
* Added config.platform to let you specify what your target environment looks like and make sure you do not inadvertently install dependencies that would break it
* Added `exclude-from-classmap` in the autoload config that lets you ignore sub-paths of classmapped directories, or psr-0/4 directories when building optimized autoloaders
* Added `path` repository type to install/symlink packages from local paths
* Added possibility to reference script handlers from within other handlers using @script-name to reduce duplication
* Added `suggests` command to show what packages are suggested, use -v to see more details
* Added `content-hash` inside the composer.lock to restrict the warnings about outdated lock file to some specific changes in the composer.json file
* Added `archive-format` and `archive-dir` config options to specify default values for the archive command
* Added --classmap-authoritative to `install`, `update`, `require`, `remove` and `dump-autoload` commands, forcing the optimized classmap to be authoritative
* Added -A / --with-dependencies to the `validate` command to allow validating all your dependencies recursively
* Added --strict to the `validate` command to treat any warning as an error that then returns a non-zero exit code
* Added a dependency on composer/semver, which is the externalized lib for all the version constraints parsing and handling
* Added support for classmap autoloading to load plugin classes and script handlers
* Added `bin-compat` config option that if set to `full` will create .bat proxy for binaries even if Composer runs in a linux VM
* Added SPDX 2.0 support, and externalized that in a composer/spdx-licenses lib
* Added warnings when the classmap autoloader finds duplicate classes
* Added --file to the `archive` command to choose the filename
* Added Ctrl+C handling in create-project to cancel the operation cleanly
* Fixed version guessing to use ^ always, default to stable versions, and avoid versions that require a higher php version than you have
* Fixed the lock file switching back and forth between old and new URL when a package URL is changed and many people run updates
* Fixed partial updates updating things they shouldn't when the current vendor dir was out of date with the lock file
* Fixed PHAR file creation to be more reproducible and always generate the exact same phar file from a given source
* Fixed issue when checking out git branches or tags that are also the name of a file in the repo
* Many minor fixes and documentation additions and UX improvements
### [1.0.0-alpha10] - 2015-04-14
* Break: The following event classes are deprecated and you should update your script handlers to use the new ones in type hints:
- `Composer\Script\CommandEvent` is deprecated, use `Composer\Script\Event`
- `Composer\Script\PackageEvent` is deprecated, use `Composer\Installer\PackageEvent`
* Break: Output is now split between stdout and stderr. Any irrelevant output to each command is on stderr as per unix best practices.
* Added support for npm-style semver operators (`^` and `-` ranges, ` ` = AND, `||` = OR)
* Added --prefer-lowest to `update` command to allow testing a package with the lowest declared dependencies
* Added support for parsing semver build metadata `+anything` at the end of versions
* Added --sort-packages option to `require` command for sorting dependencies
* Added --no-autoloader to `install` and `update` commands to skip autoload generation
* Added --list to `run-script` command to see available scripts
* Added --absolute to `config` command to get back absolute paths
* Added `classmap-authoritative` config option, if enabled only the classmap info will be used by the composer autoloader
* Added support for branch-alias on numeric branches
* Added support for the `https_proxy`/`HTTPS_PROXY` env vars used only for https URLs
* Added support for using real composer repos as local paths in `create-project` command
* Added --no-dev to `licenses` command
* Added support for PHP 7.0 nightly builds
* Fixed detection of stability when parsing multiple constraints
* Fixed installs from lock file containing updated composer.json requirement
* Fixed the autoloader suffix in vendor/autoload.php changing in every build
* Many minor fixes, documentation additions and UX improvements
### [1.0.0-alpha9] - 2014-12-07
* Added `remove` command to do the reverse of `require`
* Added --ignore-platform-reqs to `install`/`update` commands to install even if you are missing a php extension or have an invalid php version
* Added a warning when abandoned packages are being installed
* Added auto-selection of the version constraint in the `require` command, which can now be used simply as `composer require foo/bar`
* Added ability to define custom composer commands using scripts
* Added `browse` command to open a browser to the given package's repo URL (or homepage with `-H`)
* Added an `autoload-dev` section to declare dev-only autoload rules + a --no-dev flag to dump-autoload
* Added an `auth.json` file, with `store-auths` config option
* Added a `http-basic` config option to store login/pwds to hosts
* Added failover to source/dist and vice-versa in case a download method fails
* Added --path (-P) flag to the show command to see the install path of packages
* Added --update-with-dependencies and --update-no-dev flags to the require command
* Added `optimize-autoloader` config option to force the `-o` flag from the config
* Added `clear-cache` command
* Added a GzipDownloader to download single gzipped files
* Added `ssh` support in the `github-protocols` config option
* Added `pre-dependencies-solving` and `post-dependencies-solving` events
* Added `pre-archive-cmd` and `post-archive-cmd` script events to the `archive` command
* Added a `no-api` flag to GitHub VCS repos to skip the API but still get zip downloads
* Added http-basic auth support for private git repos not on github
* Added support for autoloading `.hh` files when running HHVM
* Added support for PHP 5.6
* Added support for OTP auth when retrieving a GitHub API key
* Fixed isolation of `files` autoloaded scripts to ensure they can not affect anything
* Improved performance of solving dependencies
* Improved SVN and Perforce support
* A boatload of minor fixes, documentation additions and UX improvements
### [1.0.0-alpha8] - 2014-01-06
* Break: The `install` command now has --dev enabled by default. --no-dev can be used to install without dev requirements
* Added `composer-plugin` package type to allow extensibility, and deprecated `composer-installer`
* Added `psr-4` autoloading support and deprecated `target-dir` since it is a better alternative
* Added --no-plugins flag to replace --no-custom-installers where available
* Added `global` command to operate Composer in a user-global directory
* Added `licenses` command to list the license of all your dependencies
* Added `pre-status-cmd` and `post-status-cmd` script events to the `status` command
* Added `post-root-package-install` and `post-create-project-cmd` script events to the `create-project` command
* Added `pre-autoload-dump` script event
* Added --rollback flag to self-update
* Added --no-install flag to create-project to skip installing the dependencies
* Added a `hhvm` platform package to require Facebook's HHVM implementation of PHP
* Added `github-domains` config option to allow using GitHub Enterprise with Composer's GitHub support
* Added `prepend-autoloader` config option to allow appending Composer's autoloader instead of the default prepend behavior
* Added Perforce support to the VCS repository
* Added a vendor/composer/autoload_files.php file that lists all files being included by the files autoloader
* Added support for the `no_proxy` env var and other proxy support improvements
* Added many robustness tweaks to make sure zip downloads work more consistently and corrupted caches are invalidated
* Added the release date to `composer -V` output
* Added `autoloader-suffix` config option to allow overriding the randomly generated autoloader class suffix
* Fixed BitBucket API usage
* Fixed parsing of inferred stability flags that are more stable than the minimum stability
* Fixed installation order of plugins/custom installers
* Fixed tilde and wildcard version constraints to be more intuitive regarding stabilities
* Fixed handling of target-dir changes when updating packages
* Improved performance of the class loader
* Improved memory usage and performance of solving dependencies
* Tons of minor bug fixes and improvements
### [1.0.0-alpha7] - 2013-05-04
* Break: For forward compatibility, you should change your deployment scripts to run `composer install --no-dev`. The install command will install dev dependencies by default starting in the next release
* Break: The `update` command now has --dev enabled by default. --no-dev can be used to update without dev requirements, but it will create an incomplete lock file and is discouraged
* Break: Removed support for lock files created before 2012-09-15 due to their outdated unusable format
* Added `prefer-stable` flag to pick stable packages over unstable ones when possible
* Added `preferred-install` config option to always enable --prefer-source or --prefer-dist
* Added `diagnose` command to to system/network checks and find common problems
* Added wildcard support in the update whitelist, e.g. to update all packages of a vendor do `composer update vendor/*`
* Added `archive` command to archive the current directory or a given package
* Added `run-script` command to manually trigger scripts
* Added `proprietary` as valid license identifier for non-free code
* Added a `php-64bit` platform package that you can require to force a 64bit php
* Added a `lib-ICU` platform package
* Added a new official package type `project` for project-bootstrapping packages
* Added zip/dist local cache to speed up repetitive installations
* Added `post-autoload-dump` script event
* Added `Event::getDevMode` to let script handlers know if dev requirements are being installed
* Added `discard-changes` config option to control the default behavior when updating "dirty" dependencies
* Added `use-include-path` config option to make the autoloader look for files in the include path too
* Added `cache-ttl`, `cache-files-ttl` and `cache-files-maxsize` config option
* Added `cache-dir`, `cache-files-dir`, `cache-repo-dir` and `cache-vcs-dir` config option
* Added support for using http(s) authentication to non-github repos
* Added support for using multiple autoloaders at once (e.g. PHPUnit + application both using Composer autoloader)
* Added support for .inc files for classmap autoloading (legacy support, do not do this on new projects!)
* Added support for version constraints in show command, e.g. `composer show monolog/monolog 1.4.*`
* Added support for svn repositories containing packages in a deeper path (see package-path option)
* Added an `artifact` repository to scan a directory containing zipped packages
* Added --no-dev flag to `install` and `update` commands
* Added --stability (-s) flag to create-project to lower the required stability
* Added --no-progress to `install` and `update` to hide the progress indicators
* Added --available (-a) flag to the `show` command to display only available packages
* Added --name-only (-N) flag to the `show` command to show only package names (one per line, no formatting)
* Added --optimize-autoloader (-o) flag to optimize the autoloader from the `install` and `update` commands
* Added -vv and -vvv flags to get more verbose output, can be useful to debug some issues
* Added COMPOSER_NO_INTERACTION env var to do the equivalent of --no-interaction (should be set on build boxes, CI, PaaS)
* Added PHP 5.2 compatibility to the autoloader configuration files so they can be used to configure another autoloader
* Fixed handling of platform requirements of the root package when installing from lock
* Fixed handling of require-dev dependencies
* Fixed handling of unstable packages that should be downgraded to stable packages when updating to new version constraints
* Fixed parsing of the `~` operator combined with unstable versions
* Fixed the `require` command corrupting the json if the new requirement was invalid
* Fixed support of aliases used together with `<version>#<reference>` constraints
* Improved output of dependency solver problems by grouping versions of a package together
* Improved performance of classmap generation
* Improved mercurial support in various places
* Improved lock file format to minimize unnecessary diffs
* Improved the `config` command to support all options
* Improved the coverage of the `validate` command
* Tons of minor bug fixes and improvements
### [1.0.0-alpha6] - 2012-10-23
* Schema: Added ability to pass additional options to repositories (i.e. ssh keys/client certificates to secure private repos)
* Schema: Added a new `~` operator that should be preferred over `>=`, see http://getcomposer.org/doc/01-basic-usage.md#package-versions
* Schema: Version constraints `<x.y` are assumed to be `<x.y-dev` unless specified as `<x.y-stable` to reduce confusion
* Added `config` command to edit/list config values, including --global switch for system config
* Added OAuth token support for the GitHub API
* Added ability to specify CLI commands as scripts in addition to PHP callbacks
* Added --prefer-dist flag to force installs of dev packages from zip archives instead of clones
* Added --working-dir (-d) flag to change the working directory
* Added --profile flag to all commands to display execution time and memory usage
* Added `github-protocols` config key to define the order of preferred protocols for github.com clones
* Added ability to interactively reset changes to vendor dirs while updating
* Added support for hg bookmarks in the hg driver
* Added support for svn repositories not following the standard trunk/branch/tags scheme
* Fixed git clones of dev versions so that you end up on a branch and not in detached HEAD
* Fixed "Package not installed" issues with --dev installs
* Fixed the lock file format to be a snapshot of all the package info at the time of update
* Fixed order of autoload requires to follow package dependencies
* Fixed rename() failures with "Access denied" on windows
* Improved memory usage to be more reasonable and not grow with the repository size
* Improved performance and memory usage of installs from composer.lock
* Improved performance of a few essential code paths
* Many bug small fixes and docs improvements
### [1.0.0-alpha5] - 2012-08-18
* Added `dump-autoload` command to only regenerate the autoloader
* Added --optimize to `dump-autoload` to generate a more performant classmap-based autoloader for production
* Added `status` command to show if any source-installed dependency has local changes, use --verbose to see changed files
* Added --verbose flag to `install` and `update` that shows the new commits when updating source-installed dependencies
* Added --no-update flag to `require` to only modify the composer.json file but skip the update
* Added --no-custom-installers and --no-scripts to `install`, `update` and `create-project` to prevent all automatic code execution
* Added support for installing archives that contain only a single file
* Fixed APC related issues in the autoload script on high load websites
* Fixed installation of branches containing capital letters
* Fixed installation of custom dev versions/branches
* Improved the coverage of the `validate` command
* Improved PEAR scripts/binaries support
* Improved and fixed the output of various commands
* Improved error reporting on network failures and some other edge cases
* Various minor bug fixes and docs improvements
### [1.0.0-alpha4] - 2012-07-04
* Break: The default `minimum-stability` is now `stable`, [read more](https://groups.google.com/d/topic/composer-dev/_g3ASeIFlrc/discussion)
* Break: Custom installers now receive the IO instance and a Composer instance in their constructor
* Schema: Added references for dev versions, requiring `dev-master#abcdef` for example will force the abcdef commit
* Schema: Added `support` key with some more metadata (email, issues, forum, wiki, irc, source)
* Schema: Added `!=` operator for version constraints in `require`/`require-dev`
* Added a recommendation for package names to be `lower-cased/with-dashes`, it will be enforced for new packages on Pacakgist
* Added `require` command to add a package to your requirements and install it
* Added a whitelist to `update`. Calling `composer update foo/bar foo/baz` allows you to update only those packages
* Added support for overriding repositories in the system config (define repositories in ~/.composer/config.json)
* Added `lib-*` packages to the platform repository, e.g. `lib-pcre` contains the pcre version
* Added caching of GitHub metadata (faster startup time with custom GitHub VCS repos)
* Added caching of SVN metadata (faster startup time with custom SVN VCS repos)
* Added support for file:// URLs to GitDriver
* Added --self flag to the `show` command to display the infos of the root package
* Added --dev flag to `create-project` command
* Added --no-scripts to `install` and `update` commands to avoid triggering the scripts
* Added `COMPOSER_ROOT_VERSION` env var to specify the version of the root package (fixes some edge cases)
* Added support for multiple custom installers in one package
* Added files autoloading method which requires files on every request, e.g. to load functional code
* Added automatic recovery for lock files that contain references to rewritten (force pushed) commits
* Improved PEAR repositories support and package.xml extraction
* Improved and fixed the output of various commands
* Fixed the order of installation of requirements (they are always installed before the packages requiring them)
* Cleaned up / refactored the dependency solver code as well as the output for unsolvable requirements
* Various bug fixes and docs improvements
### [1.0.0-alpha3] - 2012-05-13
* Schema: Added `require-dev` for development-time requirements (tests, etc), install with --dev
* Schema: Added author.role to list the author's role in the project
* Schema: Added `minimum-stability` + `@<stability>` flags in require for restricting packages to a certain stability
* Schema: Removed `recommend`
* Schema: `suggest` is now informational and can use any description for a package, not only a constraint
* Break: vendor/.composer/autoload.php has been moved to vendor/autoload.php, other files are now in vendor/composer/
* Added caching of repository metadata (faster startup times & failover if packagist is down)
* Added removal of packages that are not needed anymore
* Added include_path support for legacy projects that are full of require_once statements
* Added installation notifications API to allow better statistics on Composer repositories
* Added support for proxies that require authentication
* Added support for private github repositories over https
* Added autoloading support for root packages that use target-dir
* Added awareness of the root package presence and support for it's provide/replace/conflict keys
* Added IOInterface::isDecorated to test for colored output support
* Added validation of licenses based on the [SPDX registry](https://spdx.org/licenses/)
* Improved repository protocol to have large cacheable parts
* Fixed various bugs relating to package aliasing, proxy configuration, binaries
* Various bug fixes and docs improvements
### [1.0.0-alpha2] - 2012-04-03
* Added `create-project` command to install a project from scratch with composer
* Added automated `classmap` autoloading support for non-PSR-0 compliant projects
* Added human readable error reporting when deps can not be solved
* Added support for private GitHub and SVN repositories (use --no-interaction for CI)
* Added "file" downloader type to download plain files
* Added support for authentication with svn repositories
* Added autoload support for PEAR repositories
* Improved clones from GitHub which now automatically select between git/https/http protocols
* Improved `validate` command to give more feedback
* Improved the `search` & `show` commands output
* Removed dependency on filter_var
* Various robustness & error handling improvements, docs fixes and more bug fixes
### 1.0.0-alpha1 - 2012-03-01
* Initial release
[1.9.3]: https://github.com/composer/composer/compare/1.9.2...1.9.3
[1.9.2]: https://github.com/composer/composer/compare/1.9.1...1.9.2
[1.9.1]: https://github.com/composer/composer/compare/1.9.0...1.9.1
[1.9.0]: https://github.com/composer/composer/compare/1.8.6...1.9.0
[1.8.6]: https://github.com/composer/composer/compare/1.8.5...1.8.6
[1.8.5]: https://github.com/composer/composer/compare/1.8.4...1.8.5
[1.8.4]: https://github.com/composer/composer/compare/1.8.3...1.8.4
[1.8.3]: https://github.com/composer/composer/compare/1.8.2...1.8.3
[1.8.2]: https://github.com/composer/composer/compare/1.8.1...1.8.2
[1.8.1]: https://github.com/composer/composer/compare/1.8.0...1.8.1
[1.8.0]: https://github.com/composer/composer/compare/1.7.3...1.8.0
[1.7.3]: https://github.com/composer/composer/compare/1.7.2...1.7.3
[1.7.2]: https://github.com/composer/composer/compare/1.7.1...1.7.2
[1.7.1]: https://github.com/composer/composer/compare/1.7.0...1.7.1
[1.7.0]: https://github.com/composer/composer/compare/1.7.0-RC...1.7.0
[1.7.0-RC]: https://github.com/composer/composer/compare/1.6.5...1.7.0-RC
[1.6.5]: https://github.com/composer/composer/compare/1.6.4...1.6.5
[1.6.4]: https://github.com/composer/composer/compare/1.6.3...1.6.4
[1.6.3]: https://github.com/composer/composer/compare/1.6.2...1.6.3
[1.6.2]: https://github.com/composer/composer/compare/1.6.1...1.6.2
[1.6.1]: https://github.com/composer/composer/compare/1.6.0...1.6.1
[1.6.0]: https://github.com/composer/composer/compare/1.6.0-RC...1.6.0
[1.6.0-RC]: https://github.com/composer/composer/compare/1.5.6...1.6.0-RC
[1.5.6]: https://github.com/composer/composer/compare/1.5.5...1.5.6
[1.5.5]: https://github.com/composer/composer/compare/1.5.4...1.5.5
[1.5.4]: https://github.com/composer/composer/compare/1.5.3...1.5.4
[1.5.3]: https://github.com/composer/composer/compare/1.5.2...1.5.3
[1.5.2]: https://github.com/composer/composer/compare/1.5.1...1.5.2
[1.5.1]: https://github.com/composer/composer/compare/1.5.0...1.5.1
[1.5.0]: https://github.com/composer/composer/compare/1.4.3...1.5.0
[1.4.3]: https://github.com/composer/composer/compare/1.4.2...1.4.3
[1.4.2]: https://github.com/composer/composer/compare/1.4.1...1.4.2
[1.4.1]: https://github.com/composer/composer/compare/1.4.0...1.4.1
[1.4.0]: https://github.com/composer/composer/compare/1.3.3...1.4.0
[1.3.3]: https://github.com/composer/composer/compare/1.3.2...1.3.3
[1.3.2]: https://github.com/composer/composer/compare/1.3.1...1.3.2
[1.3.1]: https://github.com/composer/composer/compare/1.3.0...1.3.1
[1.3.0]: https://github.com/composer/composer/compare/1.3.0-RC...1.3.0
[1.3.0-RC]: https://github.com/composer/composer/compare/1.2.4...1.3.0-RC
[1.2.4]: https://github.com/composer/composer/compare/1.2.3...1.2.4
[1.2.3]: https://github.com/composer/composer/compare/1.2.2...1.2.3
[1.2.2]: https://github.com/composer/composer/compare/1.2.1...1.2.2
[1.2.1]: https://github.com/composer/composer/compare/1.2.0...1.2.1
[1.2.0]: https://github.com/composer/composer/compare/1.2.0-RC...1.2.0
[1.2.0-RC]: https://github.com/composer/composer/compare/1.1.3...1.2.0-RC
[1.1.3]: https://github.com/composer/composer/compare/1.1.2...1.1.3
[1.1.2]: https://github.com/composer/composer/compare/1.1.1...1.1.2
[1.1.1]: https://github.com/composer/composer/compare/1.1.0...1.1.1
[1.1.0]: https://github.com/composer/composer/compare/1.0.3...1.1.0
[1.1.0-RC]: https://github.com/composer/composer/compare/1.0.3...1.1.0-RC
[1.0.3]: https://github.com/composer/composer/compare/1.0.2...1.0.3
[1.0.2]: https://github.com/composer/composer/compare/1.0.1...1.0.2
[1.0.1]: https://github.com/composer/composer/compare/1.0.0...1.0.1
[1.0.0]: https://github.com/composer/composer/compare/1.0.0-beta2...1.0.0
[1.0.0-beta2]: https://github.com/composer/composer/compare/1.0.0-beta1...1.0.0-beta2
[1.0.0-beta1]: https://github.com/composer/composer/compare/1.0.0-alpha11...1.0.0-beta1
[1.0.0-alpha11]: https://github.com/composer/composer/compare/1.0.0-alpha10...1.0.0-alpha11
[1.0.0-alpha10]: https://github.com/composer/composer/compare/1.0.0-alpha9...1.0.0-alpha10
[1.0.0-alpha9]: https://github.com/composer/composer/compare/1.0.0-alpha8...1.0.0-alpha9
[1.0.0-alpha8]: https://github.com/composer/composer/compare/1.0.0-alpha7...1.0.0-alpha8
[1.0.0-alpha7]: https://github.com/composer/composer/compare/1.0.0-alpha6...1.0.0-alpha7
[1.0.0-alpha6]: https://github.com/composer/composer/compare/1.0.0-alpha5...1.0.0-alpha6
[1.0.0-alpha5]: https://github.com/composer/composer/compare/1.0.0-alpha4...1.0.0-alpha5
[1.0.0-alpha4]: https://github.com/composer/composer/compare/1.0.0-alpha3...1.0.0-alpha4
[1.0.0-alpha3]: https://github.com/composer/composer/compare/1.0.0-alpha2...1.0.0-alpha3
[1.0.0-alpha2]: https://github.com/composer/composer/compare/1.0.0-alpha1...1.0.0-alpha2
+19
View File
@@ -0,0 +1,19 @@
Copyright (c) Nils Adermann, Jordi Boggiano
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+39
View File
@@ -0,0 +1,39 @@
* add rule
* p = direct literal; always < 0 for installed rpm rules
* d, if < 0 direct literal, if > 0 offset into whatprovides, if == 0 rule is assertion (look at p only)
*
*
* A requires b, b provided by B1,B2,B3 => (-A|B1|B2|B3)
*
* p < 0 : pkg id of A
* d > 0 : Offset in whatprovidesdata (list of providers of b)
*
* A conflicts b, b provided by B1,B2,B3 => (-A|-B1), (-A|-B2), (-A|-B3)
* p < 0 : pkg id of A
* d < 0 : Id of solvable (e.g. B1)
*
* d == 0: unary rule, assertion => (A) or (-A)
*
* Install: p > 0, d = 0 (A) user requested install
* Remove: p < 0, d = 0 (-A) user requested remove (also: uninstallable)
* Requires: p < 0, d > 0 (-A|B1|B2|...) d: <list of providers for requirement of p>
* Updates: p > 0, d > 0 (A|B1|B2|...) d: <list of updates for solvable p>
* Conflicts: p < 0, d < 0 (-A|-B) either p (conflict issuer) or d (conflict provider) (binary rule)
* also used for obsoletes
* ?: p > 0, d < 0 (A|-B)
* No-op ?: p = 0, d = 0 (null) (used as policy rule placeholder)
*
* resulting watches:
* ------------------
* Direct assertion (no watch needed)( if d <0 ) --> d = 0, w1 = p, w2 = 0
* Binary rule: p = first literal, d = 0, w2 = second literal, w1 = p
* every other : w1 = p, w2 = whatprovidesdata[d];
* Disabled rule: w1 = 0
*
* always returns a rule for non-rpm rules
p > 0, d = 0, (A), w1 = p, w2 = 0
p < 0, d = 0, (-A), w1 = p, w2 = 0
p !=0, d = 0, (p|q), w1 = p, w2 = q
+62
View File
@@ -0,0 +1,62 @@
Composer - Dependency Management for PHP
========================================
Composer helps you declare, manage, and install dependencies of PHP projects.
See [https://getcomposer.org/](https://getcomposer.org/) for more information and documentation.
[![Build Status](https://travis-ci.org/composer/composer.svg?branch=master)](https://travis-ci.org/composer/composer)
Installation / Usage
--------------------
Download and install Composer by following the [official instructions](https://getcomposer.org/download/).
For usage, see [the documentation](https://getcomposer.org/doc/).
Packages
--------
Find packages on [Packagist](https://packagist.org).
Community
---------
IRC channels are on irc.freenode.org: [#composer](irc://irc.freenode.org/composer)
for users and [#composer-dev](irc://irc.freenode.org/composer-dev) for development.
For support, Stack Overflow also offers a good collection of
[Composer related questions](https://stackoverflow.com/questions/tagged/composer-php).
Please note that this project is released with a
[Contributor Code of Conduct](http://contributor-covenant.org/version/1/4/).
By participating in this project and its community you agree to abide by those terms.
Requirements
------------
PHP 5.3.2 or above (at least 5.3.4 recommended to avoid potential bugs)
Authors
-------
- Nils Adermann | [GitHub](https://github.com/naderman) | [Twitter](https://twitter.com/naderman) | <naderman@naderman.de> | [naderman.de](http://naderman.de)
- Jordi Boggiano | [GitHub](https://github.com/Seldaek) | [Twitter](https://twitter.com/seldaek) | <j.boggiano@seld.be> | [seld.be](http://seld.be)
See also the list of [contributors](https://github.com/composer/composer/contributors) who participated in this project.
Security Reports
----------------
Please send any sensitive issue to [security@packagist.org](mailto:security@packagist.org). Thanks!
License
-------
Composer is licensed under the MIT License - see the [LICENSE](LICENSE) file for details
Acknowledgments
---------------
- This project's Solver started out as a PHP port of openSUSE's
[Libzypp satsolver](https://en.opensuse.org/openSUSE:Libzypp_satsolver).
+31
View File
@@ -0,0 +1,31 @@
#!/usr/bin/env php
<?php
$cwd = getcwd();
chdir(__DIR__.'/../');
$ts = rtrim(shell_exec('git log -n1 --pretty=%ct HEAD'));
if (!is_numeric($ts)) {
echo 'Could not detect date using "git log -n1 --pretty=%ct HEAD"'.PHP_EOL;
exit(1);
}
// Install with the current version to force it having the right ClassLoader version
// Install without dev packages to clean up the included classmap from phpunit classes
shell_exec('php bin/composer config autoloader-suffix ComposerPhar' . $ts);
shell_exec('php bin/composer install -q --no-dev');
shell_exec('php bin/composer config autoloader-suffix --unset');
chdir($cwd);
require __DIR__.'/../src/bootstrap.php';
use Composer\Compiler;
error_reporting(-1);
ini_set('display_errors', 1);
try {
$compiler = new Compiler();
$compiler->compile();
} catch (\Exception $e) {
echo 'Failed to compile phar: ['.get_class($e).'] '.$e->getMessage().' at '.$e->getFile().':'.$e->getLine().PHP_EOL;
exit(1);
}
+62
View File
@@ -0,0 +1,62 @@
#!/usr/bin/env php
<?php
if (PHP_SAPI !== 'cli' && PHP_SAPI !== 'phpdbg') {
echo 'Warning: Composer should be invoked via the CLI version of PHP, not the '.PHP_SAPI.' SAPI'.PHP_EOL;
}
setlocale(LC_ALL, 'C');
require __DIR__.'/../src/bootstrap.php';
use Composer\Console\Application;
use Composer\XdebugHandler\XdebugHandler;
error_reporting(-1);
// Restart without xdebug
$xdebug = new XdebugHandler('Composer', '--ansi');
$xdebug->check();
unset($xdebug);
if (defined('HHVM_VERSION') && version_compare(HHVM_VERSION, '4.0', '>=')) {
echo 'HHVM 4.0 has dropped support for Composer, please use PHP instead. Aborting.'.PHP_EOL;
exit(1);
}
if (function_exists('ini_set')) {
@ini_set('display_errors', 1);
$memoryInBytes = function ($value) {
$unit = strtolower(substr($value, -1, 1));
$value = (int) $value;
switch($unit) {
case 'g':
$value *= 1024;
// no break (cumulative multiplier)
case 'm':
$value *= 1024;
// no break (cumulative multiplier)
case 'k':
$value *= 1024;
}
return $value;
};
$memoryLimit = trim(ini_get('memory_limit'));
// Increase memory_limit if it is lower than 1.5GB
if ($memoryLimit != -1 && $memoryInBytes($memoryLimit) < 1024 * 1024 * 1536) {
@ini_set('memory_limit', '1536M');
}
// Set user defined memory limit
if ($memoryLimit = getenv('COMPOSER_MEMORY_LIMIT')) {
@ini_set('memory_limit', $memoryLimit);
}
unset($memoryInBytes, $memoryLimit);
}
putenv('COMPOSER_BINARY='.realpath($_SERVER['argv'][0]));
// run the command application
$application = new Application();
$application->run();
+86
View File
@@ -0,0 +1,86 @@
{
"name": "composer/composer",
"type": "library",
"description": "Composer helps you declare, manage and install dependencies of PHP projects. It ensures you have the right stack everywhere.",
"keywords": [
"package",
"dependency",
"autoload"
],
"homepage": "https://getcomposer.org/",
"license": "MIT",
"authors": [
{
"name": "Nils Adermann",
"email": "naderman@naderman.de",
"homepage": "http://www.naderman.de"
},
{
"name": "Jordi Boggiano",
"email": "j.boggiano@seld.be",
"homepage": "http://seld.be"
}
],
"require": {
"php": "^5.3.2 || ^7.0",
"composer/ca-bundle": "^1.0",
"composer/semver": "^1.0",
"composer/spdx-licenses": "^1.2",
"composer/xdebug-handler": "^1.1",
"justinrainbow/json-schema": "^3.0 || ^4.0 || ^5.0",
"psr/log": "^1.0",
"seld/jsonlint": "^1.4",
"seld/phar-utils": "^1.0",
"symfony/console": "^2.7 || ^3.0 || ^4.0",
"symfony/filesystem": "^2.7 || ^3.0 || ^4.0",
"symfony/finder": "^2.7 || ^3.0 || ^4.0",
"symfony/process": "^2.7 || ^3.0 || ^4.0"
},
"conflict": {
"symfony/console": "2.8.38"
},
"require-dev": {
"phpunit/phpunit": "^4.8.35 || ^5.7",
"phpunit/phpunit-mock-objects": "^2.3 || ^3.0"
},
"suggest": {
"ext-openssl": "Enabling the openssl extension allows you to access https URLs for repositories and packages",
"ext-zip": "Enabling the zip extension allows you to unzip archives",
"ext-zlib": "Allow gzip compression of HTTP requests"
},
"config": {
"platform": {
"php": "5.3.9"
}
},
"extra": {
"branch-alias": {
"dev-master": "1.9-dev"
}
},
"autoload": {
"psr-4": {
"Composer\\": "src/Composer"
}
},
"autoload-dev": {
"psr-4": {
"Composer\\Test\\": "tests/Composer/Test"
}
},
"bin": [
"bin/composer"
],
"scripts": {
"compile": "@php -dphar.readonly=0 bin/compile",
"test": "phpunit"
},
"scripts-descriptions": {
"compile": "Compile composer.phar",
"test": "Run all tests"
},
"support": {
"issues": "https://github.com/composer/composer/issues",
"irc": "irc://irc.freenode.org/composer"
}
}
+1827
View File
File diff suppressed because it is too large. Load diff
+156
View File
@@ -0,0 +1,156 @@
# Introduction
Composer is a tool for dependency management in PHP. It allows you to declare
the libraries your project depends on and it will manage (install/update) them
for you.
## Dependency management
Composer is **not** a package manager in the same sense as Yum or Apt are. Yes,
it deals with "packages" or libraries, but it manages them on a per-project
basis, installing them in a directory (e.g. `vendor`) inside your project. By
default it does not install anything globally. Thus, it is a dependency
manager. It does however support a "global" project for convenience via the
[global](03-cli.md#global) command.
This idea is not new and Composer is strongly inspired by node's
[npm](https://www.npmjs.com/) and ruby's [bundler](https://bundler.io/).
Suppose:
1. You have a project that depends on a number of libraries.
1. Some of those libraries depend on other libraries.
Composer:
1. Enables you to declare the libraries you depend on.
1. Finds out which versions of which packages can and need to be installed, and
installs them (meaning it downloads them into your project).
See the [Basic usage](01-basic-usage.md) chapter for more details on declaring
dependencies.
## System Requirements
Composer requires PHP 5.3.2+ to run. A few sensitive php settings and compile
flags are also required, but when using the installer you will be warned about
any incompatibilities.
To install packages from sources instead of simple zip archives, you will need
git, svn, fossil or hg depending on how the package is version-controlled.
Composer is multi-platform and we strive to make it run equally well on Windows,
Linux and macOS.
## Installation - Linux / Unix / macOS
### Downloading the Composer Executable
Composer offers a convenient installer that you can execute directly from the
command line. Feel free to [download this file](https://getcomposer.org/installer)
or review it on [GitHub](https://github.com/composer/getcomposer.org/blob/master/web/installer)
if you wish to know more about the inner workings of the installer. The source
is plain PHP.
There are in short, two ways to install Composer. Locally as part of your
project, or globally as a system wide executable.
#### Locally
To install Composer locally, run the installer in your project directory. See
[the Download page](https://getcomposer.org/download/) for instructions.
The installer will check a few PHP settings and then download `composer.phar`
to your working directory. This file is the Composer binary. It is a PHAR
(PHP archive), which is an archive format for PHP which can be run on
the command line, amongst other things.
Now run `php composer.phar` in order to run Composer.
You can install Composer to a specific directory by using the `--install-dir`
option and additionally (re)name it as well using the `--filename` option. When
running the installer when following
[the Download page instructions](https://getcomposer.org/download/) add the
following parameters:
```sh
php composer-setup.php --install-dir=bin --filename=composer
```
Now run `php bin/composer` in order to run Composer.
#### Globally
You can place the Composer PHAR anywhere you wish. If you put it in a directory
that is part of your `PATH`, you can access it globally. On Unix systems you
can even make it executable and invoke it without directly using the `php`
interpreter.
After running the installer following [the Download page instructions](https://getcomposer.org/download/)
you can run this to move composer.phar to a directory that is in your path:
```sh
mv composer.phar /usr/local/bin/composer
```
If you like to install it only for your user and avoid requiring root permissions,
you can use `~/.local/bin` instead which is available by default on some
Linux distributions.
> **Note:** If the above fails due to permissions, you may need to run it again
> with sudo.
> **Note:** On some versions of macOS the `/usr` directory does not exist by
> default. If you receive the error "/usr/local/bin/composer: No such file or
> directory" then you must create the directory manually before proceeding:
> `mkdir -p /usr/local/bin`.
> **Note:** For information on changing your PATH, please read the
> [Wikipedia article](https://en.wikipedia.org/wiki/PATH_(variable)) and/or use Google.
Now run `composer` in order to run Composer instead of `php composer.phar`.
## Installation - Windows
### Using the Installer
This is the easiest way to get Composer set up on your machine.
Download and run
[Composer-Setup.exe](https://getcomposer.org/Composer-Setup.exe). It will
install the latest Composer version and set up your PATH so that you can
call `composer` from any directory in your command line.
> **Note:** Close your current terminal. Test usage with a new terminal: This is
> important since the PATH only gets loaded when the terminal starts.
### Manual Installation
Change to a directory on your `PATH` and run the installer following
[the Download page instructions](https://getcomposer.org/download/)
to download `composer.phar`.
Create a new `composer.bat` file alongside `composer.phar`:
```sh
C:\bin>echo @php "%~dp0composer.phar" %*>composer.bat
```
Add the directory to your PATH environment variable if it isn't already.
For information on changing your PATH variable, please see
[this article](https://www.computerhope.com/issues/ch000549.htm) and/or
use Google.
Close your current terminal. Test usage with a new terminal:
```sh
C:\Users\username>composer -V
Composer version 1.0.0 2016-01-10 20:34:53
```
## Using Composer
Now that you've installed Composer, you are ready to use it! Head on over to the
next chapter for a short and simple demonstration.
[Basic usage](01-basic-usage.md) &rarr;
+266
View File
@@ -0,0 +1,266 @@
# Basic usage
## Introduction
For our basic usage introduction, we will be installing `monolog/monolog`,
a logging library. If you have not yet installed Composer, refer to the
[Intro](00-intro.md) chapter.
> **Note:** for the sake of simplicity, this introduction will assume you
> have performed a [local](00-intro.md#locally) install of Composer.
## `composer.json`: Project setup
To start using Composer in your project, all you need is a `composer.json`
file. This file describes the dependencies of your project and may contain
other metadata as well.
### The `require` key
The first (and often only) thing you specify in `composer.json` is the
[`require`](04-schema.md#require) key. You are simply telling Composer which
packages your project depends on.
```json
{
"require": {
"monolog/monolog": "1.0.*"
}
}
```
As you can see, [`require`](04-schema.md#require) takes an object that maps
**package names** (e.g. `monolog/monolog`) to **version constraints** (e.g.
`1.0.*`).
Composer uses this information to search for the right set of files in package
"repositories" that you register using the [`repositories`](04-schema.md#repositories)
key, or in Packagist, the default package repository. In the above example,
since no other repository has been registered in the `composer.json` file, it is
assumed that the `monolog/monolog` package is registered on Packagist. (See more
about Packagist [below](#packagist), or read more about repositories
[here](05-repositories.md)).
### Package names
The package name consists of a vendor name and the project's name. Often these
will be identical - the vendor name only exists to prevent naming clashes. For
example, it would allow two different people to create a library named `json`.
One might be named `igorw/json` while the other might be `seldaek/json`.
Read more about publishing packages and package naming [here](02-libraries.md).
(Note that you can also specify "platform packages" as dependencies, allowing
you to require certain versions of server software. See
[platform packages](#platform-packages) below.)
### Package version constraints
In our example, we are requesting the Monolog package with the version constraint
[`1.0.*`](https://semver.mwl.be/#?package=monolog%2Fmonolog&version=1.0.*).
This means any version in the `1.0` development branch, or any version that is
greater than or equal to 1.0 and less than 1.1 (`>=1.0 <1.1`).
Please read [versions](articles/versions.md) for more in-depth information on
versions, how versions relate to each other, and on version constraints.
> **How does Composer download the right files?** When you specify a dependency in
> `composer.json`, Composer first takes the name of the package that you have requested
> and searches for it in any repositories that you have registered using the
> [`repositories`](04-schema.md#repositories) key. If you have not registered
> any extra repositories, or it does not find a package with that name in the
> repositories you have specified, it falls back to Packagist (more [below](#packagist)).
>
> When Composer finds the right package, either in Packagist or in a repo you have specified,
> it then uses the versioning features of the package's VCS (i.e., branches and tags)
> to attempt to find the best match for the version constraint you have specified. Be sure to read
> about versions and package resolution in the [versions article](articles/versions.md).
> **Note:** If you are trying to require a package but Composer throws an error
> regarding package stability, the version you have specified may not meet your
> default minimum stability requirements. By default only stable releases are taken
> into consideration when searching for valid package versions in your VCS.
>
> You might run into this if you are trying to require dev, alpha, beta, or RC
> versions of a package. Read more about stability flags and the `minimum-stability`
> key on the [schema page](04-schema.md).
## Installing dependencies
To install the defined dependencies for your project, run the
[`install`](03-cli.md#install) command.
```sh
php composer.phar install
```
When you run this command, one of two things may happen:
### Installing without `composer.lock`
If you have never run the command before and there is also no `composer.lock` file present,
Composer simply resolves all dependencies listed in your `composer.json` file and downloads
the latest version of their files into the `vendor` directory in your project. (The `vendor`
directory is the conventional location for all third-party code in a project). In our
example from above, you would end up with the Monolog source files in
`vendor/monolog/monolog/`. If Monolog listed any dependencies, those would also be in
folders under `vendor/`.
> **Tip:** If you are using git for your project, you probably want to add
> `vendor` in your `.gitignore`. You really don't want to add all of that
> third-party code to your versioned repository.
When Composer has finished installing, it writes all of the packages and the exact versions
of them that it downloaded to the `composer.lock` file, locking the project to those specific
versions. You should commit the `composer.lock` file to your project repo so that all people
working on the project are locked to the same versions of dependencies (more below).
### Installing with `composer.lock`
This brings us to the second scenario. If there is already a `composer.lock` file as well as a
`composer.json` file when you run `composer install`, it means either you ran the
`install` command before, or someone else on the project ran the `install` command and
committed the `composer.lock` file to the project (which is good).
Either way, running `install` when a `composer.lock` file is present resolves and installs
all dependencies that you listed in `composer.json`, but Composer uses the exact versions listed
in `composer.lock` to ensure that the package versions are consistent for everyone
working on your project. As a result you will have all dependencies requested by your
`composer.json` file, but they may not all be at the very latest available versions
(some of the dependencies listed in the `composer.lock` file may have released newer versions since
the file was created). This is by design, it ensures that your project does not break because of
unexpected changes in dependencies.
### Commit your `composer.lock` file to version control
Committing this file to VC is important because it will cause anyone who sets
up the project to use the exact same
versions of the dependencies that you are using. Your CI server, production
machines, other developers in your team, everything and everyone runs on the
same dependencies, which mitigates the potential for bugs affecting only some
parts of the deployments. Even if you develop alone, in six months when
reinstalling the project you can feel confident the dependencies installed are
still working even if your dependencies released many new versions since then.
(See note below about using the `update` command.)
## Updating dependencies to their latest versions
As mentioned above, the `composer.lock` file prevents you from automatically getting
the latest versions of your dependencies. To update to the latest versions, use the
[`update`](03-cli.md#update) command. This will fetch the latest matching
versions (according to your `composer.json` file) and update the lock file
with the new versions. (This is equivalent to deleting the `composer.lock` file
and running `install` again.)
```sh
php composer.phar update
```
> **Note:** Composer will display a Warning when executing an `install` command
> if the `composer.lock` has not been updated since changes were made to the
> `composer.json` that might affect dependency resolution.
If you only want to install or update one dependency, you can whitelist them:
```sh
php composer.phar update monolog/monolog [...]
```
> **Note:** For libraries it is not necessary to commit the lock
> file, see also: [Libraries - Lock file](02-libraries.md#lock-file).
## Packagist
[Packagist](https://packagist.org/) is the main Composer repository. A Composer
repository is basically a package source: a place where you can get packages
from. Packagist aims to be the central repository that everybody uses. This
means that you can automatically `require` any package that is available there,
without further specifying where Composer should look for the package.
If you go to the [Packagist website](https://packagist.org/) (packagist.org),
you can browse and search for packages.
Any open source project using Composer is recommended to publish their packages
on Packagist. A library does not need to be on Packagist to be used by Composer,
but it enables discovery and adoption by other developers more quickly.
## Platform packages
Composer has platform packages, which are virtual packages for things that are
installed on the system but are not actually installable by Composer. This
includes PHP itself, PHP extensions and some system libraries.
* `php` represents the PHP version of the user, allowing you to apply
constraints, e.g. `^7.1`. To require a 64bit version of php, you can
require the `php-64bit` package.
* `hhvm` represents the version of the HHVM runtime and allows you to apply
a constraint, e.g., `^2.3`.
* `ext-<name>` allows you to require PHP extensions (includes core
extensions). Versioning can be quite inconsistent here, so it's often
a good idea to set the constraint to `*`. An example of an extension
package name is `ext-gd`.
* `lib-<name>` allows constraints to be made on versions of libraries used by
PHP. The following are available: `curl`, `iconv`, `icu`, `libxml`,
`openssl`, `pcre`, `uuid`, `xsl`.
You can use [`show --platform`](03-cli.md#show) to get a list of your locally
available platform packages.
## Autoloading
For libraries that specify autoload information, Composer generates a
`vendor/autoload.php` file. You can simply include this file and start
using the classes that those libraries provide without any extra work:
```php
require __DIR__ . '/vendor/autoload.php';
$log = new Monolog\Logger('name');
$log->pushHandler(new Monolog\Handler\StreamHandler('app.log', Monolog\Logger::WARNING));
$log->addWarning('Foo');
```
You can even add your own code to the autoloader by adding an
[`autoload`](04-schema.md#autoload) field to `composer.json`.
```json
{
"autoload": {
"psr-4": {"Acme\\": "src/"}
}
}
```
Composer will register a [PSR-4](http://www.php-fig.org/psr/psr-4/) autoloader
for the `Acme` namespace.
You define a mapping from namespaces to directories. The `src` directory would
be in your project root, on the same level as `vendor` directory is. An example
filename would be `src/Foo.php` containing an `Acme\Foo` class.
After adding the [`autoload`](04-schema.md#autoload) field, you have to re-run
[`dump-autoload`](03-cli.md#dump-autoload) to re-generate the
`vendor/autoload.php` file.
Including that file will also return the autoloader instance, so you can store
the return value of the include call in a variable and add more namespaces.
This can be useful for autoloading classes in a test suite, for example.
```php
$loader = require __DIR__ . '/vendor/autoload.php';
$loader->addPsr4('Acme\\Test\\', __DIR__);
```
In addition to PSR-4 autoloading, Composer also supports PSR-0, classmap and
files autoloading. See the [`autoload`](04-schema.md#autoload) reference for
more information.
See also the docs on [optimizing the autoloader](articles/autoloader-optimization.md).
> **Note:** Composer provides its own autoloader. If you don't want to use that
> one, you can include `vendor/composer/autoload_*.php` files, which return
> associative arrays allowing you to configure your own autoloader.
&larr; [Intro](00-intro.md) | [Libraries](02-libraries.md) &rarr;
+153
View File
@@ -0,0 +1,153 @@
# Libraries
This chapter will tell you how to make your library installable through
Composer.
## Every project is a package
As soon as you have a `composer.json` in a directory, that directory is a
package. When you add a [`require`](04-schema.md#require) to a project, you are
making a package that depends on other packages. The only difference between
your project and a library is that your project is a package without a name.
In order to make that package installable you need to give it a name. You do
this by adding the [`name`](04-schema.md#name) property in `composer.json`:
```json
{
"name": "acme/hello-world",
"require": {
"monolog/monolog": "1.0.*"
}
}
```
In this case the project name is `acme/hello-world`, where `acme` is the vendor
name. Supplying a vendor name is mandatory.
> **Note:** If you don't know what to use as a vendor name, your GitHub
> username is usually a good bet. While package names are case insensitive, the
> convention is all lowercase and dashes for word separation.
## Library Versioning
In the vast majority of cases, you will be maintaining your library using some
sort of version control system like git, svn, hg or fossil. In these cases,
Composer infers versions from your VCS and you **should not** specify a version
in your `composer.json` file. (See the [Versions article](articles/versions.md)
to learn about how Composer uses VCS branches and tags to resolve version
constraints.)
If you are maintaining packages by hand (i.e., without a VCS), you'll need to
specify the version explicitly by adding a `version` value in your `composer.json`
file:
```json
{
"version": "1.0.0"
}
```
> **Note:** When you add a hardcoded version to a VCS, the version will conflict
> with tag names. Composer will not be able to determine the version number.
### VCS Versioning
Composer uses your VCS's branch and tag features to resolve the version
constraints you specify in your `require` field to specific sets of files.
When determining valid available versions, Composer looks at all of your tags
and branches and translates their names into an internal list of options that
it then matches against the version constraint you provided.
For more on how Composer treats tags and branches and how it resolves package
version constraints, read the [versions](articles/versions.md) article.
## Lock file
For your library you may commit the `composer.lock` file if you want to. This
can help your team to always test against the same dependency versions.
However, this lock file will not have any effect on other projects that depend
on it. It only has an effect on the main project.
If you do not want to commit the lock file and you are using git, add it to
the `.gitignore`.
## Publishing to a VCS
Once you have a VCS repository (version control system, e.g. git) containing a
`composer.json` file, your library is already composer-installable. In this
example we will publish the `acme/hello-world` library on GitHub under
`github.com/username/hello-world`.
Now, to test installing the `acme/hello-world` package, we create a new
project locally. We will call it `acme/blog`. This blog will depend on
`acme/hello-world`, which in turn depends on `monolog/monolog`. We can
accomplish this by creating a new `blog` directory somewhere, containing a
`composer.json`:
```json
{
"name": "acme/blog",
"require": {
"acme/hello-world": "dev-master"
}
}
```
The name is not needed in this case, since we don't want to publish the blog
as a library. It is added here to clarify which `composer.json` is being
described.
Now we need to tell the blog app where to find the `hello-world` dependency.
We do this by adding a package repository specification to the blog's
`composer.json`:
```json
{
"name": "acme/blog",
"repositories": [
{
"type": "vcs",
"url": "https://github.com/username/hello-world"
}
],
"require": {
"acme/hello-world": "dev-master"
}
}
```
For more details on how package repositories work and what other types are
available, see [Repositories](05-repositories.md).
That's all. You can now install the dependencies by running Composer's
[`install`](03-cli.md#install) command!
**Recap:** Any git/svn/hg/fossil repository containing a `composer.json` can be
added to your project by specifying the package repository and declaring the
dependency in the [`require`](04-schema.md#require) field.
## Publishing to packagist
Alright, so now you can publish packages. But specifying the VCS repository
every time is cumbersome. You don't want to force all your users to do that.
The other thing that you may have noticed is that we did not specify a package
repository for `monolog/monolog`. How did that work? The answer is Packagist.
[Packagist](https://packagist.org/) is the main package repository for
Composer, and it is enabled by default. Anything that is published on
Packagist is available automatically through Composer. Since
[Monolog is on Packagist](https://packagist.org/packages/monolog/monolog), we
can depend on it without having to specify any additional repositories.
If we wanted to share `hello-world` with the world, we would publish it on
Packagist as well. Doing so is really easy.
You simply visit [Packagist](https://packagist.org) and hit the "Submit"
button. This will prompt you to sign up if you haven't already, and then
allows you to submit the URL to your VCS repository, at which point Packagist
will start crawling it. Once it is done, your package will be available to
anyone!
&larr; [Basic usage](01-basic-usage.md) | [Command-line interface](03-cli.md) &rarr;
+941
View File
@@ -0,0 +1,941 @@
# Command-line interface / Commands
You've already learned how to use the command-line interface to do some
things. This chapter documents all the available commands.
To get help from the command-line, simply call `composer` or `composer list`
to see the complete list of commands, then `--help` combined with any of those
can give you more information.
As Composer uses [symfony/console](https://github.com/symfony/console) you can call commands by short name if it's not ambiguous.
```sh
composer dump
```
calls `composer dump-autoload`.
## Global Options
The following options are available with every command:
* **--verbose (-v):** Increase verbosity of messages.
* **--help (-h):** Display help information.
* **--quiet (-q):** Do not output any message.
* **--no-interaction (-n):** Do not ask any interactive question.
* **--no-plugins:** Disables plugins.
* **--no-cache:** Disables the use of the cache directory. Same as setting the COMPOSER_CACHE_DIR
env var to /dev/null (or NUL on Windows).
* **--working-dir (-d):** If specified, use the given directory as working directory.
* **--profile:** Display timing and memory usage information
* **--ansi:** Force ANSI output.
* **--no-ansi:** Disable ANSI output.
* **--version (-V):** Display this application version.
## Process Exit Codes
* **0:** OK
* **1:** Generic/unknown error code
* **2:** Dependency solving error code
## init
In the [Libraries](02-libraries.md) chapter we looked at how to create a
`composer.json` by hand. There is also an `init` command available that makes
it a bit easier to do this.
When you run the command it will interactively ask you to fill in the fields,
while using some smart defaults.
```sh
php composer.phar init
```
### Options
* **--name:** Name of the package.
* **--description:** Description of the package.
* **--author:** Author name of the package.
* **--type:** Type of package.
* **--homepage:** Homepage of the package.
* **--require:** Package to require with a version constraint. Should be
in format `foo/bar:1.0.0`.
* **--require-dev:** Development requirements, see **--require**.
* **--stability (-s):** Value for the `minimum-stability` field.
* **--license (-l):** License of package.
* **--repository:** Provide one (or more) custom repositories. They will be stored
in the generated composer.json, and used for auto-completion when prompting for
the list of requires. Every repository can be either an HTTP URL pointing
to a `composer` repository or a JSON string which similar to what the
[repositories](04-schema.md#repositories) key accepts.
## install / i
The `install` command reads the `composer.json` file from the current
directory, resolves the dependencies, and installs them into `vendor`.
```sh
php composer.phar install
```
If there is a `composer.lock` file in the current directory, it will use the
exact versions from there instead of resolving them. This ensures that
everyone using the library will get the same versions of the dependencies.
If there is no `composer.lock` file, Composer will create one after dependency
resolution.
### Options
* **--prefer-source:** There are two ways of downloading a package: `source`
and `dist`. For stable versions Composer will use the `dist` by default.
The `source` is a version control repository. If `--prefer-source` is
enabled, Composer will install from `source` if there is one. This is
useful if you want to make a bugfix to a project and get a local git
clone of the dependency directly.
* **--prefer-dist:** Reverse of `--prefer-source`, Composer will install
from `dist` if possible. This can speed up installs substantially on build
servers and other use cases where you typically do not run updates of the
vendors. It is also a way to circumvent problems with git if you do not
have a proper setup.
* **--dry-run:** If you want to run through an installation without actually
installing a package, you can use `--dry-run`. This will simulate the
installation and show you what would happen.
* **--dev:** Install packages listed in `require-dev` (this is the default behavior).
* **--no-dev:** Skip installing packages listed in `require-dev`. The autoloader
generation skips the `autoload-dev` rules.
* **--no-autoloader:** Skips autoloader generation.
* **--no-scripts:** Skips execution of scripts defined in `composer.json`.
* **--no-progress:** Removes the progress display that can mess with some
terminals or scripts which don't handle backspace characters.
* **--no-suggest:** Skips suggested packages in the output.
* **--optimize-autoloader (-o):** Convert PSR-0/4 autoloading to classmap to get a faster
autoloader. This is recommended especially for production, but can take
a bit of time to run so it is currently not done by default.
* **--classmap-authoritative (-a):** Autoload classes from the classmap only.
Implicitly enables `--optimize-autoloader`.
* **--apcu-autoloader:** Use APCu to cache found/not-found classes.
* **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*`
requirements and force the installation even if the local machine does not
fulfill these. See also the [`platform`](06-config.md#platform) config option.
## update / u
In order to get the latest versions of the dependencies and to update the
`composer.lock` file, you should use the `update` command. This command is also
aliased as `upgrade` as it does the same as `upgrade` does if you are thinking
of `apt-get` or similar package managers.
```sh
php composer.phar update
```
This will resolve all dependencies of the project and write the exact versions
into `composer.lock`.
If you only want to update a few packages and not all, you can list them as such:
```sh
php composer.phar update vendor/package vendor/package2
```
You can also use wildcards to update a bunch of packages at once:
```sh
php composer.phar update "vendor/*"
```
### Options
* **--prefer-source:** Install packages from `source` when available.
* **--prefer-dist:** Install packages from `dist` when available.
* **--dry-run:** Simulate the command without actually doing anything.
* **--dev:** Install packages listed in `require-dev` (this is the default behavior).
* **--no-dev:** Skip installing packages listed in `require-dev`. The autoloader generation skips the `autoload-dev` rules.
* **--lock:** Only updates the lock file hash to suppress warning about the
lock file being out of date.
* **--no-autoloader:** Skips autoloader generation.
* **--no-scripts:** Skips execution of scripts defined in `composer.json`.
* **--no-progress:** Removes the progress display that can mess with some
terminals or scripts which don't handle backspace characters.
* **--no-suggest:** Skips suggested packages in the output.
* **--with-dependencies:** Add also dependencies of whitelisted packages to the whitelist, except those that are root requirements.
* **--with-all-dependencies:** Add also all dependencies of whitelisted packages to the whitelist, including those that are root requirements.
* **--optimize-autoloader (-o):** Convert PSR-0/4 autoloading to classmap to get a faster
autoloader. This is recommended especially for production, but can take
a bit of time to run so it is currently not done by default.
* **--classmap-authoritative (-a):** Autoload classes from the classmap only.
Implicitly enables `--optimize-autoloader`.
* **--apcu-autoloader:** Use APCu to cache found/not-found classes.
* **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*`
requirements and force the installation even if the local machine does not
fulfill these. See also the [`platform`](06-config.md#platform) config option.
* **--prefer-stable:** Prefer stable versions of dependencies.
* **--prefer-lowest:** Prefer lowest versions of dependencies. Useful for testing minimal
versions of requirements, generally used with `--prefer-stable`.
* **--interactive:** Interactive interface with autocompletion to select the packages to update.
* **--root-reqs:** Restricts the update to your first degree dependencies.
## require
The `require` command adds new packages to the `composer.json` file from
the current directory. If no file exists one will be created on the fly.
```sh
php composer.phar require
```
After adding/changing the requirements, the modified requirements will be
installed or updated.
If you do not want to choose requirements interactively, you can pass them
to the command.
```sh
php composer.phar require vendor/package:2.* vendor/package2:dev-master
```
If you do not specify a package, composer will prompt you to search for a package, and given results, provide a list of matches to require.
### Options
* **--dev:** Add packages to `require-dev`.
* **--prefer-source:** Install packages from `source` when available.
* **--prefer-dist:** Install packages from `dist` when available.
* **--no-progress:** Removes the progress display that can mess with some
terminals or scripts which don't handle backspace characters.
* **--no-suggest:** Skips suggested packages in the output.
* **--no-update:** Disables the automatic update of the dependencies.
* **--no-scripts:** Skips execution of scripts defined in `composer.json`.
* **--update-no-dev:** Run the dependency update with the `--no-dev` option.
* **--update-with-dependencies:** Also update dependencies of the newly required packages, except those that are root requirements.
* **--update-with-all-dependencies:** Also update dependencies of the newly required packages, including those that are root requirements.
* **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*`
requirements and force the installation even if the local machine does not
fulfill these. See also the [`platform`](06-config.md#platform) config option.
* **--prefer-stable:** Prefer stable versions of dependencies.
* **--prefer-lowest:** Prefer lowest versions of dependencies. Useful for testing minimal
versions of requirements, generally used with `--prefer-stable`.
* **--sort-packages:** Keep packages sorted in `composer.json`.
* **--optimize-autoloader (-o):** Convert PSR-0/4 autoloading to classmap to
get a faster autoloader. This is recommended especially for production, but
can take a bit of time to run so it is currently not done by default.
* **--classmap-authoritative (-a):** Autoload classes from the classmap only.
Implicitly enables `--optimize-autoloader`.
* **--apcu-autoloader:** Use APCu to cache found/not-found classes.
## remove
The `remove` command removes packages from the `composer.json` file from
the current directory.
```sh
php composer.phar remove vendor/package vendor/package2
```
After removing the requirements, the modified requirements will be
uninstalled.
### Options
* **--dev:** Remove packages from `require-dev`.
* **--no-progress:** Removes the progress display that can mess with some
terminals or scripts which don't handle backspace characters.
* **--no-update:** Disables the automatic update of the dependencies.
* **--no-scripts:** Skips execution of scripts defined in `composer.json`.
* **--update-no-dev:** Run the dependency update with the --no-dev option.
* **--update-with-dependencies:** Also update dependencies of the removed packages.
* **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*`
requirements and force the installation even if the local machine does not
fulfill these. See also the [`platform`](06-config.md#platform) config option.
* **--optimize-autoloader (-o):** Convert PSR-0/4 autoloading to classmap to
get a faster autoloader. This is recommended especially for production, but
can take a bit of time to run so it is currently not done by default.
* **--classmap-authoritative (-a):** Autoload classes from the classmap only.
Implicitly enables `--optimize-autoloader`.
* **--apcu-autoloader:** Use APCu to cache found/not-found classes.
## check-platform-reqs
The check-platform-reqs command checks that your PHP and extensions versions
match the platform requirements of the installed packages. This can be used
to verify that a production server has all the extensions needed to run a
project after installing it for example.
Unlike update/install, this command will ignore config.platform settings and
check the real platform packages so you can be certain you have the required
platform dependencies.
## global
The global command allows you to run other commands like `install`, `remove`, `require`
or `update` as if you were running them from the [COMPOSER_HOME](#composer-home)
directory.
This is merely a helper to manage a project stored in a central location that
can hold CLI tools or Composer plugins that you want to have available everywhere.
This can be used to install CLI utilities globally. Here is an example:
```sh
php composer.phar global require friendsofphp/php-cs-fixer
```
Now the `php-cs-fixer` binary is available globally. Make sure your global
[vendor binaries](articles/vendor-binaries.md) directory is in your `$PATH`
environment variable, you can get its location with the following command :
```sh
php composer.phar global config bin-dir --absolute
```
If you wish to update the binary later on you can run a global update:
```sh
php composer.phar global update
```
## search
The search command allows you to search through the current project's package
repositories. Usually this will be packagist. You simply pass it the
terms you want to search for.
```sh
php composer.phar search monolog
```
You can also search for more than one term by passing multiple arguments.
### Options
* **--only-name (-N):** Search only in name.
* **--type (-t):** Search for a specific package type.
## show
To list all of the available packages, you can use the `show` command.
```sh
php composer.phar show
```
To filter the list you can pass a package mask using wildcards.
```sh
php composer.phar show monolog/*
monolog/monolog 1.19.0 Sends your logs to files, sockets, inboxes, databases and various web services
```
If you want to see the details of a certain package, you can pass the package
name.
```sh
php composer.phar show monolog/monolog
name : monolog/monolog
versions : master-dev, 1.0.2, 1.0.1, 1.0.0, 1.0.0-RC1
type : library
names : monolog/monolog
source : [git] https://github.com/Seldaek/monolog.git 3d4e60d0cbc4b888fe5ad223d77964428b1978da
dist : [zip] https://github.com/Seldaek/monolog/zipball/3d4e60d0cbc4b888fe5ad223d77964428b1978da 3d4e60d0cbc4b888fe5ad223d77964428b1978da
license : MIT
autoload
psr-0
Monolog : src/
requires
php >=5.3.0
```
You can even pass the package version, which will tell you the details of that
specific version.
```sh
php composer.phar show monolog/monolog 1.0.2
```
### Options
* **--all :** List all packages available in all your repositories.
* **--installed (-i):** List the packages that are installed (this is enabled by default, and deprecated).
* **--platform (-p):** List only platform packages (php & extensions).
* **--available (-a):** List available packages only.
* **--self (-s):** List the root package info.
* **--name-only (-N):** List package names only.
* **--path (-P):** List package paths.
* **--tree (-t):** List your dependencies as a tree. If you pass a package name it will show the dependency tree for that package.
* **--latest (-l):** List all installed packages including their latest version.
* **--outdated (-o):** Implies --latest, but this lists *only* packages that have a newer version available.
* **--minor-only (-m):** Use with --latest. Only shows packages that have minor SemVer-compatible updates.
* **--direct (-D):** Restricts the list of packages to your direct dependencies.
* **--strict:** Return a non-zero exit code when there are outdated packages.
* **--format (-f):** Lets you pick between text (default) or json output format.
## outdated
The `outdated` command shows a list of installed packages that have updates available,
including their current and latest versions. This is basically an alias for
`composer show -lo`.
The color coding is as such:
- **green (=)**: Dependency is in the latest version and is up to date.
- **yellow (~)**: Dependency has a new version available that includes backwards compatibility breaks according to semver, so upgrade when
you can but it may involve work.
- **red (!)**: Dependency has a new version that is semver-compatible and you should upgrade it.
### Options
* **--all (-a):** Show all packages, not just outdated (alias for `composer show -l`).
* **--direct (-D):** Restricts the list of packages to your direct dependencies.
* **--strict:** Returns non-zero exit code if any package is outdated.
* **--minor-only (-m):** Only shows packages that have minor SemVer-compatible updates.
* **--format (-f):** Lets you pick between text (default) or json output format.
## browse / home
The `browse` (aliased to `home`) opens a package's repository URL or homepage
in your browser.
### Options
* **--homepage (-H):** Open the homepage instead of the repository URL.
* **--show (-s):** Only show the homepage or repository URL.
## suggests
Lists all packages suggested by currently installed set of packages. You can
optionally pass one or multiple package names in the format of `vendor/package`
to limit output to suggestions made by those packages only.
Use the `--by-package` or `--by-suggestion` flags to group the output by
the package offering the suggestions or the suggested packages respectively.
Use the `--verbose (-v)` flag to display the suggesting package and the suggestion reason.
This implies `--by-package --by-suggestion`, showing both lists.
### Options
* **--by-package:** Groups output by suggesting package.
* **--by-suggestion:** Groups output by suggested package.
* **--no-dev:** Excludes suggestions from `require-dev` packages.
## depends (why)
The `depends` command tells you which other packages depend on a certain
package. As with installation `require-dev` relationships are only considered
for the root package.
```sh
php composer.phar depends doctrine/lexer
doctrine/annotations v1.2.7 requires doctrine/lexer (1.*)
doctrine/common v2.6.1 requires doctrine/lexer (1.*)
```
You can optionally specify a version constraint after the package to limit the
search.
Add the `--tree` or `-t` flag to show a recursive tree of why the package is
depended upon, for example:
```sh
php composer.phar depends psr/log -t
psr/log 1.0.0 Common interface for logging libraries
|- aboutyou/app-sdk 2.6.11 (requires psr/log 1.0.*)
| `- __root__ (requires aboutyou/app-sdk ^2.6)
|- monolog/monolog 1.17.2 (requires psr/log ~1.0)
| `- laravel/framework v5.2.16 (requires monolog/monolog ~1.11)
| `- __root__ (requires laravel/framework ^5.2)
`- symfony/symfony v3.0.2 (requires psr/log ~1.0)
`- __root__ (requires symfony/symfony ^3.0)
```
### Options
* **--recursive (-r):** Recursively resolves up to the root package.
* **--tree (-t):** Prints the results as a nested tree, implies -r.
## prohibits (why-not)
The `prohibits` command tells you which packages are blocking a given package
from being installed. Specify a version constraint to verify whether upgrades
can be performed in your project, and if not why not. See the following
example:
```sh
php composer.phar prohibits symfony/symfony 3.1
laravel/framework v5.2.16 requires symfony/var-dumper (2.8.*|3.0.*)
```
Note that you can also specify platform requirements, for example to check
whether you can upgrade your server to PHP 8.0:
```sh
php composer.phar prohibits php:8
doctrine/cache v1.6.0 requires php (~5.5|~7.0)
doctrine/common v2.6.1 requires php (~5.5|~7.0)
doctrine/instantiator 1.0.5 requires php (>=5.3,<8.0-DEV)
```
As with `depends` you can request a recursive lookup, which will list all
packages depending on the packages that cause the conflict.
### Options
* **--recursive (-r):** Recursively resolves up to the root package.
* **--tree (-t):** Prints the results as a nested tree, implies -r.
## validate
You should always run the `validate` command before you commit your
`composer.json` file, and before you tag a release. It will check if your
`composer.json` is valid.
```sh
php composer.phar validate
```
### Options
* **--no-check-all:** Do not emit a warning if requirements in `composer.json` use unbound or overly strict version constraints.
* **--no-check-lock:** Do not emit an error if `composer.lock` exists and is not up to date.
* **--no-check-publish:** Do not emit an error if `composer.json` is unsuitable for publishing as a package on Packagist but is otherwise valid.
* **--with-dependencies:** Also validate the composer.json of all installed dependencies.
* **--strict:** Return a non-zero exit code for warnings as well as errors.
## status
If you often need to modify the code of your dependencies and they are
installed from source, the `status` command allows you to check if you have
local changes in any of them.
```sh
php composer.phar status
```
With the `--verbose` option you get some more information about what was
changed:
```sh
php composer.phar status -v
You have changes in the following dependencies:
vendor/seld/jsonlint:
M README.mdown
```
## self-update (selfupdate)
To update Composer itself to the latest version, run the `self-update`
command. It will replace your `composer.phar` with the latest version.
```sh
php composer.phar self-update
```
If you would like to instead update to a specific release simply specify it:
```sh
php composer.phar self-update 1.0.0-alpha7
```
If you have installed Composer for your entire system (see [global installation](00-intro.md#globally)),
you may have to run the command with `root` privileges
```sh
sudo -H composer self-update
```
### Options
* **--rollback (-r):** Rollback to the last version you had installed.
* **--clean-backups:** Delete old backups during an update. This makes the
current version of Composer the only backup available after the update.
* **--no-progress:** Do not output download progress.
* **--update-keys:** Prompt user for a key update.
* **--stable:** Force an update to the stable channel.
* **--preview:** Force an update to the preview channel.
* **--snapshot:** Force an update to the snapshot channel.
## config
The `config` command allows you to edit composer config settings and repositories
in either the local `composer.json` file or the global `config.json` file.
Additionally it lets you edit most properties in the local `composer.json`.
```sh
php composer.phar config --list
```
### Usage
`config [options] [setting-key] [setting-value1] ... [setting-valueN]`
`setting-key` is a configuration option name and `setting-value1` is a
configuration value. For settings that can take an array of values (like
`github-protocols`), more than one setting-value arguments are allowed.
You can also edit the values of the following properties:
`description`, `homepage`, `keywords`, `license`, `minimum-stability`,
`name`, `prefer-stable`, `type` and `version`.
See the [Config](06-config.md) chapter for valid configuration options.
### Options
* **--global (-g):** Operate on the global config file located at
`$COMPOSER_HOME/config.json` by default. Without this option, this command
affects the local composer.json file or a file specified by `--file`.
* **--editor (-e):** Open the local composer.json file using in a text editor as
defined by the `EDITOR` env variable. With the `--global` option, this opens
the global config file.
* **--auth (-a):** Affect auth config file (only used for --editor).
* **--unset:** Remove the configuration element named by `setting-key`.
* **--list (-l):** Show the list of current config variables. With the `--global`
option this lists the global configuration only.
* **--file="..." (-f):** Operate on a specific file instead of composer.json. Note
that this cannot be used in conjunction with the `--global` option.
* **--absolute:** Returns absolute paths when fetching *-dir config values
instead of relative.
### Modifying Repositories
In addition to modifying the config section, the `config` command also supports making
changes to the repositories section by using it the following way:
```sh
php composer.phar config repositories.foo vcs https://github.com/foo/bar
```
If your repository requires more configuration options, you can instead pass its JSON representation :
```sh
php composer.phar config repositories.foo '{"type": "vcs", "url": "http://svn.example.org/my-project/", "trunk-path": "master"}'
```
### Modifying Extra Values
In addition to modifying the config section, the `config` command also supports making
changes to the extra section by using it the following way:
```sh
php composer.phar config extra.foo.bar value
```
The dots indicate array nesting, a max depth of 3 levels is allowed though. The above
would set `"extra": { "foo": { "bar": "value" } }`.
## create-project
You can use Composer to create new projects from an existing package. This is
the equivalent of doing a git clone/svn checkout followed by a `composer install`
of the vendors.
There are several applications for this:
1. You can deploy application packages.
2. You can check out any package and start developing on patches for example.
3. Projects with multiple developers can use this feature to bootstrap the
initial application for development.
To create a new project using Composer you can use the `create-project` command.
Pass it a package name, and the directory to create the project in. You can also
provide a version as third argument, otherwise the latest version is used.
If the directory does not currently exist, it will be created during installation.
```sh
php composer.phar create-project doctrine/orm path 2.2.*
```
It is also possible to run the command without params in a directory with an
existing `composer.json` file to bootstrap a project.
By default the command checks for the packages on packagist.org.
### Options
* **--stability (-s):** Minimum stability of package. Defaults to `stable`.
* **--prefer-source:** Install packages from `source` when available.
* **--prefer-dist:** Install packages from `dist` when available.
* **--repository:** Provide a custom repository to search for the package,
which will be used instead of packagist. Can be either an HTTP URL pointing
to a `composer` repository, a path to a local `packages.json` file, or a
JSON string which similar to what the [repositories](04-schema.md#repositories)
key accepts.
* **--dev:** Install packages listed in `require-dev`.
* **--no-dev:** Disables installation of require-dev packages.
* **--no-scripts:** Disables the execution of the scripts defined in the root
package.
* **--no-progress:** Removes the progress display that can mess with some
terminals or scripts which don't handle backspace characters.
* **--no-secure-http:** Disable the secure-http config option temporarily while
installing the root package. Use at your own risk. Using this flag is a bad
idea.
* **--keep-vcs:** Skip the deletion of the VCS metadata for the created
project. This is mostly useful if you run the command in non-interactive
mode.
* **--remove-vcs:** Force-remove the VCS metadata without prompting.
* **--no-install:** Disables installation of the vendors.
* **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*`
requirements and force the installation even if the local machine does not
fulfill these.
## dump-autoload (dumpautoload)
If you need to update the autoloader because of new classes in a classmap
package for example, you can use `dump-autoload` to do that without having to
go through an install or update.
Additionally, it can dump an optimized autoloader that converts PSR-0/4 packages
into classmap ones for performance reasons. In large applications with many
classes, the autoloader can take up a substantial portion of every request's
time. Using classmaps for everything is less convenient in development, but
using this option you can still use PSR-0/4 for convenience and classmaps for
performance.
### Options
* **--no-scripts:** Skips the execution of all scripts defined in `composer.json` file.
* **--optimize (-o):** Convert PSR-0/4 autoloading to classmap to get a faster
autoloader. This is recommended especially for production, but can take
a bit of time to run so it is currently not done by default.
* **--classmap-authoritative (-a):** Autoload classes from the classmap only.
Implicitly enables `--optimize`.
* **--apcu:** Use APCu to cache found/not-found classes.
* **--no-dev:** Disables autoload-dev rules.
## clear-cache (clearcache)
Deletes all content from Composer's cache directories.
## licenses
Lists the name, version and license of every package installed. Use
`--format=json` to get machine readable output.
### Options
* **--format:** Format of the output: text or json (default: "text")
* **--no-dev:** Remove dev dependencies from the output
## run-script
### Options
* **--timeout:** Set the script timeout in seconds, or 0 for no timeout.
* **--dev:** Sets the dev mode.
* **--no-dev:** Disable dev mode.
* **--list (-l):** List user defined scripts.
To run [scripts](articles/scripts.md) manually you can use this command,
give it the script name and optionally any required arguments.
## exec
Executes a vendored binary/script. You can execute any command and this will
ensure that the Composer bin-dir is pushed on your PATH before the command
runs.
### Options
* **--list (-l):** List the available composer binaries.
## diagnose
If you think you found a bug, or something is behaving strangely, you might
want to run the `diagnose` command to perform automated checks for many common
problems.
```sh
php composer.phar diagnose
```
## archive
This command is used to generate a zip/tar archive for a given package in a
given version. It can also be used to archive your entire project without
excluded/ignored files.
```sh
php composer.phar archive vendor/package 2.0.21 --format=zip
```
### Options
* **--format (-f):** Format of the resulting archive: tar or zip (default:
"tar")
* **--dir:** Write the archive to this directory (default: ".")
* **--file:** Write the archive with the given file name.
## help
To get more information about a certain command, you can use `help`.
```sh
php composer.phar help install
```
## Command-line completion
Command-line completion can be enabled by following instructions
[on this page](https://github.com/bamarni/symfony-console-autocomplete).
## Environment variables
You can set a number of environment variables that override certain settings.
Whenever possible it is recommended to specify these settings in the `config`
section of `composer.json` instead. It is worth noting that the env vars will
always take precedence over the values specified in `composer.json`.
### COMPOSER
By setting the `COMPOSER` env variable it is possible to set the filename of
`composer.json` to something else.
For example:
```sh
COMPOSER=composer-other.json php composer.phar install
```
The generated lock file will use the same name: `composer-other.lock` in this example.
### COMPOSER_ALLOW_SUPERUSER
If set to 1, this env disables the warning about running commands as root/super user.
It also disables automatic clearing of sudo sessions, so you should really only set this
if you use Composer as super user at all times like in docker containers.
### COMPOSER_AUTH
The `COMPOSER_AUTH` var allows you to set up authentication as an environment variable.
The contents of the variable should be a JSON formatted object containing http-basic,
github-oauth, bitbucket-oauth, ... objects as needed, and following the
[spec from the config](06-config.md#gitlab-oauth).
### COMPOSER_BIN_DIR
By setting this option you can change the `bin` ([Vendor Binaries](articles/vendor-binaries.md))
directory to something other than `vendor/bin`.
### COMPOSER_CACHE_DIR
The `COMPOSER_CACHE_DIR` var allows you to change the Composer cache directory,
which is also configurable via the [`cache-dir`](06-config.md#cache-dir) option.
By default it points to `$COMPOSER_HOME/cache` on \*nix and macOS, and
`C:\Users\<user>\AppData\Local\Composer` (or `%LOCALAPPDATA%/Composer`) on Windows.
### COMPOSER_CAFILE
By setting this environmental value, you can set a path to a certificate bundle
file to be used during SSL/TLS peer verification.
### COMPOSER_DISCARD_CHANGES
This env var controls the [`discard-changes`](06-config.md#discard-changes) config option.
### COMPOSER_HOME
The `COMPOSER_HOME` var allows you to change the Composer home directory. This
is a hidden, global (per-user on the machine) directory that is shared between
all projects.
By default it points to `C:\Users\<user>\AppData\Roaming\Composer` on Windows
and `/Users/<user>/.composer` on macOS. On \*nix systems that follow the [XDG Base
Directory Specifications](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html),
it points to `$XDG_CONFIG_HOME/composer`. On other \*nix systems, it points to
`/home/<user>/.composer`.
#### COMPOSER_HOME/config.json
You may put a `config.json` file into the location which `COMPOSER_HOME` points
to. Composer will merge this configuration with your project's `composer.json`
when you run the `install` and `update` commands.
This file allows you to set [repositories](05-repositories.md) and
[configuration](06-config.md) for the user's projects.
In case global configuration matches _local_ configuration, the _local_
configuration in the project's `composer.json` always wins.
### COMPOSER_HTACCESS_PROTECT
Defaults to `1`. If set to `0`, Composer will not create `.htaccess` files in the
composer home, cache, and data directories.
### COMPOSER_MEMORY_LIMIT
If set, the value is used as php's memory_limit.
### COMPOSER_MIRROR_PATH_REPOS
If set to 1, this env changes the default path repository strategy to `mirror` instead
of `symlink`. As it is the default strategy being set it can still be overwritten by
repository options.
### COMPOSER_NO_INTERACTION
If set to 1, this env var will make Composer behave as if you passed the
`--no-interaction` flag to every command. This can be set on build boxes/CI.
### COMPOSER_PROCESS_TIMEOUT
This env var controls the time Composer waits for commands (such as git
commands) to finish executing. The default value is 300 seconds (5 minutes).
### COMPOSER_ROOT_VERSION
By setting this var you can specify the version of the root package, if it can
not be guessed from VCS info and is not present in `composer.json`.
### COMPOSER_VENDOR_DIR
By setting this var you can make Composer install the dependencies into a
directory other than `vendor`.
### http_proxy or HTTP_PROXY
If you are using Composer from behind an HTTP proxy, you can use the standard
`http_proxy` or `HTTP_PROXY` env vars. Simply set it to the URL of your proxy.
Many operating systems already set this variable for you.
Using `http_proxy` (lowercased) or even defining both might be preferable since
some tools like git or curl will only use the lower-cased `http_proxy` version.
Alternatively you can also define the git proxy using
`git config --global http.proxy <proxy url>`.
If you are using Composer in a non-CLI context (i.e. integration into a CMS or
similar use case), and need to support proxies, please provide the `CGI_HTTP_PROXY`
environment variable instead. See [httpoxy.org](https://httpoxy.org/) for further
details.
### HTTP_PROXY_REQUEST_FULLURI
If you use a proxy but it does not support the request_fulluri flag, then you
should set this env var to `false` or `0` to prevent Composer from setting the
request_fulluri option.
### HTTPS_PROXY_REQUEST_FULLURI
If you use a proxy but it does not support the request_fulluri flag for HTTPS
requests, then you should set this env var to `false` or `0` to prevent Composer
from setting the request_fulluri option.
### COMPOSER_SELF_UPDATE_TARGET
If set, makes the self-update command write the new Composer phar file into that path instead of overwriting itself. Useful for updating Composer on read-only filesystem.
### no_proxy or NO_PROXY
If you are behind a proxy and would like to disable it for certain domains, you
can use the `no_proxy` or `NO_PROXY` env var. Simply set it to a comma separated list of
domains the proxy should *not* be used for.
The env var accepts domains, IP addresses, and IP address blocks in CIDR
notation. You can restrict the filter to a particular port (e.g. `:80`). You
can also set it to `*` to ignore the proxy for all HTTP requests.
&larr; [Libraries](02-libraries.md) | [Schema](04-schema.md) &rarr;
+924
View File
@@ -0,0 +1,924 @@
# The composer.json Schema
This chapter will explain all of the fields available in `composer.json`.
## JSON schema
We have a [JSON schema](http://json-schema.org) that documents the format and
can also be used to validate your `composer.json`. In fact, it is used by the
`validate` command. You can find it at: https://getcomposer.org/schema.json
## Root Package
The root package is the package defined by the `composer.json` at the root of
your project. It is the main `composer.json` that defines your project
requirements.
Certain fields only apply when in the root package context. One example of
this is the `config` field. Only the root package can define configuration.
The config of dependencies is ignored. This makes the `config` field
`root-only`.
> **Note:** A package can be the root package or not, depending on the context.
> For example, if your project depends on the `monolog` library, your project
> is the root package. However, if you clone `monolog` from GitHub in order to
> fix a bug in it, then `monolog` is the root package.
## Properties
### name
The name of the package. It consists of vendor name and project name,
separated by `/`. Examples:
* monolog/monolog
* igorw/event-source
The name can contain any character, including white spaces, and it's case
insensitive (`foo/bar` and `Foo/Bar` are considered the same package). In order
to simplify its installation, it's recommended to define a short and lowercase
name that doesn't include non-alphanumeric characters or white spaces.
Required for published packages (libraries).
### description
A short description of the package. Usually this is one line long.
Required for published packages (libraries).
### version
The version of the package. In most cases this is not required and should
be omitted (see below).
This must follow the format of `X.Y.Z` or `vX.Y.Z` with an optional suffix
of `-dev`, `-patch` (`-p`), `-alpha` (`-a`), `-beta` (`-b`) or `-RC`.
The patch, alpha, beta and RC suffixes can also be followed by a number.
Examples:
- 1.0.0
- 1.0.2
- 1.1.0
- 0.2.5
- 1.0.0-dev
- 1.0.0-alpha3
- 1.0.0-beta2
- 1.0.0-RC5
- v2.0.4-p1
Optional if the package repository can infer the version from somewhere, such
as the VCS tag name in the VCS repository. In that case it is also recommended
to omit it.
> **Note:** Packagist uses VCS repositories, so the statement above is very
> much true for Packagist as well. Specifying the version yourself will
> most likely end up creating problems at some point due to human error.
### type
The type of the package. It defaults to `library`.
Package types are used for custom installation logic. If you have a package
that needs some special logic, you can define a custom type. This could be a
`symfony-bundle`, a `wordpress-plugin` or a `typo3-cms-extension`. These types
will all be specific to certain projects, and they will need to provide an
installer capable of installing packages of that type.
Out of the box, Composer supports four types:
- **library:** This is the default. It will simply copy the files to `vendor`.
- **project:** This denotes a project rather than a library. For example
application shells like the [Symfony standard edition](https://github.com/symfony/symfony-standard),
CMSs like the [SilverStripe installer](https://github.com/silverstripe/silverstripe-installer)
or full fledged applications distributed as packages. This can for example
be used by IDEs to provide listings of projects to initialize when creating
a new workspace.
- **metapackage:** An empty package that contains requirements and will trigger
their installation, but contains no files and will not write anything to the
filesystem. As such, it does not require a dist or source key to be
installable.
- **composer-plugin:** A package of type `composer-plugin` may provide an
installer for other packages that have a custom type. Read more in the
[dedicated article](articles/custom-installers.md).
Only use a custom type if you need custom logic during installation. It is
recommended to omit this field and have it default to `library`.
### keywords
An array of keywords that the package is related to. These can be used for
searching and filtering.
Examples:
- logging
- events
- database
- redis
- templating
Optional.
### homepage
An URL to the website of the project.
Optional.
### readme
A relative path to the readme document.
Optional.
### time
Release date of the version.
Must be in `YYYY-MM-DD` or `YYYY-MM-DD HH:MM:SS` format.
Optional.
### license
The license of the package. This can be either a string or an array of strings.
The recommended notation for the most common licenses is (alphabetical):
- Apache-2.0
- BSD-2-Clause
- BSD-3-Clause
- BSD-4-Clause
- GPL-2.0-only / GPL-2.0-or-later
- GPL-3.0-only / GPL-3.0-or-later
- LGPL-2.1-only / LGPL-2.1-or-later
- LGPL-3.0-only / LGPL-3.0-or-later
- MIT
Optional, but it is highly recommended to supply this. More identifiers are
listed at the [SPDX Open Source License Registry](https://spdx.org/licenses/).
For closed-source software, you may use `"proprietary"` as the license identifier.
An Example:
```json
{
"license": "MIT"
}
```
For a package, when there is a choice between licenses ("disjunctive license"),
multiple can be specified as array.
An Example for disjunctive licenses:
```json
{
"license": [
"LGPL-2.1-only",
"GPL-3.0-or-later"
]
}
```
Alternatively they can be separated with "or" and enclosed in parenthesis;
```json
{
"license": "(LGPL-2.1-only or GPL-3.0-or-later)"
}
```
Similarly when multiple licenses need to be applied ("conjunctive license"),
they should be separated with "and" and enclosed in parenthesis.
### authors
The authors of the package. This is an array of objects.
Each author object can have following properties:
* **name:** The author's name. Usually their real name.
* **email:** The author's email address.
* **homepage:** An URL to the author's website.
* **role:** The author's role in the project (e.g. developer or translator)
An example:
```json
{
"authors": [
{
"name": "Nils Adermann",
"email": "naderman@naderman.de",
"homepage": "http://www.naderman.de",
"role": "Developer"
},
{
"name": "Jordi Boggiano",
"email": "j.boggiano@seld.be",
"homepage": "https://seld.be",
"role": "Developer"
}
]
}
```
Optional, but highly recommended.
### support
Various information to get support about the project.
Support information includes the following:
* **email:** Email address for support.
* **issues:** URL to the issue tracker.
* **forum:** URL to the forum.
* **wiki:** URL to the wiki.
* **irc:** IRC channel for support, as irc://server/channel.
* **source:** URL to browse or download the sources.
* **docs:** URL to the documentation.
* **rss:** URL to the RSS feed.
* **chat:** URL to the chat channel.
An example:
```json
{
"support": {
"email": "support@example.org",
"irc": "irc://irc.freenode.org/composer"
}
}
```
Optional.
### Package links
All of the following take an object which maps package names to
versions of the package via version constraints. Read more about
versions [here](articles/versions.md).
Example:
```json
{
"require": {
"monolog/monolog": "1.0.*"
}
}
```
All links are optional fields.
`require` and `require-dev` additionally support stability flags ([root-only](04-schema.md#root-package)).
These allow you to further restrict or expand the stability of a package beyond
the scope of the [minimum-stability](#minimum-stability) setting. You can apply
them to a constraint, or apply them to an empty constraint if you want to
allow unstable packages of a dependency for example.
Example:
```json
{
"require": {
"monolog/monolog": "1.0.*@beta",
"acme/foo": "@dev"
}
}
```
If one of your dependencies has a dependency on an unstable package you need to
explicitly require it as well, along with its sufficient stability flag.
Example:
Assuming `doctrine/doctrine-fixtures-bundle` requires `"doctrine/data-fixtures": "dev-master"`
then inside the root composer.json you need to add the second line below to allow dev
releases for the `doctrine/data-fixtures` package :
```json
{
"require": {
"doctrine/doctrine-fixtures-bundle": "dev-master",
"doctrine/data-fixtures": "@dev"
}
}
```
`require` and `require-dev` additionally support explicit references (i.e.
commit) for dev versions to make sure they are locked to a given state, even
when you run update. These only work if you explicitly require a dev version
and append the reference with `#<ref>`. This is also a
[root-only](04-schema.md#root-package) feature and will be ignored in
dependencies.
Example:
```json
{
"require": {
"monolog/monolog": "dev-master#2eb0c0978d290a1c45346a1955188929cb4e5db7",
"acme/foo": "1.0.x-dev#abc123"
}
}
```
> **Note:** This feature has severe technical limitations, as the
> composer.json metadata will still be read from the branch name you specify
> before the hash. You should therefore only use this as a temporary solution
> during development to remediate transient issues, until you can switch to
> tagged releases. The Composer team does not actively support this feature
> and will not accept bug reports related to it.
It is also possible to inline-alias a package constraint so that it matches
a constraint that it otherwise would not. For more information [see the
aliases article](articles/aliases.md).
`require` and `require-dev` also support references to specific PHP versions
and PHP extensions your project needs to run successfully.
Example:
```json
{
"require" : {
"php" : "^5.5 || ^7.0",
"ext-mbstring": "*"
}
}
```
> **Note:** It is important to list PHP extensions your project requires.
> Not all PHP installations are created equal: some may miss extensions you
> may consider as standard (such as `ext-mysqli` which is not installed by
> default in Fedora/CentOS minimal installation systems). Failure to list
> required PHP extensions may lead to a bad user experience: Composer will
> install your package without any errors but it will then fail at run-time.
> The `composer show --platform` command lists all PHP extensions available on
> your system. You may use it to help you compile the list of extensions you
> use and require. Alternatively you may use third party tools to analyze
> your project for the list of extensions used.
#### require
Lists packages required by this package. The package will not be installed
unless those requirements can be met.
#### require-dev <span>([root-only](04-schema.md#root-package))</span>
Lists packages required for developing this package, or running
tests, etc. The dev requirements of the root package are installed by default.
Both `install` or `update` support the `--no-dev` option that prevents dev
dependencies from being installed.
#### conflict
Lists packages that conflict with this version of this package. They
will not be allowed to be installed together with your package.
Note that when specifying ranges like `<1.0 >=1.1` in a `conflict` link,
this will state a conflict with all versions that are less than 1.0 *and* equal
or newer than 1.1 at the same time, which is probably not what you want. You
probably want to go for `<1.0 || >=1.1` in this case.
#### replace
Lists packages that are replaced by this package. This allows you to fork a
package, publish it under a different name with its own version numbers, while
packages requiring the original package continue to work with your fork because
it replaces the original package.
This is also useful for packages that contain sub-packages, for example the main
symfony/symfony package contains all the Symfony Components which are also
available as individual packages. If you require the main package it will
automatically fulfill any requirement of one of the individual components,
since it replaces them.
Caution is advised when using replace for the sub-package purpose explained
above. You should then typically only replace using `self.version` as a version
constraint, to make sure the main package only replaces the sub-packages of
that exact version, and not any other version, which would be incorrect.
#### provide
List of other packages that are provided by this package. This is mostly
useful for common interfaces. A package could depend on some virtual
`logger` package, any library that implements this logger interface would
simply list it in `provide`.
#### suggest
Suggested packages that can enhance or work well with this package. These are
informational and are displayed after the package is installed, to give
your users a hint that they could add more packages, even though they are not
strictly required.
The format is like package links above, except that the values are free text
and not version constraints.
Example:
```json
{
"suggest": {
"monolog/monolog": "Allows more advanced logging of the application flow",
"ext-xml": "Needed to support XML format in class Foo"
}
}
```
### autoload
Autoload mapping for a PHP autoloader.
[`PSR-4`](http://www.php-fig.org/psr/psr-4/) and [`PSR-0`](http://www.php-fig.org/psr/psr-0/)
autoloading, `classmap` generation and `files` includes are supported.
PSR-4 is the recommended way since it offers greater ease of use (no need
to regenerate the autoloader when you add classes).
#### PSR-4
Under the `psr-4` key you define a mapping from namespaces to paths, relative to the
package root. When autoloading a class like `Foo\\Bar\\Baz` a namespace prefix
`Foo\\` pointing to a directory `src/` means that the autoloader will look for a
file named `src/Bar/Baz.php` and include it if present. Note that as opposed to
the older PSR-0 style, the prefix (`Foo\\`) is **not** present in the file path.
Namespace prefixes must end in `\\` to avoid conflicts between similar prefixes.
For example `Foo` would match classes in the `FooBar` namespace so the trailing
backslashes solve the problem: `Foo\\` and `FooBar\\` are distinct.
The PSR-4 references are all combined, during install/update, into a single
key => value array which may be found in the generated file
`vendor/composer/autoload_psr4.php`.
Example:
```json
{
"autoload": {
"psr-4": {
"Monolog\\": "src/",
"Vendor\\Namespace\\": ""
}
}
}
```
If you need to search for a same prefix in multiple directories,
you can specify them as an array as such:
```json
{
"autoload": {
"psr-4": { "Monolog\\": ["src/", "lib/"] }
}
}
```
If you want to have a fallback directory where any namespace will be looked for,
you can use an empty prefix like:
```json
{
"autoload": {
"psr-4": { "": "src/" }
}
}
```
#### PSR-0
Under the `psr-0` key you define a mapping from namespaces to paths, relative to the
package root. Note that this also supports the PEAR-style non-namespaced convention.
Please note namespace declarations should end in `\\` to make sure the autoloader
responds exactly. For example `Foo` would match in `FooBar` so the trailing
backslashes solve the problem: `Foo\\` and `FooBar\\` are distinct.
The PSR-0 references are all combined, during install/update, into a single key => value
array which may be found in the generated file `vendor/composer/autoload_namespaces.php`.
Example:
```json
{
"autoload": {
"psr-0": {
"Monolog\\": "src/",
"Vendor\\Namespace\\": "src/",
"Vendor_Namespace_": "src/"
}
}
}
```
If you need to search for a same prefix in multiple directories,
you can specify them as an array as such:
```json
{
"autoload": {
"psr-0": { "Monolog\\": ["src/", "lib/"] }
}
}
```
The PSR-0 style is not limited to namespace declarations only but may be
specified right down to the class level. This can be useful for libraries with
only one class in the global namespace. If the php source file is also located
in the root of the package, for example, it may be declared like this:
```json
{
"autoload": {
"psr-0": { "UniqueGlobalClass": "" }
}
}
```
If you want to have a fallback directory where any namespace can be, you can
use an empty prefix like:
```json
{
"autoload": {
"psr-0": { "": "src/" }
}
}
```
#### Classmap
The `classmap` references are all combined, during install/update, into a single
key => value array which may be found in the generated file
`vendor/composer/autoload_classmap.php`. This map is built by scanning for
classes in all `.php` and `.inc` files in the given directories/files.
You can use the classmap generation support to define autoloading for all libraries
that do not follow PSR-0/4. To configure this you specify all directories or files
to search for classes.
Example:
```json
{
"autoload": {
"classmap": ["src/", "lib/", "Something.php"]
}
}
```
#### Files
If you want to require certain files explicitly on every request then you can use
the `files` autoloading mechanism. This is useful if your package includes PHP functions
that cannot be autoloaded by PHP.
Example:
```json
{
"autoload": {
"files": ["src/MyLibrary/functions.php"]
}
}
```
#### Exclude files from classmaps
If you want to exclude some files or folders from the classmap you can use the `exclude-from-classmap` property.
This might be useful to exclude test classes in your live environment, for example, as those will be skipped
from the classmap even when building an optimized autoloader.
The classmap generator will ignore all files in the paths configured here. The paths are absolute from the package
root directory (i.e. composer.json location), and support `*` to match anything but a slash, and `**` to
match anything. `**` is implicitly added to the end of the paths.
Example:
```json
{
"autoload": {
"exclude-from-classmap": ["/Tests/", "/test/", "/tests/"]
}
}
```
#### Optimizing the autoloader
The autoloader can have quite a substantial impact on your request time
(50-100ms per request in large frameworks using a lot of classes). See the
[article about optimizing the autoloader](articles/autoloader-optimization.md)
for more details on how to reduce this impact.
### autoload-dev <span>([root-only](04-schema.md#root-package))</span>
This section allows to define autoload rules for development purposes.
Classes needed to run the test suite should not be included in the main autoload
rules to avoid polluting the autoloader in production and when other people use
your package as a dependency.
Therefore, it is a good idea to rely on a dedicated path for your unit tests
and to add it within the autoload-dev section.
Example:
```json
{
"autoload": {
"psr-4": { "MyLibrary\\": "src/" }
},
"autoload-dev": {
"psr-4": { "MyLibrary\\Tests\\": "tests/" }
}
}
```
### include-path
> **DEPRECATED**: This is only present to support legacy projects, and all new code
> should preferably use autoloading. As such it is a deprecated practice, but the
> feature itself will not likely disappear from Composer.
A list of paths which should get appended to PHP's `include_path`.
Example:
```json
{
"include-path": ["lib/"]
}
```
Optional.
### target-dir
> **DEPRECATED**: This is only present to support legacy PSR-0 style autoloading,
> and all new code should preferably use PSR-4 without target-dir and projects
> using PSR-0 with PHP namespaces are encouraged to migrate to PSR-4 instead.
Defines the installation target.
In case the package root is below the namespace declaration you cannot
autoload properly. `target-dir` solves this problem.
An example is Symfony. There are individual packages for the components. The
Yaml component is under `Symfony\Component\Yaml`. The package root is that
`Yaml` directory. To make autoloading possible, we need to make sure that it
is not installed into `vendor/symfony/yaml`, but instead into
`vendor/symfony/yaml/Symfony/Component/Yaml`, so that the autoloader can load
it from `vendor/symfony/yaml`.
To do that, `autoload` and `target-dir` are defined as follows:
```json
{
"autoload": {
"psr-0": { "Symfony\\Component\\Yaml\\": "" }
},
"target-dir": "Symfony/Component/Yaml"
}
```
Optional.
### minimum-stability <span>([root-only](04-schema.md#root-package))</span>
This defines the default behavior for filtering packages by stability. This
defaults to `stable`, so if you rely on a `dev` package, you should specify
it in your file to avoid surprises.
All versions of each package are checked for stability, and those that are less
stable than the `minimum-stability` setting will be ignored when resolving
your project dependencies. (Note that you can also specify stability requirements
on a per-package basis using stability flags in the version constraints that you
specify in a `require` block (see [package links](#package-links) for more details).
Available options (in order of stability) are `dev`, `alpha`, `beta`, `RC`,
and `stable`.
### prefer-stable <span>([root-only](04-schema.md#root-package))</span>
When this is enabled, Composer will prefer more stable packages over unstable
ones when finding compatible stable packages is possible. If you require a
dev version or only alphas are available for a package, those will still be
selected granted that the minimum-stability allows for it.
Use `"prefer-stable": true` to enable.
### repositories <span>([root-only](04-schema.md#root-package))</span>
Custom package repositories to use.
By default Composer only uses the packagist repository. By specifying
repositories you can get packages from elsewhere.
Repositories are not resolved recursively. You can only add them to your main
`composer.json`. Repository declarations of dependencies' `composer.json`s are
ignored.
The following repository types are supported:
* **composer:** A Composer repository is simply a `packages.json` file served
via the network (HTTP, FTP, SSH), that contains a list of `composer.json`
objects with additional `dist` and/or `source` information. The `packages.json`
file is loaded using a PHP stream. You can set extra options on that stream
using the `options` parameter.
* **vcs:** The version control system repository can fetch packages from git,
svn, fossil and hg repositories.
* **pear:** With this you can import any pear repository into your Composer
project.
* **package:** If you depend on a project that does not have any support for
composer whatsoever you can define the package inline using a `package`
repository. You basically inline the `composer.json` object.
For more information on any of these, see [Repositories](05-repositories.md).
Example:
```json
{
"repositories": [
{
"type": "composer",
"url": "http://packages.example.com"
},
{
"type": "composer",
"url": "https://packages.example.com",
"options": {
"ssl": {
"verify_peer": "true"
}
}
},
{
"type": "vcs",
"url": "https://github.com/Seldaek/monolog"
},
{
"type": "pear",
"url": "https://pear2.php.net"
},
{
"type": "package",
"package": {
"name": "smarty/smarty",
"version": "3.1.7",
"dist": {
"url": "https://www.smarty.net/files/Smarty-3.1.7.zip",
"type": "zip"
},
"source": {
"url": "https://smarty-php.googlecode.com/svn/",
"type": "svn",
"reference": "tags/Smarty_3_1_7/distribution/"
}
}
}
]
}
```
> **Note:** Order is significant here. When looking for a package, Composer
will look from the first to the last repository, and pick the first match.
By default Packagist is added last which means that custom repositories can
override packages from it.
Using JSON object notation is also possible. However, JSON key/value pairs
are to be considered unordered so consistent behaviour cannot be guaranteed.
```json
{
"repositories": {
"foo": {
"type": "composer",
"url": "http://packages.foo.com"
}
}
}
```
### config <span>([root-only](04-schema.md#root-package))</span>
A set of configuration options. It is only used for projects. See
[Config](06-config.md) for a description of each individual option.
### scripts <span>([root-only](04-schema.md#root-package))</span>
Composer allows you to hook into various parts of the installation process
through the use of scripts.
See [Scripts](articles/scripts.md) for events details and examples.
### extra
Arbitrary extra data for consumption by `scripts`.
This can be virtually anything. To access it from within a script event
handler, you can do:
```php
$extra = $event->getComposer()->getPackage()->getExtra();
```
Optional.
### bin
A set of files that should be treated as binaries and symlinked into the `bin-dir`
(from config).
See [Vendor Binaries](articles/vendor-binaries.md) for more details.
Optional.
### archive
A set of options for creating package archives.
The following options are supported:
* **exclude:** Allows configuring a list of patterns for excluded paths. The
pattern syntax matches .gitignore files. A leading exclamation mark (!) will
result in any matching files to be included even if a previous pattern
excluded them. A leading slash will only match at the beginning of the project
relative path. An asterisk will not expand to a directory separator.
Example:
```json
{
"archive": {
"exclude": ["/foo/bar", "baz", "/*.test", "!/foo/bar/baz"]
}
}
```
The example will include `/dir/foo/bar/file`, `/foo/bar/baz`, `/file.php`,
`/foo/my.test` but it will exclude `/foo/bar/any`, `/foo/baz`, and `/my.test`.
Optional.
### abandoned
Indicates whether this package has been abandoned.
It can be boolean or a package name/URL pointing to a recommended alternative.
Examples:
Use `"abandoned": true` to indicates this package is abandoned.
Use `"abandoned": "monolog/monolog"` to indicates this package is abandoned and the
recommended alternative is `monolog/monolog`.
Defaults to false.
Optional.
### non-feature-branches
A list of regex patterns of branch names that are non-numeric (e.g. "latest" or something),
that will NOT be handled as feature branches. This is an array of strings.
If you have non-numeric branch names, for example like "latest", "current", "latest-stable"
or something, that do not look like a version number, then Composer handles such branches
as feature branches. This means it searches for parent branches, that look like a version
or ends at special branches (like master) and the root package version number becomes the
version of the parent branch or at least master or something.
To handle non-numeric named branches as versions instead of searching for a parent branch
with a valid version or special branch name like master, you can set patterns for branch
names, that should be handled as dev version branches.
This is really helpful when you have dependencies using "self.version", so that not dev-master,
but the same branch is installed (in the example: latest-testing).
An example:
If you have a testing branch, that is heavily maintained during a testing phase and is
deployed to your staging environment, normally `composer show -s` will give you `versions : * dev-master`.
If you configure `latest-.*` as a pattern for non-feature-branches like this:
```json
{
"non-feature-branches": ["latest-.*"]
}
```
Then `composer show -s` will give you `versions : * dev-latest-testing`.
Optional.
&larr; [Command-line interface](03-cli.md) | [Repositories](05-repositories.md) &rarr;
+717
View File
@@ -0,0 +1,717 @@
# Repositories
This chapter will explain the concept of packages and repositories, what kinds
of repositories are available, and how they work.
## Concepts
Before we look at the different types of repositories that exist, we need to
understand some of the basic concepts that Composer is built on.
### Package
Composer is a dependency manager. It installs packages locally. A package is
essentially a directory containing something. In this case it is PHP
code, but in theory it could be anything. And it contains a package
description which has a name and a version. The name and the version are used
to identify the package.
In fact, internally Composer sees every version as a separate package. While
this distinction does not matter when you are using Composer, it's quite
important when you want to change it.
In addition to the name and the version, there is useful metadata. The
information most relevant for installation is the source definition, which
describes where to get the package contents. The package data points to the
contents of the package. And there are two options here: dist and source.
**Dist:** The dist is a packaged version of the package data. Usually a
released version, usually a stable release.
**Source:** The source is used for development. This will usually originate
from a source code repository, such as git. You can fetch this when you want
to modify the downloaded package.
Packages can supply either of these, or even both. Depending on certain
factors, such as user-supplied options and stability of the package, one will
be preferred.
### Repository
A repository is a package source. It's a list of packages/versions. Composer
will look in all your repositories to find the packages your project requires.
By default only the Packagist repository is registered in Composer. You can
add more repositories to your project by declaring them in `composer.json`.
Repositories are only available to the root package and the repositories
defined in your dependencies will not be loaded. Read the
[FAQ entry](faqs/why-can't-composer-load-repositories-recursively.md) if you
want to learn why.
## Types
### Composer
The main repository type is the `composer` repository. It uses a single
`packages.json` file that contains all of the package metadata.
This is also the repository type that packagist uses. To reference a
`composer` repository, supply the path before the `packages.json` file.
In the case of packagist, that file is located at `/packages.json`, so the URL of
the repository would be `repo.packagist.org`. For `example.org/packages.json` the
repository URL would be `example.org`.
#### packages
The only required field is `packages`. The JSON structure is as follows:
```json
{
"packages": {
"vendor/package-name": {
"dev-master": { @composer.json },
"1.0.x-dev": { @composer.json },
"0.0.1": { @composer.json },
"1.0.0": { @composer.json }
}
}
}
```
The `@composer.json` marker would be the contents of the `composer.json` from
that package version including as a minimum:
* name
* version
* dist or source
Here is a minimal package definition:
```json
{
"name": "smarty/smarty",
"version": "3.1.7",
"dist": {
"url": "https://www.smarty.net/files/Smarty-3.1.7.zip",
"type": "zip"
}
}
```
It may include any of the other fields specified in the [schema](04-schema.md).
#### notify-batch
The `notify-batch` field allows you to specify a URL that will be called
every time a user installs a package. The URL can be either an absolute path
(that will use the same domain as the repository) or a fully qualified URL.
An example value:
```json
{
"notify-batch": "/downloads/"
}
```
For `example.org/packages.json` containing a `monolog/monolog` package, this
would send a `POST` request to `example.org/downloads/` with following
JSON request body:
```json
{
"downloads": [
{"name": "monolog/monolog", "version": "1.2.1.0"}
]
}
```
The version field will contain the normalized representation of the version
number.
This field is optional.
#### provider-includes and providers-url
The `provider-includes` field allows you to list a set of files that list
package names provided by this repository. The hash should be a sha256 of
the files in this case.
The `providers-url` describes how provider files are found on the server. It
is an absolute path from the repository root. It must contain the placeholders
`%package%` and `%hash%`.
An example:
```json
{
"provider-includes": {
"providers-a.json": {
"sha256": "f5b4bc0b354108ef08614e569c1ed01a2782e67641744864a74e788982886f4c"
},
"providers-b.json": {
"sha256": "b38372163fac0573053536f5b8ef11b86f804ea8b016d239e706191203f6efac"
}
},
"providers-url": "/p/%package%$%hash%.json"
}
```
Those files contain lists of package names and hashes to verify the file
integrity, for example:
```json
{
"providers": {
"acme/foo": {
"sha256": "38968de1305c2e17f4de33aea164515bc787c42c7e2d6e25948539a14268bb82"
},
"acme/bar": {
"sha256": "4dd24c930bd6e1103251306d6336ac813b563a220d9ca14f4743c032fb047233"
}
}
}
```
The file above declares that acme/foo and acme/bar can be found in this
repository, by loading the file referenced by `providers-url`, replacing
`%package%` by the vendor namespaced package name and `%hash%` by the
sha256 field. Those files themselves contain package definitions as
described [above](#packages).
These fields are optional. You probably don't need them for your own custom
repository.
#### stream options
The `packages.json` file is loaded using a PHP stream. You can set extra
options on that stream using the `options` parameter. You can set any valid
PHP stream context option. See [Context options and
parameters](https://php.net/manual/en/context.php) for more information.
### VCS
VCS stands for version control system. This includes versioning systems like
git, svn, fossil or hg. Composer has a repository type for installing packages
from these systems.
#### Loading a package from a VCS repository
There are a few use cases for this. The most common one is maintaining your
own fork of a third party library. If you are using a certain library for your
project and you decide to change something in the library, you will want your
project to use the patched version. If the library is on GitHub (this is the
case most of the time), you can simply fork it there and push your changes to
your fork. After that you update the project's `composer.json`. All you have
to do is add your fork as a repository and update the version constraint to
point to your custom branch. In `composer.json`, you should prefix your custom
branch name with `"dev-"`. For version constraint naming conventions see
[Libraries](02-libraries.md) for more information.
Example assuming you patched monolog to fix a bug in the `bugfix` branch:
```json
{
"repositories": [
{
"type": "vcs",
"url": "https://github.com/igorw/monolog"
}
],
"require": {
"monolog/monolog": "dev-bugfix"
}
}
```
When you run `php composer.phar update`, you should get your modified version
of `monolog/monolog` instead of the one from packagist.
Note that you should not rename the package unless you really intend to fork
it in the long term, and completely move away from the original package.
Composer will correctly pick your package over the original one since the
custom repository has priority over packagist. If you want to rename the
package, you should do so in the default (often master) branch and not in a
feature branch, since the package name is taken from the default branch.
Also note that the override will not work if you change the `name` property
in your forked repository's `composer.json` file as this needs to match the
original for the override to work.
If other dependencies rely on the package you forked, it is possible to
inline-alias it so that it matches a constraint that it otherwise would not.
For more information [see the aliases article](articles/aliases.md).
#### Using private repositories
Exactly the same solution allows you to work with your private repositories at
GitHub and BitBucket:
```json
{
"require": {
"vendor/my-private-repo": "dev-master"
},
"repositories": [
{
"type": "vcs",
"url": "git@bitbucket.org:vendor/my-private-repo.git"
}
]
}
```
The only requirement is the installation of SSH keys for a git client.
#### Git alternatives
Git is not the only version control system supported by the VCS repository.
The following are supported:
* **Git:** [git-scm.com](https://git-scm.com)
* **Subversion:** [subversion.apache.org](https://subversion.apache.org)
* **Mercurial:** [mercurial-scm.org](https://www.mercurial-scm.org)
* **Fossil**: [fossil-scm.org](https://www.fossil-scm.org/)
To get packages from these systems you need to have their respective clients
installed. That can be inconvenient. And for this reason there is special
support for GitHub and BitBucket that use the APIs provided by these sites, to
fetch the packages without having to install the version control system. The
VCS repository provides `dist`s for them that fetch the packages as zips.
* **GitHub:** [github.com](https://github.com) (Git)
* **BitBucket:** [bitbucket.org](https://bitbucket.org) (Git and Mercurial)
The VCS driver to be used is detected automatically based on the URL. However,
should you need to specify one for whatever reason, you can use `git-bitbucket`,
`hg-bitbucket`, `github`, `gitlab`, `perforce`, `fossil`, `git`, `svn` or `hg`
as the repository type instead of `vcs`.
If you set the `no-api` key to `true` on a github repository it will clone the
repository as it would with any other git repository instead of using the
GitHub API. But unlike using the `git` driver directly, Composer will still
attempt to use github's zip files.
Please note:
* **To let Composer choose which driver to use** the repository type needs to be defined as "vcs"
* **If you already used a private repository**, this means Composer should have cloned it in cache. If you want to install the same package with drivers, remember to launch the command `composer clearcache` followed by the command `composer update` to update composer cache and install the package from dist.
#### BitBucket Driver Configuration
The BitBucket driver uses OAuth to access your private repositories via the BitBucket REST APIs and you will need to create an OAuth consumer to use the driver, please refer to [Atlassian's Documentation](https://confluence.atlassian.com/bitbucket/oauth-on-bitbucket-cloud-238027431.html). You will need to fill the callback url with something to satisfy BitBucket, but the address does not need to go anywhere and is not used by Composer.
After creating an OAuth consumer in the BitBucket control panel, you need to setup your auth.json file with
the credentials like this (more info [here](https://getcomposer.org/doc/06-config.md#bitbucket-oauth)):
```json
{
"bitbucket-oauth": {
"bitbucket.org": {
"consumer-key": "myKey",
"consumer-secret": "mySecret"
}
}
}
```
**Note that the repository endpoint needs to be https rather than git.**
Alternatively if you prefer not to have your OAuth credentials on your filesystem you may export the ```bitbucket-oauth``` block above to the [COMPOSER_AUTH](https://getcomposer.org/doc/03-cli.md#composer-auth) environment variable instead.
#### Subversion Options
Since Subversion has no native concept of branches and tags, Composer assumes
by default that code is located in `$url/trunk`, `$url/branches` and
`$url/tags`. If your repository has a different layout you can change those
values. For example if you used capitalized names you could configure the
repository like this:
```json
{
"repositories": [
{
"type": "vcs",
"url": "http://svn.example.org/projectA/",
"trunk-path": "Trunk",
"branches-path": "Branches",
"tags-path": "Tags"
}
]
}
```
If you have no branches or tags directory you can disable them entirely by
setting the `branches-path` or `tags-path` to `false`.
If the package is in a sub-directory, e.g. `/trunk/foo/bar/composer.json` and
`/tags/1.0/foo/bar/composer.json`, then you can make Composer access it by
setting the `"package-path"` option to the sub-directory, in this example it
would be `"package-path": "foo/bar/"`.
If you have a private Subversion repository you can save credentials in the
http-basic section of your config (See [Schema](04-schema.md)):
```json
{
"http-basic": {
"svn.example.org": {
"username": "username",
"password": "password"
}
}
}
```
If your Subversion client is configured to store credentials by default these
credentials will be saved for the current user and existing saved credentials
for this server will be overwritten. To change this behavior by setting the
`"svn-cache-credentials"` option in your repository configuration:
```json
{
"repositories": [
{
"type": "vcs",
"url": "http://svn.example.org/projectA/",
"svn-cache-credentials": false
}
]
}
```
### PEAR
It is possible to install packages from any PEAR channel by using the `pear`
repository. Composer will prefix all package names with `pear-{channelName}/`
to avoid conflicts. All packages are also aliased with prefix
`pear-{channelAlias}/`.
Example using `pear2.php.net`:
```json
{
"repositories": [
{
"type": "pear",
"url": "https://pear2.php.net"
}
],
"require": {
"pear-pear2.php.net/PEAR2_Text_Markdown": "*",
"pear-pear2/PEAR2_HTTP_Request": "*"
}
}
```
In this case the short name of the channel is `pear2`, so the
`PEAR2_HTTP_Request` package name becomes `pear-pear2/PEAR2_HTTP_Request`.
> **Note:** The `pear` repository requires doing quite a few requests per
> package, so this may considerably slow down the installation process.
#### Custom vendor alias
It is possible to alias PEAR channel packages with a custom vendor name.
Example:
Suppose you have a private PEAR repository and wish to use Composer to
incorporate dependencies from a VCS. Your PEAR repository contains the
following packages:
* `BasePackage`
* `IntermediatePackage`, which depends on `BasePackage`
* `TopLevelPackage1` and `TopLevelPackage2` which both depend
on `IntermediatePackage`
Without a vendor alias, Composer will use the PEAR channel name as the
vendor portion of the package name:
* `pear-pear.foobar.repo/BasePackage`
* `pear-pear.foobar.repo/IntermediatePackage`
* `pear-pear.foobar.repo/TopLevelPackage1`
* `pear-pear.foobar.repo/TopLevelPackage2`
Suppose at a later time you wish to migrate your PEAR packages to a
Composer repository and naming scheme, and adopt the vendor name of `foobar`.
Projects using your PEAR packages would not see the updated packages, since
they have a different vendor name (`foobar/IntermediatePackage` vs
`pear-pear.foobar.repo/IntermediatePackage`).
By specifying `vendor-alias` for the PEAR repository from the start, you can
avoid this scenario and future-proof your package names.
To illustrate, the following example would get the `BasePackage`,
`TopLevelPackage1`, and `TopLevelPackage2` packages from your PEAR repository
and `IntermediatePackage` from a Github repository:
```json
{
"repositories": [
{
"type": "git",
"url": "https://github.com/foobar/intermediate.git"
},
{
"type": "pear",
"url": "http://pear.foobar.repo",
"vendor-alias": "foobar"
}
],
"require": {
"foobar/TopLevelPackage1": "*",
"foobar/TopLevelPackage2": "*"
}
}
```
### Package
If you want to use a project that does not support Composer through any of the
means above, you still can define the package yourself by using a `package`
repository.
Basically, you define the same information that is included in the `composer`
repository's `packages.json`, but only for a single package. Again, the
minimum required fields are `name`, `version`, and either of `dist` or
`source`.
Here is an example for the smarty template engine:
```json
{
"repositories": [
{
"type": "package",
"package": {
"name": "smarty/smarty",
"version": "3.1.7",
"dist": {
"url": "https://www.smarty.net/files/Smarty-3.1.7.zip",
"type": "zip"
},
"source": {
"url": "http://smarty-php.googlecode.com/svn/",
"type": "svn",
"reference": "tags/Smarty_3_1_7/distribution/"
},
"autoload": {
"classmap": ["libs/"]
}
}
}
],
"require": {
"smarty/smarty": "3.1.*"
}
}
```
Typically you would leave the source part off, as you don't really need it.
> **Note**: This repository type has a few limitations and should be avoided
> whenever possible:
>
> - Composer will not update the package unless you change the `version` field.
> - Composer will not update the commit references, so if you use `master` as
> reference you will have to delete the package to force an update, and will
> have to deal with an unstable lock file.
The `"package"` key in a `package` repository may be set to an array to define multiple versions of a package:
```json
{
"repositories": [
{
"type": "package",
"package": [
{
"name": "foo/bar",
"version": "1.0.0",
...
},
{
"name": "foo/bar",
"version": "2.0.0",
...
}
]
}
]
}
```
## Hosting your own
While you will probably want to put your packages on packagist most of the
time, there are some use cases for hosting your own repository.
* **Private company packages:** If you are part of a company that uses Composer
for their packages internally, you might want to keep those packages private.
* **Separate ecosystem:** If you have a project which has its own ecosystem,
and the packages aren't really reusable by the greater PHP community, you
might want to keep them separate to packagist. An example of this would be
wordpress plugins.
For hosting your own packages, a native `composer` type of repository is
recommended, which provides the best performance.
There are a few tools that can help you create a `composer` repository.
### Private Packagist
[Private Packagist](https://packagist.com/) is a hosted or self-hosted
application providing private package hosting as well as mirroring of
GitHub, Packagist.org and other package repositories.
Check out [Packagist.com](https://packagist.com/) for more information.
### Satis
Satis is a static `composer` repository generator. It is a bit like an ultra-
lightweight, static file-based version of packagist.
You give it a `composer.json` containing repositories, typically VCS and
package repository definitions. It will fetch all the packages that are
`require`d and dump a `packages.json` that is your `composer` repository.
Check [the satis GitHub repository](https://github.com/composer/satis) and
the [Satis article](articles/handling-private-packages-with-satis.md) for more
information.
### Artifact
There are some cases, when there is no ability to have one of the previously
mentioned repository types online, even the VCS one. Typical example could be
cross-organisation library exchange through built artifacts. Of course, most
of the times they are private. To simplify maintenance, one can simply use a
repository of type `artifact` with a folder containing ZIP archives of those
private packages:
```json
{
"repositories": [
{
"type": "artifact",
"url": "path/to/directory/with/zips/"
}
],
"require": {
"private-vendor-one/core": "15.6.2",
"private-vendor-two/connectivity": "*",
"acme-corp/parser": "10.3.5"
}
}
```
Each zip artifact is a ZIP archive with `composer.json` in root folder:
```sh
unzip -l acme-corp-parser-10.3.5.zip
composer.json
...
```
If there are two archives with different versions of a package, they are both
imported. When an archive with a newer version is added in the artifact folder
and you run `update`, that version will be imported as well and Composer will
update to the latest version.
### Path
In addition to the artifact repository, you can use the path one, which allows
you to depend on a local directory, either absolute or relative. This can be
especially useful when dealing with monolithic repositories.
For instance, if you have the following directory structure in your repository:
```
- apps
\_ my-app
\_ composer.json
- packages
\_ my-package
\_ composer.json
```
Then, to add the package `my/package` as a dependency, in your
`apps/my-app/composer.json` file, you can use the following configuration:
```json
{
"repositories": [
{
"type": "path",
"url": "../../packages/my-package"
}
],
"require": {
"my/package": "*"
}
}
```
If the package is a local VCS repository, the version may be inferred by
the branch or tag that is currently checked out. Otherwise, the version should
be explicitly defined in the package's `composer.json` file. If the version
cannot be resolved by these means, it is assumed to be `dev-master`.
The local package will be symlinked if possible, in which case the output in
the console will read `Symlinking from ../../packages/my-package`. If symlinking
is _not_ possible the package will be copied. In that case, the console will
output `Mirrored from ../../packages/my-package`.
Instead of default fallback strategy you can force to use symlink with
`"symlink": true` or mirroring with `"symlink": false` option. Forcing
mirroring can be useful when deploying or generating package from a
monolithic repository.
> **Note:** On Windows, directory symlinks are implemented using NTFS junctions
> because they can be created by non-admin users. Mirroring will always be used
> on versions below Windows 7 or if `proc_open` has been disabled.
```json
{
"repositories": [
{
"type": "path",
"url": "../../packages/my-package",
"options": {
"symlink": false
}
}
]
}
```
Leading tildes are expanded to the current user's home folder, and environment
variables are parsed in both Windows and Linux/Mac notations. For example
`~/git/mypackage` will automatically load the mypackage clone from
`/home/<username>/git/mypackage`, equivalent to `$HOME/git/mypackage` or
`%USERPROFILE%/git/mypackage`.
> **Note:** Repository paths can also contain wildcards like ``*`` and ``?``.
> For details, see the [PHP glob function](http://php.net/glob).
## Disabling Packagist.org
You can disable the default Packagist.org repository by adding this to your
`composer.json`:
```json
{
"repositories": [
{
"packagist.org": false
}
]
}
```
You can disable Packagist.org globally by using the global config flag:
```bash
composer config -g repo.packagist false
```
&larr; [Schema](04-schema.md) | [Config](06-config.md) &rarr;
+299
View File
@@ -0,0 +1,299 @@
# Config
This chapter will describe the `config` section of the `composer.json`
[schema](04-schema.md).
## process-timeout
Defaults to `300`. The duration processes like git clones can run before
Composer assumes they died out. You may need to make this higher if you have a
slow connection or huge vendors.
To disable the process timeout on a custom command under `scripts`, a static
helper is available:
```json
{
"scripts": {
"test": [
"Composer\\Config::disableProcessTimeout",
"phpunit"
]
}
}
```
## use-include-path
Defaults to `false`. If `true`, the Composer autoloader will also look for classes
in the PHP include path.
## preferred-install
Defaults to `auto` and can be any of `source`, `dist` or `auto`. This option
allows you to set the install method Composer will prefer to use. Can
optionally be a hash of patterns for more granular install preferences.
```json
{
"config": {
"preferred-install": {
"my-organization/stable-package": "dist",
"my-organization/*": "source",
"partner-organization/*": "auto",
"*": "dist"
}
}
}
```
> **Note:** Order matters. More specific patterns should be earlier than
> more relaxed patterns. When mixing the string notation with the hash
> configuration in global and package configurations the string notation
> is translated to a `*` package pattern.
## store-auths
What to do after prompting for authentication, one of: `true` (always store),
`false` (do not store) and `"prompt"` (ask every time), defaults to `"prompt"`.
## github-protocols
Defaults to `["https", "ssh", "git"]`. A list of protocols to use when cloning
from github.com, in priority order. By default `git` is present but only if [secure-http](#secure-http)
is disabled, as the git protocol is not encrypted. If you want your origin remote
push URLs to be using https and not ssh (`git@github.com:...`), then set the protocol
list to be only `["https"]` and Composer will stop overwriting the push URL to an ssh
URL.
## github-oauth
A list of domain names and oauth keys. For example using `{"github.com":
"oauthtoken"}` as the value of this option will use `oauthtoken` to access
private repositories on github and to circumvent the low IP-based rate limiting
of their API. [Read
more](articles/troubleshooting.md#api-rate-limit-and-oauth-tokens) on how to get
an OAuth token for GitHub.
## gitlab-oauth
A list of domain names and oauth keys. For example using `{"gitlab.com":
"oauthtoken"}` as the value of this option will use `oauthtoken` to access
private repositories on gitlab. Please note: If the package is not hosted at
gitlab.com the domain names must be also specified with the
[`gitlab-domains`](06-config.md#gitlab-domains) option.
## gitlab-token
A list of domain names and private tokens. For example using `{"gitlab.com":
"privatetoken"}` as the value of this option will use `privatetoken` to access
private repositories on gitlab. Please note: If the package is not hosted at
gitlab.com the domain names must be also specified with the
[`gitlab-domains`](06-config.md#gitlab-domains) option.
## disable-tls
Defaults to `false`. If set to true all HTTPS URLs will be tried with HTTP
instead and no network level encryption is performed. Enabling this is a
security risk and is NOT recommended. The better way is to enable the
php_openssl extension in php.ini.
## secure-http
Defaults to `true`. If set to true only HTTPS URLs are allowed to be
downloaded via Composer. If you really absolutely need HTTP access to something
then you can disable it, but using [Let's Encrypt](https://letsencrypt.org/) to
get a free SSL certificate is generally a better alternative.
## bitbucket-oauth
A list of domain names and consumers. For example using `{"bitbucket.org":
{"consumer-key": "myKey", "consumer-secret": "mySecret"}}`. [Read](https://confluence.atlassian.com/bitbucket/oauth-on-bitbucket-cloud-238027431.html)
how to set up a consumer on Bitbucket.
## cafile
Location of Certificate Authority file on local filesystem. In PHP 5.6+ you
should rather set this via openssl.cafile in php.ini, although PHP 5.6+ should
be able to detect your system CA file automatically.
## capath
If cafile is not specified or if the certificate is not found there, the
directory pointed to by capath is searched for a suitable certificate.
capath must be a correctly hashed certificate directory.
## http-basic
A list of domain names and username/passwords to authenticate against them. For
example using `{"example.org": {"username": "alice", "password": "foo"}}` as the
value of this option will let Composer authenticate against example.org.
> **Note:** Authentication-related config options like `http-basic` and
> `github-oauth` can also be specified inside a `auth.json` file that goes
> besides your `composer.json`. That way you can gitignore it and every
> developer can place their own credentials in there.
## platform
Lets you fake platform packages (PHP and extensions) so that you can emulate a
production env or define your target platform in the config. Example: `{"php":
"7.0.3", "ext-something": "4.0.3"}`.
## vendor-dir
Defaults to `vendor`. You can install dependencies into a different directory if
you want to. `$HOME` and `~` will be replaced by your home directory's path in
vendor-dir and all `*-dir` options below.
## bin-dir
Defaults to `vendor/bin`. If a project includes binaries, they will be symlinked
into this directory.
## data-dir
Defaults to `C:\Users\<user>\AppData\Roaming\Composer` on Windows,
`$XDG_DATA_HOME/composer` on unix systems that follow the XDG Base Directory
Specifications, and `$home` on other unix systems. Right now it is only
used for storing past composer.phar files to be able to rollback to older
versions. See also [COMPOSER_HOME](03-cli.md#composer-home).
## cache-dir
Defaults to `C:\Users\<user>\AppData\Local\Composer` on Windows,
`$XDG_CACHE_HOME/composer` on unix systems that follow the XDG Base Directory
Specifications, and `$home/cache` on other unix systems. Stores all the caches
used by Composer. See also [COMPOSER_HOME](03-cli.md#composer-home).
## cache-files-dir
Defaults to `$cache-dir/files`. Stores the zip archives of packages.
## cache-repo-dir
Defaults to `$cache-dir/repo`. Stores repository metadata for the `composer`
type and the VCS repos of type `svn`, `fossil`, `github` and `bitbucket`.
## cache-vcs-dir
Defaults to `$cache-dir/vcs`. Stores VCS clones for loading VCS repository
metadata for the `git`/`hg` types and to speed up installs.
## cache-files-ttl
Defaults to `15552000` (6 months). Composer caches all dist (zip, tar, ..)
packages that it downloads. Those are purged after six months of being unused by
default. This option allows you to tweak this duration (in seconds) or disable
it completely by setting it to 0.
## cache-files-maxsize
Defaults to `300MiB`. Composer caches all dist (zip, tar, ..) packages that it
downloads. When the garbage collection is periodically ran, this is the maximum
size the cache will be able to use. Older (less used) files will be removed
first until the cache fits.
## bin-compat
Defaults to `auto`. Determines the compatibility of the binaries to be installed.
If it is `auto` then Composer only installs .bat proxy files when on Windows. If
set to `full` then both .bat files for Windows and scripts for Unix-based
operating systems will be installed for each binary. This is mainly useful if you
run Composer inside a linux VM but still want the .bat proxies available for use
in the Windows host OS.
## prepend-autoloader
Defaults to `true`. If `false`, the Composer autoloader will not be prepended to
existing autoloaders. This is sometimes required to fix interoperability issues
with other autoloaders.
## autoloader-suffix
Defaults to `null`. String to be used as a suffix for the generated Composer
autoloader. When null a random one will be generated.
## optimize-autoloader
Defaults to `false`. If `true`, always optimize when dumping the autoloader.
## sort-packages
Defaults to `false`. If `true`, the `require` command keeps packages sorted
by name in `composer.json` when adding a new package.
## classmap-authoritative
Defaults to `false`. If `true`, the Composer autoloader will only load classes
from the classmap. Implies `optimize-autoloader`.
## apcu-autoloader
Defaults to `false`. If `true`, the Composer autoloader will check for APCu and
use it to cache found/not-found classes when the extension is enabled.
## github-domains
Defaults to `["github.com"]`. A list of domains to use in github mode. This is
used for GitHub Enterprise setups.
## github-expose-hostname
Defaults to `true`. If `false`, the OAuth tokens created to access the
github API will have a date instead of the machine hostname.
## gitlab-domains
Defaults to `["gitlab.com"]`. A list of domains of GitLab servers.
This is used if you use the `gitlab` repository type.
## use-github-api
Defaults to `true`. Similar to the `no-api` key on a specific repository,
setting `use-github-api` to `false` will define the global behavior for all
GitHub repositories to clone the repository as it would with any other git
repository instead of using the GitHub API. But unlike using the `git`
driver directly, Composer will still attempt to use GitHub's zip files.
## notify-on-install
Defaults to `true`. 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.
## discard-changes
Defaults to `false` and can be any of `true`, `false` or `"stash"`. This option
allows you to set the default style of handling dirty updates when in
non-interactive mode. `true` will always discard changes in vendors, while
`"stash"` will try to stash and reapply. Use this for CI servers or deploy
scripts if you tend to have modified vendors.
## archive-format
Defaults to `tar`. Composer allows you to add a default archive format when the
workflow needs to create a dedicated archiving format.
## archive-dir
Defaults to `.`. Composer allows you to add a default archive directory when the
workflow needs to create a dedicated archiving format. Or for easier development
between modules.
Example:
```json
{
"config": {
"archive-dir": "/home/user/.composer/repo"
}
}
```
## htaccess-protect
Defaults to `true`. If set to `false`, Composer will not create `.htaccess` files
in the composer home, cache, and data directories.
&larr; [Repositories](05-repositories.md) | [Community](07-community.md) &rarr;
+35
View File
@@ -0,0 +1,35 @@
# Community
There are many people using Composer already, and quite a few of them are
contributing.
## Contributing
If you would like to contribute to Composer, please read the
[README](https://github.com/composer/composer) and
[CONTRIBUTING](https://github.com/composer/composer/blob/master/.github/CONTRIBUTING.md)
documents.
The most important guidelines are described as follows:
> All code contributions - including those of people having commit access - must
> go through a pull request and approved by a core developer before being
> merged. This is to ensure proper review of all the code.
>
> Fork the project, create a feature branch, and send us a pull request.
>
> To ensure a consistent code base, you should make sure the code follows
> the [PSR-2 Coding Standards](http://www.php-fig.org/psr/psr-2/).
## IRC / mailing list
Mailing lists for [user support](https://groups.google.com/group/composer-users) and
[development](https://groups.google.com/group/composer-dev).
IRC channels are on irc.freenode.org: [#composer](irc://irc.freenode.org/composer)
for users and [#composer-dev](irc://irc.freenode.org/composer-dev) for development.
Stack Overflow has a growing collection of
[Composer related questions](https://stackoverflow.com/questions/tagged/composer-php).
&larr; [Config](06-config.md)
+111
View File
@@ -0,0 +1,111 @@
<!--
tagline: Alias branch names to versions
-->
# Aliases
## Why aliases?
When you are using a VCS repository, you will only get comparable versions for
branches that look like versions, such as `2.0` or `2.0.x`. For your `master` branch, you
will get a `dev-master` version. For your `bugfix` branch, you will get a
`dev-bugfix` version.
If your `master` branch is used to tag releases of the `1.0` development line,
i.e. `1.0.1`, `1.0.2`, `1.0.3`, etc., any package depending on it will
probably require version `1.0.*`.
If anyone wants to require the latest `dev-master`, they have a problem: Other
packages may require `1.0.*`, so requiring that dev version will lead to
conflicts, since `dev-master` does not match the `1.0.*` constraint.
Enter aliases.
## Branch alias
The `dev-master` branch is one in your main VCS repo. It is rather common that
someone will want the latest master dev version. Thus, Composer allows you to
alias your `dev-master` branch to a `1.0.x-dev` version. It is done by
specifying a `branch-alias` field under `extra` in `composer.json`:
```json
{
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
}
}
```
If you alias a non-comparable version (such as dev-develop) `dev-` must prefix the
branch name. You may also alias a comparable version (i.e. start with numbers,
and end with `.x-dev`), but only as a more specific version.
For example, 1.x-dev could be aliased as 1.2.x-dev.
The alias must be a comparable dev version, and the `branch-alias` must be present on
the branch that it references. For `dev-master`, you need to commit it on the
`master` branch.
As a result, anyone can now require `1.0.*` and it will happily install
`dev-master`.
In order to use branch aliasing, you must own the repository of the package
being aliased. If you want to alias a third party package without maintaining
a fork of it, use inline aliases as described below.
## Require inline alias
Branch aliases are great for aliasing main development lines. But in order to
use them you need to have control over the source repository, and you need to
commit changes to version control.
This is not really fun when you want to try a bugfix of some library that
is a dependency of your local project.
For this reason, you can alias packages in your `require` and `require-dev`
fields. Let's say you found a bug in the `monolog/monolog` package. You cloned
[Monolog](https://github.com/Seldaek/monolog) on GitHub and fixed the issue in
a branch named `bugfix`. Now you want to install that version of monolog in your
local project.
You are using `symfony/monolog-bundle` which requires `monolog/monolog` version
`1.*`. So you need your `dev-bugfix` to match that constraint.
Add this to your project's root `composer.json`:
```json
{
"repositories": [
{
"type": "vcs",
"url": "https://github.com/you/monolog"
}
],
"require": {
"symfony/monolog-bundle": "2.0",
"monolog/monolog": "dev-bugfix as 1.0.x-dev"
}
}
```
Or let composer add it for you with:
```
php composer.phar require monolog/monolog:"dev-bugfix as 1.0.x-dev"
```
That will fetch the `dev-bugfix` version of `monolog/monolog` from your GitHub
and alias it to `1.0.x-dev`.
> **Note:** Inline aliasing is a root-only feature. If a package with inline
> aliases is required, the alias (right of the `as`) is used as the version
> constraint. The part left of the `as` is discarded. As a consequence, if
> A requires B and B requires `monolog/monolog` version `dev-bugfix as 1.0.x-dev`,
> installing A will make B require `1.0.x-dev`, which may exist as a branch
> alias or an actual `1.0` branch. If it does not, it must be
> inline-aliased again in A's `composer.json`.
> **Note:** Inline aliasing should be avoided, especially for published
> packages/libraries. If you found a bug, try and get your fix merged upstream.
> This helps to avoid issues for users of your package.
@@ -0,0 +1,111 @@
<!--
tagline: How to reduce the performance impact of the autoloader
-->
# Autoloader Optimization
By default, the Composer autoloader runs relatively fast. However, due to the way
PSR-4 and PSR-0 autoloading rules are set up, it needs to check the filesystem
before resolving a classname conclusively. This slows things down quite a bit,
but it is convenient in development environments because when you add a new class
it can immediately be discovered/used without having to rebuild the autoloader
configuration.
The problem however is in production you generally want things to happen as fast
as possible, as you can simply rebuild the configuration every time you deploy and
new classes do not appear at random between deploys.
For this reason, Composer offers a few strategies to optimize the autoloader.
> **Note:** You **should not** enable any of these optimizations in **development** as
> they all will cause various problems when adding/removing classes. The performance
> gains are not worth the trouble in a development setting.
## Optimization Level 1: Class map generation
### How to run it?
There are a few options to enable this:
- Set `"optimize-autoloader": true` inside the config key of composer.json
- Call `install` or `update` with `-o` / `--optimize-autoloader`
- Call `dump-autoload` with `-o` / `--optimize`
### What does it do?
Class map generation essentially converts PSR-4/PSR-0 rules into classmap rules.
This makes everything quite a bit faster as for known classes the class map
returns instantly the path, and Composer can guarantee the class is in there so
there is no filesystem check needed.
On PHP 5.6+, the class map is also cached in opcache which improves the initialization
time greatly. If you make sure opcache is enabled, then the class map should load
almost instantly and then class loading is fast.
### Trade-offs
There are no real trade-offs with this method. It should always be enabled in
production.
The only issue is it does not keep track of autoload misses (i.e. when
it can not find a given class), so those fallback to PSR-4 rules and can still
result in slow filesystem checks. To solve this issue two Level 2 optimization
options exist, and you can decide to enable either if you have a lot of
class_exists checks that are done for classes that do not exist in your project.
## Optimization Level 2/A: Authoritative class maps
### How to run it?
There are a few options to enable this:
- Set `"classmap-authoritative": true` inside the config key of composer.json
- Call `install` or `update` with `-a` / `--classmap-authoritative`
- Call `dump-autoload` with `-a` / `--classmap-authoritative`
### What does it do?
Enabling this automatically enables Level 1 class map optimizations.
This option is very simple, it says that if something is not found in the classmap,
then it does not exist and the autoloader should not attempt to look on the
filesystem according to PSR-4 rules.
### Trade-offs
This option makes the autoloader always return very quickly. On the flipside it
also means that in case a class is generated at runtime for some reason, it will
not be allowed to be autoloaded. If your project or any of your dependencies does that
then you might experience "class not found" issues in production. Enable this with care.
> Note: This can not be combined with Level 2/B optimizations. You have to choose one as
> they address the same issue in different ways.
## Optimization Level 2/B: APCu cache
### How to run it?
There are a few options to enable this:
- Set `"apcu-autoloader": true` inside the config key of composer.json
- Call `install` or `update` with `--apcu-autoloader`
- Call `dump-autoload` with `--apcu`
### What does it do?
This option adds an APCu cache as a fallback for the class map. It will not
automatically generate the class map though, so you should still enable Level 1
optimizations manually if you so desire.
Whether a class is found or not, that fact is always cached in APCu so it can be
returned quickly on the next request.
### Trade-offs
This option requires APCu which may or may not be available to you. It also
uses APCu memory for autoloading purposes, but it is safe to use and can not
result in classes not being found like the authoritative class map
optimization above.
> Note: This can not be combined with Level 2/A optimizations. You have to choose one as
> they address the same issue in different ways.
@@ -0,0 +1,204 @@
<!--
tagline: Modify the way certain types of packages are installed
-->
# Setting up and using custom installers
## Synopsis
At times it may be necessary for a package to require additional actions during
installation, such as installing packages outside of the default `vendor`
library.
In these cases you could consider creating a Custom Installer to handle your
specific logic.
## Calling a Custom Installer
Suppose that your project already has a Custom Installer for specific modules
then invoking that installer is a matter of defining the correct [type][1] in
your package file.
> _See the next chapter for an instruction how to create Custom Installers._
Every Custom Installer defines which [type][1] string it will recognize. Once
recognized it will completely override the default installer and only apply its
own logic.
An example use-case would be:
> phpDocumentor features Templates that need to be installed outside of the
> default /vendor folder structure. As such they have chosen to adopt the
> `phpdocumentor-template` [type][1] and create a plugin providing the Custom
> Installer to send these templates to the correct folder.
An example composer.json of such a template package would be:
```json
{
"name": "phpdocumentor/template-responsive",
"type": "phpdocumentor-template",
"require": {
"phpdocumentor/template-installer-plugin": "*"
}
}
```
> **IMPORTANT**: to make sure that the template installer is present at the
> time the template package is installed, template packages should require
> the plugin package.
## Creating an Installer
A Custom Installer is defined as a class that implements the
[`Composer\Installer\InstallerInterface`][4] and is usually distributed in a
Composer Plugin.
A basic Installer Plugin would thus compose of three files:
1. the package file: composer.json
2. The Plugin class, e.g.: `My\Project\Composer\Plugin.php`, containing a class that implements `Composer\Plugin\PluginInterface`.
3. The Installer class, e.g.: `My\Project\Composer\Installer.php`, containing a class that implements `Composer\Installer\InstallerInterface`.
### composer.json
The package file is the same as any other package file but with the following
requirements:
1. the [type][1] attribute must be `composer-plugin`.
2. the [extra][2] attribute must contain an element `class` defining the
class name of the plugin (including namespace). If a package contains
multiple plugins this can be array of class names.
Example:
```json
{
"name": "phpdocumentor/template-installer-plugin",
"type": "composer-plugin",
"license": "MIT",
"autoload": {
"psr-0": {"phpDocumentor\\Composer": "src/"}
},
"extra": {
"class": "phpDocumentor\\Composer\\TemplateInstallerPlugin"
},
"require": {
"composer-plugin-api": "^1.0"
},
"require-dev": {
"composer/composer": "^1.3"
}
}
```
The example above has Composer itself in its require-dev, which allows you to use
the Composer classes in your test suite for example.
### The Plugin class
The class defining the Composer plugin must implement the
[`Composer\Plugin\PluginInterface`][3]. It can then register the Custom
Installer in its `activate()` method.
The class may be placed in any location and have any name, as long as it is
autoloadable and matches the `extra.class` element in the package definition.
Example:
```php
<?php
namespace phpDocumentor\Composer;
use Composer\Composer;
use Composer\IO\IOInterface;
use Composer\Plugin\PluginInterface;
class TemplateInstallerPlugin implements PluginInterface
{
public function activate(Composer $composer, IOInterface $io)
{
$installer = new TemplateInstaller($io, $composer);
$composer->getInstallationManager()->addInstaller($installer);
}
}
```
### The Custom Installer class
The class that executes the custom installation should implement the
[`Composer\Installer\InstallerInterface`][4] (or extend another installer that
implements that interface). It defines the [type][1] string as it will be
recognized by packages that will use this installer in the `supports()` method.
> **NOTE**: _choose your [type][1] name carefully, it is recommended to follow
> the format: `vendor-type`_. For example: `phpdocumentor-template`.
The InstallerInterface class defines the following methods (please see the
source for the exact signature):
* **supports()**, here you test whether the passed [type][1] matches the name
that you declared for this installer (see the example).
* **isInstalled()**, determines whether a supported package is installed or not.
* **install()**, here you can determine the actions that need to be executed
upon installation.
* **update()**, here you define the behavior that is required when Composer is
invoked with the update argument.
* **uninstall()**, here you can determine the actions that need to be executed
when the package needs to be removed.
* **getInstallPath()**, this method should return the location where the
package is to be installed, _relative from the location of composer.json._
Example:
```php
<?php
namespace phpDocumentor\Composer;
use Composer\Package\PackageInterface;
use Composer\Installer\LibraryInstaller;
class TemplateInstaller extends LibraryInstaller
{
/**
* {@inheritDoc}
*/
public function getInstallPath(PackageInterface $package)
{
$prefix = substr($package->getPrettyName(), 0, 23);
if ('phpdocumentor/template-' !== $prefix) {
throw new \InvalidArgumentException(
'Unable to install template, phpdocumentor templates '
.'should always start their package name with '
.'"phpdocumentor/template-"'
);
}
return 'data/templates/'.substr($package->getPrettyName(), 23);
}
/**
* {@inheritDoc}
*/
public function supports($packageType)
{
return 'phpdocumentor-template' === $packageType;
}
}
```
The example demonstrates that it is quite simple to extend the
[`Composer\Installer\LibraryInstaller`][5] class to strip a prefix
(`phpdocumentor/template-`) and use the remaining part to assemble a completely
different installation path.
> _Instead of being installed in `/vendor` any package installed using this
> Installer will be put in the `/data/templates/<stripped name>` folder._
[1]: ../04-schema.md#type
[2]: ../04-schema.md#extra
[3]: https://github.com/composer/composer/blob/master/src/Composer/Plugin/PluginInterface.php
[4]: https://github.com/composer/composer/blob/master/src/Composer/Installer/InstallerInterface.php
[5]: https://github.com/composer/composer/blob/master/src/Composer/Installer/LibraryInstaller.php
@@ -0,0 +1,356 @@
<!--
tagline: Hosting and installing private Composer packages
-->
# Handling private packages
# Private Packagist
[Private Packagist](https://packagist.com) is a commercial package hosting product
offering professional support and web based management of private and public packages,
and granular access permissions. Private Packagist provides mirroring for packages' zip
files which makes installs faster and independent from third party systems - e.g.
you can deploy even if GitHub is down because your zip files are mirrored.
Private Packagist is available as a hosted SaaS solution or as an on-premise self-hosted
package, providing an easy interactive set up experience.
Some of Private Packagist's revenue is used to pay for Composer and Packagist.org
development and hosting so using it is a good way to support the maintenance of
these open source projects financially. You can find more information about how to
set up your own package archive on [Packagist.com](https://packagist.com).
# Satis
Satis on the other hand is open source but only a static `composer` repository
generator. It is a bit like an ultra-lightweight, static file-based version of
packagist and can be used to host the metadata of your company's private
packages, or your own. You can get it from
[GitHub](https://github.com/composer/satis) or install via CLI:
php composer.phar create-project composer/satis --stability=dev --keep-vcs
## Setup
For example let's assume you have a few packages you want to reuse across your
company but don't really want to open-source. You would first define a Satis
configuration: a json file with an arbitrary name that lists your curated
[repositories](../05-repositories.md).
Here is an example configuration, you see that it holds a few VCS repositories,
but those could be any types of [repositories](../05-repositories.md). Then it
uses `"require-all": true` which selects all versions of all packages in the
repositories you defined.
The default file Satis looks for is `satis.json` in the root of the repository.
```json
{
"name": "My Repository",
"homepage": "http://packages.example.org",
"repositories": [
{ "type": "vcs", "url": "https://github.com/mycompany/privaterepo" },
{ "type": "vcs", "url": "http://svn.example.org/private/repo" },
{ "type": "vcs", "url": "https://github.com/mycompany/privaterepo2" }
],
"require-all": true
}
```
If you want to cherry pick which packages you want, you can list all the
packages you want to have in your satis repository inside the classic composer
`require` key, using a `"*"` constraint to make sure all versions are selected,
or another constraint if you want really specific versions.
```json
{
"repositories": [
{ "type": "vcs", "url": "https://github.com/mycompany/privaterepo" },
{ "type": "vcs", "url": "http://svn.example.org/private/repo" },
{ "type": "vcs", "url": "https://github.com/mycompany/privaterepo2" }
],
"require": {
"company/package": "*",
"company/package2": "*",
"company/package3": "2.0.0"
}
}
```
Once you've done this, you run:
php bin/satis build <configuration file> <build dir>
When you ironed out that process, what you would typically do is run this
command as a cron job on a server. It would then update all your package info
much like Packagist does.
Note that if your private packages are hosted on GitHub, your server should
have an ssh key that gives it access to those packages, and then you should add
the `--no-interaction` (or `-n`) flag to the command to make sure it falls back
to ssh key authentication instead of prompting for a password. This is also a
good trick for continuous integration servers.
Set up a virtual-host that points to that `web/` directory, let's say it is
`packages.example.org`. Alternatively, with PHP >= 5.4.0, you can use the
built-in CLI server `php -S localhost:port -t satis-output-dir/` for a
temporary solution.
### Partial Updates
You can tell Satis to selectively update only particular packages or process
only a repository with a given URL. This cuts down the time it takes to rebuild
the `package.json` file and is helpful if you use (custom) webhooks to trigger
rebuilds whenever code is pushed into one of your repositories.
To rebuild only particular packages, pass the package names on the command line
like so:
php bin/satis build satis.json web/ this/package that/other-package
Note that this will still need to pull and scan all of your VCS repositories
because any VCS repository might contain (on any branch) one of the selected
packages.
If you want to scan only the selected package and not all VCS repositories you need
to declare a *name* for all your package (this only work on VCS repositories type) :
```json
{
"repositories": [
{ "name": "company/privaterepo", "type": "vcs", "url": "https://github.com/mycompany/privaterepo" },
{ "name": "private/repo", "type": "vcs", "url": "http://svn.example.org/private/repo" },
{ "name": "mycompany/privaterepo2", "type": "vcs", "url": "https://github.com/mycompany/privaterepo2" }
]
}
```
If you want to scan only a single repository and update all packages found in
it, pass the VCS repository URL as an optional argument:
php bin/satis build --repository-url https://only.my/repo.git satis.json web/
## Usage
In your projects all you need to add now is your own composer repository using
the `packages.example.org` as URL, then you can require your private packages
and everything should work smoothly. You don't need to copy all your
repositories in every project anymore. Only that one unique repository that
will update itself.
```json
{
"repositories": [ { "type": "composer", "url": "http://packages.example.org/" } ],
"require": {
"company/package": "1.2.0",
"company/package2": "1.5.2",
"company/package3": "dev-master"
}
}
```
### Security
To secure your private repository you can host it over SSH or SSL using a client
certificate. In your project you can use the `options` parameter to specify the
connection options for the server.
Example using a custom repository using SSH (requires the SSH2 PECL extension):
```json
{
"repositories": [{
"type": "composer",
"url": "ssh2.sftp://example.org",
"options": {
"ssh2": {
"username": "composer",
"pubkey_file": "/home/composer/.ssh/id_rsa.pub",
"privkey_file": "/home/composer/.ssh/id_rsa"
}
}
}]
}
```
> **Tip:** See [ssh2 context options] for more information.
Example using SSL/TLS (HTTPS) using a client certificate:
```json
{
"repositories": [{
"type": "composer",
"url": "https://example.org",
"options": {
"ssl": {
"local_cert": "/home/composer/.ssl/composer.pem"
}
}
}]
}
```
> **Tip:** See [ssl context options] for more information.
Example using a custom HTTP Header field for token authentication:
```json
{
"repositories": [{
"type": "composer",
"url": "https://example.org",
"options": {
"http": {
"header": [
"API-TOKEN: YOUR-API-TOKEN"
]
}
}
}]
}
```
### Authentication
When your private repositories are password protected, you can store the
authentication details permanently. The first time Composer needs to
authenticate against some domain it will prompt you for a username/password and
then you will be asked whether you want to store it.
The storage can be done either globally in the `COMPOSER_HOME/auth.json` file
(`COMPOSER_HOME` defaults to `~/.composer` or `%APPDATA%/Composer` on Windows)
or also in the project directory directly sitting besides your composer.json.
You can also configure these by hand using the config command if you need to
configure a production machine to be able to run non-interactive installs. For
example to enter credentials for example.org one could type:
composer config http-basic.example.org username password
That will store it in the current directory's auth.json, but if you want it
available globally you can use the `--global` (`-g`) flag.
### Downloads
When GitHub, GitLab or BitBucket repositories are mirrored on your local satis, the
build process will include the location of the downloads these platforms make
available. This means that the repository and your setup depend on the
availability of these services.
At the same time, this implies that all code which is hosted somewhere else (on
another service or for example in Subversion) will not have downloads available
and thus installations usually take a lot longer.
To enable your satis installation to create downloads for all (Git, Mercurial
and Subversion) your packages, add the following to your `satis.json`:
``` json
{
"archive": {
"directory": "dist",
"format": "tar",
"prefix-url": "https://amazing.cdn.example.org",
"skip-dev": true
}
}
```
#### Options explained
* `directory`: required, the location of the dist files (inside the
`output-dir`)
* `format`: optional, `zip` (default) or `tar`
* `prefix-url`: optional, location of the downloads, homepage (from
`satis.json`) followed by `directory` by default
* `skip-dev`: optional, `false` by default, when enabled (`true`) satis will
not create downloads for branches
* `absolute-directory`: optional, a _local_ directory where the dist files are
dumped instead of `output-dir`/`directory`
* `whitelist`: optional, if set as a list of package names, satis will only
dump the dist files of these packages
* `blacklist`: optional, if set as a list of package names, satis will not
dump the dist files of these packages
* `checksum`: optional, `true` by default, when disabled (`false`) satis will
not provide the sha1 checksum for the dist files
Once enabled, all downloads (include those from GitHub and BitBucket) will be
replaced with a _local_ version.
#### prefix-url
Prefixing the URL with another host is especially helpful if the downloads end
up in a private Amazon S3 bucket or on a CDN host. A CDN would drastically
improve download times and therefore package installation.
Example: A `prefix-url` of `https://my-bucket.s3.amazonaws.com` (and
`directory` set to `dist`) creates download URLs which look like the following:
`https://my-bucket.s3.amazonaws.com/dist/vendor-package-version-ref.zip`.
### Web outputs
* `output-html`: optional, `true` by default, when disabled (`false`) satis
will not generate the `output-dir`/index.html page.
* `twig-template`: optional, a path to a personalized [Twig] template for
the `output-dir`/index.html page.
### Abandoned packages
To enable your satis installation to indicate that some packages are abandoned,
add the following to your `satis.json`:
```json
{
"abandoned": {
"company/package": true,
"company/package2": "company/newpackage"
}
}
```
The `true` value indicates that the package is truly abandoned while the
`"company/newpackage"` value specifies that the package is replaced by the
`company/newpackage` package.
Note that all packages set as abandoned in their own `composer.json` file will
be marked abandoned as well.
### Resolving dependencies
It is possible to make satis automatically resolve and add all dependencies for
your projects. This can be used with the Downloads functionality to have a
complete local mirror of packages. Add the following to your `satis.json`:
```json
{
"require-dependencies": true,
"require-dev-dependencies": true
}
```
When searching for packages, satis will attempt to resolve all the required
packages from the listed repositories. Therefore, if you are requiring a
package from Packagist, you will need to define it in your `satis.json`.
Dev dependencies are packaged only if the `require-dev-dependencies` parameter
is set to true.
### Other options
* `providers`: optional, `false` by default, when enabled (`true`) each
package will be dumped into a separate include file which will be only
loaded by composer when the package is really required. Speeds up composer
handling for repositories with huge number of packages like f.i. packagist.
* `output-dir`: optional, defines where to output the repository files if not
provided as an argument when calling the `build` command.
* `config`: optional, lets you define all config options from composer, except
`archive-format` and `archive-dir` as the configuration is done through
[archive](#downloads) instead. See docs on [config schema] for more details.
* `notify-batch`: optional, specify a URL that will be called every time a
user installs a package. See [notify-batch].
[ssh2 context options]: https://secure.php.net/manual/en/wrappers.ssh2.php#refsect1-wrappers.ssh2-options
[ssl context options]: https://secure.php.net/manual/en/context.ssl.php
[Twig]: https://twig.sensiolabs.org/
[config schema]: https://getcomposer.org/doc/04-schema.md#config
[notify-batch]: https://getcomposer.org/doc/05-repositories.md#notify-batch
@@ -0,0 +1,59 @@
<!--
tagline: Access privately hosted packages
-->
# HTTP basic authentication
Your [Satis or Toran Proxy](handling-private-packages-with-satis.md) server
could be secured with http basic authentication. In order to allow your project
to have access to these packages you will have to tell composer how to
authenticate with your credentials.
The simplest way to provide your credentials is providing your set
of credentials inline with the repository specification such as:
```json
{
"repositories": [
{
"type": "composer",
"url": "https://extremely:secret@repo.example.org"
}
]
}
```
This will basically teach composer how to authenticate automatically
when reading packages from the provided composer repository.
This does not work for everybody especially when you don't want to
hard code your credentials into your composer.json. There is a second
way to provide these details and it is via interaction. If you don't
provide the authentication credentials composer will prompt you upon
connection to enter the username and password.
The third way if you want to pre-configure it is via an `auth.json` file
located in your `COMPOSER_HOME` or besides your `composer.json`.
The file should contain a set of hostnames followed each with their own
username/password pairs, for example:
```json
{
"http-basic": {
"repo.example1.org": {
"username": "my-username1",
"password": "my-secret-password1"
},
"repo.example2.org": {
"username": "my-username2",
"password": "my-secret-password2"
}
}
}
```
The main advantage of the auth.json file is that it can be gitignored so
that every developer in your team can place their own credentials in there,
which makes revocation of credentials much easier than if you all share the
same.
+290
View File
@@ -0,0 +1,290 @@
<!--
tagline: Modify and extend Composer's functionality
-->
# Setting up and using plugins
## Synopsis
You may wish to alter or expand Composer's functionality with your own. For
example if your environment poses special requirements on the behaviour of
Composer which do not apply to the majority of its users or if you wish to
accomplish something with composer in a way that is not desired by most users.
In these cases you could consider creating a plugin to handle your
specific logic.
## Creating a Plugin
A plugin is a regular Composer package which ships its code as part of the
package and may also depend on further packages.
### Plugin Package
The package file is the same as any other package file but with the following
requirements:
1. The [type][1] attribute must be `composer-plugin`.
2. The [extra][2] attribute must contain an element `class` defining the
class name of the plugin (including namespace). If a package contains
multiple plugins, this can be array of class names.
3. You must require the special package called `composer-plugin-api`
to define which Plugin API versions your plugin is compatible with.
The required version of the `composer-plugin-api` follows the same [rules][7]
as a normal package's.
The current composer plugin API version is 1.1.0.
An example of a valid plugin `composer.json` file (with the autoloading
part omitted):
```json
{
"name": "my/plugin-package",
"type": "composer-plugin",
"require": {
"composer-plugin-api": "^1.1"
},
"extra": {
"class": "My\\Plugin"
}
}
```
### Plugin Class
Every plugin has to supply a class which implements the
[`Composer\Plugin\PluginInterface`][3]. The `activate()` method of the plugin
is called after the plugin is loaded and receives an instance of
[`Composer\Composer`][4] as well as an instance of
[`Composer\IO\IOInterface`][5]. Using these two objects all configuration can
be read and all internal objects and state can be manipulated as desired.
Example:
```php
<?php
namespace phpDocumentor\Composer;
use Composer\Composer;
use Composer\IO\IOInterface;
use Composer\Plugin\PluginInterface;
class TemplateInstallerPlugin implements PluginInterface
{
public function activate(Composer $composer, IOInterface $io)
{
$installer = new TemplateInstaller($io, $composer);
$composer->getInstallationManager()->addInstaller($installer);
}
}
```
## Event Handler
Furthermore plugins may implement the
[`Composer\EventDispatcher\EventSubscriberInterface`][6] in order to have its
event handlers automatically registered with the `EventDispatcher` when the
plugin is loaded.
To register a method to an event, implement the method `getSubscribedEvents()`
and have it return an array. The array key must be the
[event name](https://getcomposer.org/doc/articles/scripts.md#event-names)
and the value is the name of the method in this class to be called.
```php
public static function getSubscribedEvents()
{
return array(
'post-autoload-dump' => 'methodToBeCalled',
// ^ event name ^ ^ method name ^
);
}
```
By default, the priority of an event handler is set to 0. The priority can be
changed by attaching a tuple where the first value is the method name, as
before, and the second value is an integer representing the priority.
Higher integers represent higher priorities. Priority 2 is called before
priority 1, etc.
```php
public static function getSubscribedEvents()
{
return array(
// Will be called before events with priority 0
'post-autoload-dump' => array('methodToBeCalled', 1)
);
}
```
If multiple methods should be called, then an array of tuples can be attached
to each event. The tuples do not need to include the priority. If it is
omitted, it will default to 0.
```php
public static function getSubscribedEvents()
{
return array(
'post-autoload-dump' => array(
array('methodToBeCalled' ), // Priority defaults to 0
array('someOtherMethodName', 1), // This fires first
)
);
}
```
Here's a complete example:
```php
<?php
namespace Naderman\Composer\AWS;
use Composer\Composer;
use Composer\EventDispatcher\EventSubscriberInterface;
use Composer\IO\IOInterface;
use Composer\Plugin\PluginInterface;
use Composer\Plugin\PluginEvents;
use Composer\Plugin\PreFileDownloadEvent;
class AwsPlugin implements PluginInterface, EventSubscriberInterface
{
protected $composer;
protected $io;
public function activate(Composer $composer, IOInterface $io)
{
$this->composer = $composer;
$this->io = $io;
}
public static function getSubscribedEvents()
{
return array(
PluginEvents::PRE_FILE_DOWNLOAD => array(
array('onPreFileDownload', 0)
),
);
}
public function onPreFileDownload(PreFileDownloadEvent $event)
{
$protocol = parse_url($event->getProcessedUrl(), PHP_URL_SCHEME);
if ($protocol === 's3') {
$awsClient = new AwsClient($this->io, $this->composer->getConfig());
$s3RemoteFilesystem = new S3RemoteFilesystem($this->io, $event->getRemoteFilesystem()->getOptions(), $awsClient);
$event->setRemoteFilesystem($s3RemoteFilesystem);
}
}
}
```
## Plugin capabilities
Composer defines a standard set of capabilities which may be implemented by plugins.
Their goal is to make the plugin ecosystem more stable as it reduces the need to mess
with [`Composer\Composer`][4]'s internal state, by providing explicit extension points
for common plugin requirements.
Capable Plugins classes must implement the [`Composer\Plugin\Capable`][8] interface
and declare their capabilities in the `getCapabilities()` method.
This method must return an array, with the _key_ as a Composer Capability class name,
and the _value_ as the Plugin's own implementation class name of said Capability:
```php
<?php
namespace My\Composer;
use Composer\Composer;
use Composer\IO\IOInterface;
use Composer\Plugin\PluginInterface;
use Composer\Plugin\Capable;
class Plugin implements PluginInterface, Capable
{
public function activate(Composer $composer, IOInterface $io)
{
}
public function getCapabilities()
{
return array(
'Composer\Plugin\Capability\CommandProvider' => 'My\Composer\CommandProvider',
);
}
}
```
### Command provider
The [`Composer\Plugin\Capability\CommandProvider`][9] capability allows to register
additional commands for Composer :
```php
<?php
namespace My\Composer;
use Composer\Plugin\Capability\CommandProvider as CommandProviderCapability;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Composer\Command\BaseCommand;
class CommandProvider implements CommandProviderCapability
{
public function getCommands()
{
return array(new Command);
}
}
class Command extends BaseCommand
{
protected function configure()
{
$this->setName('custom-plugin-command');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$output->writeln('Executing');
}
}
```
Now the `custom-plugin-command` is available alongside Composer commands.
> _Composer commands are based on the [Symfony Console Component][10]._
## Running plugins manually
Plugins for an event can be run manually by the `run-script` command. This works the same way as
[running scripts manually](scripts.md#running-scripts-manually).
## Using Plugins
Plugin packages are automatically loaded as soon as they are installed and will
be loaded when composer starts up if they are found in the current project's
list of installed packages. Additionally all plugin packages installed in the
`COMPOSER_HOME` directory using the composer global command are loaded before
local project plugins are loaded.
> You may pass the `--no-plugins` option to composer commands to disable all
> installed plugins. This may be particularly helpful if any of the plugins
> causes errors and you wish to update or uninstall it.
[1]: ../04-schema.md#type
[2]: ../04-schema.md#extra
[3]: https://github.com/composer/composer/blob/master/src/Composer/Plugin/PluginInterface.php
[4]: https://github.com/composer/composer/blob/master/src/Composer/Composer.php
[5]: https://github.com/composer/composer/blob/master/src/Composer/IO/IOInterface.php
[6]: https://github.com/composer/composer/blob/master/src/Composer/EventDispatcher/EventSubscriberInterface.php
[7]: ../01-basic-usage.md#package-versions
[8]: https://github.com/composer/composer/blob/master/src/Composer/Plugin/Capable.php
[9]: https://github.com/composer/composer/blob/master/src/Composer/Plugin/Capability/CommandProvider.php
[10]: https://symfony.com/doc/current/components/console.html
+357
View File
@@ -0,0 +1,357 @@
<!--
tagline: Script are callbacks that are called before/after installing packages
-->
# Scripts
## What is a script?
A script, in Composer's terms, can either be a PHP callback (defined as a
static method) or any command-line executable command. Scripts are useful
for executing a package's custom code or package-specific commands during
the Composer execution process.
> **Note:** Only scripts defined in the root package's `composer.json` are
> executed. If a dependency of the root package specifies its own scripts,
> Composer does not execute those additional scripts.
## Event names
Composer fires the following named events during its execution process:
### Command Events
- **pre-install-cmd**: occurs before the `install` command is executed with a
lock file present.
- **post-install-cmd**: occurs after the `install` command has been executed
with a lock file present.
- **pre-update-cmd**: occurs before the `update` command is executed, or before
the `install` command is executed without a lock file present.
- **post-update-cmd**: occurs after the `update` command has been executed, or
after the `install` command has been executed without a lock file present.
- **post-status-cmd**: occurs after the `status` command has been executed.
- **pre-archive-cmd**: occurs before the `archive` command is executed.
- **post-archive-cmd**: occurs after the `archive` command has been executed.
- **pre-autoload-dump**: occurs before the autoloader is dumped, either during
`install`/`update`, or via the `dump-autoload` command.
- **post-autoload-dump**: occurs after the autoloader has been dumped, either
during `install`/`update`, or via the `dump-autoload` command.
- **post-root-package-install**: occurs after the root package has been
installed, during the `create-project` command.
- **post-create-project-cmd**: occurs after the `create-project` command has
been executed.
### Installer Events
- **pre-dependencies-solving**: occurs before the dependencies are resolved.
- **post-dependencies-solving**: occurs after the dependencies have been resolved.
### Package Events
- **pre-package-install**: occurs before a package is installed.
- **post-package-install**: occurs after a package has been installed.
- **pre-package-update**: occurs before a package is updated.
- **post-package-update**: occurs after a package has been updated.
- **pre-package-uninstall**: occurs before a package is uninstalled.
- **post-package-uninstall**: occurs after a package has been uninstalled.
### Plugin Events
- **init**: occurs after a Composer instance is done being initialized.
- **command**: occurs before any Composer Command is executed on the CLI. It
provides you with access to the input and output objects of the program.
- **pre-file-download**: occurs before files are downloaded and allows
you to manipulate the `RemoteFilesystem` object prior to downloading files
based on the URL to be downloaded.
- **pre-command-run**: occurs before a command is executed and allows you to
manipulate the `InputInterface` object's options and arguments to tweak
a command's behavior.
> **Note:** Composer makes no assumptions about the state of your dependencies
> prior to `install` or `update`. Therefore, you should not specify scripts
> that require Composer-managed dependencies in the `pre-update-cmd` or
> `pre-install-cmd` event hooks. If you need to execute scripts prior to
> `install` or `update` please make sure they are self-contained within your
> root package.
## Defining scripts
The root JSON object in `composer.json` should have a property called
`"scripts"`, which contains pairs of named events and each event's
corresponding scripts. An event's scripts can be defined as either a string
(only for a single script) or an array (for single or multiple scripts.)
For any given event:
- Scripts execute in the order defined when their corresponding event is fired.
- An array of scripts wired to a single event can contain both PHP callbacks
and command-line executable commands.
- PHP classes containing defined callbacks must be autoloadable via Composer's
autoload functionality.
- Callbacks can only autoload classes from psr-0, psr-4 and classmap
definitions. If a defined callback relies on functions defined outside of a
class, the callback itself is responsible for loading the file containing these
functions.
Script definition example:
```json
{
"scripts": {
"post-update-cmd": "MyVendor\\MyClass::postUpdate",
"post-package-install": [
"MyVendor\\MyClass::postPackageInstall"
],
"post-install-cmd": [
"MyVendor\\MyClass::warmCache",
"phpunit -c app/"
],
"post-autoload-dump": [
"MyVendor\\MyClass::postAutoloadDump"
],
"post-create-project-cmd": [
"php -r \"copy('config/local-example.php', 'config/local.php');\""
]
}
}
```
Using the previous definition example, here's the class `MyVendor\MyClass`
that might be used to execute the PHP callbacks:
```php
<?php
namespace MyVendor;
use Composer\Script\Event;
use Composer\Installer\PackageEvent;
class MyClass
{
public static function postUpdate(Event $event)
{
$composer = $event->getComposer();
// do stuff
}
public static function postAutoloadDump(Event $event)
{
$vendorDir = $event->getComposer()->getConfig()->get('vendor-dir');
require $vendorDir . '/autoload.php';
some_function_from_an_autoloaded_file();
}
public static function postPackageInstall(PackageEvent $event)
{
$installedPackage = $event->getOperation()->getPackage();
// do stuff
}
public static function warmCache(Event $event)
{
// make cache toasty
}
}
```
**Note:** During a composer install or update process, a variable named
`COMPOSER_DEV_MODE` will be added to the environment. If the command was run
with the `--no-dev` flag, this variable will be set to 0, otherwise it will be
set to 1.
## Event classes
When an event is fired, your PHP callback receives as first argument a
`Composer\EventDispatcher\Event` object. This object has a `getName()` method
that lets you retrieve the event name.
Depending on the [script types](#event-names) you will get various event
subclasses containing various getters with relevant data and associated
objects:
- Base class: [`Composer\EventDispatcher\Event`](https://getcomposer.org/apidoc/master/Composer/EventDispatcher/Event.html)
- Command Events: [`Composer\Script\Event`](https://getcomposer.org/apidoc/master/Composer/Script/Event.html)
- Installer Events: [`Composer\Installer\InstallerEvent`](https://getcomposer.org/apidoc/master/Composer/Installer/InstallerEvent.html)
- Package Events: [`Composer\Installer\PackageEvent`](https://getcomposer.org/apidoc/master/Composer/Installer/PackageEvent.html)
- Plugin Events:
- init: [`Composer\EventDispatcher\Event`](https://getcomposer.org/apidoc/master/Composer/EventDispatcher/Event.html)
- command: [`Composer\Plugin\CommandEvent`](https://getcomposer.org/apidoc/master/Composer/Plugin/CommandEvent.html)
- pre-file-download: [`Composer\Plugin\PreFileDownloadEvent`](https://getcomposer.org/apidoc/master/Composer/Plugin/PreFileDownloadEvent.html)
## Running scripts manually
If you would like to run the scripts for an event manually, the syntax is:
```sh
composer run-script [--dev] [--no-dev] script
```
For example `composer run-script post-install-cmd` will run any
**post-install-cmd** scripts and [plugins](plugins.md) that have been defined.
You can also give additional arguments to the script handler by appending `--`
followed by the handler arguments. e.g.
`composer run-script post-install-cmd -- --check` will pass`--check` along to
the script handler. Those arguments are received as CLI arg by CLI handlers,
and can be retrieved as an array via `$event->getArguments()` by PHP handlers.
## Writing custom commands
If you add custom scripts that do not fit one of the predefined event name
above, you can either run them with run-script or also run them as native
Composer commands. For example the handler defined below is executable by
simply running `composer test`:
```json
{
"scripts": {
"test": "phpunit"
}
}
```
Similar to the `run-script` command you can give additional arguments to scripts,
e.g. `composer test -- --filter <pattern>` will pass `--filter <pattern>` along
to the `phpunit` script.
> **Note:** Before executing scripts, Composer's bin-dir is temporarily pushed
> on top of the PATH environment variable so that binaries of dependencies
> are easily accessible. In this example no matter if the `phpunit` binary is
> actually in `vendor/bin/phpunit` or `bin/phpunit` it will be found and executed.
Although Composer is not intended to manage long-running processes and other
such aspects of PHP projects, it can sometimes be handy to disable the process
timeout on custom commands. This timeout defaults to 300 seconds and can be
overridden in a variety of ways depending on the desired effect:
- disable it for all commands using the config key `process-timeout`,
- disable it for the current or future invocations of composer using the
environment variable `COMPOSER_PROCESS_TIMEOUT`,
- for a specific invocation using the `--timeout` flag of the `run-script` command,
- using a static helper for specific scripts.
To disable the timeout for specific scripts with the static helper directly in
composer.json:
```json
{
"scripts": {
"test": [
"Composer\\Config::disableProcessTimeout",
"phpunit"
]
}
}
```
To disable the timeout for every script on a given project, you can use the
composer.json configuration:
```json
{
"config": {
"process-timeout": 0
}
}
```
It's also possible to set the global environment variable to disable the timeout
of all following scripts in the current terminal environment:
```
export COMPOSER_PROCESS_TIMEOUT=0
```
To disable the timeout of a single script call, you must use the `run-script` composer
command and specify the `--timeout` parameter:
```
composer run-script --timeout=0 test
```
## Referencing scripts
To enable script re-use and avoid duplicates, you can call a script from another
one by prefixing the command name with `@`:
```json
{
"scripts": {
"test": [
"@clearCache",
"phpunit"
],
"clearCache": "rm -rf cache/*"
}
}
```
You can also refer a script and pass it new arguments:
```json
{
"scripts": {
"tests": "phpunit",
"testsVerbose": "@tests -vvv"
}
}
```
## Calling Composer commands
To call Composer commands, you can use `@composer` which will automatically
resolve to whatever composer.phar is currently being used:
```json
{
"scripts": {
"test": [
"@composer install",
"phpunit"
]
}
}
```
One limitation of this is that you can not call multiple composer commands in
a row like `@composer install && @composer foo`. You must split them up in a
JSON array of commands.
## Executing PHP scripts
To execute PHP scripts, you can use `@php` which will automatically
resolve to whatever php process is currently being used:
```json
{
"scripts": {
"test": [
"@php script.php",
"phpunit"
]
}
}
```
One limitation of this is that you can not call multiple commands in
a row like `@php install && @php foo`. You must split them up in a
JSON array of commands.
You can also call a shell/bash script, which will have the path to
the PHP executable available in it as a `PHP_BINARY` env var.
## Custom descriptions.
You can set custom script descriptions with the following in your `composer.json`:
```json
{
"scripts-descriptions": {
"test": "Run all tests!"
}
}
```
> **Note:** You can only set custom descriptions of custom commands.
+309
View File
@@ -0,0 +1,309 @@
<!--
tagline: Solving problems
-->
# Troubleshooting
This is a list of common pitfalls on using Composer, and how to avoid them.
## General
1. Before asking anyone, run [`composer diagnose`](../03-cli.md#diagnose) to check
for common problems. If it all checks out, proceed to the next steps.
2. When facing any kind of problems using Composer, be sure to **work with the
latest version**. See [self-update](../03-cli.md#self-update) for details.
3. Make sure you have no problems with your setup by running the installer's
checks via `curl -sS https://getcomposer.org/installer | php -- --check`.
4. Ensure you're **installing vendors straight from your `composer.json`** via
`rm -rf vendor && composer update -v` when troubleshooting, excluding any
possible interferences with existing vendor installations or `composer.lock`
entries.
5. Try clearing Composer's cache by running `composer clear-cache`.
## Package not found
1. Double-check you **don't have typos** in your `composer.json` or repository
branches and tag names.
2. Be sure to **set the right
[minimum-stability](../04-schema.md#minimum-stability)**. To get started or be
sure this is no issue, set `minimum-stability` to "dev".
3. Packages **not coming from [Packagist](https://packagist.org/)** should
always be **defined in the root package** (the package depending on all
vendors).
4. Use the **same vendor and package name** throughout all branches and tags of
your repository, especially when maintaining a third party fork and using
`replace`.
5. If you are updating to a recently published version of a package, be aware that
Packagist has a delay of up to 1 minute before new packages are visible to Composer.
6. If you are updating a single package, it may depend on newer versions itself.
In this case add the `--with-dependencies` argument **or** add all dependencies which
need an update to the command.
## Package not found on travis-ci.org
1. Check the ["Package not found"](#package-not-found) item above.
2. If the package tested is a dependency of one of its dependencies (cyclic
dependency), the problem might be that Composer is not able to detect the version
of the package properly. If it is a git clone it is generally alright and Composer
will detect the version of the current branch, but travis does shallow clones so
that process can fail when testing pull requests and feature branches in general.
The best solution is to define the version you are on via an environment variable
called COMPOSER_ROOT_VERSION. You set it to `dev-master` for example to define
the root package's version as `dev-master`.
Use: `before_script: COMPOSER_ROOT_VERSION=dev-master composer install` to export
the variable for the call to composer.
## Package not found in a Jenkins-build
1. Check the ["Package not found"](#package-not-found) item above.
2. Reason for failing is similar to the problem which can occur on travis-ci.org: The
git-clone / checkout within Jenkins leaves the branch in a "detached HEAD"-state. As
a result, Composer is not able to identify the version of the current checked out branch
and may not be able to resolve a cyclic dependency. To solve this problem, you can use
the "Additional Behaviours" -> "Check out to specific local branch" in your Git-settings
for your Jenkins-job, where your "local branch" shall be the same branch as you are
checking out. Using this, the checkout will not be in detached state any more and cyclic
dependency is recognized correctly.
## I have a dependency which contains a "repositories" definition in its composer.json, but it seems to be ignored.
The [`repositories`](../04-schema.md#repositories) configuration property is defined as [root-only](../04-schema.md#root-package). It is not inherited. You can read more about the reasons behind this in the "[why can't
composer load repositories recursively?](../faqs/why-can't-composer-load-repositories-recursively.md)" article.
The simplest work-around to this limitation, is moving or duplicating the `repositories` definition into your root
composer.json.
## I have locked a dependency to a specific commit but get unexpected results.
While Composer supports locking dependencies to a specific commit using the `#commit-ref` syntax, there are certain
caveats that one should take into account. The most important one is [documented](../04-schema.md#package-links), but
frequently overlooked:
> **Note:** While this is convenient at times, it should not be how you use
> packages in the long term because it comes with a technical limitation. The
> composer.json metadata will still be read from the branch name you specify
> before the hash. Because of that in some cases it will not be a practical
> workaround, and you should always try to switch to tagged releases as soon
> as you can.
There is no simple work-around to this limitation. It is therefore strongly recommended that you do not use it.
## Need to override a package version
Let's say your project depends on package A, which in turn depends on a specific
version of package B (say 0.1). But you need a different version of said package B (say 0.11).
You can fix this by aliasing version 0.11 to 0.1:
composer.json:
```json
{
"require": {
"A": "0.2",
"B": "0.11 as 0.1"
}
}
```
See [aliases](aliases.md) for more information.
## Memory limit errors
Composer may sometimes fail on some commands with this message:
`PHP Fatal error: Allowed memory size of XXXXXX bytes exhausted <...>`
In this case, the PHP `memory_limit` should be increased.
> **Note:** Composer internally increases the `memory_limit` to `1.5G`.
To get the current `memory_limit` value, run:
```sh
php -r "echo ini_get('memory_limit').PHP_EOL;"
```
Try increasing the limit in your `php.ini` file (ex. `/etc/php5/cli/php.ini` for
Debian-like systems):
```ini
; Use -1 for unlimited or define an explicit value like 2G
memory_limit = -1
```
Composer also respects a memory limit defined by the `COMPOSER_MEMORY_LIMIT` environment variable:
```sh
COMPOSER_MEMORY_LIMIT=-1 composer.phar <...>
```
Or, you can increase the limit with a command-line argument:
```sh
php -d memory_limit=-1 composer.phar <...>
```
This issue can also happen on cPanel instances, when the shell fork bomb protection is activated. For more information, see the [documentation](https://documentation.cpanel.net/display/68Docs/Shell+Fork+Bomb+Protection) of the fork bomb feature on the cPanel site.
## Xdebug impact on Composer
To improve performance when the xdebug extension is enabled, Composer automatically restarts PHP without it.
You can override this behavior by using an environment variable: `COMPOSER_ALLOW_XDEBUG=1`.
Composer will always show a warning if xdebug is being used, but you can override this with an environment variable:
`COMPOSER_DISABLE_XDEBUG_WARN=1`. If you see this warning unexpectedly, then the restart process has failed:
please report this [issue](https://github.com/composer/composer/issues).
## "The system cannot find the path specified" (Windows)
1. Open regedit.
2. Search for an `AutoRun` key inside `HKEY_LOCAL_MACHINE\Software\Microsoft\Command Processor`,
`HKEY_CURRENT_USER\Software\Microsoft\Command Processor`
or `HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Command Processor`.
3. Check if it contains any path to non-existent file, if it's the case, remove them.
## API rate limit and OAuth tokens
Because of GitHub's rate limits on their API it can happen that Composer prompts
for authentication asking your username and password so it can go ahead with its work.
If you would prefer not to provide your GitHub credentials to Composer you can
manually create a token using the following procedure:
1. [Create](https://github.com/settings/tokens) an OAuth token on GitHub.
[Read more](https://github.com/blog/1509-personal-api-tokens) on this.
2. Add it to the configuration running `composer config -g github-oauth.github.com <oauthtoken>`
Now Composer should install/update without asking for authentication.
## proc_open(): fork failed errors
If composer shows proc_open() fork failed on some commands:
`PHP Fatal error: Uncaught exception 'ErrorException' with message 'proc_open(): fork failed - Cannot allocate memory' in phar`
This could be happening because the VPS runs out of memory and has no Swap space enabled.
```sh
free -m
total used free shared buffers cached
Mem: 2048 357 1690 0 0 237
-/+ buffers/cache: 119 1928
Swap: 0 0 0
```
To enable the swap you can use for example:
```sh
/bin/dd if=/dev/zero of=/var/swap.1 bs=1M count=1024
/sbin/mkswap /var/swap.1
/sbin/swapon /var/swap.1
```
You can make a permanent swap file following this [tutorial](https://www.digitalocean.com/community/tutorials/how-to-add-swap-on-ubuntu-14-04).
## Degraded Mode
Due to some intermittent issues on Travis and other systems, we introduced a
degraded network mode which helps Composer finish successfully but disables
a few optimizations. This is enabled automatically when an issue is first
detected. If you see this issue sporadically you probably don't have to worry
(a slow or overloaded network can also cause those time outs), but if it
appears repeatedly you might want to look at the options below to identify
and resolve it.
If you have been pointed to this page, you want to check a few things:
- If you are using ESET antivirus, go in "Advanced Settings" and disable "HTTP-scanner"
under "web access protection"
- If you are using IPv6, try disabling it. If that solves your issues, get in touch
with your ISP or server host, the problem is not at the Packagist level but in the
routing rules between you and Packagist (i.e. the internet at large). The best way to get
these fixed is raise awareness to the network engineers that have the power to fix it.
Take a look at the next section for IPv6 workarounds.
- If none of the above helped, please report the error.
## Operation timed out (IPv6 issues)
You may run into errors if IPv6 is not configured correctly. A common error is:
```
The "https://getcomposer.org/version" file could not be downloaded: failed to
open stream: Operation timed out
```
We recommend you fix your IPv6 setup. If that is not possible, you can try the
following workarounds:
**Workaround Linux:**
On linux, it seems that running this command helps to make ipv4 traffic have a
higher prio than ipv6, which is a better alternative than disabling ipv6 entirely:
```bash
sudo sh -c "echo 'precedence ::ffff:0:0/96 100' >> /etc/gai.conf"
```
**Workaround Windows:**
On windows the only way is to disable ipv6 entirely I am afraid (either in windows or in your home router).
**Workaround Mac OS X:**
Get name of your network device:
```bash
networksetup -listallnetworkservices
```
Disable IPv6 on that device (in this case "Wi-Fi"):
```bash
networksetup -setv6off Wi-Fi
```
Run composer ...
You can enable IPv6 again with:
```bash
networksetup -setv6automatic Wi-Fi
```
That said, if this fixes your problem, please talk to your ISP about it to
try and resolve the routing errors. That's the best way to get things resolved
for everyone.
## Composer hangs with SSH ControlMaster
When you try to install packages from a Git repository and you use the `ControlMaster`
setting for your SSH connection, Composer might hang endlessly and you see a `sh`
process in the `defunct` state in your process list.
The reason for this is a SSH Bug: https://bugzilla.mindrot.org/show_bug.cgi?id=1988
As a workaround, open a SSH connection to your Git host before running Composer:
```bash
ssh -t git@mygitserver.tld
composer update
```
See also https://github.com/composer/composer/issues/4180 for more information.
## Zip archives are not unpacked correctly.
Composer can unpack zipballs using either a system-provided `unzip` utility or PHP's
native `ZipArchive` class. The `ZipArchive` class is preferred on Windows. On other
OSes where ZIP files can contain permissions and symlinks, the `unzip` utility is
preferred. You're advised to install it if you need these features.
+110
View File
@@ -0,0 +1,110 @@
<!--
tagline: Expose command-line scripts from packages
-->
# Vendor binaries and the `vendor/bin` directory
## What is a vendor binary?
Any command line script that a Composer package would like to pass along
to a user who installs the package should be listed as a vendor binary.
If a package contains other scripts that are not needed by the package
users (like build or compile scripts) that code should not be listed
as a vendor binary.
## How is it defined?
It is defined by adding the `bin` key to a project's `composer.json`.
It is specified as an array of files so multiple binaries can be added
for any given project.
```json
{
"bin": ["bin/my-script", "bin/my-other-script"]
}
```
## What does defining a vendor binary in composer.json do?
It instructs Composer to install the package's binaries to `vendor/bin`
for any project that **depends** on that project.
This is a convenient way to expose useful scripts that would
otherwise be hidden deep in the `vendor/` directory.
## What happens when Composer is run on a composer.json that defines vendor binaries?
For the binaries that a package defines directly, nothing happens.
## What happens when Composer is run on a composer.json that has dependencies with vendor binaries listed?
Composer looks for the binaries defined in all of the dependencies. A
symlink is created from each dependency's binaries to `vendor/bin`.
Say package `my-vendor/project-a` has binaries setup like this:
```json
{
"name": "my-vendor/project-a",
"bin": ["bin/project-a-bin"]
}
```
Running `composer install` for this `composer.json` will not do
anything with `bin/project-a-bin`.
Say project `my-vendor/project-b` has requirements setup like this:
```json
{
"name": "my-vendor/project-b",
"require": {
"my-vendor/project-a": "*"
}
}
```
Running `composer install` for this `composer.json` will look at
all of project-a's binaries and install them to `vendor/bin`.
In this case, Composer will make `vendor/my-vendor/project-a/bin/project-a-bin`
available as `vendor/bin/project-a-bin`. On a Unix-like platform
this is accomplished by creating a symlink.
## What about Windows and .bat files?
Packages managed entirely by Composer do not *need* to contain any
`.bat` files for Windows compatibility. Composer handles installation
of binaries in a special way when run in a Windows environment:
* A `.bat` file is generated automatically to reference the binary
* A Unix-style proxy file with the same name as the binary is generated
automatically (useful for Cygwin or Git Bash)
Packages that need to support workflows that may not include Composer
are welcome to maintain custom `.bat` files. In this case, the package
should **not** list the `.bat` file as a binary as it is not needed.
## Can vendor binaries be installed somewhere other than vendor/bin?
Yes, there are two ways an alternate vendor binary location can be specified:
1. Setting the `bin-dir` configuration setting in `composer.json`
1. Setting the environment variable `COMPOSER_BIN_DIR`
An example of the former looks like this:
```json
{
"config": {
"bin-dir": "scripts"
}
}
```
Running `composer install` for this `composer.json` will result in
all of the vendor binaries being installed in `scripts/` instead of
`vendor/bin/`.
You can set `bin-dir` to `./` to put binaries in your project root.
+230
View File
@@ -0,0 +1,230 @@
<!--
tagline: Versions explained.
-->
# Versions and constraints
## Composer Versions vs VCS Versions
Because Composer is heavily geared toward utilizing version control systems
like git, the term "version" can be a little ambiguous. In the sense of a
version control system, a "version" is a specific set of files that contain
specific data. In git terminology, this is a "ref", or a specific commit,
which may be represented by a branch HEAD or a tag. When you check out that
version in your VCS -- for example, tag `v1.1` or commit `e35fa0d` --, you're
asking for a single, known set of files, and you always get the same files back.
In Composer, what's often referred to casually as a version -- that is,
the string that follows the package name in a require line (e.g., `~1.1` or
`1.2.*`) -- is actually more specifically a version constraint. Composer
uses version constraints to figure out which refs in a VCS it should be
checking out (or simply to verify that a given library is acceptable in
the case of a statically-maintained library with a `version` specification
in `composer.json`).
## VCS Tags and Branches
*For the following discussion, let's assume the following sample library
repository:*
```sh
~/my-library$ git branch
v1
v2
my-feature
another-feature
~/my-library$ git tag
v1.0
v1.0.1
v1.0.2
v1.1-BETA
v1.1-RC1
v1.1-RC2
v1.1
v1.1.1
v2.0-BETA
v2.0-RC1
v2.0
v2.0.1
v2.0.2
```
### Tags
Normally, Composer deals with tags (as opposed to branches -- if you don't
know what this means, read up on
[version control systems](https://en.wikipedia.org/wiki/Version_control#Common_vocabulary)).
When you write a version constraint, it may reference a specific tag (e.g.,
`1.1`) or it may reference a valid range of tags (e.g., `>=1.1 <2.0`, or
`~4.0`). To resolve these constraints, Composer first asks the VCS to list
all available tags, then creates an internal list of available versions based
on these tags. In the above example, composer's internal list includes versions
`1.0`, `1.0.1`, `1.0.2`, the beta release of `1.1`, the first and second
release candidates of `1.1`, the final release version `1.1`, etc.... (Note
that Composer automatically removes the 'v' prefix in the actual tagname to
get a valid final version number.)
When Composer has a complete list of available versions from your VCS, it then
finds the highest version that matches all version constraints in your project
(it's possible that other packages require more specific versions of the
library than you do, so the version it chooses may not always be the highest
available version) and it downloads a zip archive of that tag to unpack in the
correct location in your `vendor` directory.
### Branches
If you want Composer to check out a branch instead of a tag, you need to point it to the branch using the special `dev-*` prefix (or sometimes suffix; see below). If you're checking out a branch, it's assumed that you want to *work* on the branch and Composer actually clones the repo into the correct place in your `vendor` directory. For tags, it copies the right files without actually cloning the repo. (You can modify this behavior with --prefer-source and --prefer-dist, see [install options](../03-cli.md#install).)
In the above example, if you wanted to check out the `my-feature` branch, you would specify `dev-my-feature` as the version constraint in your `require` clause. This would result in Composer cloning the `my-library` repository into my `vendor` directory and checking out the `my-feature` branch.
When branch names look like versions, we have to clarify for composer that we're trying to check out a branch and not a tag. In the above example, we have two version branches: `v1` and `v2`. To get Composer to check out one of these branches, you must specify a version constraint that looks like this: `v1.x-dev`. The `.x` is an arbitrary string that Composer requires to tell it that we're talking about the `v1` branch and not a `v1` tag (alternatively, you can name the branch `v1.x` instead of `v1`). In the case of a branch with a version-like name (`v1`, in this case), you append `-dev` as a suffix, rather than using `dev-` as a prefix.
### Minimum Stability
There's one more thing that will affect which files are checked out of a library's VCS and added to your project: Composer allows you to specify stability constraints to limit which tags are considered valid. In the above example, note that the library released a beta and two release candidates for version `1.1` before the final official release. To receive these versions when running `composer install` or `composer update`, we have to explicitly tell Composer that we are ok with release candidates and beta releases (and alpha releases, if we want those). This can be done using either a project-wide `minimum-stability` value in `composer.json` or using "stability flags" in version constraints. Read more on the [schema page](../04-schema.md#minimum-stability).
## Writing Version Constraints
Now that you have an idea of how Composer sees versions, let's talk about how
to specify version constraints for your project dependencies.
### Exact Version Constraint
You can specify the exact version of a package. This will tell Composer to
install this version and this version only. If other dependencies require
a different version, the solver will ultimately fail and abort any install
or update procedures.
Example: `1.0.2`
### Version Range
By using comparison operators you can specify ranges of valid versions. Valid
operators are `>`, `>=`, `<`, `<=`, `!=`.
You can define multiple ranges. Ranges separated by a space (<code>&nbsp;</code>)
or comma (`,`) will be treated as a **logical AND**. A double pipe (`||`)
will be treated as a **logical OR**. AND has higher precedence than OR.
> **Note:** Be careful when using unbounded ranges as you might end up
> unexpectedly installing versions that break backwards compatibility.
> Consider using the [caret](#caret-version-range-) operator instead for safety.
Examples:
* `>=1.0`
* `>=1.0 <2.0`
* `>=1.0 <1.1 || >=1.2`
### Hyphenated Version Range ( - )
Inclusive set of versions. Partial versions on the right include are completed
with a wildcard. For example `1.0 - 2.0` is equivalent to `>=1.0.0 <2.1` as the
`2.0` becomes `2.0.*`. On the other hand `1.0.0 - 2.1.0` is equivalent to
`>=1.0.0 <=2.1.0`.
Example: `1.0 - 2.0`
### Wildcard Version Range (.*)
You can specify a pattern with a `*` wildcard. `1.0.*` is the equivalent of
`>=1.0 <1.1`.
Example: `1.0.*`
## Next Significant Release Operators
### Tilde Version Range (~)
The `~` operator is best explained by example: `~1.2` is equivalent to
`>=1.2 <2.0.0`, while `~1.2.3` is equivalent to `>=1.2.3 <1.3.0`. As you can see
it is mostly useful for projects respecting [semantic
versioning](https://semver.org/). A common usage would be to mark the minimum
minor version you depend on, like `~1.2` (which allows anything up to, but not
including, 2.0). Since in theory there should be no backwards compatibility
breaks until 2.0, that works well. Another way of looking at it is that using
`~` specifies a minimum version, but allows the last digit specified to go up.
Example: `~1.2`
> **Note:** Although `2.0-beta.1` is strictly before `2.0`, a version constraint
> like `~1.2` would not install it. As said above `~1.2` only means the `.2`
> can change but the `1.` part is fixed.
> **Note:** The `~` operator has an exception on its behavior for the major
> release number. This means for example that `~1` is the same as `~1.0` as
> it will not allow the major number to increase trying to keep backwards
> compatibility.
### Caret Version Range (^)
The `^` operator behaves very similarly but it sticks closer to semantic
versioning, and will always allow non-breaking updates. For example `^1.2.3`
is equivalent to `>=1.2.3 <2.0.0` as none of the releases until 2.0 should
break backwards compatibility. For pre-1.0 versions it also acts with safety
in mind and treats `^0.3` as `>=0.3.0 <0.4.0`.
This is the recommended operator for maximum interoperability when writing
library code.
Example: `^1.2.3`
## Stability Constraints
If you are using a constraint that does not explicitly define a stability,
Composer will default internally to `-dev` or `-stable`, depending on the
operator(s) used. This happens transparently.
If you wish to explicitly consider only the stable release in the comparison,
add the suffix `-stable`.
Examples:
Constraint | Internally
------------------- | ------------------------
`1.2.3` | `=1.2.3.0-stable`
`>1.2` | `>1.2.0.0-stable`
`>=1.2` | `>=1.2.0.0-dev`
`>=1.2-stable` | `>=1.2.0.0-stable`
`<1.3` | `<1.3.0.0-dev`
`<=1.3` | `<=1.3.0.0-stable`
`1 - 2` | `>=1.0.0.0-dev <3.0.0.0-dev`
`~1.3` | `>=1.3.0.0-dev <2.0.0.0-dev`
`1.4.*` | `>=1.4.0.0-dev <1.5.0.0-dev`
To allow various stabilities without enforcing them at the constraint level
however, you may use [stability-flags](../04-schema.md#package-links) like
`@<stability>` (e.g. `@dev`) to let composer know that a given package
can be installed in a different stability than your default minimum-stability
setting. All available stability flags are listed on the minimum-stability
section of the [schema page](../04-schema.md#minimum-stability).
## Summary
```json
"require": {
"vendor/package": "1.3.2", // exactly 1.3.2
// >, <, >=, <= | specify upper / lower bounds
"vendor/package": ">=1.3.2", // anything above or equal to 1.3.2
"vendor/package": "<1.3.2", // anything below 1.3.2
// * | wildcard
"vendor/package": "1.3.*", // >=1.3.0 <1.4.0
// ~ | allows last digit specified to go up
"vendor/package": "~1.3.2", // >=1.3.2 <1.4.0
"vendor/package": "~1.3", // >=1.3.0 <2.0.0
// ^ | doesn't allow breaking changes (major version fixed - following semver)
"vendor/package": "^1.3.2", // >=1.3.2 <2.0.0
"vendor/package": "^0.3.2", // >=0.3.2 <0.4.0 // except if major version is 0
}
```
## Testing Version Constraints
You can test version constraints using [semver.mwl.be](https://semver.mwl.be).
Fill in a package name and it will autofill the default version constraint
which Composer would add to your `composer.json` file. You can adjust the
version constraint and the tool will highlight all releases that match.
+55
View File
@@ -0,0 +1,55 @@
# Default Solver Policy
A solver policy defines behaviour variables of the dependency solver. It decides
which versions are considered newer than others, which packages should be
preferred over others and whether operations like downgrades or uninstall are
allowed.
## Selection of preferred Packages
The following describe package pool situations with user requests and the
resulting order in which the solver will try to install them.
The rules are to be applied in the order of these descriptions.
### Repository priorities
Packages Repo1.Av1, Repo2.Av1
* priority(Repo1) >= priority(Repo2) => (Repo1.Av1, Repo2.Av1)
* priority(Repo1) < priority(Repo2) => (Repo2.Av1, Repo1.Av1)
### Package versions
Packages: Av1, Av2, Av3
* Installed: Av2
Request: install A
* (Av3)
### Virtual Packages (provides)
Packages Av1, Bv1
* Av1 provides Xv1
* Bv1 provides Xv1
Request: install X
* priority(Av1.repo) >= priority(Bv1.repo) => (Av1, Bv1)
* priority(Av1.repo) < priority(Bv1.repo) => (Bv1, Av1)
### Package replacements
Packages: Av1, Bv2
* Bv2 replaces Av1
Request: install A
* priority(Av1.repo) >= priority(Bv2.repo) => (Av1, Bv2)
* priority(Av1.repo) < priority(Bv2.repo) => (Bv2, Av1)
Bv2 version is ignored, only the replacement version for A matters.
@@ -0,0 +1,50 @@
# How do I install a package to a custom path for my framework?
Each framework may have one or many different required package installation
paths. Composer can be configured to install packages to a folder other than
the default `vendor` folder by using
[composer/installers](https://github.com/composer/installers).
If you are a **package author** and want your package installed to a custom
directory, simply require `composer/installers` and set the appropriate `type`.
This is common if your package is intended for a specific framework such as
CakePHP, Drupal or WordPress. Here is an example composer.json file for a
WordPress theme:
```json
{
"name": "you/themename",
"type": "wordpress-theme",
"require": {
"composer/installers": "~1.0"
}
}
```
Now when your theme is installed with Composer it will be placed into
`wp-content/themes/themename/` folder. Check the
[current supported types](https://github.com/composer/installers#current-supported-package-types)
for your package.
As a **package consumer** you can set or override the install path for a package
that requires composer/installers by configuring the `installer-paths` extra. A
useful example would be for a Drupal multisite setup where the package should be
installed into your sites subdirectory. Here we are overriding the install path
for a module that uses composer/installers:
```json
{
"extra": {
"installer-paths": {
"sites/example.com/modules/{$name}": ["vendor/package"]
}
}
}
```
Now the package would be installed to your folder location, rather than the default
composer/installers determined location.
> **Note:** You cannot use this to change the path of any package. This is only
> applicable to packages that require `composer/installers` and use a custom type
> that it handles.
@@ -0,0 +1,42 @@
# How do I install Composer programmatically?
As noted on the download page, the installer script contains a
signature which changes when the installer code changes and as such
it should not be relied upon in the long term.
An alternative is to use this script which only works with UNIX utilities:
```bash
#!/bin/sh
EXPECTED_SIGNATURE="$(wget -q -O - https://composer.github.io/installer.sig)"
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
ACTUAL_SIGNATURE="$(php -r "echo hash_file('sha384', 'composer-setup.php');")"
if [ "$EXPECTED_SIGNATURE" != "$ACTUAL_SIGNATURE" ]
then
>&2 echo 'ERROR: Invalid installer signature'
rm composer-setup.php
exit 1
fi
php composer-setup.php --quiet
RESULT=$?
rm composer-setup.php
exit $RESULT
```
The script will exit with 1 in case of failure, or 0 on success, and is quiet
if no error occurs.
Alternatively, if you want to rely on an exact copy of the installer, you can fetch
a specific version from GitHub's history. The commit hash should be enough to
give it uniqueness and authenticity as long as you can trust the GitHub servers.
For example:
```bash
wget https://raw.githubusercontent.com/composer/getcomposer.org/76a7060ccb93902cd7576b67264ad91c8a2700e2/web/installer -O - -q | php -- --quiet
```
You may replace the commit hash by whatever the last commit hash is on
https://github.com/composer/getcomposer.org/commits/master
@@ -0,0 +1,19 @@
# How do I install untrusted packages safely? Is it safe to run Composer as superuser or root?
Certain Composer commands, including `exec`, `install`, and `update` allow third party code to
execute on your system. This is from its "plugins" and "scripts" features. Plugins and scripts have
full access to the user account which runs Composer. For this reason, it is strongly advised to
**avoid running Composer as super-user/root**.
You can disable plugins and scripts during package installation or updates with the following
syntax so only Composer's code, and no third party code, will execute:
```sh
composer install --no-plugins --no-scripts ...
composer update --no-plugins --no-scripts ...
```
The `exec` command will always run third party code as the user which runs `composer`.
In some cases, like in CI systems or such where you want to install untrusted dependencies, the
safest way to do it is to run the above command.
@@ -0,0 +1,32 @@
# Should I commit the dependencies in my vendor directory?
The general recommendation is **no**. The vendor directory (or wherever your
dependencies are installed) should be added to `.gitignore`/`svn:ignore`/etc.
The best practice is to then have all the developers use Composer to install
the dependencies. Similarly, the build server, CI, deployment tools etc should
be adapted to run Composer as part of their project bootstrapping.
While it can be tempting to commit it in some environment, it leads to a few
problems:
- Large VCS repository size and diffs when you update code.
- Duplication of the history of all your dependencies in your own VCS.
- Adding dependencies installed via git to a git repo will show them as
submodules. This is problematic because they are not real submodules, and you
will run into issues.
If you really feel like you must do this, you have a few options:
1. Limit yourself to installing tagged releases (no dev versions), so that you
only get zipped installs, and avoid problems with the git "submodules".
2. Use --prefer-dist or set `preferred-install` to `dist` in your
[config](../04-schema.md#config).
3. Remove the `.git` directory of every dependency after the installation, then
you can add them to your git repo. You can do that with `rm -rf vendor/**/.git`
in ZSH or `find vendor/ -type d -name ".git" -exec rm -rf {} \;` in Bash.
but this means you will have to delete those dependencies from disk before
running composer update.
4. Add a .gitignore rule (`/vendor/**/.git`) to ignore all the vendor `.git` folders.
This approach does not require that you delete dependencies from disk prior to
running a composer update.
@@ -0,0 +1,4 @@
# Which version numbering system does Composer itself use?
Composer uses [Semantic Versioning (aka SemVer)
2.0.0](https://semver.org/spec/v2.0.0.html).
@@ -0,0 +1,21 @@
# Why are unbound version constraints a bad idea?
A version constraint without an upper bound such as `*`, `>=3.4` or
`dev-master` will allow updates to any future version of the dependency.
This includes major versions breaking backward compatibility.
Once a release of your package is tagged, you cannot tweak its dependencies
anymore in case a dependency breaks BC - you have to do a new release but the
previous one stays broken.
The only good alternative is to define an upper bound on your constraints,
which you can increase in a new release after testing that your package is
compatible with the new major version of your dependency.
For example instead of using `>=3.4` you should use `~3.4` which allows all
versions up to `3.999` but does not include `4.0` and above. The `^` operator
works very well with libraries following [semantic versioning](https://semver.org).
**Note:** As a package maintainer, you can make the life of your users easier
by providing an [alias version](../articles/aliases.md) for your development
branch to allow it to match bound constraints.
@@ -0,0 +1,21 @@
# Why are version constraints combining comparisons and wildcards a bad idea?
This is a fairly common mistake people make, defining version constraints in
their package requires like `>=2.*` or `>=1.1.*`.
If you think about it and what it really means though, you will quickly
realize that it does not make much sense. If we decompose `>=2.*`, you
have two parts:
- `>=2` which says the package should be in version 2.0.0 or above.
- `2.*` which says the package should be between version 2.0.0 (inclusive)
and 3.0.0 (exclusive).
As you see, both rules agree on the fact that the package must be >=2.0.0,
but it is not possible to determine if when you wrote that you were thinking
of a package in version 3.0.0 or not. Should it match because you asked for
`>=2` or should it not match because you asked for a `2.*`?
For this reason, Composer throws an error and says that this is invalid.
The easy way to fix it is to think about what you really mean, and use only
one of those rules.
@@ -0,0 +1,35 @@
# Why can't Composer load repositories recursively?
You may run into problems when using custom repositories because Composer does
not load the repositories of your requirements, so you have to redefine those
repositories in all your `composer.json` files.
Before going into details as to why this is like that, you have to understand
that the main use of custom VCS & package repositories is to temporarily try
some things, or use a fork of a project until your pull request is merged, etc.
You should not use them to keep track of private packages. For that you should
rather look into [Private Packagist](https://packagist.com) which lets you
configure all your private packages in one place, and avoids the slow-downs
associated with inline VCS repositories.
There are three ways the dependency solver could work with custom repositories:
- Fetch the repositories of root package, get all the packages from the defined
repositories, then resolve requirements. This is the current state and it works well
except for the limitation of not loading repositories recursively.
- Fetch the repositories of root package, while initializing packages from the
defined repos, initialize recursively all repos found in those packages, and
their package's packages, etc, then resolve requirements. It could work, but it
slows down the initialization a lot since VCS repos can each take a few seconds,
and it could end up in a completely broken state since many versions of a package
could define the same packages inside a package repository, but with different
dist/source. There are many many ways this could go wrong.
- Fetch the repositories of root package, then fetch the repositories of the
first level dependencies, then fetch the repositories of their dependencies, etc,
then resolve requirements. This sounds more efficient, but it suffers from the
same problems as the second solution, because loading the repositories of the
dependencies is not as easy as it sounds. You need to load all the repos of all
the potential matches for a requirement, which again might have conflicting
package definitions.
+22
View File
@@ -0,0 +1,22 @@
# `Composer` type repository fixtures
This directory contains some examples of what `composer` type repositories can
look like. They serve as illustrating examples accompanying the docs, but can
also be used as (initial) fixtures for tests.
* `repo-composer-plain` is a simple, plain `packages.json` file
* `repo-composer-with-includes` uses the `includes` mechanism
* `repo-composer-with-providers` uses the `providers` mechanism
## Sample Packages used in these fixtures
All these repositories contain the following packages.
* `foo/bar` versions 1.0.0, 1.0.1 and 1.1.0; dev-default and 1.0.x-dev branches.
On dev-default and in 1.1.0, `bar/baz` ~1.0 is required.
* `qux/quux` only has a dev-default branch. It `replace`s `gar/nix`.
* `gar/nix` has a 1.0.0 version and a dev-default branch. It is being replaced
by `qux/quux`.
* `bar/baz` has a 1.0.0 version and 1.0.x-dev as well as dev-default branches.
Additionally, 1.1.x-dev is a branch alias for dev-default.
@@ -0,0 +1,158 @@
{
"packages": {
"bar/baz": {
"1.0.0": {
"name": "bar/baz",
"version": "1.0.0",
"version_normalized": "1.0.0.0",
"source": {
"type": "hg",
"url": "http://some.where/over/the/rainbow/",
"reference": "35810817c14d"
},
"time": "2014-10-13 12:04:55",
"type": "library"
},
"1.0.x-dev": {
"name": "bar/baz",
"version": "1.0.x-dev",
"version_normalized": "1.0.9999999.9999999-dev",
"source": {
"type": "hg",
"url": "http://some.where/over/the/rainbow/",
"reference": "ffff9aae6ed5"
},
"time": "2014-10-13 12:05:37",
"type": "library"
},
"dev-default": {
"name": "bar/baz",
"version": "dev-default",
"version_normalized": "9999999-dev",
"source": {
"type": "hg",
"url": "http://some.where/over/the/rainbow/",
"reference": "f317e556f2e2"
},
"time": "2014-10-13 12:06:45",
"type": "library",
"extra": {
"branch-alias": {
"dev-default": "1.1.x-dev"
}
}
}
},
"foo/bar": {
"1.0.0": {
"name": "foo/bar",
"version": "1.0.0",
"version_normalized": "1.0.0.0",
"source": {
"type": "hg",
"url": "http://some.where/over/the/rainbow/",
"reference": "249dec95a52a"
},
"time": "2014-10-11 15:42:00",
"type": "library"
},
"1.0.1": {
"name": "foo/bar",
"version": "1.0.1",
"version_normalized": "1.0.1.0",
"source": {
"type": "hg",
"url": "http://some.where/over/the/rainbow/",
"reference": "21e3328295d4"
},
"time": "2014-10-11 15:45:56",
"type": "library"
},
"1.0.x-dev": {
"name": "foo/bar",
"version": "1.0.x-dev",
"version_normalized": "1.0.9999999.9999999-dev",
"source": {
"type": "hg",
"url": "http://some.where/over/the/rainbow/",
"reference": "14dc17c8e860"
},
"time": "2014-10-11 15:45:59",
"type": "library"
},
"1.1.0": {
"name": "foo/bar",
"version": "1.1.0",
"version_normalized": "1.1.0.0",
"source": {
"type": "hg",
"url": "http://some.where/over/the/rainbow/",
"reference": "d2fa3e69ad5b"
},
"require": {
"bar/baz": "~1.0"
},
"time": "2014-10-11 15:43:16",
"type": "library"
},
"dev-default": {
"name": "foo/bar",
"version": "dev-default",
"version_normalized": "9999999-dev",
"source": {
"type": "hg",
"url": "http://some.where/over/the/rainbow/",
"reference": "8e5a5c224336"
},
"require": {
"bar/baz": "~1.0"
},
"time": "2014-10-11 15:43:18",
"type": "library"
}
},
"gar/nix": {
"1.0.0": {
"name": "gar/nix",
"version": "1.0.0",
"version_normalized": "1.0.0.0",
"source": {
"type": "hg",
"url": "http://some.where/over/the/rainbow/",
"reference": "44977145d64e"
},
"time": "2014-10-13 12:03:33",
"type": "library"
},
"dev-default": {
"name": "gar/nix",
"version": "dev-default",
"version_normalized": "9999999-dev",
"source": {
"type": "hg",
"url": "http://some.where/over/the/rainbow/",
"reference": "51cca95a31c2"
},
"time": "2014-10-13 12:03:35",
"type": "library"
}
},
"qux/quux": {
"dev-default": {
"name": "qux/quux",
"version": "dev-default",
"version_normalized": "9999999-dev",
"source": {
"type": "hg",
"url": "http://some.where/over/the/rainbow/",
"reference": "4a10a567baa5"
},
"replace": {
"gar/nix": "1.0.*"
},
"time": "2014-10-11 15:48:15",
"type": "library"
}
}
}
}
@@ -0,0 +1,50 @@
{
"packages": {
"bar\/baz": {
"1.0.0": {
"name": "bar\/baz",
"version": "1.0.0",
"version_normalized": "1.0.0.0",
"source": {
"type": "hg",
"url": "http:\/\/some.where\/over\/the\/rainbow\/",
"reference": "35810817c14d"
},
"time": "2014-10-13 12:04:55",
"type": "library",
"uid": 0
},
"1.0.x-dev": {
"name": "bar\/baz",
"version": "1.0.x-dev",
"version_normalized": "1.0.9999999.9999999-dev",
"source": {
"type": "hg",
"url": "http:\/\/some.where\/over\/the\/rainbow\/",
"reference": "ffff9aae6ed5"
},
"time": "2014-10-13 12:05:37",
"type": "library",
"uid": 1
},
"dev-default": {
"name": "bar\/baz",
"version": "dev-default",
"version_normalized": "9999999-dev",
"source": {
"type": "hg",
"url": "http:\/\/some.where\/over\/the\/rainbow\/",
"reference": "f317e556f2e2"
},
"time": "2014-10-13 12:06:45",
"type": "library",
"extra": {
"branch-alias": {
"dev-default": "1.1.x-dev"
}
},
"uid": 2
}
}
}
}
@@ -0,0 +1,77 @@
{
"packages": {
"foo\/bar": {
"1.0.0": {
"name": "foo\/bar",
"version": "1.0.0",
"version_normalized": "1.0.0.0",
"source": {
"type": "hg",
"url": "http:\/\/some.where\/over\/the\/rainbow\/",
"reference": "249dec95a52a"
},
"time": "2014-10-11 15:42:00",
"type": "library",
"uid": 3
},
"1.0.1": {
"name": "foo\/bar",
"version": "1.0.1",
"version_normalized": "1.0.1.0",
"source": {
"type": "hg",
"url": "http:\/\/some.where\/over\/the\/rainbow\/",
"reference": "21e3328295d4"
},
"time": "2014-10-11 15:45:56",
"type": "library",
"uid": 4
},
"1.0.x-dev": {
"name": "foo\/bar",
"version": "1.0.x-dev",
"version_normalized": "1.0.9999999.9999999-dev",
"source": {
"type": "hg",
"url": "http:\/\/some.where\/over\/the\/rainbow\/",
"reference": "14dc17c8e860"
},
"time": "2014-10-11 15:45:59",
"type": "library",
"uid": 5
},
"1.1.0": {
"name": "foo\/bar",
"version": "1.1.0",
"version_normalized": "1.1.0.0",
"source": {
"type": "hg",
"url": "http:\/\/some.where\/over\/the\/rainbow\/",
"reference": "d2fa3e69ad5b"
},
"require": {
"bar\/baz": "~1.0"
},
"time": "2014-10-11 15:43:16",
"type": "library",
"uid": 6
},
"dev-default": {
"name": "foo\/bar",
"version": "dev-default",
"version_normalized": "9999999-dev",
"source": {
"type": "hg",
"url": "http:\/\/some.where\/over\/the\/rainbow\/",
"reference": "8e5a5c224336"
},
"require": {
"bar\/baz": "~1.0"
},
"time": "2014-10-11 15:43:18",
"type": "library",
"uid": 7
}
}
}
}
@@ -0,0 +1,50 @@
{
"packages": {
"qux\/quux": {
"dev-default": {
"name": "qux\/quux",
"version": "dev-default",
"version_normalized": "9999999-dev",
"source": {
"type": "hg",
"url": "http:\/\/some.where\/over\/the\/rainbow\/",
"reference": "4a10a567baa5"
},
"replace": {
"gar\/nix": "1.0.*"
},
"time": "2014-10-11 15:48:15",
"type": "library",
"uid": 10
}
},
"gar\/nix": {
"1.0.0": {
"name": "gar\/nix",
"version": "1.0.0",
"version_normalized": "1.0.0.0",
"source": {
"type": "hg",
"url": "http:\/\/some.where\/over\/the\/rainbow\/",
"reference": "44977145d64e"
},
"time": "2014-10-13 12:03:33",
"type": "library",
"uid": 8
},
"dev-default": {
"name": "gar\/nix",
"version": "dev-default",
"version_normalized": "9999999-dev",
"source": {
"type": "hg",
"url": "http:\/\/some.where\/over\/the\/rainbow\/",
"reference": "51cca95a31c2"
},
"time": "2014-10-13 12:03:35",
"type": "library",
"uid": 9
}
}
}
}
@@ -0,0 +1,16 @@
{
"providers": {
"bar\/baz": {
"sha256": "923363b3c22e73abb2e3fd891c8156dd4d0821a97fd3e428bc910833e3e46dbe"
},
"foo\/bar": {
"sha256": "4baabb3303afa3e34a4d3af18fb138e5f3b79029c1f8d9ab5b477ea15776ba0a"
},
"gar\/nix": {
"sha256": "5d210670cb46c8364c8e3fb449967b9bea558b971e5b082f330ae4f1d484c321"
},
"qux\/quux": {
"sha256": "c142d1a07ca354be46b613f59f1d601923a5a00ccc5fcce50a77ecdd461eb72d"
}
}
}
@@ -0,0 +1,22 @@
{
"packages": {
"qux\/quux": {
"dev-default": {
"name": "qux\/quux",
"version": "dev-default",
"version_normalized": "9999999-dev",
"source": {
"type": "hg",
"url": "http:\/\/some.where\/over\/the\/rainbow\/",
"reference": "4a10a567baa5"
},
"replace": {
"gar\/nix": "1.0.*"
},
"time": "2014-10-11 15:48:15",
"type": "library",
"uid": 10
}
}
}
}
@@ -0,0 +1,9 @@
{
"packages": [],
"providers-url": "\/p\/%package%$%hash%.json",
"provider-includes": {
"p\/provider-active$1893a061e579543822389ecd12d791c612db0c05e22d90e9286e233cacd86ed8.json": {
"sha256": "1893a061e579543822389ecd12d791c612db0c05e22d90e9286e233cacd86ed8"
}
}
}
@@ -0,0 +1,110 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"description": "A representation of packages metadata.",
"type": "object",
"oneOf": [
{ "required": [ "packages" ] },
{ "required": [ "providers" ] },
{ "required": [ "provider-includes", "providers-url" ] }
],
"properties": {
"packages": {
"type": ["object", "array"],
"description": "A hashmap of package names in the form of <vendor>/<name>.",
"additionalProperties": { "$ref": "#/definitions/versions" }
},
"providers-url": {
"type": "string",
"description": "Endpoint to retrieve provider data from, e.g. '/p/%package%$%hash%.json'."
},
"provider-includes": {
"type": "object",
"description": "A hashmap of provider listings.",
"additionalProperties": { "$ref": "#/definitions/provider" }
},
"providers": {
"type": "object",
"description": "A hashmap of package names in the form of <vendor>/<name>.",
"additionalProperties": { "$ref": "#/definitions/provider" }
},
"notify-batch": {
"type": "string",
"description": "Endpoint to call after multiple packages have been installed, e.g. '/downloads/'."
},
"search": {
"type": "string",
"description": "Endpoint that provides search capabilities, e.g. '/search.json?q=%query%&type=%type%'."
},
"warning": {
"type": "string",
"description": "A message that will be output by Composer as a warning when this source is consulted."
}
},
"definitions": {
"versions": {
"type": "object",
"description": "A hashmap of versions and their metadata.",
"additionalProperties": { "$ref": "#/definitions/version" }
},
"version": {
"type": "object",
"oneOf": [
{ "$ref": "#/definitions/package" },
{ "$ref": "#/definitions/metapackage" }
]
},
"package-base": {
"properties": {
"name": { "type": "string" },
"type": { "type": "string" },
"version": { "type": "string" },
"version_normalized": {
"type": "string",
"description": "Normalized version, optional but can save computational time on client side."
},
"autoload": { "type": "object" },
"require": { "type": "object" },
"replace": { "type": "object" },
"conflict": { "type": "object" },
"provide": { "type": "object" },
"time": { "type": "string" }
},
"additionalProperties": true
},
"package": {
"allOf": [
{ "$ref": "#/definitions/package-base" },
{
"properties": {
"dist": { "type": "object" },
"source": { "type": "object" }
}
},
{ "oneOf": [
{ "required": [ "name", "version", "source" ] },
{ "required": [ "name", "version", "dist" ] }
] }
]
},
"metapackage": {
"allOf": [
{ "$ref": "#/definitions/package-base" },
{
"properties": {
"type": { "type": "string", "enum": [ "metapackage" ] }
},
"required": [ "name", "version", "type" ]
}
]
},
"provider": {
"type": "object",
"properties": {
"sha256": {
"type": "string",
"description": "Hash value that can be used to validate the resource."
}
}
}
}
}
+849
View File
@@ -0,0 +1,849 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"name": "Package",
"type": "object",
"additionalProperties": false,
"required": [ "name", "description" ],
"properties": {
"name": {
"type": "string",
"description": "Package name, including 'vendor-name/' prefix."
},
"type": {
"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.",
"type": "string",
"pattern": "^[a-z0-9-]+$"
},
"target-dir": {
"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.",
"type": "string"
},
"description": {
"type": "string",
"description": "Short package description."
},
"keywords": {
"type": "array",
"items": {
"type": "string",
"description": "A tag/keyword that this package relates to."
}
},
"homepage": {
"type": "string",
"description": "Homepage URL for the project.",
"format": "uri"
},
"readme": {
"type": "string",
"description": "Relative path to the readme document."
},
"version": {
"type": "string",
"description": "Package version, see https://getcomposer.org/doc/04-schema.md#version for more info on valid schemes.",
"pattern": "^v?\\d+(((\\.\\d+)?\\.\\d+)?\\.\\d+)?|^dev-"
},
"time": {
"type": "string",
"description": "Package release date, in 'YYYY-MM-DD', 'YYYY-MM-DD HH:MM:SS' or 'YYYY-MM-DDTHH:MM:SSZ' format."
},
"license": {
"type": ["string", "array"],
"description": "License name. Or an array of license names."
},
"authors": {
"$ref": "#/definitions/authors"
},
"require": {
"type": "object",
"description": "This is a hash of package name (keys) and version constraints (values) that are required to run this package.",
"additionalProperties": {
"type": "string"
}
},
"replace": {
"type": "object",
"description": "This is a hash of package name (keys) and version constraints (values) that can be replaced by this package.",
"additionalProperties": {
"type": "string"
}
},
"conflict": {
"type": "object",
"description": "This is a hash of package name (keys) and version constraints (values) that conflict with this package.",
"additionalProperties": {
"type": "string"
}
},
"provide": {
"type": "object",
"description": "This is a hash of package name (keys) and version constraints (values) that this package provides in addition to this package's name.",
"additionalProperties": {
"type": "string"
}
},
"require-dev": {
"type": "object",
"description": "This is a hash of package name (keys) and version constraints (values) that this package requires for developing it (testing tools and such).",
"additionalProperties": {
"type": "string"
}
},
"suggest": {
"type": "object",
"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).",
"additionalProperties": {
"type": "string"
}
},
"config": {
"type": "object",
"description": "Composer options.",
"properties": {
"process-timeout": {
"type": "integer",
"description": "The timeout in seconds for process executions, defaults to 300 (5mins)."
},
"use-include-path": {
"type": "boolean",
"description": "If true, the Composer autoloader will also look for classes in the PHP include path."
},
"preferred-install": {
"type": ["string", "object"],
"description": "The install method Composer will prefer to use, defaults to auto and can be any of source, dist, auto, or a hash of {\"pattern\": \"preference\"}."
},
"notify-on-install": {
"type": "boolean",
"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."
},
"github-protocols": {
"type": "array",
"description": "A list of protocols to use for github.com clones, in priority order, defaults to [\"git\", \"https\", \"http\"].",
"items": {
"type": "string"
}
},
"github-oauth": {
"type": "object",
"description": "A hash of domain name => github API oauth tokens, typically {\"github.com\":\"<token>\"}.",
"additionalProperties": {
"type": "string"
}
},
"gitlab-oauth": {
"type": "object",
"description": "A hash of domain name => gitlab API oauth tokens, typically {\"gitlab.com\":\"<token>\"}.",
"additionalProperties": {
"type": "string"
}
},
"gitlab-token": {
"type": "object",
"description": "A hash of domain name => gitlab private tokens, typically {\"gitlab.com\":\"<token>\"}.",
"additionalProperties": true
},
"disable-tls": {
"type": "boolean",
"description": "Defaults to `false`. If set to true all HTTPS URLs will be tried with HTTP instead and no network level encryption is performed. Enabling this is a security risk and is NOT recommended. The better way is to enable the php_openssl extension in php.ini."
},
"secure-http": {
"type": "boolean",
"description": "Defaults to `true`. If set to true only HTTPS URLs are allowed to be downloaded via Composer. If you really absolutely need HTTP access to something then you can disable it, but using \"Let's Encrypt\" to get a free SSL certificate is generally a better alternative."
},
"cafile": {
"type": "string",
"description": "A way to set the path to the openssl CA file. In PHP 5.6+ you should rather set this via openssl.cafile in php.ini, although PHP 5.6+ should be able to detect your system CA file automatically."
},
"capath": {
"type": "string",
"description": "If cafile is not specified or if the certificate is not found there, the directory pointed to by capath is searched for a suitable certificate. capath must be a correctly hashed certificate directory."
},
"http-basic": {
"type": "object",
"description": "A hash of domain name => {\"username\": \"...\", \"password\": \"...\"}.",
"additionalProperties": {
"type": "object",
"required": ["username", "password"],
"properties": {
"username": {
"type": "string",
"description": "The username used for HTTP Basic authentication"
},
"password": {
"type": "string",
"description": "The password used for HTTP Basic authentication"
}
}
}
},
"store-auths": {
"type": ["string", "boolean"],
"description": "What to do after prompting for authentication, one of: true (store), false (do not store) or \"prompt\" (ask every time), defaults to prompt."
},
"platform": {
"type": "object",
"description": "This is a hash of package name (keys) and version (values) that will be used to mock the platform packages on this machine.",
"additionalProperties": {
"type": "string"
}
},
"vendor-dir": {
"type": "string",
"description": "The location where all packages are installed, defaults to \"vendor\"."
},
"bin-dir": {
"type": "string",
"description": "The location where all binaries are linked, defaults to \"vendor/bin\"."
},
"data-dir": {
"type": "string",
"description": "The location where old phar files are stored, defaults to \"$home\" except on XDG Base Directory compliant unixes."
},
"cache-dir": {
"type": "string",
"description": "The location where all caches are located, defaults to \"~/.composer/cache\" on *nix and \"%LOCALAPPDATA%\\Composer\" on windows."
},
"cache-files-dir": {
"type": "string",
"description": "The location where files (zip downloads) are cached, defaults to \"{$cache-dir}/files\"."
},
"cache-repo-dir": {
"type": "string",
"description": "The location where repo (git/hg repo clones) are cached, defaults to \"{$cache-dir}/repo\"."
},
"cache-vcs-dir": {
"type": "string",
"description": "The location where vcs infos (git clones, github api calls, etc. when reading vcs repos) are cached, defaults to \"{$cache-dir}/vcs\"."
},
"cache-ttl": {
"type": "integer",
"description": "The default cache time-to-live, defaults to 15552000 (6 months)."
},
"cache-files-ttl": {
"type": "integer",
"description": "The cache time-to-live for files, defaults to the value of cache-ttl."
},
"cache-files-maxsize": {
"type": ["string", "integer"],
"description": "The cache max size for the files cache, defaults to \"300MiB\"."
},
"bin-compat": {
"enum": ["auto", "full"],
"description": "The compatibility of the binaries, defaults to \"auto\" (automatically guessed) and can be \"full\" (compatible with both Windows and Unix-based systems)."
},
"discard-changes": {
"type": ["string", "boolean"],
"description": "The default style of handling dirty updates, defaults to false and can be any of true, false or \"stash\"."
},
"autoloader-suffix": {
"type": "string",
"description": "Optional string to be used as a suffix for the generated Composer autoloader. When null a random one will be generated."
},
"optimize-autoloader": {
"type": "boolean",
"description": "Always optimize when dumping the autoloader."
},
"prepend-autoloader": {
"type": "boolean",
"description": "If false, the composer autoloader will not be prepended to existing autoloaders, defaults to true."
},
"classmap-authoritative": {
"type": "boolean",
"description": "If true, the composer autoloader will not scan the filesystem for classes that are not found in the class map, defaults to false."
},
"apcu-autoloader": {
"type": "boolean",
"description": "If true, the Composer autoloader will check for APCu and use it to cache found/not-found classes when the extension is enabled, defaults to false."
},
"github-domains": {
"type": "array",
"description": "A list of domains to use in github mode. This is used for GitHub Enterprise setups, defaults to [\"github.com\"].",
"items": {
"type": "string"
}
},
"github-expose-hostname": {
"type": "boolean",
"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."
},
"gitlab-domains": {
"type": "array",
"description": "A list of domains to use in gitlab mode. This is used for custom GitLab setups, defaults to [\"gitlab.com\"].",
"items": {
"type": "string"
}
},
"use-github-api": {
"type": "boolean",
"description": "Defaults to true. If set to false, globally disables the use of the GitHub API for all GitHub repositories and clones the repository as it would for any other repository."
},
"archive-format": {
"type": "string",
"description": "The default archiving format when not provided on cli, defaults to \"tar\"."
},
"archive-dir": {
"type": "string",
"description": "The default archive path when not provided on cli, defaults to \".\"."
},
"htaccess-protect": {
"type": "boolean",
"description": "Defaults to true. If set to false, Composer will not create .htaccess files in the composer home, cache, and data directories."
},
"sort-packages": {
"type": "boolean",
"description": "Defaults to false. If set to true, Composer will sort packages when adding/updating a new dependency."
}
}
},
"extra": {
"type": ["object", "array"],
"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.",
"additionalProperties": true
},
"autoload": {
"$ref": "#/definitions/autoload"
},
"autoload-dev": {
"type": "object",
"description": "Description of additional autoload rules for development purpose (eg. a test suite).",
"properties": {
"psr-0": {
"type": "object",
"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.",
"additionalProperties": {
"type": ["string", "array"],
"items": {
"type": "string"
}
}
},
"psr-4": {
"type": "object",
"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.",
"additionalProperties": {
"type": ["string", "array"],
"items": {
"type": "string"
}
}
},
"classmap": {
"type": "array",
"description": "This is an array of directories that contain classes to be included in the class-map generation process."
},
"files": {
"type": "array",
"description": "This is an array of files that are always required on every request."
}
}
},
"archive": {
"type": ["object"],
"description": "Options for creating package archives for distribution.",
"properties": {
"exclude": {
"type": "array",
"description": "A list of patterns for paths to exclude or include if prefixed with an exclamation mark."
}
}
},
"repositories": {
"type": ["object", "array"],
"description": "A set of additional repositories where packages can be found.",
"additionalProperties": {
"oneOf": [
{ "$ref": "#/definitions/repository" },
{ "type": "boolean", "enum": [false] }
]
},
"items": {
"oneOf": [
{ "$ref": "#/definitions/repository" },
{
"type": "object",
"additionalProperties": { "type": "boolean", "enum": [false] },
"minProperties": 1,
"maxProperties": 1
}
]
}
},
"minimum-stability": {
"type": ["string"],
"description": "The minimum stability the packages must have to be install-able. Possible values are: dev, alpha, beta, RC, stable.",
"pattern": "^dev|alpha|beta|rc|RC|stable$"
},
"prefer-stable": {
"type": ["boolean"],
"description": "If set to true, stable packages will be preferred to dev packages when possible, even if the minimum-stability allows unstable packages."
},
"bin": {
"type": ["string", "array"],
"description": "A set of files, or a single file, that should be treated as binaries and symlinked into bin-dir (from config).",
"items": {
"type": "string"
}
},
"include-path": {
"type": ["array"],
"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.",
"items": {
"type": "string"
}
},
"scripts": {
"type": ["object"],
"description": "Script listeners that will be executed before/after some events.",
"properties": {
"pre-install-cmd": {
"type": ["array", "string"],
"description": "Occurs before the install command is executed, contains one or more Class::method callables or shell commands."
},
"post-install-cmd": {
"type": ["array", "string"],
"description": "Occurs after the install command is executed, contains one or more Class::method callables or shell commands."
},
"pre-update-cmd": {
"type": ["array", "string"],
"description": "Occurs before the update command is executed, contains one or more Class::method callables or shell commands."
},
"post-update-cmd": {
"type": ["array", "string"],
"description": "Occurs after the update command is executed, contains one or more Class::method callables or shell commands."
},
"pre-status-cmd": {
"type": ["array", "string"],
"description": "Occurs before the status command is executed, contains one or more Class::method callables or shell commands."
},
"post-status-cmd": {
"type": ["array", "string"],
"description": "Occurs after the status command is executed, contains one or more Class::method callables or shell commands."
},
"pre-package-install": {
"type": ["array", "string"],
"description": "Occurs before a package is installed, contains one or more Class::method callables or shell commands."
},
"post-package-install": {
"type": ["array", "string"],
"description": "Occurs after a package is installed, contains one or more Class::method callables or shell commands."
},
"pre-package-update": {
"type": ["array", "string"],
"description": "Occurs before a package is updated, contains one or more Class::method callables or shell commands."
},
"post-package-update": {
"type": ["array", "string"],
"description": "Occurs after a package is updated, contains one or more Class::method callables or shell commands."
},
"pre-package-uninstall": {
"type": ["array", "string"],
"description": "Occurs before a package has been uninstalled, contains one or more Class::method callables or shell commands."
},
"post-package-uninstall": {
"type": ["array", "string"],
"description": "Occurs after a package has been uninstalled, contains one or more Class::method callables or shell commands."
},
"pre-autoload-dump": {
"type": ["array", "string"],
"description": "Occurs before the autoloader is dumped, contains one or more Class::method callables or shell commands."
},
"post-autoload-dump": {
"type": ["array", "string"],
"description": "Occurs after the autoloader is dumped, contains one or more Class::method callables or shell commands."
},
"post-root-package-install": {
"type": ["array", "string"],
"description": "Occurs after the root-package is installed, contains one or more Class::method callables or shell commands."
},
"post-create-project-cmd": {
"type": ["array", "string"],
"description": "Occurs after the create-project command is executed, contains one or more Class::method callables or shell commands."
}
}
},
"scripts-descriptions": {
"type": ["object"],
"description": "Descriptions for custom commands, shown in console help.",
"additionalProperties": {
"type": "string"
}
},
"support": {
"type": "object",
"properties": {
"email": {
"type": "string",
"description": "Email address for support.",
"format": "email"
},
"issues": {
"type": "string",
"description": "URL to the issue tracker.",
"format": "uri"
},
"forum": {
"type": "string",
"description": "URL to the forum.",
"format": "uri"
},
"wiki": {
"type": "string",
"description": "URL to the wiki.",
"format": "uri"
},
"irc": {
"type": "string",
"description": "IRC channel for support, as irc://server/channel.",
"format": "uri"
},
"chat": {
"type": "string",
"description": "URL to the support chat.",
"format": "uri"
},
"source": {
"type": "string",
"description": "URL to browse or download the sources.",
"format": "uri"
},
"docs": {
"type": "string",
"description": "URL to the documentation.",
"format": "uri"
},
"rss": {
"type": "string",
"description": "URL to the RSS feed.",
"format": "uri"
}
}
},
"non-feature-branches": {
"type": ["array"],
"description": "A set of string or regex patterns for non-numeric branch names that will not be handled as feature branches.",
"items": {
"type": "string"
}
},
"abandoned": {
"type": ["boolean", "string"],
"description": "Indicates whether this package has been abandoned, it can be boolean or a package name/URL pointing to a recommended alternative. Defaults to false."
},
"_comment": {
"type": ["array", "string"],
"description": "A key to store comments in"
}
},
"definitions": {
"authors": {
"type": "array",
"description": "List of authors that contributed to the package. This is typically the main maintainers, not the full list.",
"items": {
"type": "object",
"additionalProperties": false,
"required": [ "name"],
"properties": {
"name": {
"type": "string",
"description": "Full name of the author."
},
"email": {
"type": "string",
"description": "Email address of the author.",
"format": "email"
},
"homepage": {
"type": "string",
"description": "Homepage URL for the author.",
"format": "uri"
},
"role": {
"type": "string",
"description": "Author's role in the project."
}
}
}
},
"autoload": {
"type": "object",
"description": "Description of how the package can be autoloaded.",
"properties": {
"psr-0": {
"type": "object",
"description": "This is a hash of namespaces (keys) and the directories they can be found in (values, can be arrays of paths) by the autoloader.",
"additionalProperties": {
"type": ["string", "array"],
"items": {
"type": "string"
}
}
},
"psr-4": {
"type": "object",
"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.",
"additionalProperties": {
"type": ["string", "array"],
"items": {
"type": "string"
}
}
},
"classmap": {
"type": "array",
"description": "This is an array of directories that contain classes to be included in the class-map generation process."
},
"files": {
"type": "array",
"description": "This is an array of files that are always required on every request."
},
"exclude-from-classmap": {
"type": "array",
"description": "This is an array of patterns to exclude from autoload classmap generation. (e.g. \"exclude-from-classmap\": [\"/test/\", \"/tests/\", \"/Tests/\"]"
}
}
},
"repository": {
"type": "object",
"oneOf": [
{ "$ref": "#/definitions/composer-repository" },
{ "$ref": "#/definitions/vcs-repository" },
{ "$ref": "#/definitions/path-repository" },
{ "$ref": "#/definitions/artifact-repository" },
{ "$ref": "#/definitions/pear-repository" },
{ "$ref": "#/definitions/package-repository" }
]
},
"composer-repository": {
"type": "object",
"required": ["type", "url"],
"properties": {
"type": { "type": "string", "enum": ["composer"] },
"url": { "type": "string" },
"options": {
"type": "object",
"additionalProperties": true
},
"allow_ssl_downgrade": { "type": "boolean" },
"force-lazy-providers": { "type": "boolean" }
}
},
"vcs-repository": {
"type": "object",
"required": ["type", "url"],
"properties": {
"type": { "type": "string", "enum": ["vcs", "github", "git", "gitlab", "git-bitbucket", "hg", "hg-bitbucket", "fossil", "perforce", "svn"] },
"url": { "type": "string" },
"no-api": { "type": "boolean" },
"secure-http": { "type": "boolean" },
"svn-cache-credentials": { "type": "boolean" },
"trunk-path": { "type": ["string", "boolean"] },
"branches-path": { "type": ["string", "boolean"] },
"tags-path": { "type": ["string", "boolean"] },
"package-path": { "type": "string" },
"depot": { "type": "string" },
"branch": { "type": "string" },
"unique_perforce_client_name": { "type": "string" },
"p4user": { "type": "string" },
"p4password": { "type": "string" }
}
},
"path-repository": {
"type": "object",
"required": ["type", "url"],
"properties": {
"type": { "type": "string", "enum": ["path"] },
"url": { "type": "string" },
"options": {
"type": "object",
"properties": {
"symlink": { "type": ["boolean", "null"] }
},
"additionalProperties": true
}
}
},
"artifact-repository": {
"type": "object",
"required": ["type", "url"],
"properties": {
"type": { "type": "string", "enum": ["artifact"] },
"url": { "type": "string" }
}
},
"pear-repository": {
"type": "object",
"required": ["type", "url"],
"properties": {
"type": { "type": "string", "enum": ["pear"] },
"url": { "type": "string" },
"vendor-alias": { "type": "string" }
}
},
"package-repository": {
"type": "object",
"required": ["type", "package"],
"properties": {
"type": { "type": "string", "enum": ["package"] },
"package": {
"oneOf": [
{ "$ref": "#/definitions/inline-package" },
{
"type": "array",
"items": { "$ref": "#/definitions/inline-package" }
}
]
}
}
},
"inline-package": {
"type": "object",
"required": ["name", "version"],
"properties": {
"name": {
"type": "string",
"description": "Package name, including 'vendor-name/' prefix."
},
"type": {
"type": "string"
},
"target-dir": {
"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.",
"type": "string"
},
"description": {
"type": "string"
},
"keywords": {
"type": "array",
"items": {
"type": "string"
}
},
"homepage": {
"type": "string",
"format": "uri"
},
"version": {
"type": "string"
},
"time": {
"type": "string"
},
"license": {
"type": [
"string",
"array"
]
},
"authors": {
"$ref": "#/definitions/authors"
},
"require": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"replace": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"conflict": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"provide": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"require-dev": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"suggest": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"extra": {
"type": ["object", "array"],
"additionalProperties": true
},
"autoload": {
"$ref": "#/definitions/autoload"
},
"archive": {
"type": ["object"],
"properties": {
"exclude": {
"type": "array"
}
}
},
"bin": {
"type": ["string", "array"],
"description": "A set of files, or a single file, that should be treated as binaries and symlinked into bin-dir (from config).",
"items": {
"type": "string"
}
},
"include-path": {
"type": ["array"],
"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.",
"items": {
"type": "string"
}
},
"source": {
"type": "object",
"required": ["type", "url", "reference"],
"properties": {
"type": {
"type": "string"
},
"url": {
"type": "string"
},
"reference": {
"type": "string"
},
"mirrors": {
"type": "array"
}
}
},
"dist": {
"type": "object",
"required": ["type", "url"],
"properties": {
"type": {
"type": "string"
},
"url": {
"type": "string"
},
"reference": {
"type": "string"
},
"shasum": {
"type": "string"
},
"mirrors": {
"type": "array"
}
}
}
},
"additionalProperties": true
}
}
}
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,445 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Autoload;
/**
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
*
* $loader = new \Composer\Autoload\ClassLoader();
*
* // register classes with namespaces
* $loader->add('Symfony\Component', __DIR__.'/component');
* $loader->add('Symfony', __DIR__.'/framework');
*
* // activate the autoloader
* $loader->register();
*
* // to enable searching the include path (eg. for PEAR packages)
* $loader->setUseIncludePath(true);
*
* In this example, if you try to use a class in the Symfony\Component
* namespace or one of its children (Symfony\Component\Console for instance),
* the autoloader will first look for the class under the component/
* directory, and it will then fallback to the framework/ directory if not
* found before giving up.
*
* This class is loosely based on the Symfony UniversalClassLoader.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
* @see http://www.php-fig.org/psr/psr-0/
* @see http://www.php-fig.org/psr/psr-4/
*/
class ClassLoader
{
// PSR-4
private $prefixLengthsPsr4 = array();
private $prefixDirsPsr4 = array();
private $fallbackDirsPsr4 = array();
// PSR-0
private $prefixesPsr0 = array();
private $fallbackDirsPsr0 = array();
private $useIncludePath = false;
private $classMap = array();
private $classMapAuthoritative = false;
private $missingClasses = array();
private $apcuPrefix;
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
return call_user_func_array('array_merge', $this->prefixesPsr0);
}
return array();
}
public function getPrefixesPsr4()
{
return $this->prefixDirsPsr4;
}
public function getFallbackDirs()
{
return $this->fallbackDirsPsr0;
}
public function getFallbackDirsPsr4()
{
return $this->fallbackDirsPsr4;
}
public function getClassMap()
{
return $this->classMap;
}
/**
* @param array $classMap Class to filename map
*/
public function addClassMap(array $classMap)
{
if ($this->classMap) {
$this->classMap = array_merge($this->classMap, $classMap);
} else {
$this->classMap = $classMap;
}
}
/**
* Registers a set of PSR-0 directories for a given prefix, either
* appending or prepending to the ones previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
*/
public function add($prefix, $paths, $prepend = false)
{
if (!$prefix) {
if ($prepend) {
$this->fallbackDirsPsr0 = array_merge(
(array) $paths,
$this->fallbackDirsPsr0
);
} else {
$this->fallbackDirsPsr0 = array_merge(
$this->fallbackDirsPsr0,
(array) $paths
);
}
return;
}
$first = $prefix[0];
if (!isset($this->prefixesPsr0[$first][$prefix])) {
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
return;
}
if ($prepend) {
$this->prefixesPsr0[$first][$prefix] = array_merge(
(array) $paths,
$this->prefixesPsr0[$first][$prefix]
);
} else {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$this->prefixesPsr0[$first][$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-4 directories for a given namespace, either
* appending or prepending to the ones previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories
*
* @throws \InvalidArgumentException
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
if (!$prefix) {
// Register directories for the root namespace.
if ($prepend) {
$this->fallbackDirsPsr4 = array_merge(
(array) $paths,
$this->fallbackDirsPsr4
);
} else {
$this->fallbackDirsPsr4 = array_merge(
$this->fallbackDirsPsr4,
(array) $paths
);
}
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
// Register directories for a new namespace.
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
} elseif ($prepend) {
// Prepend directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
(array) $paths,
$this->prefixDirsPsr4[$prefix]
);
} else {
// Append directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$this->prefixDirsPsr4[$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-0 directories for a given prefix,
* replacing any others previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 base directories
*/
public function set($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr0 = (array) $paths;
} else {
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
}
}
/**
* Registers a set of PSR-4 directories for a given namespace,
* replacing any others previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
*
* @throws \InvalidArgumentException
*/
public function setPsr4($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr4 = (array) $paths;
} else {
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
}
}
/**
* Turns on searching the include path for class files.
*
* @param bool $useIncludePath
*/
public function setUseIncludePath($useIncludePath)
{
$this->useIncludePath = $useIncludePath;
}
/**
* Can be used to check if the autoloader uses the include path to check
* for classes.
*
* @return bool
*/
public function getUseIncludePath()
{
return $this->useIncludePath;
}
/**
* Turns off searching the prefix and fallback directories for classes
* that have not been registered with the class map.
*
* @param bool $classMapAuthoritative
*/
public function setClassMapAuthoritative($classMapAuthoritative)
{
$this->classMapAuthoritative = $classMapAuthoritative;
}
/**
* Should class lookup fail if not found in the current class map?
*
* @return bool
*/
public function isClassMapAuthoritative()
{
return $this->classMapAuthoritative;
}
/**
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
*
* @param string|null $apcuPrefix
*/
public function setApcuPrefix($apcuPrefix)
{
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
}
/**
* The APCu prefix in use, or null if APCu caching is not enabled.
*
* @return string|null
*/
public function getApcuPrefix()
{
return $this->apcuPrefix;
}
/**
* Registers this instance as an autoloader.
*
* @param bool $prepend Whether to prepend the autoloader or not
*/
public function register($prepend = false)
{
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
}
/**
* Unregisters this instance as an autoloader.
*/
public function unregister()
{
spl_autoload_unregister(array($this, 'loadClass'));
}
/**
* Loads the given class or interface.
*
* @param string $class The name of the class
* @return bool|null True if loaded, null otherwise
*/
public function loadClass($class)
{
if ($file = $this->findFile($class)) {
includeFile($file);
return true;
}
}
/**
* Finds the path to the file where the class is defined.
*
* @param string $class The name of the class
*
* @return string|false The path if found, false otherwise
*/
public function findFile($class)
{
// class map lookup
if (isset($this->classMap[$class])) {
return $this->classMap[$class];
}
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
return false;
}
if (null !== $this->apcuPrefix) {
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
if ($hit) {
return $file;
}
}
$file = $this->findFileWithExtension($class, '.php');
// Search for Hack files if we are running on HHVM
if (false === $file && defined('HHVM_VERSION')) {
$file = $this->findFileWithExtension($class, '.hh');
}
if (null !== $this->apcuPrefix) {
apcu_add($this->apcuPrefix.$class, $file);
}
if (false === $file) {
// Remember that this class does not exist.
$this->missingClasses[$class] = true;
}
return $file;
}
private function findFileWithExtension($class, $ext)
{
// PSR-4 lookup
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
$first = $class[0];
if (isset($this->prefixLengthsPsr4[$first])) {
$subPath = $class;
while (false !== $lastPos = strrpos($subPath, '\\')) {
$subPath = substr($subPath, 0, $lastPos);
$search = $subPath . '\\';
if (isset($this->prefixDirsPsr4[$search])) {
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
foreach ($this->prefixDirsPsr4[$search] as $dir) {
if (file_exists($file = $dir . $pathEnd)) {
return $file;
}
}
}
}
}
// PSR-4 fallback dirs
foreach ($this->fallbackDirsPsr4 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
return $file;
}
}
// PSR-0 lookup
if (false !== $pos = strrpos($class, '\\')) {
// namespaced class name
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
} else {
// PEAR-like class name
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
}
if (isset($this->prefixesPsr0[$first])) {
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
if (0 === strpos($class, $prefix)) {
foreach ($dirs as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
}
}
}
// PSR-0 fallback dirs
foreach ($this->fallbackDirsPsr0 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
// PSR-0 include paths.
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
return $file;
}
return false;
}
}
/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*/
function includeFile($file)
{
include $file;
}
@@ -0,0 +1,222 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/*
* This file is copied from the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*/
namespace Composer\Autoload;
use Symfony\Component\Finder\Finder;
use Composer\IO\IOInterface;
use Composer\Util\Filesystem;
/**
* ClassMapGenerator
*
* @author Gyula Sallai <salla016@gmail.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
class ClassMapGenerator
{
/**
* Generate a class map file
*
* @param \Traversable $dirs Directories or a single path to search in
* @param string $file The name of the class map file
*/
public static function dump($dirs, $file)
{
$maps = array();
foreach ($dirs as $dir) {
$maps = array_merge($maps, static::createMap($dir));
}
file_put_contents($file, sprintf('<?php return %s;', var_export($maps, true)));
}
/**
* Iterate over all files in the given directory searching for classes
*
* @param \Iterator|string $path The path to search in or an iterator
* @param string $blacklist Regex that matches against the file path that exclude from the classmap.
* @param IOInterface $io IO object
* @param string $namespace Optional namespace prefix to filter by
*
* @throws \RuntimeException When the path is neither an existing file nor directory
* @return array A class map array
*/
public static function createMap($path, $blacklist = null, IOInterface $io = null, $namespace = null)
{
if (is_string($path)) {
if (is_file($path)) {
$path = array(new \SplFileInfo($path));
} elseif (is_dir($path)) {
$path = Finder::create()->files()->followLinks()->name('/\.(php|inc|hh)$/')->in($path);
} else {
throw new \RuntimeException(
'Could not scan for classes inside "'.$path.
'" which does not appear to be a file nor a folder'
);
}
}
$map = array();
$filesystem = new Filesystem();
$cwd = realpath(getcwd());
foreach ($path as $file) {
$filePath = $file->getPathname();
if (!in_array(pathinfo($filePath, PATHINFO_EXTENSION), array('php', 'inc', 'hh'))) {
continue;
}
if (!$filesystem->isAbsolutePath($filePath)) {
$filePath = $cwd . '/' . $filePath;
$filePath = $filesystem->normalizePath($filePath);
} else {
$filePath = preg_replace('{[\\\\/]{2,}}', '/', $filePath);
}
// check the realpath of the file against the blacklist as the path might be a symlink and the blacklist is realpath'd so symlink are resolved
if ($blacklist && preg_match($blacklist, strtr(realpath($filePath), '\\', '/'))) {
continue;
}
// check non-realpath of file for directories symlink in project dir
if ($blacklist && preg_match($blacklist, strtr($filePath, '\\', '/'))) {
continue;
}
$classes = self::findClasses($filePath);
foreach ($classes as $class) {
// skip classes not within the given namespace prefix
if (null !== $namespace && 0 !== strpos($class, $namespace)) {
continue;
}
if (!isset($map[$class])) {
$map[$class] = $filePath;
} elseif ($io && $map[$class] !== $filePath && !preg_match('{/(test|fixture|example|stub)s?/}i', strtr($map[$class].' '.$filePath, '\\', '/'))) {
$io->writeError(
'<warning>Warning: Ambiguous class resolution, "'.$class.'"'.
' was found in both "'.$map[$class].'" and "'.$filePath.'", the first will be used.</warning>'
);
}
}
}
return $map;
}
/**
* Extract the classes in the given file
*
* @param string $path The file to check
* @throws \RuntimeException
* @return array The found classes
*/
private static function findClasses($path)
{
$extraTypes = PHP_VERSION_ID < 50400 ? '' : '|trait';
if (defined('HHVM_VERSION') && version_compare(HHVM_VERSION, '3.3', '>=')) {
$extraTypes .= '|enum';
}
// Use @ here instead of Silencer to actively suppress 'unhelpful' output
// @link https://github.com/composer/composer/pull/4886
$contents = @php_strip_whitespace($path);
if (!$contents) {
if (!file_exists($path)) {
$message = 'File at "%s" does not exist, check your classmap definitions';
} elseif (!is_readable($path)) {
$message = 'File at "%s" is not readable, check its permissions';
} elseif ('' === trim(file_get_contents($path))) {
// The input file was really empty and thus contains no classes
return array();
} else {
$message = 'File at "%s" could not be parsed as PHP, it may be binary or corrupted';
}
$error = error_get_last();
if (isset($error['message'])) {
$message .= PHP_EOL . 'The following message may be helpful:' . PHP_EOL . $error['message'];
}
throw new \RuntimeException(sprintf($message, $path));
}
// return early if there is no chance of matching anything in this file
if (!preg_match('{\b(?:class|interface'.$extraTypes.')\s}i', $contents)) {
return array();
}
// strip heredocs/nowdocs
$contents = preg_replace('{<<<[ \t]*([\'"]?)(\w+)\\1(?:\r\n|\n|\r)(?:.*?)(?:\r\n|\n|\r)(?:\s*)\\2(?=\s+|[;,.)])}s', 'null', $contents);
// strip strings
$contents = preg_replace('{"[^"\\\\]*+(\\\\.[^"\\\\]*+)*+"|\'[^\'\\\\]*+(\\\\.[^\'\\\\]*+)*+\'}s', 'null', $contents);
// strip leading non-php code if needed
if (substr($contents, 0, 2) !== '<?') {
$contents = preg_replace('{^.+?<\?}s', '<?', $contents, 1, $replacements);
if ($replacements === 0) {
return array();
}
}
// strip non-php blocks in the file
$contents = preg_replace('{\?>(?:[^<]++|<(?!\?))*+<\?}s', '?><?', $contents);
// strip trailing non-php code if needed
$pos = strrpos($contents, '?>');
if (false !== $pos && false === strpos(substr($contents, $pos), '<?')) {
$contents = substr($contents, 0, $pos);
}
// strip comments if short open tags are in the file
if (preg_match('{(<\?)(?!(php|hh))}i', $contents)) {
$contents = preg_replace('{//.* | /\*(?:[^*]++|\*(?!/))*\*/}x', '', $contents);
}
preg_match_all('{
(?:
\b(?<![\$:>])(?P<type>class|interface'.$extraTypes.') \s++ (?P<name>[a-zA-Z_\x7f-\xff:][a-zA-Z0-9_\x7f-\xff:\-]*+)
| \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*+ [\{;]
)
}ix', $contents, $matches);
$classes = array();
$namespace = '';
for ($i = 0, $len = count($matches['type']); $i < $len; $i++) {
if (!empty($matches['ns'][$i])) {
$namespace = str_replace(array(' ', "\t", "\r", "\n"), '', $matches['nsname'][$i]) . '\\';
} else {
$name = $matches['name'][$i];
// skip anon classes extending/implementing
if ($name === 'extends' || $name === 'implements') {
continue;
}
if ($name[0] === ':') {
// This is an XHP class, https://github.com/facebook/xhp
$name = 'xhp'.substr(str_replace(array('-', ':'), array('_', '__'), $name), 1);
} elseif ($matches['type'][$i] === 'enum') {
// In Hack, something like:
// enum Foo: int { HERP = '123'; }
// The regex above captures the colon, which isn't part of
// the class name.
$name = rtrim($name, ':');
}
$classes[] = ltrim($namespace . $name, '\\');
}
}
return $classes;
}
}
+257
View File
@@ -0,0 +1,257 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer;
use Composer\IO\IOInterface;
use Composer\Util\Filesystem;
use Composer\Util\Silencer;
use Symfony\Component\Finder\Finder;
/**
* Reads/writes to a filesystem cache
*
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
class Cache
{
private static $cacheCollected = false;
private $io;
private $root;
private $enabled = true;
private $whitelist;
private $filesystem;
/**
* @param IOInterface $io
* @param string $cacheDir location of the cache
* @param string $whitelist List of characters that are allowed in path names (used in a regex character class)
* @param Filesystem $filesystem optional filesystem instance
*/
public function __construct(IOInterface $io, $cacheDir, $whitelist = 'a-z0-9.', Filesystem $filesystem = null)
{
$this->io = $io;
$this->root = rtrim($cacheDir, '/\\') . '/';
$this->whitelist = $whitelist;
$this->filesystem = $filesystem ?: new Filesystem();
if (!self::isUsable($cacheDir)) {
$this->enabled = false;
return;
}
if (
(!is_dir($this->root) && !Silencer::call('mkdir', $this->root, 0777, true))
|| !is_writable($this->root)
) {
$this->io->writeError('<warning>Cannot create cache directory ' . $this->root . ', or directory is not writable. Proceeding without cache</warning>');
$this->enabled = false;
}
}
public static function isUsable($path)
{
return !preg_match('{(^|[\\\\/])(\$null|nul|NUL|/dev/null)([\\\\/]|$)}', $path);
}
public function isEnabled()
{
return $this->enabled;
}
public function getRoot()
{
return $this->root;
}
public function read($file)
{
if ($this->enabled) {
$file = preg_replace('{[^'.$this->whitelist.']}i', '-', $file);
if (file_exists($this->root . $file)) {
$this->io->writeError('Reading '.$this->root . $file.' from cache', true, IOInterface::DEBUG);
return file_get_contents($this->root . $file);
}
}
return false;
}
public function write($file, $contents)
{
if ($this->enabled) {
$file = preg_replace('{[^'.$this->whitelist.']}i', '-', $file);
$this->io->writeError('Writing '.$this->root . $file.' into cache', true, IOInterface::DEBUG);
try {
return file_put_contents($this->root . $file, $contents);
} catch (\ErrorException $e) {
$this->io->writeError('<warning>Failed to write into cache: '.$e->getMessage().'</warning>', true, IOInterface::DEBUG);
if (preg_match('{^file_put_contents\(\): Only ([0-9]+) of ([0-9]+) bytes written}', $e->getMessage(), $m)) {
// Remove partial file.
unlink($this->root . $file);
$message = sprintf(
'<warning>Writing %1$s into cache failed after %2$u of %3$u bytes written, only %4$u bytes of free space available</warning>',
$this->root . $file,
$m[1],
$m[2],
@disk_free_space($this->root . dirname($file))
);
$this->io->writeError($message);
return false;
}
throw $e;
}
}
return false;
}
/**
* Copy a file into the cache
*/
public function copyFrom($file, $source)
{
if ($this->enabled) {
$file = preg_replace('{[^'.$this->whitelist.']}i', '-', $file);
$this->filesystem->ensureDirectoryExists(dirname($this->root . $file));
if (!file_exists($source)) {
$this->io->writeError('<error>'.$source.' does not exist, can not write into cache</error>');
} elseif ($this->io->isDebug()) {
$this->io->writeError('Writing '.$this->root . $file.' into cache from '.$source);
}
return copy($source, $this->root . $file);
}
return false;
}
/**
* Copy a file out of the cache
*/
public function copyTo($file, $target)
{
if ($this->enabled) {
$file = preg_replace('{[^'.$this->whitelist.']}i', '-', $file);
if (file_exists($this->root . $file)) {
try {
touch($this->root . $file, filemtime($this->root . $file), time());
} catch (\ErrorException $e) {
// fallback in case the above failed due to incorrect ownership
// see https://github.com/composer/composer/issues/4070
Silencer::call('touch', $this->root . $file);
}
$this->io->writeError('Reading '.$this->root . $file.' from cache', true, IOInterface::DEBUG);
return copy($this->root . $file, $target);
}
}
return false;
}
public function gcIsNecessary()
{
return (!self::$cacheCollected && !mt_rand(0, 50));
}
public function remove($file)
{
if ($this->enabled) {
$file = preg_replace('{[^'.$this->whitelist.']}i', '-', $file);
if (file_exists($this->root . $file)) {
return $this->filesystem->unlink($this->root . $file);
}
}
return false;
}
public function clear()
{
if ($this->enabled) {
$this->filesystem->emptyDirectory($this->root);
return true;
}
return false;
}
public function gc($ttl, $maxSize)
{
if ($this->enabled) {
$expire = new \DateTime();
$expire->modify('-'.$ttl.' seconds');
$finder = $this->getFinder()->date('until '.$expire->format('Y-m-d H:i:s'));
foreach ($finder as $file) {
$this->filesystem->unlink($file->getPathname());
}
$totalSize = $this->filesystem->size($this->root);
if ($totalSize > $maxSize) {
$iterator = $this->getFinder()->sortByAccessedTime()->getIterator();
while ($totalSize > $maxSize && $iterator->valid()) {
$filepath = $iterator->current()->getPathname();
$totalSize -= $this->filesystem->size($filepath);
$this->filesystem->unlink($filepath);
$iterator->next();
}
}
self::$cacheCollected = true;
return true;
}
return false;
}
public function sha1($file)
{
if ($this->enabled) {
$file = preg_replace('{[^'.$this->whitelist.']}i', '-', $file);
if (file_exists($this->root . $file)) {
return sha1_file($this->root . $file);
}
}
return false;
}
public function sha256($file)
{
if ($this->enabled) {
$file = preg_replace('{[^'.$this->whitelist.']}i', '-', $file);
if (file_exists($this->root . $file)) {
return hash_file('sha256', $this->root . $file);
}
}
return false;
}
protected function getFinder()
{
return Finder::create()->in($this->root)->files();
}
}
@@ -0,0 +1,48 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
class AboutCommand extends BaseCommand
{
protected function configure()
{
$this
->setName('about')
->setDescription('Shows the short information about Composer.')
->setHelp(
<<<EOT
<info>php composer.phar about</info>
EOT
)
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$this->getIO()->write(
<<<EOT
<info>Composer - Dependency Manager for PHP</info>
<comment>Composer is a dependency manager tracking local dependencies of your projects and libraries.
See https://getcomposer.org/ for more information.</comment>
EOT
);
return 0;
}
}
@@ -0,0 +1,166 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Command;
use Composer\Factory;
use Composer\IO\IOInterface;
use Composer\Config;
use Composer\Composer;
use Composer\Repository\CompositeRepository;
use Composer\Repository\RepositoryFactory;
use Composer\Script\ScriptEvents;
use Composer\Plugin\CommandEvent;
use Composer\Plugin\PluginEvents;
use Composer\Util\Filesystem;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Creates an archive of a package for distribution.
*
* @author Nils Adermann <naderman@naderman.de>
*/
class ArchiveCommand extends BaseCommand
{
protected function configure()
{
$this
->setName('archive')
->setDescription('Creates an archive of this composer package.')
->setDefinition(array(
new InputArgument('package', InputArgument::OPTIONAL, 'The package to archive instead of the current project'),
new InputArgument('version', InputArgument::OPTIONAL, 'A version constraint to find the package to archive'),
new InputOption('format', 'f', InputOption::VALUE_REQUIRED, 'Format of the resulting archive: tar or zip'),
new InputOption('dir', null, InputOption::VALUE_REQUIRED, 'Write the archive to this directory'),
new InputOption('file', null, InputOption::VALUE_REQUIRED, 'Write the archive with the given file name.'
.' Note that the format will be appended.'),
new InputOption('ignore-filters', false, InputOption::VALUE_NONE, 'Ignore filters when saving package'),
))
->setHelp(
<<<EOT
The <info>archive</info> command creates an archive of the specified format
containing the files and directories of the Composer project or the specified
package in the specified version and writes it to the specified directory.
<info>php composer.phar archive [--format=zip] [--dir=/foo] [package [version]]</info>
Read more at https://getcomposer.org/doc/03-cli.md#archive
EOT
)
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$config = Factory::createConfig();
$composer = $this->getComposer(false);
if ($composer) {
$commandEvent = new CommandEvent(PluginEvents::COMMAND, 'archive', $input, $output);
$eventDispatcher = $composer->getEventDispatcher();
$eventDispatcher->dispatch($commandEvent->getName(), $commandEvent);
$eventDispatcher->dispatchScript(ScriptEvents::PRE_ARCHIVE_CMD);
}
if (null === $input->getOption('format')) {
$input->setOption('format', $config->get('archive-format'));
}
if (null === $input->getOption('dir')) {
$input->setOption('dir', $config->get('archive-dir'));
}
$returnCode = $this->archive(
$this->getIO(),
$config,
$input->getArgument('package'),
$input->getArgument('version'),
$input->getOption('format'),
$input->getOption('dir'),
$input->getOption('file'),
$input->getOption('ignore-filters'),
$composer
);
if (0 === $returnCode && $composer) {
$composer->getEventDispatcher()->dispatchScript(ScriptEvents::POST_ARCHIVE_CMD);
}
return $returnCode;
}
protected function archive(IOInterface $io, Config $config, $packageName = null, $version = null, $format = 'tar', $dest = '.', $fileName = null, $ignoreFilters = false, Composer $composer = null)
{
if ($composer) {
$archiveManager = $composer->getArchiveManager();
} else {
$factory = new Factory;
$downloadManager = $factory->createDownloadManager($io, $config);
$archiveManager = $factory->createArchiveManager($config, $downloadManager);
}
if ($packageName) {
$package = $this->selectPackage($io, $packageName, $version);
if (!$package) {
return 1;
}
} else {
$package = $this->getComposer()->getPackage();
}
$io->writeError('<info>Creating the archive into "'.$dest.'".</info>');
$packagePath = $archiveManager->archive($package, $format, $dest, $fileName, $ignoreFilters);
$fs = new Filesystem;
$shortPath = $fs->findShortestPath(getcwd(), $packagePath, true);
$io->writeError('Created: ', false);
$io->write(strlen($shortPath) < strlen($packagePath) ? $shortPath : $packagePath);
return 0;
}
protected function selectPackage(IOInterface $io, $packageName, $version = null)
{
$io->writeError('<info>Searching for the specified package.</info>');
if ($composer = $this->getComposer(false)) {
$localRepo = $composer->getRepositoryManager()->getLocalRepository();
$repo = new CompositeRepository(array_merge(array($localRepo), $composer->getRepositoryManager()->getRepositories()));
} else {
$defaultRepos = RepositoryFactory::defaultRepos($this->getIO());
$io->writeError('No composer.json found in the current directory, searching packages from ' . implode(', ', array_keys($defaultRepos)));
$repo = new CompositeRepository($defaultRepos);
}
$packages = $repo->findPackages($packageName, $version);
if (count($packages) > 1) {
$package = reset($packages);
$io->writeError('<info>Found multiple matches, selected '.$package->getPrettyString().'.</info>');
$io->writeError('Alternatives were '.implode(', ', array_map(function ($p) {
return $p->getPrettyString();
}, $packages)).'.');
$io->writeError('<comment>Please use a more specific constraint to pick a different package.</comment>');
} elseif ($packages) {
$package = reset($packages);
$io->writeError('<info>Found an exact match '.$package->getPrettyString().'.</info>');
} else {
$io->writeError('<error>Could not find a package matching '.$packageName.'.</error>');
return false;
}
return $package;
}
}
@@ -0,0 +1,181 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Command;
use Composer\Composer;
use Composer\Config;
use Composer\Console\Application;
use Composer\Factory;
use Composer\IO\IOInterface;
use Composer\IO\NullIO;
use Composer\Plugin\PreCommandRunEvent;
use Composer\Plugin\PluginEvents;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Command\Command;
/**
* Base class for Composer commands
*
* @author Ryan Weaver <ryan@knplabs.com>
* @author Konstantin Kudryashov <ever.zet@gmail.com>
*/
abstract class BaseCommand extends Command
{
/**
* @var Composer|null
*/
private $composer;
/**
* @var IOInterface
*/
private $io;
/**
* @param bool $required
* @param bool|null $disablePlugins
* @throws \RuntimeException
* @return Composer
*/
public function getComposer($required = true, $disablePlugins = null)
{
if (null === $this->composer) {
$application = $this->getApplication();
if ($application instanceof Application) {
/* @var $application Application */
$this->composer = $application->getComposer($required, $disablePlugins);
} elseif ($required) {
throw new \RuntimeException(
'Could not create a Composer\Composer instance, you must inject '.
'one if this command is not used with a Composer\Console\Application instance'
);
}
}
return $this->composer;
}
/**
* @param Composer $composer
*/
public function setComposer(Composer $composer)
{
$this->composer = $composer;
}
/**
* Removes the cached composer instance
*/
public function resetComposer()
{
$this->composer = null;
$this->getApplication()->resetComposer();
}
/**
* Whether or not this command is meant to call another command.
*
* This is mainly needed to avoid duplicated warnings messages.
*
* @return bool
*/
public function isProxyCommand()
{
return false;
}
/**
* @return IOInterface
*/
public function getIO()
{
if (null === $this->io) {
$application = $this->getApplication();
if ($application instanceof Application) {
/* @var $application Application */
$this->io = $application->getIO();
} else {
$this->io = new NullIO();
}
}
return $this->io;
}
/**
* @param IOInterface $io
*/
public function setIO(IOInterface $io)
{
$this->io = $io;
}
/**
* {@inheritDoc}
*/
protected function initialize(InputInterface $input, OutputInterface $output)
{
// initialize a plugin-enabled Composer instance, either local or global
$disablePlugins = $input->hasParameterOption('--no-plugins');
$composer = $this->getComposer(false, $disablePlugins);
if (null === $composer) {
$composer = Factory::createGlobal($this->getIO(), $disablePlugins);
}
if ($composer) {
$preCommandRunEvent = new PreCommandRunEvent(PluginEvents::PRE_COMMAND_RUN, $input, $this->getName());
$composer->getEventDispatcher()->dispatch($preCommandRunEvent->getName(), $preCommandRunEvent);
}
if (true === $input->hasParameterOption(array('--no-ansi')) && $input->hasOption('no-progress')) {
$input->setOption('no-progress', true);
}
parent::initialize($input, $output);
}
/**
* Returns preferSource and preferDist values based on the configuration.
*
* @param Config $config
* @param InputInterface $input
* @param bool $keepVcsRequiresPreferSource
*
* @return bool[] An array composed of the preferSource and preferDist values
*/
protected function getPreferredInstallOptions(Config $config, InputInterface $input, $keepVcsRequiresPreferSource = false)
{
$preferSource = false;
$preferDist = false;
switch ($config->get('preferred-install')) {
case 'source':
$preferSource = true;
break;
case 'dist':
$preferDist = true;
break;
case 'auto':
default:
// noop
break;
}
if ($input->getOption('prefer-source') || $input->getOption('prefer-dist') || ($keepVcsRequiresPreferSource && $input->hasOption('keep-vcs') && $input->getOption('keep-vcs'))) {
$preferSource = $input->getOption('prefer-source') || ($keepVcsRequiresPreferSource && $input->hasOption('keep-vcs') && $input->getOption('keep-vcs'));
$preferDist = $input->getOption('prefer-dist');
}
return array($preferSource, $preferDist);
}
}
@@ -0,0 +1,255 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Command;
use Composer\DependencyResolver\Pool;
use Composer\Package\Link;
use Composer\Package\PackageInterface;
use Composer\Repository\ArrayRepository;
use Composer\Repository\CompositeRepository;
use Composer\Repository\PlatformRepository;
use Composer\Repository\RepositoryFactory;
use Composer\Plugin\CommandEvent;
use Composer\Plugin\PluginEvents;
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
use Composer\Package\Version\VersionParser;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Base implementation for commands mapping dependency relationships.
*
* @author Niels Keurentjes <niels.keurentjes@omines.com>
*/
class BaseDependencyCommand extends BaseCommand
{
const ARGUMENT_PACKAGE = 'package';
const ARGUMENT_CONSTRAINT = 'constraint';
const OPTION_RECURSIVE = 'recursive';
const OPTION_TREE = 'tree';
protected $colors;
/**
* Set common options and arguments.
*/
protected function configure()
{
$this->setDefinition(array(
new InputArgument(self::ARGUMENT_PACKAGE, InputArgument::REQUIRED, 'Package to inspect'),
new InputArgument(self::ARGUMENT_CONSTRAINT, InputArgument::OPTIONAL, 'Optional version constraint', '*'),
new InputOption(self::OPTION_RECURSIVE, 'r', InputOption::VALUE_NONE, 'Recursively resolves up to the root package'),
new InputOption(self::OPTION_TREE, 't', InputOption::VALUE_NONE, 'Prints the results as a nested tree'),
));
}
/**
* Execute the command.
*
* @param InputInterface $input
* @param OutputInterface $output
* @param bool $inverted Whether to invert matching process (why-not vs why behaviour)
* @return int Exit code of the operation.
*/
protected function doExecute(InputInterface $input, OutputInterface $output, $inverted = false)
{
// Emit command event on startup
$composer = $this->getComposer();
$commandEvent = new CommandEvent(PluginEvents::COMMAND, $this->getName(), $input, $output);
$composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
// Prepare repositories and set up a pool
$platformOverrides = $composer->getConfig()->get('platform') ?: array();
$repository = new CompositeRepository(array(
new ArrayRepository(array($composer->getPackage())),
$composer->getRepositoryManager()->getLocalRepository(),
new PlatformRepository(array(), $platformOverrides),
));
$pool = new Pool();
$pool->addRepository($repository);
// Parse package name and constraint
list($needle, $textConstraint) = array_pad(
explode(':', $input->getArgument(self::ARGUMENT_PACKAGE)),
2,
$input->getArgument(self::ARGUMENT_CONSTRAINT)
);
// Find packages that are or provide the requested package first
$packages = $pool->whatProvides(strtolower($needle));
if (empty($packages)) {
throw new \InvalidArgumentException(sprintf('Could not find package "%s" in your project', $needle));
}
// If the version we ask for is not installed then we need to locate it in remote repos and add it.
// This is needed for why-not to resolve conflicts from an uninstalled version against installed packages.
if (!$repository->findPackage($needle, $textConstraint)) {
$defaultRepos = new CompositeRepository(RepositoryFactory::defaultRepos($this->getIO()));
if ($match = $defaultRepos->findPackage($needle, $textConstraint)) {
$repository->addRepository(new ArrayRepository(array(clone $match)));
}
}
// Include replaced packages for inverted lookups as they are then the actual starting point to consider
$needles = array($needle);
if ($inverted) {
foreach ($packages as $package) {
$needles = array_merge($needles, array_map(function (Link $link) {
return $link->getTarget();
}, $package->getReplaces()));
}
}
// Parse constraint if one was supplied
if ('*' !== $textConstraint) {
$versionParser = new VersionParser();
$constraint = $versionParser->parseConstraints($textConstraint);
} else {
$constraint = null;
}
// Parse rendering options
$renderTree = $input->getOption(self::OPTION_TREE);
$recursive = $renderTree || $input->getOption(self::OPTION_RECURSIVE);
// Resolve dependencies
$results = $repository->getDependents($needles, $constraint, $inverted, $recursive);
if (empty($results)) {
$extra = (null !== $constraint) ? sprintf(' in versions %smatching %s', $inverted ? 'not ' : '', $textConstraint) : '';
$this->getIO()->writeError(sprintf(
'<info>There is no installed package depending on "%s"%s</info>',
$needle,
$extra
));
} elseif ($renderTree) {
$this->initStyles($output);
$root = $packages[0];
$this->getIO()->write(sprintf('<info>%s</info> %s %s', $root->getPrettyName(), $root->getPrettyVersion(), $root->getDescription()));
$this->printTree($results);
} else {
$this->printTable($output, $results);
}
return 0;
}
/**
* Assembles and prints a bottom-up table of the dependencies.
*
* @param OutputInterface $output
* @param array $results
*/
protected function printTable(OutputInterface $output, $results)
{
$table = array();
$doubles = array();
do {
$queue = array();
$rows = array();
foreach ($results as $result) {
/**
* @var PackageInterface $package
* @var Link $link
*/
list($package, $link, $children) = $result;
$unique = (string) $link;
if (isset($doubles[$unique])) {
continue;
}
$doubles[$unique] = true;
$version = (strpos($package->getPrettyVersion(), 'No version set') === 0) ? '-' : $package->getPrettyVersion();
$rows[] = array($package->getPrettyName(), $version, $link->getDescription(), sprintf('%s (%s)', $link->getTarget(), $link->getPrettyConstraint()));
if ($children) {
$queue = array_merge($queue, $children);
}
}
$results = $queue;
$table = array_merge($rows, $table);
} while (!empty($results));
// Render table
$renderer = new Table($output);
$renderer->setStyle('compact');
$rendererStyle = $renderer->getStyle();
$rendererStyle->setVerticalBorderChar('');
$rendererStyle->setCellRowContentFormat('%s ');
$renderer->setRows($table)->render();
}
/**
* Init styles for tree
*
* @param OutputInterface $output
*/
protected function initStyles(OutputInterface $output)
{
$this->colors = array(
'green',
'yellow',
'cyan',
'magenta',
'blue',
);
foreach ($this->colors as $color) {
$style = new OutputFormatterStyle($color);
$output->getFormatter()->setStyle($color, $style);
}
}
/**
* Recursively prints a tree of the selected results.
*
* @param array $results Results to be printed at this level.
* @param string $prefix Prefix of the current tree level.
* @param int $level Current level of recursion.
*/
protected function printTree($results, $prefix = '', $level = 1)
{
$count = count($results);
$idx = 0;
foreach ($results as $result) {
/**
* @var PackageInterface $package
* @var Link $link
* @var array|bool $children
*/
list($package, $link, $children) = $result;
$color = $this->colors[$level % count($this->colors)];
$prevColor = $this->colors[($level - 1) % count($this->colors)];
$isLast = (++$idx == $count);
$versionText = (strpos($package->getPrettyVersion(), 'No version set') === 0) ? '' : $package->getPrettyVersion();
$packageText = rtrim(sprintf('<%s>%s</%1$s> %s', $color, $package->getPrettyName(), $versionText));
$linkText = sprintf('%s <%s>%s</%2$s> %s', $link->getDescription(), $prevColor, $link->getTarget(), $link->getPrettyConstraint());
$circularWarn = $children === false ? '(circular dependency aborted here)' : '';
$this->writeTreeLine(rtrim(sprintf("%s%s%s (%s) %s", $prefix, $isLast ? '└──' : '├──', $packageText, $linkText, $circularWarn)));
if ($children) {
$this->printTree($children, $prefix . ($isLast ? ' ' : '│ '), $level + 1);
}
}
}
private function writeTreeLine($line)
{
$io = $this->getIO();
if (!$io->isDecorated()) {
$line = str_replace(array('└', '├', '──', '│'), array('`-', '|-', '-', '|'), $line);
}
$io->write($line);
}
}
@@ -0,0 +1,162 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Command;
use Composer\Package\Link;
use Composer\Package\PackageInterface;
use Composer\Semver\Constraint\Constraint;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Composer\Repository\PlatformRepository;
class CheckPlatformReqsCommand extends BaseCommand
{
protected function configure()
{
$this->setName('check-platform-reqs')
->setDescription('Check that platform requirements are satisfied.')
->setDefinition(array(
new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables checking of require-dev packages requirements.'),
))
->setHelp(
<<<EOT
Checks that your PHP and extensions versions match the platform requirements of the installed packages.
Unlike update/install, this command will ignore config.platform settings and check the real platform packages so you can be certain you have the required platform dependencies.
<info>php composer.phar check-platform-reqs</info>
EOT
);
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$composer = $this->getComposer();
$requires = $composer->getPackage()->getRequires();
if ($input->getOption('no-dev')) {
$dependencies = $composer->getLocker()->getLockedRepository(!$input->getOption('no-dev'))->getPackages();
} else {
$dependencies = $composer->getRepositoryManager()->getLocalRepository()->getPackages();
// fallback to lockfile if installed repo is empty
if (!$dependencies) {
$dependencies = $composer->getLocker()->getLockedRepository(true)->getPackages();
}
$requires += $composer->getPackage()->getDevRequires();
}
foreach ($requires as $require => $link) {
$requires[$require] = array($link);
}
foreach ($dependencies as $package) {
foreach ($package->getRequires() as $require => $link) {
$requires[$require][] = $link;
}
}
ksort($requires);
$platformRepo = new PlatformRepository(array(), array());
$currentPlatformPackages = $platformRepo->getPackages();
$currentPlatformPackageMap = array();
/**
* @var PackageInterface $currentPlatformPackage
*/
foreach ($currentPlatformPackages as $currentPlatformPackage) {
$currentPlatformPackageMap[$currentPlatformPackage->getName()] = $currentPlatformPackage;
}
$results = array();
$exitCode = 0;
/**
* @var Link[] $links
*/
foreach ($requires as $require => $links) {
if (preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $require)) {
if (isset($currentPlatformPackageMap[$require])) {
$pass = true;
$version = $currentPlatformPackageMap[$require]->getVersion();
foreach ($links as $link) {
if (!$link->getConstraint()->matches(new Constraint('=', $version))) {
$results[] = array(
$currentPlatformPackageMap[$require]->getPrettyName(),
$currentPlatformPackageMap[$require]->getPrettyVersion(),
$link,
'<error>failed</error>',
);
$pass = false;
$exitCode = max($exitCode, 1);
}
}
if ($pass) {
$results[] = array(
$currentPlatformPackageMap[$require]->getPrettyName(),
$currentPlatformPackageMap[$require]->getPrettyVersion(),
null,
'<info>success</info>',
);
}
} else {
$results[] = array(
$require,
'n/a',
$links[0],
'<error>missing</error>',
);
$exitCode = max($exitCode, 2);
}
}
}
$this->printTable($output, $results);
return $exitCode;
}
protected function printTable(OutputInterface $output, $results)
{
$table = array();
$rows = array();
foreach ($results as $result) {
/**
* @var Link|null $link
*/
list($platformPackage, $version, $link, $status) = $result;
$rows[] = array(
$platformPackage,
$version,
$link ? sprintf('%s %s %s (%s)', $link->getSource(), $link->getDescription(), $link->getTarget(), $link->getPrettyConstraint()) : '',
$status,
);
}
$table = array_merge($rows, $table);
// Render table
$renderer = new Table($output);
$renderer->setStyle('compact');
$rendererStyle = $renderer->getStyle();
$rendererStyle->setVerticalBorderChar('');
$rendererStyle->setCellRowContentFormat('%s ');
$renderer->setRows($table)->render();
}
}
@@ -0,0 +1,76 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Command;
use Composer\Cache;
use Composer\Factory;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* @author David Neilsen <petah.p@gmail.com>
*/
class ClearCacheCommand extends BaseCommand
{
protected function configure()
{
$this
->setName('clear-cache')
->setAliases(array('clearcache'))
->setDescription('Clears composer\'s internal package cache.')
->setHelp(
<<<EOT
The <info>clear-cache</info> deletes all cached packages from composer's
cache directory.
Read more at https://getcomposer.org/doc/03-cli.md#clear-cache-clearcache-
EOT
)
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$config = Factory::createConfig();
$io = $this->getIO();
$cachePaths = array(
'cache-vcs-dir' => $config->get('cache-vcs-dir'),
'cache-repo-dir' => $config->get('cache-repo-dir'),
'cache-files-dir' => $config->get('cache-files-dir'),
'cache-dir' => $config->get('cache-dir'),
);
foreach ($cachePaths as $key => $cachePath) {
$cachePath = realpath($cachePath);
if (!$cachePath) {
$io->writeError("<info>Cache directory does not exist ($key): $cachePath</info>");
continue;
}
$cache = new Cache($io, $cachePath);
if (!$cache->isEnabled()) {
$io->writeError("<info>Cache is not enabled ($key): $cachePath</info>");
continue;
}
$io->writeError("<info>Clearing cache ($key): $cachePath</info>");
$cache->clear();
}
$io->writeError('<info>All caches cleared.</info>');
return 0;
}
}
@@ -0,0 +1,755 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Command;
use Composer\Util\Platform;
use Composer\Util\Silencer;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Composer\Config;
use Composer\Config\JsonConfigSource;
use Composer\Factory;
use Composer\IO\IOInterface;
use Composer\Json\JsonFile;
use Composer\Semver\VersionParser;
use Composer\Package\BasePackage;
/**
* @author Joshua Estes <Joshua.Estes@iostudio.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
class ConfigCommand extends BaseCommand
{
/**
* @var Config
*/
protected $config;
/**
* @var JsonFile
*/
protected $configFile;
/**
* @var JsonConfigSource
*/
protected $configSource;
/**
* @var JsonFile
*/
protected $authConfigFile;
/**
* @var JsonConfigSource
*/
protected $authConfigSource;
/**
* {@inheritDoc}
*/
protected function configure()
{
$this
->setName('config')
->setDescription('Sets config options.')
->setDefinition(array(
new InputOption('global', 'g', InputOption::VALUE_NONE, 'Apply command to the global config file'),
new InputOption('editor', 'e', InputOption::VALUE_NONE, 'Open editor'),
new InputOption('auth', 'a', InputOption::VALUE_NONE, 'Affect auth config file (only used for --editor)'),
new InputOption('unset', null, InputOption::VALUE_NONE, 'Unset the given setting-key'),
new InputOption('list', 'l', InputOption::VALUE_NONE, 'List configuration settings'),
new InputOption('file', 'f', InputOption::VALUE_REQUIRED, 'If you want to choose a different composer.json or config.json'),
new InputOption('absolute', null, InputOption::VALUE_NONE, 'Returns absolute paths when fetching *-dir config values instead of relative'),
new InputArgument('setting-key', null, 'Setting key'),
new InputArgument('setting-value', InputArgument::IS_ARRAY, 'Setting value'),
))
->setHelp(
<<<EOT
This command allows you to edit composer config settings and repositories
in either the local composer.json file or the global config.json file.
Additionally it lets you edit most properties in the local composer.json.
To set a config setting:
<comment>%command.full_name% bin-dir bin/</comment>
To read a config setting:
<comment>%command.full_name% bin-dir</comment>
Outputs: <info>bin</info>
To edit the global config.json file:
<comment>%command.full_name% --global</comment>
To add a repository:
<comment>%command.full_name% repositories.foo vcs https://bar.com</comment>
To remove a repository (repo is a short alias for repositories):
<comment>%command.full_name% --unset repo.foo</comment>
To disable packagist:
<comment>%command.full_name% repo.packagist false</comment>
You can alter repositories in the global config.json file by passing in the
<info>--global</info> option.
To edit the file in an external editor:
<comment>%command.full_name% --editor</comment>
To choose your editor you can set the "EDITOR" env variable.
To get a list of configuration values in the file:
<comment>%command.full_name% --list</comment>
You can always pass more than one option. As an example, if you want to edit the
global config.json file.
<comment>%command.full_name% --editor --global</comment>
Read more at https://getcomposer.org/doc/03-cli.md#config
EOT
)
;
}
/**
* {@inheritDoc}
*/
protected function initialize(InputInterface $input, OutputInterface $output)
{
parent::initialize($input, $output);
if ($input->getOption('global') && null !== $input->getOption('file')) {
throw new \RuntimeException('--file and --global can not be combined');
}
$io = $this->getIO();
$this->config = Factory::createConfig($io);
// Get the local composer.json, global config.json, or if the user
// passed in a file to use
$configFile = $input->getOption('global')
? ($this->config->get('home') . '/config.json')
: ($input->getOption('file') ?: Factory::getComposerFile());
// Create global composer.json if this was invoked using `composer global config`
if (
($configFile === 'composer.json' || $configFile === './composer.json')
&& !file_exists($configFile)
&& realpath(getcwd()) === realpath($this->config->get('home'))
) {
file_put_contents($configFile, "{\n}\n");
}
$this->configFile = new JsonFile($configFile, null, $io);
$this->configSource = new JsonConfigSource($this->configFile);
$authConfigFile = $input->getOption('global')
? ($this->config->get('home') . '/auth.json')
: dirname(realpath($configFile)) . '/auth.json';
$this->authConfigFile = new JsonFile($authConfigFile, null, $io);
$this->authConfigSource = new JsonConfigSource($this->authConfigFile, true);
// Initialize the global file if it's not there, ignoring any warnings or notices
if ($input->getOption('global') && !$this->configFile->exists()) {
touch($this->configFile->getPath());
$this->configFile->write(array('config' => new \ArrayObject));
Silencer::call('chmod', $this->configFile->getPath(), 0600);
}
if ($input->getOption('global') && !$this->authConfigFile->exists()) {
touch($this->authConfigFile->getPath());
$this->authConfigFile->write(array('bitbucket-oauth' => new \ArrayObject, 'github-oauth' => new \ArrayObject, 'gitlab-oauth' => new \ArrayObject, 'gitlab-token' => new \ArrayObject, 'http-basic' => new \ArrayObject));
Silencer::call('chmod', $this->authConfigFile->getPath(), 0600);
}
if (!$this->configFile->exists()) {
throw new \RuntimeException(sprintf('File "%s" cannot be found in the current directory', $configFile));
}
}
/**
* {@inheritDoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
// Open file in editor
if ($input->getOption('editor')) {
$editor = escapeshellcmd(getenv('EDITOR'));
if (!$editor) {
if (Platform::isWindows()) {
$editor = 'notepad';
} else {
foreach (array('editor', 'vim', 'vi', 'nano', 'pico', 'ed') as $candidate) {
if (exec('which '.$candidate)) {
$editor = $candidate;
break;
}
}
}
}
$file = $input->getOption('auth') ? $this->authConfigFile->getPath() : $this->configFile->getPath();
system($editor . ' ' . $file . (Platform::isWindows() ? '' : ' > `tty`'));
return 0;
}
if (!$input->getOption('global')) {
$this->config->merge($this->configFile->read());
$this->config->merge(array('config' => $this->authConfigFile->exists() ? $this->authConfigFile->read() : array()));
}
// List the configuration of the file settings
if ($input->getOption('list')) {
$this->listConfiguration($this->config->all(), $this->config->raw(), $output);
return 0;
}
$settingKey = $input->getArgument('setting-key');
if (!$settingKey) {
return 0;
}
// If the user enters in a config variable, parse it and save to file
if (array() !== $input->getArgument('setting-value') && $input->getOption('unset')) {
throw new \RuntimeException('You can not combine a setting value with --unset');
}
// show the value if no value is provided
if (array() === $input->getArgument('setting-value') && !$input->getOption('unset')) {
$properties = array('name', 'type', 'description', 'homepage', 'version', 'minimum-stability', 'prefer-stable', 'keywords', 'license', 'extra');
$rawData = $this->configFile->read();
$data = $this->config->all();
if (preg_match('/^repos?(?:itories)?(?:\.(.+))?/', $settingKey, $matches)) {
if (!isset($matches[1]) || $matches[1] === '') {
$value = isset($data['repositories']) ? $data['repositories'] : array();
} else {
if (!isset($data['repositories'][$matches[1]])) {
throw new \InvalidArgumentException('There is no '.$matches[1].' repository defined');
}
$value = $data['repositories'][$matches[1]];
}
} elseif (strpos($settingKey, '.')) {
$bits = explode('.', $settingKey);
if ($bits[0] === 'extra') {
$data = $rawData;
} else {
$data = $data['config'];
}
$match = false;
foreach ($bits as $bit) {
$key = isset($key) ? $key.'.'.$bit : $bit;
$match = false;
if (isset($data[$key])) {
$match = true;
$data = $data[$key];
unset($key);
}
}
if (!$match) {
throw new \RuntimeException($settingKey.' is not defined.');
}
$value = $data;
} elseif (isset($data['config'][$settingKey])) {
$value = $this->config->get($settingKey, $input->getOption('absolute') ? 0 : Config::RELATIVE_PATHS);
} elseif (in_array($settingKey, $properties, true) && isset($rawData[$settingKey])) {
$value = $rawData[$settingKey];
} else {
throw new \RuntimeException($settingKey.' is not defined');
}
if (is_array($value)) {
$value = json_encode($value);
}
$this->getIO()->write($value, true, IOInterface::QUIET);
return 0;
}
$values = $input->getArgument('setting-value'); // what the user is trying to add/change
$booleanValidator = function ($val) {
return in_array($val, array('true', 'false', '1', '0'), true);
};
$booleanNormalizer = function ($val) {
return $val !== 'false' && (bool) $val;
};
// handle config values
$uniqueConfigValues = array(
'process-timeout' => array('is_numeric', 'intval'),
'use-include-path' => array($booleanValidator, $booleanNormalizer),
'use-github-api' => array($booleanValidator, $booleanNormalizer),
'preferred-install' => array(
function ($val) {
return in_array($val, array('auto', 'source', 'dist'), true);
},
function ($val) {
return $val;
},
),
'store-auths' => array(
function ($val) {
return in_array($val, array('true', 'false', 'prompt'), true);
},
function ($val) {
if ('prompt' === $val) {
return 'prompt';
}
return $val !== 'false' && (bool) $val;
},
),
'notify-on-install' => array($booleanValidator, $booleanNormalizer),
'vendor-dir' => array('is_string', function ($val) {
return $val;
}),
'bin-dir' => array('is_string', function ($val) {
return $val;
}),
'archive-dir' => array('is_string', function ($val) {
return $val;
}),
'archive-format' => array('is_string', function ($val) {
return $val;
}),
'data-dir' => array('is_string', function ($val) {
return $val;
}),
'cache-dir' => array('is_string', function ($val) {
return $val;
}),
'cache-files-dir' => array('is_string', function ($val) {
return $val;
}),
'cache-repo-dir' => array('is_string', function ($val) {
return $val;
}),
'cache-vcs-dir' => array('is_string', function ($val) {
return $val;
}),
'cache-ttl' => array('is_numeric', 'intval'),
'cache-files-ttl' => array('is_numeric', 'intval'),
'cache-files-maxsize' => array(
function ($val) {
return preg_match('/^\s*([0-9.]+)\s*(?:([kmg])(?:i?b)?)?\s*$/i', $val) > 0;
},
function ($val) {
return $val;
},
),
'bin-compat' => array(
function ($val) {
return in_array($val, array('auto', 'full'));
},
function ($val) {
return $val;
},
),
'discard-changes' => array(
function ($val) {
return in_array($val, array('stash', 'true', 'false', '1', '0'), true);
},
function ($val) {
if ('stash' === $val) {
return 'stash';
}
return $val !== 'false' && (bool) $val;
},
),
'autoloader-suffix' => array('is_string', function ($val) {
return $val === 'null' ? null : $val;
}),
'sort-packages' => array($booleanValidator, $booleanNormalizer),
'optimize-autoloader' => array($booleanValidator, $booleanNormalizer),
'classmap-authoritative' => array($booleanValidator, $booleanNormalizer),
'apcu-autoloader' => array($booleanValidator, $booleanNormalizer),
'prepend-autoloader' => array($booleanValidator, $booleanNormalizer),
'disable-tls' => array($booleanValidator, $booleanNormalizer),
'secure-http' => array($booleanValidator, $booleanNormalizer),
'cafile' => array(
function ($val) {
return file_exists($val) && is_readable($val);
},
function ($val) {
return $val === 'null' ? null : $val;
},
),
'capath' => array(
function ($val) {
return is_dir($val) && is_readable($val);
},
function ($val) {
return $val === 'null' ? null : $val;
},
),
'github-expose-hostname' => array($booleanValidator, $booleanNormalizer),
'htaccess-protect' => array($booleanValidator, $booleanNormalizer),
);
$multiConfigValues = array(
'github-protocols' => array(
function ($vals) {
if (!is_array($vals)) {
return 'array expected';
}
foreach ($vals as $val) {
if (!in_array($val, array('git', 'https', 'ssh'))) {
return 'valid protocols include: git, https, ssh';
}
}
return true;
},
function ($vals) {
return $vals;
},
),
'github-domains' => array(
function ($vals) {
if (!is_array($vals)) {
return 'array expected';
}
return true;
},
function ($vals) {
return $vals;
},
),
'gitlab-domains' => array(
function ($vals) {
if (!is_array($vals)) {
return 'array expected';
}
return true;
},
function ($vals) {
return $vals;
},
),
);
if ($input->getOption('unset') && (isset($uniqueConfigValues[$settingKey]) || isset($multiConfigValues[$settingKey]))) {
if ($settingKey === 'disable-tls' && $this->config->get('disable-tls')) {
$this->getIO()->writeError('<info>You are now running Composer with SSL/TLS protection enabled.</info>');
}
$this->configSource->removeConfigSetting($settingKey);
return 0;
}
if (isset($uniqueConfigValues[$settingKey])) {
$this->handleSingleValue($settingKey, $uniqueConfigValues[$settingKey], $values, 'addConfigSetting');
return 0;
}
if (isset($multiConfigValues[$settingKey])) {
$this->handleMultiValue($settingKey, $multiConfigValues[$settingKey], $values, 'addConfigSetting');
return 0;
}
// handle properties
$uniqueProps = array(
'name' => array('is_string', function ($val) {
return $val;
}),
'type' => array('is_string', function ($val) {
return $val;
}),
'description' => array('is_string', function ($val) {
return $val;
}),
'homepage' => array('is_string', function ($val) {
return $val;
}),
'version' => array('is_string', function ($val) {
return $val;
}),
'minimum-stability' => array(
function ($val) {
return isset(BasePackage::$stabilities[VersionParser::normalizeStability($val)]);
},
function ($val) {
return VersionParser::normalizeStability($val);
},
),
'prefer-stable' => array($booleanValidator, $booleanNormalizer),
);
$multiProps = array(
'keywords' => array(
function ($vals) {
if (!is_array($vals)) {
return 'array expected';
}
return true;
},
function ($vals) {
return $vals;
},
),
'license' => array(
function ($vals) {
if (!is_array($vals)) {
return 'array expected';
}
return true;
},
function ($vals) {
return $vals;
},
),
);
if ($input->getOption('global') && (isset($uniqueProps[$settingKey]) || isset($multiProps[$settingKey]) || substr($settingKey, 0, 6) === 'extra.')) {
throw new \InvalidArgumentException('The '.$settingKey.' property can not be set in the global config.json file. Use `composer global config` to apply changes to the global composer.json');
}
if ($input->getOption('unset') && (isset($uniqueProps[$settingKey]) || isset($multiProps[$settingKey]))) {
$this->configSource->removeProperty($settingKey);
return 0;
}
if (isset($uniqueProps[$settingKey])) {
$this->handleSingleValue($settingKey, $uniqueProps[$settingKey], $values, 'addProperty');
return 0;
}
if (isset($multiProps[$settingKey])) {
$this->handleMultiValue($settingKey, $multiProps[$settingKey], $values, 'addProperty');
return 0;
}
// handle repositories
if (preg_match('/^repos?(?:itories)?\.(.+)/', $settingKey, $matches)) {
if ($input->getOption('unset')) {
$this->configSource->removeRepository($matches[1]);
return 0;
}
if (2 === count($values)) {
$this->configSource->addRepository($matches[1], array(
'type' => $values[0],
'url' => $values[1],
));
return 0;
}
if (1 === count($values)) {
$value = strtolower($values[0]);
if (true === $booleanValidator($value)) {
if (false === $booleanNormalizer($value)) {
$this->configSource->addRepository($matches[1], false);
return 0;
}
} else {
$value = JsonFile::parseJson($values[0]);
$this->configSource->addRepository($matches[1], $value);
return 0;
}
}
throw new \RuntimeException('You must pass the type and a url. Example: php composer.phar config repositories.foo vcs https://bar.com');
}
// handle extra
if (preg_match('/^extra\.(.+)/', $settingKey, $matches)) {
if ($input->getOption('unset')) {
$this->configSource->removeProperty($settingKey);
return 0;
}
$this->configSource->addProperty($settingKey, $values[0]);
return 0;
}
// handle platform
if (preg_match('/^platform\.(.+)/', $settingKey, $matches)) {
if ($input->getOption('unset')) {
$this->configSource->removeConfigSetting($settingKey);
return 0;
}
$this->configSource->addConfigSetting($settingKey, $values[0]);
return 0;
}
if ($settingKey === 'platform' && $input->getOption('unset')) {
$this->configSource->removeConfigSetting($settingKey);
return 0;
}
// handle auth
if (preg_match('/^(bitbucket-oauth|github-oauth|gitlab-oauth|gitlab-token|http-basic)\.(.+)/', $settingKey, $matches)) {
if ($input->getOption('unset')) {
$this->authConfigSource->removeConfigSetting($matches[1].'.'.$matches[2]);
$this->configSource->removeConfigSetting($matches[1].'.'.$matches[2]);
return 0;
}
if ($matches[1] === 'bitbucket-oauth') {
if (2 !== count($values)) {
throw new \RuntimeException('Expected two arguments (consumer-key, consumer-secret), got '.count($values));
}
$this->configSource->removeConfigSetting($matches[1].'.'.$matches[2]);
$this->authConfigSource->addConfigSetting($matches[1].'.'.$matches[2], array('consumer-key' => $values[0], 'consumer-secret' => $values[1]));
} elseif (in_array($matches[1], array('github-oauth', 'gitlab-oauth', 'gitlab-token'), true)) {
if (1 !== count($values)) {
throw new \RuntimeException('Too many arguments, expected only one token');
}
$this->configSource->removeConfigSetting($matches[1].'.'.$matches[2]);
$this->authConfigSource->addConfigSetting($matches[1].'.'.$matches[2], $values[0]);
} elseif ($matches[1] === 'http-basic') {
if (2 !== count($values)) {
throw new \RuntimeException('Expected two arguments (username, password), got '.count($values));
}
$this->configSource->removeConfigSetting($matches[1].'.'.$matches[2]);
$this->authConfigSource->addConfigSetting($matches[1].'.'.$matches[2], array('username' => $values[0], 'password' => $values[1]));
}
return 0;
}
// handle script
if (preg_match('/^scripts\.(.+)/', $settingKey, $matches)) {
if ($input->getOption('unset')) {
$this->configSource->removeProperty($settingKey);
return 0;
}
$this->configSource->addProperty($settingKey, count($values) > 1 ? $values : $values[0]);
return 0;
}
throw new \InvalidArgumentException('Setting '.$settingKey.' does not exist or is not supported by this command');
}
protected function handleSingleValue($key, array $callbacks, array $values, $method)
{
list($validator, $normalizer) = $callbacks;
if (1 !== count($values)) {
throw new \RuntimeException('You can only pass one value. Example: php composer.phar config process-timeout 300');
}
if (true !== $validation = $validator($values[0])) {
throw new \RuntimeException(sprintf(
'"%s" is an invalid value'.($validation ? ' ('.$validation.')' : ''),
$values[0]
));
}
$normalizedValue = $normalizer($values[0]);
if ($key === 'disable-tls') {
if (!$normalizedValue && $this->config->get('disable-tls')) {
$this->getIO()->writeError('<info>You are now running Composer with SSL/TLS protection enabled.</info>');
} elseif ($normalizedValue && !$this->config->get('disable-tls')) {
$this->getIO()->writeError('<warning>You are now running Composer with SSL/TLS protection disabled.</warning>');
}
}
return call_user_func(array($this->configSource, $method), $key, $normalizedValue);
}
protected function handleMultiValue($key, array $callbacks, array $values, $method)
{
list($validator, $normalizer) = $callbacks;
if (true !== $validation = $validator($values)) {
throw new \RuntimeException(sprintf(
'%s is an invalid value'.($validation ? ' ('.$validation.')' : ''),
json_encode($values)
));
}
return call_user_func(array($this->configSource, $method), $key, $normalizer($values));
}
/**
* Display the contents of the file in a pretty formatted way
*
* @param array $contents
* @param array $rawContents
* @param OutputInterface $output
* @param string|null $k
*/
protected function listConfiguration(array $contents, array $rawContents, OutputInterface $output, $k = null)
{
$origK = $k;
$io = $this->getIO();
foreach ($contents as $key => $value) {
if ($k === null && !in_array($key, array('config', 'repositories'))) {
continue;
}
$rawVal = isset($rawContents[$key]) ? $rawContents[$key] : null;
if (is_array($value) && (!is_numeric(key($value)) || ($key === 'repositories' && null === $k))) {
$k .= preg_replace('{^config\.}', '', $key . '.');
$this->listConfiguration($value, $rawVal, $output, $k);
$k = $origK;
continue;
}
if (is_array($value)) {
$value = array_map(function ($val) {
return is_array($val) ? json_encode($val) : $val;
}, $value);
$value = '['.implode(', ', $value).']';
}
if (is_bool($value)) {
$value = var_export($value, true);
}
if (is_string($rawVal) && $rawVal != $value) {
$io->write('[<comment>' . $k . $key . '</comment>] <info>' . $rawVal . ' (' . $value . ')</info>', true, IOInterface::QUIET);
} else {
$io->write('[<comment>' . $k . $key . '</comment>] <info>' . $value . '</info>', true, IOInterface::QUIET);
}
}
}
}
@@ -0,0 +1,387 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Command;
use Composer\Config;
use Composer\Factory;
use Composer\Installer;
use Composer\Installer\ProjectInstaller;
use Composer\Installer\InstallationManager;
use Composer\Installer\SuggestedPackagesReporter;
use Composer\IO\IOInterface;
use Composer\Package\BasePackage;
use Composer\DependencyResolver\Pool;
use Composer\DependencyResolver\Operation\InstallOperation;
use Composer\Package\Version\VersionSelector;
use Composer\Package\AliasPackage;
use Composer\Repository\RepositoryFactory;
use Composer\Repository\CompositeRepository;
use Composer\Repository\PlatformRepository;
use Composer\Repository\InstalledFilesystemRepository;
use Composer\Script\ScriptEvents;
use Composer\Util\Silencer;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Finder\Finder;
use Composer\Json\JsonFile;
use Composer\Config\JsonConfigSource;
use Composer\Util\Filesystem;
use Composer\Package\Version\VersionParser;
/**
* Install a package as new project into new directory.
*
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Jordi Boggiano <j.boggiano@seld.be>
* @author Tobias Munk <schmunk@usrbin.de>
* @author Nils Adermann <naderman@naderman.de>
*/
class CreateProjectCommand extends BaseCommand
{
/**
* @var SuggestedPackagesReporter
*/
protected $suggestedPackagesReporter;
protected function configure()
{
$this
->setName('create-project')
->setDescription('Creates new project from a package into given directory.')
->setDefinition(array(
new InputArgument('package', InputArgument::OPTIONAL, 'Package name to be installed'),
new InputArgument('directory', InputArgument::OPTIONAL, 'Directory where the files should be created'),
new InputArgument('version', InputArgument::OPTIONAL, 'Version, will default to latest'),
new InputOption('stability', 's', InputOption::VALUE_REQUIRED, 'Minimum-stability allowed (unless a version is specified).'),
new InputOption('prefer-source', null, InputOption::VALUE_NONE, 'Forces installation from package sources when possible, including VCS information.'),
new InputOption('prefer-dist', null, InputOption::VALUE_NONE, 'Forces installation from package dist even for dev versions.'),
new InputOption('repository', null, InputOption::VALUE_REQUIRED, 'Pick a different repository (as url or json config) to look for the package.'),
new InputOption('repository-url', null, InputOption::VALUE_REQUIRED, 'DEPRECATED: Use --repository instead.'),
new InputOption('dev', null, InputOption::VALUE_NONE, 'Enables installation of require-dev packages (enabled by default, only present for BC).'),
new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables installation of require-dev packages.'),
new InputOption('no-custom-installers', null, InputOption::VALUE_NONE, 'DEPRECATED: Use no-plugins instead.'),
new InputOption('no-scripts', null, InputOption::VALUE_NONE, 'Whether to prevent execution of all defined scripts in the root package.'),
new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'),
new InputOption('no-secure-http', null, InputOption::VALUE_NONE, 'Disable the secure-http config option temporarily while installing the root package. Use at your own risk. Using this flag is a bad idea.'),
new InputOption('keep-vcs', null, InputOption::VALUE_NONE, 'Whether to prevent deleting the vcs folder.'),
new InputOption('remove-vcs', null, InputOption::VALUE_NONE, 'Whether to force deletion of the vcs folder without prompting.'),
new InputOption('no-install', null, InputOption::VALUE_NONE, 'Whether to skip installation of the package dependencies.'),
new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'),
))
->setHelp(
<<<EOT
The <info>create-project</info> command creates a new project from a given
package into a new directory. If executed without params and in a directory
with a composer.json file it installs the packages for the current project.
You can use this command to bootstrap new projects or setup a clean
version-controlled installation for developers of your project.
<info>php composer.phar create-project vendor/project target-directory [version]</info>
You can also specify the version with the package name using = or : as separator.
<info>php composer.phar create-project vendor/project:version target-directory</info>
To install unstable packages, either specify the version you want, or use the
--stability=dev (where dev can be one of RC, beta, alpha or dev).
To setup a developer workable version you should create the project using the source
controlled code by appending the <info>'--prefer-source'</info> flag.
To install a package from another repository than the default one you
can pass the <info>'--repository=https://myrepository.org'</info> flag.
Read more at https://getcomposer.org/doc/03-cli.md#create-project
EOT
)
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$config = Factory::createConfig();
$io = $this->getIO();
list($preferSource, $preferDist) = $this->getPreferredInstallOptions($config, $input, true);
if ($input->getOption('dev')) {
$io->writeError('<warning>You are using the deprecated option "dev". Dev packages are installed by default now.</warning>');
}
if ($input->getOption('no-custom-installers')) {
$io->writeError('<warning>You are using the deprecated option "no-custom-installers". Use "no-plugins" instead.</warning>');
$input->setOption('no-plugins', true);
}
return $this->installProject(
$io,
$config,
$input,
$input->getArgument('package'),
$input->getArgument('directory'),
$input->getArgument('version'),
$input->getOption('stability'),
$preferSource,
$preferDist,
!$input->getOption('no-dev'),
$input->getOption('repository') ?: $input->getOption('repository-url'),
$input->getOption('no-plugins'),
$input->getOption('no-scripts'),
$input->getOption('no-progress'),
$input->getOption('no-install'),
$input->getOption('ignore-platform-reqs'),
!$input->getOption('no-secure-http')
);
}
public function installProject(IOInterface $io, Config $config, InputInterface $input, $packageName, $directory = null, $packageVersion = null, $stability = 'stable', $preferSource = false, $preferDist = false, $installDevPackages = false, $repository = null, $disablePlugins = false, $noScripts = false, $noProgress = false, $noInstall = false, $ignorePlatformReqs = false, $secureHttp = true)
{
$oldCwd = getcwd();
// we need to manually load the configuration to pass the auth credentials to the io interface!
$io->loadConfiguration($config);
$this->suggestedPackagesReporter = new SuggestedPackagesReporter($io);
if ($packageName !== null) {
$installedFromVcs = $this->installRootPackage($io, $config, $packageName, $directory, $packageVersion, $stability, $preferSource, $preferDist, $installDevPackages, $repository, $disablePlugins, $noScripts, $noProgress, $ignorePlatformReqs, $secureHttp);
} else {
$installedFromVcs = false;
}
$composer = Factory::create($io, null, $disablePlugins);
$composer->getDownloadManager()->setOutputProgress(!$noProgress);
$fs = new Filesystem();
if ($noScripts === false) {
// dispatch event
$composer->getEventDispatcher()->dispatchScript(ScriptEvents::POST_ROOT_PACKAGE_INSTALL, $installDevPackages);
}
// use the new config including the newly installed project
$config = $composer->getConfig();
list($preferSource, $preferDist) = $this->getPreferredInstallOptions($config, $input);
// install dependencies of the created project
if ($noInstall === false) {
$installer = Installer::create($io, $composer);
$installer->setPreferSource($preferSource)
->setPreferDist($preferDist)
->setDevMode($installDevPackages)
->setRunScripts(!$noScripts)
->setIgnorePlatformRequirements($ignorePlatformReqs)
->setSuggestedPackagesReporter($this->suggestedPackagesReporter)
->setOptimizeAutoloader($config->get('optimize-autoloader'))
->setClassMapAuthoritative($config->get('classmap-authoritative'))
->setApcuAutoloader($config->get('apcu-autoloader'));
if ($disablePlugins) {
$installer->disablePlugins();
}
$status = $installer->run();
if (0 !== $status) {
return $status;
}
}
$hasVcs = $installedFromVcs;
if (
!$input->getOption('keep-vcs')
&& $installedFromVcs
&& (
$input->getOption('remove-vcs')
|| !$io->isInteractive()
|| $io->askConfirmation('<info>Do you want to remove the existing VCS (.git, .svn..) history?</info> [<comment>Y,n</comment>]? ', true)
)
) {
$finder = new Finder();
$finder->depth(0)->directories()->in(getcwd())->ignoreVCS(false)->ignoreDotFiles(false);
foreach (array('.svn', '_svn', 'CVS', '_darcs', '.arch-params', '.monotone', '.bzr', '.git', '.hg', '.fslckout', '_FOSSIL_') as $vcsName) {
$finder->name($vcsName);
}
try {
$dirs = iterator_to_array($finder);
unset($finder);
foreach ($dirs as $dir) {
if (!$fs->removeDirectory($dir)) {
throw new \RuntimeException('Could not remove '.$dir);
}
}
} catch (\Exception $e) {
$io->writeError('<error>An error occurred while removing the VCS metadata: '.$e->getMessage().'</error>');
}
$hasVcs = false;
}
// rewriting self.version dependencies with explicit version numbers if the package's vcs metadata is gone
if (!$hasVcs) {
$package = $composer->getPackage();
$configSource = new JsonConfigSource(new JsonFile('composer.json'));
foreach (BasePackage::$supportedLinkTypes as $type => $meta) {
foreach ($package->{'get'.$meta['method']}() as $link) {
if ($link->getPrettyConstraint() === 'self.version') {
$configSource->addLink($type, $link->getTarget(), $package->getPrettyVersion());
}
}
}
}
if ($noScripts === false) {
// dispatch event
$composer->getEventDispatcher()->dispatchScript(ScriptEvents::POST_CREATE_PROJECT_CMD, $installDevPackages);
}
chdir($oldCwd);
$vendorComposerDir = $config->get('vendor-dir').'/composer';
if (is_dir($vendorComposerDir) && $fs->isDirEmpty($vendorComposerDir)) {
Silencer::call('rmdir', $vendorComposerDir);
$vendorDir = $config->get('vendor-dir');
if (is_dir($vendorDir) && $fs->isDirEmpty($vendorDir)) {
Silencer::call('rmdir', $vendorDir);
}
}
return 0;
}
protected function installRootPackage(IOInterface $io, Config $config, $packageName, $directory = null, $packageVersion = null, $stability = 'stable', $preferSource = false, $preferDist = false, $installDevPackages = false, $repository = null, $disablePlugins = false, $noScripts = false, $noProgress = false, $ignorePlatformReqs = false, $secureHttp = true)
{
if (!$secureHttp) {
$config->merge(array('config' => array('secure-http' => false)));
}
if (null === $repository) {
$sourceRepo = new CompositeRepository(RepositoryFactory::defaultRepos($io, $config));
} else {
$sourceRepo = RepositoryFactory::fromString($io, $config, $repository, true);
}
$parser = new VersionParser();
$requirements = $parser->parseNameVersionPairs(array($packageName));
$name = strtolower($requirements[0]['name']);
if (!$packageVersion && isset($requirements[0]['version'])) {
$packageVersion = $requirements[0]['version'];
}
if (null === $stability) {
if (preg_match('{^[^,\s]*?@('.implode('|', array_keys(BasePackage::$stabilities)).')$}i', $packageVersion, $match)) {
$stability = $match[1];
} else {
$stability = VersionParser::parseStability($packageVersion);
}
}
$stability = VersionParser::normalizeStability($stability);
if (!isset(BasePackage::$stabilities[$stability])) {
throw new \InvalidArgumentException('Invalid stability provided ('.$stability.'), must be one of: '.implode(', ', array_keys(BasePackage::$stabilities)));
}
$pool = new Pool($stability);
$pool->addRepository($sourceRepo);
$phpVersion = null;
$prettyPhpVersion = null;
if (!$ignorePlatformReqs) {
$platformOverrides = $config->get('platform') ?: array();
// initialize $this->repos as it is used by the parent InitCommand
$platform = new PlatformRepository(array(), $platformOverrides);
$phpPackage = $platform->findPackage('php', '*');
$phpVersion = $phpPackage->getVersion();
$prettyPhpVersion = $phpPackage->getPrettyVersion();
}
// find the latest version if there are multiple
$versionSelector = new VersionSelector($pool);
$package = $versionSelector->findBestCandidate($name, $packageVersion, $phpVersion, $stability);
if (!$package) {
$errorMessage = "Could not find package $name with " . ($packageVersion ? "version $packageVersion" : "stability $stability");
if ($phpVersion && $versionSelector->findBestCandidate($name, $packageVersion, null, $stability)) {
throw new \InvalidArgumentException($errorMessage .' in a version installable using your PHP version '.$prettyPhpVersion.'.');
}
throw new \InvalidArgumentException($errorMessage .'.');
}
if (null === $directory) {
$parts = explode("/", $name, 2);
$directory = getcwd() . DIRECTORY_SEPARATOR . array_pop($parts);
}
// handler Ctrl+C for unix-like systems
if (function_exists('pcntl_async_signals')) {
@mkdir($directory, 0777, true);
if ($realDir = realpath($directory)) {
pcntl_async_signals(true);
pcntl_signal(SIGINT, function () use ($realDir) {
$fs = new Filesystem();
$fs->removeDirectory($realDir);
exit(130);
});
}
}
$io->writeError('<info>Installing ' . $package->getName() . ' (' . $package->getFullPrettyVersion(false) . ')</info>');
if ($disablePlugins) {
$io->writeError('<info>Plugins have been disabled.</info>');
}
if ($package instanceof AliasPackage) {
$package = $package->getAliasOf();
}
$dm = $this->createDownloadManager($io, $config);
$dm->setPreferSource($preferSource)
->setPreferDist($preferDist)
->setOutputProgress(!$noProgress);
$projectInstaller = new ProjectInstaller($directory, $dm);
$im = $this->createInstallationManager();
$im->addInstaller($projectInstaller);
$im->install(new InstalledFilesystemRepository(new JsonFile('php://memory')), new InstallOperation($package));
$im->notifyInstalls($io);
// collect suggestions
$this->suggestedPackagesReporter->addSuggestionsFromPackage($package);
$installedFromVcs = 'source' === $package->getInstallationSource();
$io->writeError('<info>Created project in ' . $directory . '</info>');
chdir($directory);
$_SERVER['COMPOSER_ROOT_VERSION'] = $package->getPrettyVersion();
putenv('COMPOSER_ROOT_VERSION='.$_SERVER['COMPOSER_ROOT_VERSION']);
return $installedFromVcs;
}
protected function createDownloadManager(IOInterface $io, Config $config)
{
$factory = new Factory();
return $factory->createDownloadManager($io, $config);
}
protected function createInstallationManager()
{
return new InstallationManager();
}
}
@@ -0,0 +1,57 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* @author Niels Keurentjes <niels.keurentjes@omines.com>
*/
class DependsCommand extends BaseDependencyCommand
{
/**
* Configure command metadata.
*/
protected function configure()
{
parent::configure();
$this
->setName('depends')
->setAliases(array('why'))
->setDescription('Shows which packages cause the given package to be installed.')
->setHelp(
<<<EOT
Displays detailed information about where a package is referenced.
<info>php composer.phar depends composer/composer</info>
Read more at https://getcomposer.org/doc/03-cli.md#depends-why-
EOT
)
;
}
/**
* Execute the function.
*
* @param InputInterface $input
* @param OutputInterface $output
* @return int
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
return parent::doExecute($input, $output, false);
}
}
@@ -0,0 +1,729 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Command;
use Composer\Composer;
use Composer\Factory;
use Composer\Config;
use Composer\Downloader\TransportException;
use Composer\Repository\PlatformRepository;
use Composer\Plugin\CommandEvent;
use Composer\Plugin\PluginEvents;
use Composer\Util\ConfigValidator;
use Composer\Util\IniHelper;
use Composer\Util\ProcessExecutor;
use Composer\Util\RemoteFilesystem;
use Composer\Util\StreamContextFactory;
use Composer\SelfUpdate\Keys;
use Composer\SelfUpdate\Versions;
use Composer\IO\NullIO;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
class DiagnoseCommand extends BaseCommand
{
/** @var RemoteFilesystem */
protected $rfs;
/** @var ProcessExecutor */
protected $process;
/** @var int */
protected $exitCode = 0;
protected function configure()
{
$this
->setName('diagnose')
->setDescription('Diagnoses the system to identify common errors.')
->setHelp(
<<<EOT
The <info>diagnose</info> command checks common errors to help debugging problems.
The process exit code will be 1 in case of warnings and 2 for errors.
Read more at https://getcomposer.org/doc/03-cli.md#diagnose
EOT
)
;
}
/**
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$composer = $this->getComposer(false);
$io = $this->getIO();
if ($composer) {
$commandEvent = new CommandEvent(PluginEvents::COMMAND, 'diagnose', $input, $output);
$composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
$io->write('Checking composer.json: ', false);
$this->outputResult($this->checkComposerSchema());
}
if ($composer) {
$config = $composer->getConfig();
} else {
$config = Factory::createConfig();
}
$config->merge(array('config' => array('secure-http' => false)));
$config->prohibitUrlByConfig('http://repo.packagist.org', new NullIO);
$this->rfs = Factory::createRemoteFilesystem($io, $config);
$this->process = new ProcessExecutor($io);
$io->write('Checking platform settings: ', false);
$this->outputResult($this->checkPlatform());
$io->write('Checking git settings: ', false);
$this->outputResult($this->checkGit());
$io->write('Checking http connectivity to packagist: ', false);
$this->outputResult($this->checkHttp('http', $config));
$io->write('Checking https connectivity to packagist: ', false);
$this->outputResult($this->checkHttp('https', $config));
$opts = stream_context_get_options(StreamContextFactory::getContext('http://example.org'));
if (!empty($opts['http']['proxy'])) {
$io->write('Checking HTTP proxy: ', false);
$this->outputResult($this->checkHttpProxy());
$io->write('Checking HTTP proxy support for request_fulluri: ', false);
$this->outputResult($this->checkHttpProxyFullUriRequestParam());
$io->write('Checking HTTPS proxy support for request_fulluri: ', false);
$this->outputResult($this->checkHttpsProxyFullUriRequestParam());
}
if ($oauth = $config->get('github-oauth')) {
foreach ($oauth as $domain => $token) {
$io->write('Checking '.$domain.' oauth access: ', false);
$this->outputResult($this->checkGithubOauth($domain, $token));
}
} else {
$io->write('Checking github.com rate limit: ', false);
try {
$rate = $this->getGithubRateLimit('github.com');
if (!is_array($rate)) {
$this->outputResult($rate);
} elseif (10 > $rate['remaining']) {
$io->write('<warning>WARNING</warning>');
$io->write(sprintf(
'<comment>Github has a rate limit on their API. '
. 'You currently have <options=bold>%u</options=bold> '
. 'out of <options=bold>%u</options=bold> requests left.' . PHP_EOL
. 'See https://developer.github.com/v3/#rate-limiting and also' . PHP_EOL
. ' https://getcomposer.org/doc/articles/troubleshooting.md#api-rate-limit-and-oauth-tokens</comment>',
$rate['remaining'],
$rate['limit']
));
} else {
$this->outputResult(true);
}
} catch (\Exception $e) {
if ($e instanceof TransportException && $e->getCode() === 401) {
$this->outputResult('<comment>The oauth token for github.com seems invalid, run "composer config --global --unset github-oauth.github.com" to remove it</comment>');
} else {
$this->outputResult($e);
}
}
}
$io->write('Checking disk free space: ', false);
$this->outputResult($this->checkDiskSpace($config));
if ('phar:' === substr(__FILE__, 0, 5)) {
$io->write('Checking pubkeys: ', false);
$this->outputResult($this->checkPubKeys($config));
$io->write('Checking composer version: ', false);
$this->outputResult($this->checkVersion($config));
}
$io->write(sprintf('Composer version: <comment>%s</comment>', Composer::VERSION));
$platformOverrides = $config->get('platform') ?: array();
$platformRepo = new PlatformRepository(array(), $platformOverrides);
$phpPkg = $platformRepo->findPackage('php', '*');
$phpVersion = $phpPkg->getPrettyVersion();
if (false !== strpos($phpPkg->getDescription(), 'overridden')) {
$phpVersion .= ' - ' . $phpPkg->getDescription();
}
$io->write(sprintf('PHP version: <comment>%s</comment>', $phpVersion));
if (defined('PHP_BINARY')) {
$io->write(sprintf('PHP binary path: <comment>%s</comment>', PHP_BINARY));
}
return $this->exitCode;
}
private function checkComposerSchema()
{
$validator = new ConfigValidator($this->getIO());
list($errors, , $warnings) = $validator->validate(Factory::getComposerFile());
if ($errors || $warnings) {
$messages = array(
'error' => $errors,
'warning' => $warnings,
);
$output = '';
foreach ($messages as $style => $msgs) {
foreach ($msgs as $msg) {
$output .= '<' . $style . '>' . $msg . '</' . $style . '>' . PHP_EOL;
}
}
return rtrim($output);
}
return true;
}
private function checkGit()
{
$this->process->execute('git config color.ui', $output);
if (strtolower(trim($output)) === 'always') {
return '<comment>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.</comment>';
}
return true;
}
private function checkHttp($proto, Config $config)
{
$result = $this->checkConnectivity();
if ($result !== true) {
return $result;
}
$disableTls = false;
$result = array();
if ($proto === 'https' && $config->get('disable-tls') === true) {
$disableTls = true;
$result[] = '<warning>Composer is configured to disable SSL/TLS protection. This will leave remote HTTPS requests vulnerable to Man-In-The-Middle attacks.</warning>';
}
if ($proto === 'https' && !extension_loaded('openssl') && !$disableTls) {
$result[] = '<error>Composer is configured to use SSL/TLS protection but the openssl extension is not available.</error>';
}
try {
$this->rfs->getContents('packagist.org', $proto . '://repo.packagist.org/packages.json', false);
} catch (TransportException $e) {
if (false !== strpos($e->getMessage(), 'cafile')) {
$result[] = '<error>[' . get_class($e) . '] ' . $e->getMessage() . '</error>';
$result[] = '<error>Unable to locate a valid CA certificate file. You must set a valid \'cafile\' option.</error>';
$result[] = '<error>You can alternatively disable this error, at your own risk, by enabling the \'disable-tls\' option.</error>';
} else {
array_unshift($result, '[' . get_class($e) . '] ' . $e->getMessage());
}
}
if (count($result) > 0) {
return $result;
}
return true;
}
private function checkHttpProxy()
{
$result = $this->checkConnectivity();
if ($result !== true) {
return $result;
}
$protocol = extension_loaded('openssl') ? 'https' : 'http';
try {
$json = json_decode($this->rfs->getContents('packagist.org', $protocol . '://repo.packagist.org/packages.json', false), true);
$hash = reset($json['provider-includes']);
$hash = $hash['sha256'];
$path = str_replace('%hash%', $hash, key($json['provider-includes']));
$provider = $this->rfs->getContents('packagist.org', $protocol . '://repo.packagist.org/'.$path, false);
if (hash('sha256', $provider) !== $hash) {
return 'It seems that your proxy is modifying http traffic on the fly';
}
} catch (\Exception $e) {
return $e;
}
return true;
}
/**
* Due to various proxy servers configurations, some servers can't handle non-standard HTTP "http_proxy_request_fulluri" parameter,
* and will return error 500/501 (as not implemented), see discussion @ https://github.com/composer/composer/pull/1825.
* This method will test, if you need to disable this parameter via setting extra environment variable in your system.
*
* @return bool|string
*/
private function checkHttpProxyFullUriRequestParam()
{
$result = $this->checkConnectivity();
if ($result !== true) {
return $result;
}
$url = 'http://repo.packagist.org/packages.json';
try {
$this->rfs->getContents('packagist.org', $url, false);
} catch (TransportException $e) {
try {
$this->rfs->getContents('packagist.org', $url, false, array('http' => array('request_fulluri' => false)));
} catch (TransportException $e) {
return 'Unable to assess the situation, maybe packagist.org is down ('.$e->getMessage().')';
}
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"';
}
return true;
}
/**
* Due to various proxy servers configurations, some servers can't handle non-standard HTTP "http_proxy_request_fulluri" parameter,
* and will return error 500/501 (as not implemented), see discussion @ https://github.com/composer/composer/pull/1825.
* This method will test, if you need to disable this parameter via setting extra environment variable in your system.
*
* @return bool|string
*/
private function checkHttpsProxyFullUriRequestParam()
{
$result = $this->checkConnectivity();
if ($result !== true) {
return $result;
}
if (!extension_loaded('openssl')) {
return 'You need the openssl extension installed for this check';
}
$url = 'https://api.github.com/repos/Seldaek/jsonlint/zipball/1.0.0';
try {
$this->rfs->getContents('github.com', $url, false);
} catch (TransportException $e) {
try {
$this->rfs->getContents('github.com', $url, false, array('http' => array('request_fulluri' => false)));
} catch (TransportException $e) {
return 'Unable to assess the situation, maybe github is down ('.$e->getMessage().')';
}
return 'It seems there is a problem with your proxy server, try setting the "HTTPS_PROXY_REQUEST_FULLURI" environment variable to "false"';
}
return true;
}
private function checkGithubOauth($domain, $token)
{
$result = $this->checkConnectivity();
if ($result !== true) {
return $result;
}
$this->getIO()->setAuthentication($domain, $token, 'x-oauth-basic');
try {
$url = $domain === 'github.com' ? 'https://api.'.$domain.'/' : 'https://'.$domain.'/api/v3/';
return $this->rfs->getContents($domain, $url, false, array(
'retry-auth-failure' => false,
)) ? true : 'Unexpected error';
} catch (\Exception $e) {
if ($e instanceof TransportException && $e->getCode() === 401) {
return '<comment>The oauth token for '.$domain.' seems invalid, run "composer config --global --unset github-oauth.'.$domain.'" to remove it</comment>';
}
return $e;
}
}
/**
* @param string $domain
* @param string $token
* @throws TransportException
* @return array|string
*/
private function getGithubRateLimit($domain, $token = null)
{
$result = $this->checkConnectivity();
if ($result !== true) {
return $result;
}
if ($token) {
$this->getIO()->setAuthentication($domain, $token, 'x-oauth-basic');
}
$url = $domain === 'github.com' ? 'https://api.'.$domain.'/rate_limit' : 'https://'.$domain.'/api/rate_limit';
$json = $this->rfs->getContents($domain, $url, false, array('retry-auth-failure' => false));
$data = json_decode($json, true);
return $data['resources']['core'];
}
private function checkDiskSpace($config)
{
$minSpaceFree = 1024 * 1024;
if ((($df = @disk_free_space($dir = $config->get('home'))) !== false && $df < $minSpaceFree)
|| (($df = @disk_free_space($dir = $config->get('vendor-dir'))) !== false && $df < $minSpaceFree)
) {
return '<error>The disk hosting '.$dir.' is full</error>';
}
return true;
}
private function checkPubKeys($config)
{
$home = $config->get('home');
$errors = array();
$io = $this->getIO();
if (file_exists($home.'/keys.tags.pub') && file_exists($home.'/keys.dev.pub')) {
$io->write('');
}
if (file_exists($home.'/keys.tags.pub')) {
$io->write('Tags Public Key Fingerprint: ' . Keys::fingerprint($home.'/keys.tags.pub'));
} else {
$errors[] = '<error>Missing pubkey for tags verification</error>';
}
if (file_exists($home.'/keys.dev.pub')) {
$io->write('Dev Public Key Fingerprint: ' . Keys::fingerprint($home.'/keys.dev.pub'));
} else {
$errors[] = '<error>Missing pubkey for dev verification</error>';
}
if ($errors) {
$errors[] = '<error>Run composer self-update --update-keys to set them up</error>';
}
return $errors ?: true;
}
private function checkVersion($config)
{
$result = $this->checkConnectivity();
if ($result !== true) {
return $result;
}
$versionsUtil = new Versions($config, $this->rfs);
$latest = $versionsUtil->getLatest();
if (Composer::VERSION !== $latest['version'] && Composer::VERSION !== '@package_version@') {
return '<comment>You are not running the latest '.$versionsUtil->getChannel().' version, run `composer self-update` to update ('.Composer::VERSION.' => '.$latest['version'].')</comment>';
}
return true;
}
/**
* @param bool|string|\Exception $result
*/
private function outputResult($result)
{
$io = $this->getIO();
if (true === $result) {
$io->write('<info>OK</info>');
return;
}
$hadError = false;
$hadWarning = false;
if ($result instanceof \Exception) {
$result = '<error>['.get_class($result).'] '.$result->getMessage().'</error>';
}
if (!$result) {
// falsey results should be considered as an error, even if there is nothing to output
$hadError = true;
} else {
if (!is_array($result)) {
$result = array($result);
}
foreach ($result as $message) {
if (false !== strpos($message, '<error>')) {
$hadError = true;
} elseif (false !== strpos($message, '<warning>')) {
$hadWarning = true;
}
}
}
if ($hadError) {
$io->write('<error>FAIL</error>');
$this->exitCode = max($this->exitCode, 2);
} elseif ($hadWarning) {
$io->write('<warning>WARNING</warning>');
$this->exitCode = max($this->exitCode, 1);
}
if ($result) {
foreach ($result as $message) {
$io->write($message);
}
}
}
private function checkPlatform()
{
$output = '';
$out = function ($msg, $style) use (&$output) {
$output .= '<'.$style.'>'.$msg.'</'.$style.'>'.PHP_EOL;
};
// code below taken from getcomposer.org/installer, any changes should be made there and replicated here
$errors = array();
$warnings = array();
$displayIniMessage = false;
$iniMessage = PHP_EOL.PHP_EOL.IniHelper::getMessage();
$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.';
if (!function_exists('json_decode')) {
$errors['json'] = true;
}
if (!extension_loaded('Phar')) {
$errors['phar'] = true;
}
if (!extension_loaded('filter')) {
$errors['filter'] = true;
}
if (!extension_loaded('hash')) {
$errors['hash'] = true;
}
if (!extension_loaded('iconv') && !extension_loaded('mbstring')) {
$errors['iconv_mbstring'] = true;
}
if (!filter_var(ini_get('allow_url_fopen'), FILTER_VALIDATE_BOOLEAN)) {
$errors['allow_url_fopen'] = true;
}
if (extension_loaded('ionCube Loader') && ioncube_loader_iversion() < 40009) {
$errors['ioncube'] = ioncube_loader_version();
}
if (PHP_VERSION_ID < 50302) {
$errors['php'] = PHP_VERSION;
}
if (!isset($errors['php']) && PHP_VERSION_ID < 50304) {
$warnings['php'] = PHP_VERSION;
}
if (!extension_loaded('openssl')) {
$errors['openssl'] = true;
}
if (extension_loaded('openssl') && OPENSSL_VERSION_NUMBER < 0x1000100f) {
$warnings['openssl_version'] = true;
}
if (!defined('HHVM_VERSION') && !extension_loaded('apcu') && filter_var(ini_get('apc.enable_cli'), FILTER_VALIDATE_BOOLEAN)) {
$warnings['apc_cli'] = true;
}
if (!extension_loaded('zlib')) {
$warnings['zlib'] = true;
}
ob_start();
phpinfo(INFO_GENERAL);
$phpinfo = ob_get_clean();
if (preg_match('{Configure Command(?: *</td><td class="v">| *=> *)(.*?)(?:</td>|$)}m', $phpinfo, $match)) {
$configure = $match[1];
if (false !== strpos($configure, '--enable-sigchild')) {
$warnings['sigchild'] = true;
}
if (false !== strpos($configure, '--with-curlwrappers')) {
$warnings['curlwrappers'] = true;
}
}
if (filter_var(ini_get('xdebug.profiler_enabled'), FILTER_VALIDATE_BOOLEAN)) {
$warnings['xdebug_profile'] = true;
} elseif (extension_loaded('xdebug')) {
$warnings['xdebug_loaded'] = true;
}
if (!empty($errors)) {
foreach ($errors as $error => $current) {
switch ($error) {
case 'json':
$text = PHP_EOL."The json extension is missing.".PHP_EOL;
$text .= "Install it or recompile php without --disable-json";
break;
case 'phar':
$text = PHP_EOL."The phar extension is missing.".PHP_EOL;
$text .= "Install it or recompile php without --disable-phar";
break;
case 'filter':
$text = PHP_EOL."The filter extension is missing.".PHP_EOL;
$text .= "Install it or recompile php without --disable-filter";
break;
case 'hash':
$text = PHP_EOL."The hash extension is missing.".PHP_EOL;
$text .= "Install it or recompile php without --disable-hash";
break;
case 'iconv_mbstring':
$text = PHP_EOL."The iconv OR mbstring extension is required and both are missing.".PHP_EOL;
$text .= "Install either of them or recompile php without --disable-iconv";
break;
case 'unicode':
$text = PHP_EOL."The detect_unicode setting must be disabled.".PHP_EOL;
$text .= "Add the following to the end of your `php.ini`:".PHP_EOL;
$text .= " detect_unicode = Off";
$displayIniMessage = true;
break;
case 'suhosin':
$text = PHP_EOL."The suhosin.executor.include.whitelist setting is incorrect.".PHP_EOL;
$text .= "Add the following to the end of your `php.ini` or suhosin.ini (Example path [for Debian]: /etc/php5/cli/conf.d/suhosin.ini):".PHP_EOL;
$text .= " suhosin.executor.include.whitelist = phar ".$current;
$displayIniMessage = true;
break;
case 'php':
$text = PHP_EOL."Your PHP ({$current}) is too old, you must upgrade to PHP 5.3.2 or higher.";
break;
case 'allow_url_fopen':
$text = PHP_EOL."The allow_url_fopen setting is incorrect.".PHP_EOL;
$text .= "Add the following to the end of your `php.ini`:".PHP_EOL;
$text .= " allow_url_fopen = On";
$displayIniMessage = true;
break;
case 'ioncube':
$text = PHP_EOL."Your ionCube Loader extension ($current) is incompatible with Phar files.".PHP_EOL;
$text .= "Upgrade to ionCube 4.0.9 or higher or remove this line (path may be different) from your `php.ini` to disable it:".PHP_EOL;
$text .= " zend_extension = /usr/lib/php5/20090626+lfs/ioncube_loader_lin_5.3.so";
$displayIniMessage = true;
break;
case 'openssl':
$text = PHP_EOL."The openssl extension is missing, which means that secure HTTPS transfers are impossible.".PHP_EOL;
$text .= "If possible you should enable it or recompile php with --with-openssl";
break;
}
$out($text, 'error');
}
$output .= PHP_EOL;
}
if (!empty($warnings)) {
foreach ($warnings as $warning => $current) {
switch ($warning) {
case 'apc_cli':
$text = "The apc.enable_cli setting is incorrect.".PHP_EOL;
$text .= "Add the following to the end of your `php.ini`:".PHP_EOL;
$text .= " apc.enable_cli = Off";
$displayIniMessage = true;
break;
case 'zlib':
$text = 'The zlib extension is not loaded, this can slow down Composer a lot.'.PHP_EOL;
$text .= 'If possible, enable it or recompile php with --with-zlib'.PHP_EOL;
$displayIniMessage = true;
break;
case 'sigchild':
$text = "PHP was compiled with --enable-sigchild which can cause issues on some platforms.".PHP_EOL;
$text .= "Recompile it without this flag if possible, see also:".PHP_EOL;
$text .= " https://bugs.php.net/bug.php?id=22999";
break;
case 'curlwrappers':
$text = "PHP was compiled with --with-curlwrappers which will cause issues with HTTP authentication and GitHub.".PHP_EOL;
$text .= " Recompile it without this flag if possible";
break;
case 'php':
$text = "Your PHP ({$current}) is quite old, upgrading to PHP 5.3.4 or higher is recommended.".PHP_EOL;
$text .= " Composer works with 5.3.2+ for most people, but there might be edge case issues.";
break;
case 'openssl_version':
// Attempt to parse version number out, fallback to whole string value.
$opensslVersion = strstr(trim(strstr(OPENSSL_VERSION_TEXT, ' ')), ' ', true);
$opensslVersion = $opensslVersion ?: OPENSSL_VERSION_TEXT;
$text = "The OpenSSL library ({$opensslVersion}) used by PHP does not support TLSv1.2 or TLSv1.1.".PHP_EOL;
$text .= "If possible you should upgrade OpenSSL to version 1.0.1 or above.";
break;
case 'xdebug_loaded':
$text = "The xdebug extension is loaded, this can slow down Composer a little.".PHP_EOL;
$text .= " Disabling it when using Composer is recommended.";
break;
case 'xdebug_profile':
$text = "The xdebug.profiler_enabled setting is enabled, this can slow down Composer a lot.".PHP_EOL;
$text .= "Add the following to the end of your `php.ini` to disable it:".PHP_EOL;
$text .= " xdebug.profiler_enabled = 0";
$displayIniMessage = true;
break;
}
$out($text, 'comment');
}
}
if ($displayIniMessage) {
$out($iniMessage, 'comment');
}
return !$warnings && !$errors ? true : $output;
}
/**
* Check if allow_url_fopen is ON
*
* @return bool|string
*/
private function checkConnectivity()
{
if (!ini_get('allow_url_fopen')) {
$result = '<info>Skipped because allow_url_fopen is missing.</info>';
return $result;
}
return true;
}
}
@@ -0,0 +1,90 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Command;
use Composer\Plugin\CommandEvent;
use Composer\Plugin\PluginEvents;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
class DumpAutoloadCommand extends BaseCommand
{
protected function configure()
{
$this
->setName('dump-autoload')
->setAliases(array('dumpautoload'))
->setDescription('Dumps the autoloader.')
->setDefinition(array(
new InputOption('no-scripts', null, InputOption::VALUE_NONE, 'Skips the execution of all scripts defined in composer.json file.'),
new InputOption('optimize', 'o', InputOption::VALUE_NONE, 'Optimizes PSR0 and PSR4 packages to be loaded with classmaps too, good for production.'),
new InputOption('classmap-authoritative', 'a', InputOption::VALUE_NONE, 'Autoload classes from the classmap only. Implicitly enables `--optimize`.'),
new InputOption('apcu', null, InputOption::VALUE_NONE, 'Use APCu to cache found/not-found classes.'),
new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables autoload-dev rules.'),
))
->setHelp(
<<<EOT
<info>php composer.phar dump-autoload</info>
Read more at https://getcomposer.org/doc/03-cli.md#dump-autoload-dumpautoload-
EOT
)
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$composer = $this->getComposer();
$commandEvent = new CommandEvent(PluginEvents::COMMAND, 'dump-autoload', $input, $output);
$composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
$installationManager = $composer->getInstallationManager();
$localRepo = $composer->getRepositoryManager()->getLocalRepository();
$package = $composer->getPackage();
$config = $composer->getConfig();
$optimize = $input->getOption('optimize') || $config->get('optimize-autoloader');
$authoritative = $input->getOption('classmap-authoritative') || $config->get('classmap-authoritative');
$apcu = $input->getOption('apcu') || $config->get('apcu-autoloader');
if ($authoritative) {
$this->getIO()->writeError('<info>Generating optimized autoload files (authoritative)</info>', false);
} elseif ($optimize) {
$this->getIO()->writeError('<info>Generating optimized autoload files</info>', false);
} else {
$this->getIO()->writeError('<info>Generating autoload files</info>', false);
}
$generator = $composer->getAutoloadGenerator();
$generator->setDevMode(!$input->getOption('no-dev'));
$generator->setClassMapAuthoritative($authoritative);
$generator->setApcu($apcu);
$generator->setRunScripts(!$input->getOption('no-scripts'));
$numberOfClasses = $generator->dump($config, $localRepo, $package, $installationManager, 'composer', $optimize);
if ($authoritative) {
$this->getIO()->overwriteError('<info>Generated optimized autoload files (authoritative) containing '. $numberOfClasses .' classes</info>');
} elseif ($optimize) {
$this->getIO()->overwriteError('<info>Generated optimized autoload files containing '. $numberOfClasses .' classes</info>');
} else {
$this->getIO()->overwriteError('<info>Generated autoload files containing '. $numberOfClasses .' classes</info>');
}
return 0;
}
}
@@ -0,0 +1,97 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Input\InputArgument;
/**
* @author Davey Shafik <me@daveyshafik.com>
*/
class ExecCommand extends BaseCommand
{
protected function configure()
{
$this
->setName('exec')
->setDescription('Executes a vendored binary/script.')
->setDefinition(array(
new InputOption('list', 'l', InputOption::VALUE_NONE),
new InputArgument('binary', InputArgument::OPTIONAL, 'The binary to run, e.g. phpunit'),
new InputArgument(
'args',
InputArgument::IS_ARRAY | InputArgument::OPTIONAL,
'Arguments to pass to the binary. Use <info>--</info> to separate from composer arguments'
),
))
->setHelp(
<<<EOT
Executes a vendored binary/script.
Read more at https://getcomposer.org/doc/03-cli.md#exec
EOT
)
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$composer = $this->getComposer();
$binDir = $composer->getConfig()->get('bin-dir');
if ($input->getOption('list') || !$input->getArgument('binary')) {
$bins = glob($binDir . '/*');
$bins = array_merge($bins, array_map(function ($e) {
return "$e (local)";
}, $composer->getPackage()->getBinaries()));
if (!$bins) {
throw new \RuntimeException("No binaries found in composer.json or in bin-dir ($binDir)");
}
$this->getIO()->write(
<<<EOT
<comment>Available binaries:</comment>
EOT
);
foreach ($bins as $bin) {
// skip .bat copies
if (isset($previousBin) && $bin === $previousBin.'.bat') {
continue;
}
$previousBin = $bin;
$bin = basename($bin);
$this->getIO()->write(
<<<EOT
<info>- $bin</info>
EOT
);
}
return 0;
}
$binary = $input->getArgument('binary');
$dispatcher = $composer->getEventDispatcher();
$dispatcher->addListener('__exec_command', $binary);
if ($output->getVerbosity() === OutputInterface::VERBOSITY_NORMAL) {
$output->setVerbosity(OutputInterface::VERBOSITY_QUIET);
}
return $dispatcher->dispatchScript('__exec_command', true, $input->getArgument('args'));
}
}
@@ -0,0 +1,111 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Command;
use Composer\Factory;
use Composer\Util\Filesystem;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\StringInput;
use Symfony\Component\Console\Output\OutputInterface;
/**
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
class GlobalCommand extends BaseCommand
{
protected function configure()
{
$this
->setName('global')
->setDescription('Allows running commands in the global composer dir ($COMPOSER_HOME).')
->setDefinition(array(
new InputArgument('command-name', InputArgument::REQUIRED, ''),
new InputArgument('args', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, ''),
))
->setHelp(
<<<EOT
Use this command as a wrapper to run other Composer commands
within the global context of COMPOSER_HOME.
You can use this to install CLI utilities globally, all you need
is to add the COMPOSER_HOME/vendor/bin dir to your PATH env var.
COMPOSER_HOME is c:\Users\<user>\AppData\Roaming\Composer on Windows
and /home/<user>/.composer on unix systems.
If your system uses freedesktop.org standards, then it will first check
XDG_CONFIG_HOME or default to /home/<user>/.config/composer
Note: This path may vary depending on customizations to bin-dir in
composer.json or the environmental variable COMPOSER_BIN_DIR.
Read more at https://getcomposer.org/doc/03-cli.md#global
EOT
)
;
}
public function run(InputInterface $input, OutputInterface $output)
{
// extract real command name
$tokens = preg_split('{\s+}', $input->__toString());
$args = array();
foreach ($tokens as $token) {
if ($token && $token[0] !== '-') {
$args[] = $token;
if (count($args) >= 2) {
break;
}
}
}
// show help for this command if no command was found
if (count($args) < 2) {
return parent::run($input, $output);
}
// change to global dir
$config = Factory::createConfig();
$home = $config->get('home');
if (!is_dir($home)) {
$fs = new Filesystem();
$fs->ensureDirectoryExists($home);
if (!is_dir($home)) {
throw new \RuntimeException('Could not create home directory');
}
}
try {
chdir($home);
} catch (\Exception $e) {
throw new \RuntimeException('Could not switch to home directory "'.$home.'"', 0, $e);
}
$this->getIO()->writeError('<info>Changed current directory to '.$home.'</info>');
// create new input without "global" command prefix
$input = new StringInput(preg_replace('{\bg(?:l(?:o(?:b(?:a(?:l)?)?)?)?)?\b}', '', $input->__toString(), 1));
$this->getApplication()->resetComposer();
return $this->getApplication()->run($input, $output);
}
/**
* {@inheritDoc}
*/
public function isProxyCommand()
{
return true;
}
}
@@ -0,0 +1,168 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Command;
use Composer\Package\CompletePackageInterface;
use Composer\Repository\RepositoryInterface;
use Composer\Repository\ArrayRepository;
use Composer\Repository\RepositoryFactory;
use Composer\Util\Platform;
use Composer\Util\ProcessExecutor;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* @author Robert Schönthal <seroscho@googlemail.com>
*/
class HomeCommand extends BaseCommand
{
/**
* {@inheritDoc}
*/
protected function configure()
{
$this
->setName('browse')
->setAliases(array('home'))
->setDescription('Opens the package\'s repository URL or homepage in your browser.')
->setDefinition(array(
new InputArgument('packages', InputArgument::IS_ARRAY, 'Package(s) to browse to.'),
new InputOption('homepage', 'H', InputOption::VALUE_NONE, 'Open the homepage instead of the repository URL.'),
new InputOption('show', 's', InputOption::VALUE_NONE, 'Only show the homepage or repository URL.'),
))
->setHelp(
<<<EOT
The home command opens or shows a package's repository URL or
homepage in your default browser.
To open the homepage by default, use -H or --homepage.
To show instead of open the repository or homepage URL, use -s or --show.
Read more at https://getcomposer.org/doc/03-cli.md#browse-home
EOT
);
}
/**
* {@inheritDoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$repos = $this->initializeRepos();
$io = $this->getIO();
$return = 0;
$packages = $input->getArgument('packages');
if (!$packages) {
$io->writeError('No package specified, opening homepage for the root package');
$packages = array($this->getComposer()->getPackage()->getName());
}
foreach ($packages as $packageName) {
$handled = false;
$packageExists = false;
foreach ($repos as $repo) {
foreach ($repo->findPackages($packageName) as $package) {
$packageExists = true;
if ($package instanceof CompletePackageInterface && $this->handlePackage($package, $input->getOption('homepage'), $input->getOption('show'))) {
$handled = true;
break 2;
}
}
}
if (!$packageExists) {
$return = 1;
$io->writeError('<warning>Package '.$packageName.' not found</warning>');
}
if (!$handled) {
$return = 1;
$io->writeError('<warning>'.($input->getOption('homepage') ? 'Invalid or missing homepage' : 'Invalid or missing repository URL').' for '.$packageName.'</warning>');
}
}
return $return;
}
private function handlePackage(CompletePackageInterface $package, $showHomepage, $showOnly)
{
$support = $package->getSupport();
$url = isset($support['source']) ? $support['source'] : $package->getSourceUrl();
if (!$url || $showHomepage) {
$url = $package->getHomepage();
}
if (!$url || !filter_var($url, FILTER_VALIDATE_URL)) {
return false;
}
if ($showOnly) {
$this->getIO()->write(sprintf('<info>%s</info>', $url));
} else {
$this->openBrowser($url);
}
return true;
}
/**
* opens a url in your system default browser
*
* @param string $url
*/
private function openBrowser($url)
{
$url = ProcessExecutor::escape($url);
$process = new ProcessExecutor($this->getIO());
if (Platform::isWindows()) {
return $process->execute('start "web" explorer "' . $url . '"', $output);
}
$linux = $process->execute('which xdg-open', $output);
$osx = $process->execute('which open', $output);
if (0 === $linux) {
$process->execute('xdg-open ' . $url, $output);
} elseif (0 === $osx) {
$process->execute('open ' . $url, $output);
} else {
$this->getIO()->writeError('No suitable browser opening command found, open yourself: ' . $url);
}
}
/**
* Initializes repositories
*
* Returns an array of repos in order they should be checked in
*
* @return RepositoryInterface[]
*/
private function initializeRepos()
{
$composer = $this->getComposer(false);
if ($composer) {
return array_merge(
array(new ArrayRepository(array($composer->getPackage()))), // root package
array($composer->getRepositoryManager()->getLocalRepository()), // installed packages
$composer->getRepositoryManager()->getRepositories() // remotes
);
}
return RepositoryFactory::defaultRepos($this->getIO());
}
}
@@ -0,0 +1,822 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Command;
use Composer\DependencyResolver\Pool;
use Composer\Factory;
use Composer\Json\JsonFile;
use Composer\Package\BasePackage;
use Composer\Package\Version\VersionParser;
use Composer\Package\Version\VersionSelector;
use Composer\Repository\CompositeRepository;
use Composer\Repository\PlatformRepository;
use Composer\Repository\RepositoryFactory;
use Composer\Util\ProcessExecutor;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Process\ExecutableFinder;
use Symfony\Component\Process\Process;
/**
* @author Justin Rainbow <justin.rainbow@gmail.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
class InitCommand extends BaseCommand
{
/** @var CompositeRepository */
protected $repos;
/** @var array */
private $gitConfig;
/** @var Pool[] */
private $pools;
/**
* {@inheritdoc}
*/
protected function configure()
{
$this
->setName('init')
->setDescription('Creates a basic composer.json file in current directory.')
->setDefinition(array(
new InputOption('name', null, InputOption::VALUE_REQUIRED, 'Name of the package'),
new InputOption('description', null, InputOption::VALUE_REQUIRED, 'Description of package'),
new InputOption('author', null, InputOption::VALUE_REQUIRED, 'Author name of package'),
// new InputOption('version', null, InputOption::VALUE_NONE, 'Version of package'),
new InputOption('type', null, InputOption::VALUE_OPTIONAL, 'Type of package (e.g. library, project, metapackage, composer-plugin)'),
new InputOption('homepage', null, InputOption::VALUE_REQUIRED, 'Homepage of package'),
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"'),
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"'),
new InputOption('stability', 's', InputOption::VALUE_REQUIRED, 'Minimum stability (empty or one of: '.implode(', ', array_keys(BasePackage::$stabilities)).')'),
new InputOption('license', 'l', InputOption::VALUE_REQUIRED, 'License of package'),
new InputOption('repository', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Add custom repositories, either by URL or using JSON arrays'),
))
->setHelp(
<<<EOT
The <info>init</info> command creates a basic composer.json file
in the current directory.
<info>php composer.phar init</info>
Read more at https://getcomposer.org/doc/03-cli.md#init
EOT
)
;
}
/**
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$io = $this->getIO();
$whitelist = array('name', 'description', 'author', 'type', 'homepage', 'require', 'require-dev', 'stability', 'license');
$options = array_filter(array_intersect_key($input->getOptions(), array_flip($whitelist)));
if (isset($options['author'])) {
$options['authors'] = $this->formatAuthors($options['author']);
unset($options['author']);
}
$repositories = $input->getOption('repository');
if ($repositories) {
$config = Factory::createConfig($io);
foreach ($repositories as $repo) {
$options['repositories'][] = RepositoryFactory::configFromString($io, $config, $repo);
}
}
if (isset($options['stability'])) {
$options['minimum-stability'] = $options['stability'];
unset($options['stability']);
}
$options['require'] = isset($options['require']) ? $this->formatRequirements($options['require']) : new \stdClass;
if (array() === $options['require']) {
$options['require'] = new \stdClass;
}
if (isset($options['require-dev'])) {
$options['require-dev'] = $this->formatRequirements($options['require-dev']);
if (array() === $options['require-dev']) {
$options['require-dev'] = new \stdClass;
}
}
$file = new JsonFile(Factory::getComposerFile());
$json = $file->encode($options);
if ($input->isInteractive()) {
$io->writeError(array('', $json, ''));
if (!$io->askConfirmation('Do you confirm generation [<comment>yes</comment>]? ', true)) {
$io->writeError('<error>Command aborted</error>');
return 1;
}
}
$file->write($options);
if ($input->isInteractive() && is_dir('.git')) {
$ignoreFile = realpath('.gitignore');
if (false === $ignoreFile) {
$ignoreFile = realpath('.') . '/.gitignore';
}
if (!$this->hasVendorIgnore($ignoreFile)) {
$question = 'Would you like the <info>vendor</info> directory added to your <info>.gitignore</info> [<comment>yes</comment>]? ';
if ($io->askConfirmation($question, true)) {
$this->addVendorIgnore($ignoreFile);
}
}
}
$question = 'Would you like to install dependencies now [<comment>yes</comment>]? ';
if ($input->isInteractive() && $this->hasDependencies($options) && $io->askConfirmation($question, true)) {
$this->installDependencies($output);
}
return 0;
}
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output)
{
$git = $this->getGitConfig();
$io = $this->getIO();
$formatter = $this->getHelperSet()->get('formatter');
// initialize repos if configured
$repositories = $input->getOption('repository');
if ($repositories) {
$config = Factory::createConfig($io);
$repos = array(new PlatformRepository);
$createDefaultPackagistRepo = true;
foreach ($repositories as $repo) {
$repoConfig = RepositoryFactory::configFromString($io, $config, $repo);
if (
(isset($repoConfig['packagist']) && $repoConfig === array('packagist' => false))
|| (isset($repoConfig['packagist.org']) && $repoConfig === array('packagist.org' => false))
) {
$createDefaultPackagistRepo = false;
continue;
}
$repos[] = RepositoryFactory::createRepo($io, $config, $repoConfig);
}
if ($createDefaultPackagistRepo) {
$repos[] = RepositoryFactory::createRepo($io, $config, array(
'type' => 'composer',
'url' => 'https://repo.packagist.org',
));
}
$this->repos = new CompositeRepository($repos);
unset($repos, $config, $repositories);
}
$io->writeError(array(
'',
$formatter->formatBlock('Welcome to the Composer config generator', 'bg=blue;fg=white', true),
'',
));
// namespace
$io->writeError(array(
'',
'This command will guide you through creating your composer.json config.',
'',
));
$cwd = realpath(".");
if (!$name = $input->getOption('name')) {
$name = basename($cwd);
$name = preg_replace('{(?:([a-z])([A-Z])|([A-Z])([A-Z][a-z]))}', '\\1\\3-\\2\\4', $name);
$name = strtolower($name);
if (!empty($_SERVER['COMPOSER_DEFAULT_VENDOR'])) {
$name = $_SERVER['COMPOSER_DEFAULT_VENDOR'] . '/' . $name;
} elseif (isset($git['github.user'])) {
$name = $git['github.user'] . '/' . $name;
} elseif (!empty($_SERVER['USERNAME'])) {
$name = $_SERVER['USERNAME'] . '/' . $name;
} elseif (!empty($_SERVER['USER'])) {
$name = $_SERVER['USER'] . '/' . $name;
} elseif (get_current_user()) {
$name = get_current_user() . '/' . $name;
} else {
// package names must be in the format foo/bar
$name .= '/' . $name;
}
$name = strtolower($name);
} else {
if (!preg_match('{^[a-z0-9_.-]+/[a-z0-9_.-]+$}D', $name)) {
throw new \InvalidArgumentException(
'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_.-]+'
);
}
}
$name = $io->askAndValidate(
'Package name (<vendor>/<name>) [<comment>'.$name.'</comment>]: ',
function ($value) use ($name) {
if (null === $value) {
return $name;
}
if (!preg_match('{^[a-z0-9_.-]+/[a-z0-9_.-]+$}D', $value)) {
throw new \InvalidArgumentException(
'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_.-]+'
);
}
return $value;
},
null,
$name
);
$input->setOption('name', $name);
$description = $input->getOption('description') ?: false;
$description = $io->ask(
'Description [<comment>'.$description.'</comment>]: ',
$description
);
$input->setOption('description', $description);
if (null === $author = $input->getOption('author')) {
if (!empty($_SERVER['COMPOSER_DEFAULT_AUTHOR'])) {
$author_name = $_SERVER['COMPOSER_DEFAULT_AUTHOR'];
} elseif (isset($git['user.name'])) {
$author_name = $git['user.name'];
}
if (!empty($_SERVER['COMPOSER_DEFAULT_EMAIL'])) {
$author_email = $_SERVER['COMPOSER_DEFAULT_EMAIL'];
} elseif (isset($git['user.email'])) {
$author_email = $git['user.email'];
}
if (isset($author_name) && isset($author_email)) {
$author = sprintf('%s <%s>', $author_name, $author_email);
}
}
$self = $this;
$author = $io->askAndValidate(
'Author [<comment>'.$author.'</comment>, n to skip]: ',
function ($value) use ($self, $author) {
if ($value === 'n' || $value === 'no') {
return;
}
$value = $value ?: $author;
$author = $self->parseAuthorString($value);
return sprintf('%s <%s>', $author['name'], $author['email']);
},
null,
$author
);
$input->setOption('author', $author);
$minimumStability = $input->getOption('stability') ?: null;
$minimumStability = $io->askAndValidate(
'Minimum Stability [<comment>'.$minimumStability.'</comment>]: ',
function ($value) use ($minimumStability) {
if (null === $value) {
return $minimumStability;
}
if (!isset(BasePackage::$stabilities[$value])) {
throw new \InvalidArgumentException(
'Invalid minimum stability "'.$value.'". Must be empty or one of: '.
implode(', ', array_keys(BasePackage::$stabilities))
);
}
return $value;
},
null,
$minimumStability
);
$input->setOption('stability', $minimumStability);
$type = $input->getOption('type') ?: false;
$type = $io->ask(
'Package Type (e.g. library, project, metapackage, composer-plugin) [<comment>'.$type.'</comment>]: ',
$type
);
$input->setOption('type', $type);
if (null === $license = $input->getOption('license')) {
if (!empty($_SERVER['COMPOSER_DEFAULT_LICENSE'])) {
$license = $_SERVER['COMPOSER_DEFAULT_LICENSE'];
}
}
$license = $io->ask(
'License [<comment>'.$license.'</comment>]: ',
$license
);
$input->setOption('license', $license);
$io->writeError(array('', 'Define your dependencies.', ''));
// prepare to resolve dependencies
$repos = $this->getRepos();
$preferredStability = $minimumStability ?: 'stable';
$phpVersion = $repos->findPackage('php', '*')->getPrettyVersion();
$question = 'Would you like to define your dependencies (require) interactively [<comment>yes</comment>]? ';
$require = $input->getOption('require');
$requirements = array();
if ($require || $io->askConfirmation($question, true)) {
$requirements = $this->determineRequirements($input, $output, $require, $phpVersion, $preferredStability);
}
$input->setOption('require', $requirements);
$question = 'Would you like to define your dev dependencies (require-dev) interactively [<comment>yes</comment>]? ';
$requireDev = $input->getOption('require-dev');
$devRequirements = array();
if ($requireDev || $io->askConfirmation($question, true)) {
$devRequirements = $this->determineRequirements($input, $output, $requireDev, $phpVersion, $preferredStability);
}
$input->setOption('require-dev', $devRequirements);
}
/**
* @private
* @param string $author
* @return array
*/
public function parseAuthorString($author)
{
if (preg_match('/^(?P<name>[- .,\p{L}\p{N}\p{Mn}\'’"()]+) <(?P<email>.+?)>$/u', $author, $match)) {
if ($this->isValidEmail($match['email'])) {
return array(
'name' => trim($match['name']),
'email' => $match['email'],
);
}
}
throw new \InvalidArgumentException(
'Invalid author string. Must be in the format: '.
'John Smith <john@example.com>'
);
}
protected function findPackages($name)
{
return $this->getRepos()->search($name);
}
protected function getRepos()
{
if (!$this->repos) {
$this->repos = new CompositeRepository(array_merge(
array(new PlatformRepository),
RepositoryFactory::defaultRepos($this->getIO())
));
}
return $this->repos;
}
protected function determineRequirements(InputInterface $input, OutputInterface $output, $requires = array(), $phpVersion = null, $preferredStability = 'stable', $checkProvidedVersions = true)
{
if ($requires) {
$requires = $this->normalizeRequirements($requires);
$result = array();
$io = $this->getIO();
foreach ($requires as $requirement) {
if (!isset($requirement['version'])) {
// determine the best version automatically
list($name, $version) = $this->findBestVersionAndNameForPackage($input, $requirement['name'], $phpVersion, $preferredStability);
$requirement['version'] = $version;
// replace package name from packagist.org
$requirement['name'] = $name;
$io->writeError(sprintf(
'Using version <info>%s</info> for <info>%s</info>',
$requirement['version'],
$requirement['name']
));
} else {
// check that the specified version/constraint exists before we proceed
list($name, $version) = $this->findBestVersionAndNameForPackage($input, $requirement['name'], $phpVersion, $preferredStability, $checkProvidedVersions ? $requirement['version'] : null, 'dev');
// replace package name from packagist.org
$requirement['name'] = $name;
}
$result[] = $requirement['name'] . ' ' . $requirement['version'];
}
return $result;
}
$versionParser = new VersionParser();
$io = $this->getIO();
while (null !== $package = $io->ask('Search for a package: ')) {
$matches = $this->findPackages($package);
if (count($matches)) {
$exactMatch = null;
$choices = array();
foreach ($matches as $position => $foundPackage) {
$abandoned = '';
if (isset($foundPackage['abandoned'])) {
if (is_string($foundPackage['abandoned'])) {
$replacement = sprintf('Use %s instead', $foundPackage['abandoned']);
} else {
$replacement = 'No replacement was suggested';
}
$abandoned = sprintf('<warning>Abandoned. %s.</warning>', $replacement);
}
$choices[] = sprintf(' <info>%5s</info> %s %s', "[$position]", $foundPackage['name'], $abandoned);
if ($foundPackage['name'] === $package) {
$exactMatch = true;
break;
}
}
// no match, prompt which to pick
if (!$exactMatch) {
$io->writeError(array(
'',
sprintf('Found <info>%s</info> packages matching <info>%s</info>', count($matches), $package),
'',
));
$io->writeError($choices);
$io->writeError('');
$validator = function ($selection) use ($matches, $versionParser) {
if ('' === $selection) {
return false;
}
if (is_numeric($selection) && isset($matches[(int) $selection])) {
$package = $matches[(int) $selection];
return $package['name'];
}
if (preg_match('{^\s*(?P<name>[\S/]+)(?:\s+(?P<version>\S+))?\s*$}', $selection, $packageMatches)) {
if (isset($packageMatches['version'])) {
// parsing `acme/example ~2.3`
// validate version constraint
$versionParser->parseConstraints($packageMatches['version']);
return $packageMatches['name'].' '.$packageMatches['version'];
}
// parsing `acme/example`
return $packageMatches['name'];
}
throw new \Exception('Not a valid selection');
};
$package = $io->askAndValidate(
'Enter package # to add, or the complete package name if it is not listed: ',
$validator,
3,
false
);
}
// no constraint yet, determine the best version automatically
if (false !== $package && false === strpos($package, ' ')) {
$validator = function ($input) {
$input = trim($input);
return $input ?: false;
};
$constraint = $io->askAndValidate(
'Enter the version constraint to require (or leave blank to use the latest version): ',
$validator,
3,
false
);
if (false === $constraint) {
list($name, $constraint) = $this->findBestVersionAndNameForPackage($input, $package, $phpVersion, $preferredStability);
$io->writeError(sprintf(
'Using version <info>%s</info> for <info>%s</info>',
$constraint,
$package
));
}
$package .= ' '.$constraint;
}
if (false !== $package) {
$requires[] = $package;
}
}
}
return $requires;
}
protected function formatAuthors($author)
{
return array($this->parseAuthorString($author));
}
protected function formatRequirements(array $requirements)
{
$requires = array();
$requirements = $this->normalizeRequirements($requirements);
foreach ($requirements as $requirement) {
$requires[$requirement['name']] = $requirement['version'];
}
return $requires;
}
protected function getGitConfig()
{
if (null !== $this->gitConfig) {
return $this->gitConfig;
}
$finder = new ExecutableFinder();
$gitBin = $finder->find('git');
// TODO in v3 always call with an array
if (method_exists('Symfony\Component\Process\Process', 'fromShellCommandline')) {
$cmd = new Process(array($gitBin, 'config', '-l'));
} else {
$cmd = new Process(sprintf('%s config -l', ProcessExecutor::escape($gitBin)));
}
$cmd->run();
if ($cmd->isSuccessful()) {
$this->gitConfig = array();
preg_match_all('{^([^=]+)=(.*)$}m', $cmd->getOutput(), $matches, PREG_SET_ORDER);
foreach ($matches as $match) {
$this->gitConfig[$match[1]] = $match[2];
}
return $this->gitConfig;
}
return $this->gitConfig = array();
}
/**
* Checks the local .gitignore file for the Composer vendor directory.
*
* Tested patterns include:
* "/$vendor"
* "$vendor"
* "$vendor/"
* "/$vendor/"
* "/$vendor/*"
* "$vendor/*"
*
* @param string $ignoreFile
* @param string $vendor
*
* @return bool
*/
protected function hasVendorIgnore($ignoreFile, $vendor = 'vendor')
{
if (!file_exists($ignoreFile)) {
return false;
}
$pattern = sprintf('{^/?%s(/\*?)?$}', preg_quote($vendor));
$lines = file($ignoreFile, FILE_IGNORE_NEW_LINES);
foreach ($lines as $line) {
if (preg_match($pattern, $line)) {
return true;
}
}
return false;
}
protected function normalizeRequirements(array $requirements)
{
$parser = new VersionParser();
return $parser->parseNameVersionPairs($requirements);
}
protected function addVendorIgnore($ignoreFile, $vendor = '/vendor/')
{
$contents = "";
if (file_exists($ignoreFile)) {
$contents = file_get_contents($ignoreFile);
if ("\n" !== substr($contents, 0, -1)) {
$contents .= "\n";
}
}
file_put_contents($ignoreFile, $contents . $vendor. "\n");
}
protected function isValidEmail($email)
{
// assume it's valid if we can't validate it
if (!function_exists('filter_var')) {
return true;
}
// php <5.3.3 has a very broken email validator, so bypass checks
if (PHP_VERSION_ID < 50303) {
return true;
}
return false !== filter_var($email, FILTER_VALIDATE_EMAIL);
}
private function getPool(InputInterface $input, $minimumStability = null)
{
$key = $minimumStability ?: 'default';
if (!isset($this->pools[$key])) {
$this->pools[$key] = $pool = new Pool($minimumStability ?: $this->getMinimumStability($input));
$pool->addRepository($this->getRepos());
}
return $this->pools[$key];
}
private function getMinimumStability(InputInterface $input)
{
if ($input->hasOption('stability')) {
return $input->getOption('stability') ?: 'stable';
}
$file = Factory::getComposerFile();
if (is_file($file) && is_readable($file) && is_array($composer = json_decode(file_get_contents($file), true))) {
if (!empty($composer['minimum-stability'])) {
return $composer['minimum-stability'];
}
}
return 'stable';
}
/**
* Given a package name, this determines the best version to use in the require key.
*
* This returns a version with the ~ operator prefixed when possible.
*
* @param InputInterface $input
* @param string $name
* @param string|null $phpVersion
* @param string $preferredStability
* @param string|null $requiredVersion
* @param string $minimumStability
* @throws \InvalidArgumentException
* @return array name version
*/
private function findBestVersionAndNameForPackage(InputInterface $input, $name, $phpVersion, $preferredStability = 'stable', $requiredVersion = null, $minimumStability = null)
{
// find the latest version allowed in this pool
$versionSelector = new VersionSelector($this->getPool($input, $minimumStability));
$ignorePlatformReqs = $input->hasOption('ignore-platform-reqs') && $input->getOption('ignore-platform-reqs');
// ignore phpVersion if platform requirements are ignored
if ($ignorePlatformReqs) {
$phpVersion = null;
}
$package = $versionSelector->findBestCandidate($name, $requiredVersion, $phpVersion, $preferredStability);
if (!$package) {
// platform packages can not be found in the pool in versions other than the local platform's has
// so if platform reqs are ignored we just take the user's word for it
if ($ignorePlatformReqs && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $name)) {
return array($name, $requiredVersion ?: '*');
}
// Check whether the PHP version was the problem
if ($phpVersion && $versionSelector->findBestCandidate($name, $requiredVersion, null, $preferredStability)) {
throw new \InvalidArgumentException(sprintf(
'Package %s at version %s has a PHP requirement incompatible with your PHP version (%s)',
$name,
$requiredVersion,
$phpVersion
));
}
// Check whether the required version was the problem
if ($requiredVersion && $versionSelector->findBestCandidate($name, null, $phpVersion, $preferredStability)) {
throw new \InvalidArgumentException(sprintf(
'Could not find package %s in a version matching %s',
$name,
$requiredVersion
));
}
// Check whether the PHP version was the problem
if ($phpVersion && $versionSelector->findBestCandidate($name)) {
throw new \InvalidArgumentException(sprintf(
'Could not find package %s in any version matching your PHP version (%s)',
$name,
$phpVersion
));
}
// Check for similar names/typos
$similar = $this->findSimilar($name);
if ($similar) {
// Check whether the minimum stability was the problem but the package exists
if ($requiredVersion === null && in_array($name, $similar, true)) {
throw new \InvalidArgumentException(sprintf(
'Could not find a version of package %s matching your minimum-stability (%s). Require it with an explicit version constraint allowing its desired stability.',
$name,
$this->getMinimumStability($input)
));
}
throw new \InvalidArgumentException(sprintf(
"Could not find package %s.\n\nDid you mean " . (count($similar) > 1 ? 'one of these' : 'this') . "?\n %s",
$name,
implode("\n ", $similar)
));
}
throw new \InvalidArgumentException(sprintf(
'Could not find a matching version of package %s. Check the package spelling, your version constraint and that the package is available in a stability which matches your minimum-stability (%s).',
$name,
$this->getMinimumStability($input)
));
}
return array(
$package->getPrettyName(),
$versionSelector->findRecommendedRequireVersion($package),
);
}
private function findSimilar($package)
{
try {
$results = $this->repos->search($package);
} catch (\Exception $e) {
// ignore search errors
return array();
}
$similarPackages = array();
foreach ($results as $result) {
$similarPackages[$result['name']] = levenshtein($package, $result['name']);
}
asort($similarPackages);
return array_keys(array_slice($similarPackages, 0, 5));
}
private function installDependencies($output)
{
try {
$installCommand = $this->getApplication()->find('install');
$installCommand->run(new ArrayInput(array()), $output);
} catch (\Exception $e) {
$this->getIO()->writeError('Could not install dependencies. Run `composer install` to see more information.');
}
}
private function hasDependencies($options)
{
$requires = (array) $options['require'];
$devRequires = isset($options['require-dev']) ? (array) $options['require-dev'] : array();
return !empty($requires) || !empty($devRequires);
}
}
@@ -0,0 +1,124 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Command;
use Composer\Installer;
use Composer\Plugin\CommandEvent;
use Composer\Plugin\PluginEvents;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Output\OutputInterface;
/**
* @author Jordi Boggiano <j.boggiano@seld.be>
* @author Ryan Weaver <ryan@knplabs.com>
* @author Konstantin Kudryashov <ever.zet@gmail.com>
* @author Nils Adermann <naderman@naderman.de>
*/
class InstallCommand extends BaseCommand
{
protected function configure()
{
$this
->setName('install')
->setAliases(array('i'))
->setDescription('Installs the project dependencies from the composer.lock file if present, or falls back on the composer.json.')
->setDefinition(array(
new InputOption('prefer-source', null, InputOption::VALUE_NONE, 'Forces installation from package sources when possible, including VCS information.'),
new InputOption('prefer-dist', null, InputOption::VALUE_NONE, 'Forces installation from package dist even for dev versions.'),
new InputOption('dry-run', null, InputOption::VALUE_NONE, 'Outputs the operations but will not execute anything (implicitly enables --verbose).'),
new InputOption('dev', null, InputOption::VALUE_NONE, 'Enables installation of require-dev packages (enabled by default, only present for BC).'),
new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables installation of require-dev packages.'),
new InputOption('no-custom-installers', null, InputOption::VALUE_NONE, 'DEPRECATED: Use no-plugins instead.'),
new InputOption('no-autoloader', null, InputOption::VALUE_NONE, 'Skips autoloader generation'),
new InputOption('no-scripts', null, InputOption::VALUE_NONE, 'Skips the execution of all scripts defined in composer.json file.'),
new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'),
new InputOption('no-suggest', null, InputOption::VALUE_NONE, 'Do not show package suggestions.'),
new InputOption('verbose', 'v|vv|vvv', InputOption::VALUE_NONE, 'Shows more details including new commits pulled in when updating packages.'),
new InputOption('optimize-autoloader', 'o', InputOption::VALUE_NONE, 'Optimize autoloader during autoloader dump'),
new InputOption('classmap-authoritative', 'a', InputOption::VALUE_NONE, 'Autoload classes from the classmap only. Implicitly enables `--optimize-autoloader`.'),
new InputOption('apcu-autoloader', null, InputOption::VALUE_NONE, 'Use APCu to cache found/not-found classes.'),
new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'),
new InputArgument('packages', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, 'Should not be provided, use composer require instead to add a given package to composer.json.'),
))
->setHelp(
<<<EOT
The <info>install</info> command reads the composer.lock file from
the current directory, processes it, and downloads and installs all the
libraries and dependencies outlined in that file. If the file does not
exist it will look for composer.json and do the same.
<info>php composer.phar install</info>
Read more at https://getcomposer.org/doc/03-cli.md#install-i
EOT
)
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$io = $this->getIO();
if ($args = $input->getArgument('packages')) {
$io->writeError('<error>Invalid argument '.implode(' ', $args).'. Use "composer require '.implode(' ', $args).'" instead to add packages to your composer.json.</error>');
return 1;
}
if ($input->getOption('no-custom-installers')) {
$io->writeError('<warning>You are using the deprecated option "no-custom-installers". Use "no-plugins" instead.</warning>');
$input->setOption('no-plugins', true);
}
if ($input->getOption('dev')) {
$io->writeError('<warning>You are using the deprecated option "dev". Dev packages are installed by default now.</warning>');
}
$composer = $this->getComposer(true, $input->getOption('no-plugins'));
$composer->getDownloadManager()->setOutputProgress(!$input->getOption('no-progress'));
$commandEvent = new CommandEvent(PluginEvents::COMMAND, 'install', $input, $output);
$composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
$install = Installer::create($io, $composer);
$config = $composer->getConfig();
list($preferSource, $preferDist) = $this->getPreferredInstallOptions($config, $input);
$optimize = $input->getOption('optimize-autoloader') || $config->get('optimize-autoloader');
$authoritative = $input->getOption('classmap-authoritative') || $config->get('classmap-authoritative');
$apcu = $input->getOption('apcu-autoloader') || $config->get('apcu-autoloader');
$install
->setDryRun($input->getOption('dry-run'))
->setVerbose($input->getOption('verbose'))
->setPreferSource($preferSource)
->setPreferDist($preferDist)
->setDevMode(!$input->getOption('no-dev'))
->setDumpAutoloader(!$input->getOption('no-autoloader'))
->setRunScripts(!$input->getOption('no-scripts'))
->setSkipSuggest($input->getOption('no-suggest'))
->setOptimizeAutoloader($optimize)
->setClassMapAuthoritative($authoritative)
->setApcuAutoloader($apcu)
->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs'))
;
if ($input->getOption('no-plugins')) {
$install->disablePlugins();
}
return $install->run();
}
}
@@ -0,0 +1,161 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Command;
use Composer\Json\JsonFile;
use Composer\Plugin\CommandEvent;
use Composer\Plugin\PluginEvents;
use Composer\Package\PackageInterface;
use Composer\Repository\RepositoryInterface;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* @author Benoît Merlet <benoit.merlet@gmail.com>
*/
class LicensesCommand extends BaseCommand
{
protected function configure()
{
$this
->setName('licenses')
->setDescription('Shows information about licenses of dependencies.')
->setDefinition(array(
new InputOption('format', 'f', InputOption::VALUE_REQUIRED, 'Format of the output: text or json', 'text'),
new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables search in require-dev packages.'),
))
->setHelp(
<<<EOT
The license command displays detailed information about the licenses of
the installed dependencies.
Read more at https://getcomposer.org/doc/03-cli.md#licenses
EOT
)
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$composer = $this->getComposer();
$commandEvent = new CommandEvent(PluginEvents::COMMAND, 'licenses', $input, $output);
$composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
$root = $composer->getPackage();
$repo = $composer->getRepositoryManager()->getLocalRepository();
if ($input->getOption('no-dev')) {
$packages = $this->filterRequiredPackages($repo, $root);
} else {
$packages = $this->appendPackages($repo->getPackages(), array());
}
ksort($packages);
$io = $this->getIO();
switch ($format = $input->getOption('format')) {
case 'text':
$io->write('Name: <comment>'.$root->getPrettyName().'</comment>');
$io->write('Version: <comment>'.$root->getFullPrettyVersion().'</comment>');
$io->write('Licenses: <comment>'.(implode(', ', $root->getLicense()) ?: 'none').'</comment>');
$io->write('Dependencies:');
$io->write('');
$table = new Table($output);
$table->setStyle('compact');
$tableStyle = $table->getStyle();
$tableStyle->setVerticalBorderChar('');
$tableStyle->setCellRowContentFormat('%s ');
$table->setHeaders(array('Name', 'Version', 'License'));
foreach ($packages as $package) {
$table->addRow(array(
$package->getPrettyName(),
$package->getFullPrettyVersion(),
implode(', ', $package->getLicense()) ?: 'none',
));
}
$table->render();
break;
case 'json':
$dependencies = array();
foreach ($packages as $package) {
$dependencies[$package->getPrettyName()] = array(
'version' => $package->getFullPrettyVersion(),
'license' => $package->getLicense(),
);
}
$io->write(JsonFile::encode(array(
'name' => $root->getPrettyName(),
'version' => $root->getFullPrettyVersion(),
'license' => $root->getLicense(),
'dependencies' => $dependencies,
)));
break;
default:
throw new \RuntimeException(sprintf('Unsupported format "%s". See help for supported formats.', $format));
}
return 0;
}
/**
* Find package requires and child requires
*
* @param RepositoryInterface $repo
* @param PackageInterface $package
* @param array $bucket
* @return array
*/
private function filterRequiredPackages(RepositoryInterface $repo, PackageInterface $package, $bucket = array())
{
$requires = array_keys($package->getRequires());
$packageListNames = array_keys($bucket);
$packages = array_filter(
$repo->getPackages(),
function ($package) use ($requires, $packageListNames) {
return in_array($package->getName(), $requires) && !in_array($package->getName(), $packageListNames);
}
);
$bucket = $this->appendPackages($packages, $bucket);
foreach ($packages as $package) {
$bucket = $this->filterRequiredPackages($repo, $package, $bucket);
}
return $bucket;
}
/**
* Adds packages to the package list
*
* @param array $packages the list of packages to add
* @param array $bucket the list to add packages to
* @return array
*/
public function appendPackages(array $packages, array $bucket)
{
foreach ($packages as $package) {
$bucket[$package->getName()] = $package;
}
return $bucket;
}
}
@@ -0,0 +1,95 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
class OutdatedCommand extends ShowCommand
{
protected function configure()
{
$this
->setName('outdated')
->setDescription('Shows a list of installed packages that have updates available, including their latest version.')
->setDefinition(array(
new InputArgument('package', InputArgument::OPTIONAL, 'Package to inspect. Or a name including a wildcard (*) to filter lists of packages instead.'),
new InputOption('outdated', 'o', InputOption::VALUE_NONE, 'Show only packages that are outdated (this is the default, but present here for compat with `show`'),
new InputOption('all', 'a', InputOption::VALUE_NONE, 'Show all installed packages with their latest versions'),
new InputOption('direct', 'D', InputOption::VALUE_NONE, 'Shows only packages that are directly required by the root package'),
new InputOption('strict', null, InputOption::VALUE_NONE, 'Return a non-zero exit code when there are outdated packages'),
new InputOption('minor-only', 'm', InputOption::VALUE_NONE, 'Show only packages that have minor SemVer-compatible updates. Use with the --outdated option.'),
new InputOption('format', 'f', InputOption::VALUE_REQUIRED, 'Format of the output: text or json', 'text'),
new InputOption('ignore', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Ignore specified package(s). Use it with the --outdated option if you don\'t want to be informed about new versions of some packages.'),
))
->setHelp(
<<<EOT
The outdated command is just a proxy for `composer show -l`
The color coding (or signage if you have ANSI colors disabled) for dependency versions is as such:
- <info>green</info> (=): Dependency is in the latest version and is up to date.
- <comment>yellow</comment> (~): Dependency has a new version available that includes backwards
compatibility breaks according to semver, so upgrade when you can but it
may involve work.
- <highlight>red</highlight> (!): Dependency has a new version that is semver-compatible and you should upgrade it.
Read more at https://getcomposer.org/doc/03-cli.md#outdated
EOT
)
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$args = array(
'command' => 'show',
'--latest' => true,
);
if (!$input->getOption('all')) {
$args['--outdated'] = true;
}
if ($input->getOption('direct')) {
$args['--direct'] = true;
}
if ($input->getArgument('package')) {
$args['package'] = $input->getArgument('package');
}
if ($input->getOption('strict')) {
$args['--strict'] = true;
}
if ($input->getOption('minor-only')) {
$args['--minor-only'] = true;
}
$args['--format'] = $input->getOption('format');
$args['--ignore'] = $input->getOption('ignore');
$input = new ArrayInput($args);
return $this->getApplication()->run($input, $output);
}
/**
* {@inheritDoc}
*/
public function isProxyCommand()
{
return true;
}
}
@@ -0,0 +1,57 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* @author Niels Keurentjes <niels.keurentjes@omines.com>
*/
class ProhibitsCommand extends BaseDependencyCommand
{
/**
* Configure command metadata.
*/
protected function configure()
{
parent::configure();
$this
->setName('prohibits')
->setAliases(array('why-not'))
->setDescription('Shows which packages prevent the given package from being installed.')
->setHelp(
<<<EOT
Displays detailed information about why a package cannot be installed.
<info>php composer.phar prohibits composer/composer</info>
Read more at https://getcomposer.org/doc/03-cli.md#prohibits-why-not-
EOT
)
;
}
/**
* Execute the function.
*
* @param InputInterface $input
* @param OutputInterface $output
* @return int
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
return parent::doExecute($input, $output, true);
}
}
@@ -0,0 +1,163 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Command;
use Composer\Config\JsonConfigSource;
use Composer\Installer;
use Composer\Plugin\CommandEvent;
use Composer\Plugin\PluginEvents;
use Composer\Json\JsonFile;
use Composer\Factory;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Output\OutputInterface;
use Composer\Package\BasePackage;
/**
* @author Pierre du Plessis <pdples@gmail.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
class RemoveCommand extends BaseCommand
{
protected function configure()
{
$this
->setName('remove')
->setDescription('Removes a package from the require or require-dev.')
->setDefinition(array(
new InputArgument('packages', InputArgument::IS_ARRAY | InputArgument::REQUIRED, 'Packages that should be removed.'),
new InputOption('dev', null, InputOption::VALUE_NONE, 'Removes a package from the require-dev section.'),
new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'),
new InputOption('no-update', null, InputOption::VALUE_NONE, 'Disables the automatic update of the dependencies.'),
new InputOption('no-scripts', null, InputOption::VALUE_NONE, 'Skips the execution of all scripts defined in composer.json file.'),
new InputOption('update-no-dev', null, InputOption::VALUE_NONE, 'Run the dependency update with the --no-dev option.'),
new InputOption('update-with-dependencies', null, InputOption::VALUE_NONE, 'Allows inherited dependencies to be updated with explicit dependencies. (Deprecrated, is now default behavior)'),
new InputOption('no-update-with-dependencies', null, InputOption::VALUE_NONE, 'Does not allow inherited dependencies to be updated with explicit dependencies.'),
new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'),
new InputOption('optimize-autoloader', 'o', InputOption::VALUE_NONE, 'Optimize autoloader during autoloader dump'),
new InputOption('classmap-authoritative', 'a', InputOption::VALUE_NONE, 'Autoload classes from the classmap only. Implicitly enables `--optimize-autoloader`.'),
new InputOption('apcu-autoloader', null, InputOption::VALUE_NONE, 'Use APCu to cache found/not-found classes.'),
))
->setHelp(
<<<EOT
The <info>remove</info> command removes a package from the current
list of installed packages
<info>php composer.phar remove</info>
Read more at https://getcomposer.org/doc/03-cli.md#remove
EOT
)
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$packages = $input->getArgument('packages');
$packages = array_map('strtolower', $packages);
$file = Factory::getComposerFile();
$jsonFile = new JsonFile($file);
$composer = $jsonFile->read();
$composerBackup = file_get_contents($jsonFile->getPath());
$json = new JsonConfigSource($jsonFile);
$type = $input->getOption('dev') ? 'require-dev' : 'require';
$altType = !$input->getOption('dev') ? 'require-dev' : 'require';
$io = $this->getIO();
if ($input->getOption('update-with-dependencies')) {
$io->writeError('<warning>You are using the deprecated option "update-with-dependencies". This is now default behaviour. The --no-update-with-dependencies option can be used to remove a package without its dependencies.</warning>');
}
// make sure name checks are done case insensitively
foreach (array('require', 'require-dev') as $linkType) {
if (isset($composer[$linkType])) {
foreach ($composer[$linkType] as $name => $version) {
$composer[$linkType][strtolower($name)] = $name;
}
}
}
foreach ($packages as $package) {
if (isset($composer[$type][$package])) {
$json->removeLink($type, $composer[$type][$package]);
} elseif (isset($composer[$altType][$package])) {
$io->writeError('<warning>' . $composer[$altType][$package] . ' could not be found in ' . $type . ' but it is present in ' . $altType . '</warning>');
if ($io->isInteractive()) {
if ($io->askConfirmation('Do you want to remove it from ' . $altType . ' [<comment>yes</comment>]? ', true)) {
$json->removeLink($altType, $composer[$altType][$package]);
}
}
} elseif (isset($composer[$type]) && $matches = preg_grep(BasePackage::packageNameToRegexp($package), array_keys($composer[$type]))) {
foreach ($matches as $matchedPackage) {
$json->removeLink($type, $matchedPackage);
}
} elseif (isset($composer[$altType]) && $matches = preg_grep(BasePackage::packageNameToRegexp($package), array_keys($composer[$altType]))) {
foreach ($matches as $matchedPackage) {
$io->writeError('<warning>' . $matchedPackage . ' could not be found in ' . $type . ' but it is present in ' . $altType . '</warning>');
if ($io->isInteractive()) {
if ($io->askConfirmation('Do you want to remove it from ' . $altType . ' [<comment>yes</comment>]? ', true)) {
$json->removeLink($altType, $matchedPackage);
}
}
}
} else {
$io->writeError('<warning>'.$package.' is not required in your composer.json and has not been removed</warning>');
}
}
if ($input->getOption('no-update')) {
return 0;
}
// Update packages
$this->resetComposer();
$composer = $this->getComposer(true, $input->getOption('no-plugins'));
$composer->getDownloadManager()->setOutputProgress(!$input->getOption('no-progress'));
$commandEvent = new CommandEvent(PluginEvents::COMMAND, 'remove', $input, $output);
$composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
$install = Installer::create($io, $composer);
$updateDevMode = !$input->getOption('update-no-dev');
$optimize = $input->getOption('optimize-autoloader') || $composer->getConfig()->get('optimize-autoloader');
$authoritative = $input->getOption('classmap-authoritative') || $composer->getConfig()->get('classmap-authoritative');
$apcu = $input->getOption('apcu-autoloader') || $composer->getConfig()->get('apcu-autoloader');
$install
->setVerbose($input->getOption('verbose'))
->setDevMode($updateDevMode)
->setOptimizeAutoloader($optimize)
->setClassMapAuthoritative($authoritative)
->setApcuAutoloader($apcu)
->setUpdate(true)
->setUpdateWhitelist($packages)
->setWhitelistTransitiveDependencies(!$input->getOption('no-update-with-dependencies'))
->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs'))
->setRunScripts(!$input->getOption('no-scripts'))
;
$status = $install->run();
if ($status !== 0) {
$io->writeError("\n".'<error>Removal failed, reverting '.$file.' to its original content.</error>');
file_put_contents($jsonFile->getPath(), $composerBackup);
}
return $status;
}
}
@@ -0,0 +1,277 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Composer\Factory;
use Composer\Installer;
use Composer\Json\JsonFile;
use Composer\Json\JsonManipulator;
use Composer\Package\Version\VersionParser;
use Composer\Plugin\CommandEvent;
use Composer\Plugin\PluginEvents;
use Composer\Repository\CompositeRepository;
use Composer\Repository\PlatformRepository;
use Composer\IO\IOInterface;
use Composer\Util\Silencer;
/**
* @author Jérémy Romey <jeremy@free-agent.fr>
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
class RequireCommand extends InitCommand
{
private $newlyCreated;
private $json;
private $file;
private $composerBackup;
protected function configure()
{
$this
->setName('require')
->setDescription('Adds required packages to your composer.json and installs them.')
->setDefinition(array(
new InputArgument('packages', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, 'Optional package name can also include a version constraint, e.g. foo/bar or foo/bar:1.0.0 or foo/bar=1.0.0 or "foo/bar 1.0.0"'),
new InputOption('dev', null, InputOption::VALUE_NONE, 'Add requirement to require-dev.'),
new InputOption('prefer-source', null, InputOption::VALUE_NONE, 'Forces installation from package sources when possible, including VCS information.'),
new InputOption('prefer-dist', null, InputOption::VALUE_NONE, 'Forces installation from package dist even for dev versions.'),
new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'),
new InputOption('no-suggest', null, InputOption::VALUE_NONE, 'Do not show package suggestions.'),
new InputOption('no-update', null, InputOption::VALUE_NONE, 'Disables the automatic update of the dependencies.'),
new InputOption('no-scripts', null, InputOption::VALUE_NONE, 'Skips the execution of all scripts defined in composer.json file.'),
new InputOption('update-no-dev', null, InputOption::VALUE_NONE, 'Run the dependency update with the --no-dev option.'),
new InputOption('update-with-dependencies', null, InputOption::VALUE_NONE, 'Allows inherited dependencies to be updated, except those that are root requirements.'),
new InputOption('update-with-all-dependencies', null, InputOption::VALUE_NONE, 'Allows all inherited dependencies to be updated, including those that are root requirements.'),
new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'),
new InputOption('prefer-stable', null, InputOption::VALUE_NONE, 'Prefer stable versions of dependencies.'),
new InputOption('prefer-lowest', null, InputOption::VALUE_NONE, 'Prefer lowest versions of dependencies.'),
new InputOption('sort-packages', null, InputOption::VALUE_NONE, 'Sorts packages when adding/updating a new dependency'),
new InputOption('optimize-autoloader', 'o', InputOption::VALUE_NONE, 'Optimize autoloader during autoloader dump'),
new InputOption('classmap-authoritative', 'a', InputOption::VALUE_NONE, 'Autoload classes from the classmap only. Implicitly enables `--optimize-autoloader`.'),
new InputOption('apcu-autoloader', null, InputOption::VALUE_NONE, 'Use APCu to cache found/not-found classes.'),
))
->setHelp(
<<<EOT
The require command adds required packages to your composer.json and installs them.
If you do not specify a package, composer will prompt you to search for a package, and given results, provide a list of
matches to require.
If you do not specify a version constraint, composer will choose a suitable one based on the available package versions.
If you do not want to install the new dependencies immediately you can call it with --no-update
Read more at https://getcomposer.org/doc/03-cli.md#require
EOT
)
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
if (function_exists('pcntl_async_signals')) {
pcntl_async_signals(true);
pcntl_signal(SIGINT, array($this, 'revertComposerFile'));
pcntl_signal(SIGTERM, array($this, 'revertComposerFile'));
pcntl_signal(SIGHUP, array($this, 'revertComposerFile'));
}
$this->file = Factory::getComposerFile();
$io = $this->getIO();
$this->newlyCreated = !file_exists($this->file);
if ($this->newlyCreated && !file_put_contents($this->file, "{\n}\n")) {
$io->writeError('<error>'.$this->file.' could not be created.</error>');
return 1;
}
// check for readability by reading the file as is_readable can not be trusted on network-mounts
// see https://github.com/composer/composer/issues/8231 and https://bugs.php.net/bug.php?id=68926
if (!is_readable($this->file) && false === Silencer::call('file_get_contents', $this->file)) {
$io->writeError('<error>'.$this->file.' is not readable.</error>');
return 1;
}
if (filesize($this->file) === 0) {
file_put_contents($this->file, "{\n}\n");
}
$this->json = new JsonFile($this->file);
$this->composerBackup = file_get_contents($this->json->getPath());
// check for writability by writing to the file as is_writable can not be trusted on network-mounts
// see https://github.com/composer/composer/issues/8231 and https://bugs.php.net/bug.php?id=68926
if (!is_writable($this->file) && !Silencer::call('file_put_contents', $this->file, $this->composerBackup)) {
$io->writeError('<error>'.$this->file.' is not writable.</error>');
return 1;
}
$composer = $this->getComposer(true, $input->getOption('no-plugins'));
$repos = $composer->getRepositoryManager()->getRepositories();
$platformOverrides = $composer->getConfig()->get('platform') ?: array();
// initialize $this->repos as it is used by the parent InitCommand
$this->repos = new CompositeRepository(array_merge(
array(new PlatformRepository(array(), $platformOverrides)),
$repos
));
if ($composer->getPackage()->getPreferStable()) {
$preferredStability = 'stable';
} else {
$preferredStability = $composer->getPackage()->getMinimumStability();
}
$phpVersion = $this->repos->findPackage('php', '*')->getPrettyVersion();
try {
$requirements = $this->determineRequirements($input, $output, $input->getArgument('packages'), $phpVersion, $preferredStability, !$input->getOption('no-update'));
} catch (\Exception $e) {
if ($this->newlyCreated) {
throw new \RuntimeException('No composer.json present in the current directory, this may be the cause of the following exception.', 0, $e);
}
throw $e;
}
$requireKey = $input->getOption('dev') ? 'require-dev' : 'require';
$removeKey = $input->getOption('dev') ? 'require' : 'require-dev';
$requirements = $this->formatRequirements($requirements);
// validate requirements format
$versionParser = new VersionParser();
foreach ($requirements as $package => $constraint) {
if (strtolower($package) === $composer->getPackage()->getName()) {
$io->writeError(sprintf('<error>Root package \'%s\' cannot require itself in its composer.json</error>', $package));
return 1;
}
$versionParser->parseConstraints($constraint);
}
$sortPackages = $input->getOption('sort-packages') || $composer->getConfig()->get('sort-packages');
if (!$this->updateFileCleanly($this->json, $requirements, $requireKey, $removeKey, $sortPackages)) {
$composerDefinition = $this->json->read();
foreach ($requirements as $package => $version) {
$composerDefinition[$requireKey][$package] = $version;
unset($composerDefinition[$removeKey][$package]);
}
$this->json->write($composerDefinition);
}
$io->writeError('<info>'.$this->file.' has been '.($this->newlyCreated ? 'created' : 'updated').'</info>');
if ($input->getOption('no-update')) {
return 0;
}
try {
return $this->doUpdate($input, $output, $io, $requirements);
} catch (\Exception $e) {
$this->revertComposerFile(false);
throw $e;
}
}
private function doUpdate(InputInterface $input, OutputInterface $output, IOInterface $io, array $requirements)
{
// Update packages
$this->resetComposer();
$composer = $this->getComposer(true, $input->getOption('no-plugins'));
$composer->getDownloadManager()->setOutputProgress(!$input->getOption('no-progress'));
$updateDevMode = !$input->getOption('update-no-dev');
$optimize = $input->getOption('optimize-autoloader') || $composer->getConfig()->get('optimize-autoloader');
$authoritative = $input->getOption('classmap-authoritative') || $composer->getConfig()->get('classmap-authoritative');
$apcu = $input->getOption('apcu-autoloader') || $composer->getConfig()->get('apcu-autoloader');
$commandEvent = new CommandEvent(PluginEvents::COMMAND, 'require', $input, $output);
$composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
$install = Installer::create($io, $composer);
$install
->setVerbose($input->getOption('verbose'))
->setPreferSource($input->getOption('prefer-source'))
->setPreferDist($input->getOption('prefer-dist'))
->setDevMode($updateDevMode)
->setRunScripts(!$input->getOption('no-scripts'))
->setSkipSuggest($input->getOption('no-suggest'))
->setOptimizeAutoloader($optimize)
->setClassMapAuthoritative($authoritative)
->setApcuAutoloader($apcu)
->setUpdate(true)
->setUpdateWhitelist(array_keys($requirements))
->setWhitelistTransitiveDependencies($input->getOption('update-with-dependencies'))
->setWhitelistAllDependencies($input->getOption('update-with-all-dependencies'))
->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs'))
->setPreferStable($input->getOption('prefer-stable'))
->setPreferLowest($input->getOption('prefer-lowest'))
;
$status = $install->run();
if ($status !== 0) {
$this->revertComposerFile(false);
}
return $status;
}
private function updateFileCleanly($json, array $new, $requireKey, $removeKey, $sortPackages)
{
$contents = file_get_contents($json->getPath());
$manipulator = new JsonManipulator($contents);
foreach ($new as $package => $constraint) {
if (!$manipulator->addLink($requireKey, $package, $constraint, $sortPackages)) {
return false;
}
if (!$manipulator->removeSubNode($removeKey, $package)) {
return false;
}
}
file_put_contents($json->getPath(), $manipulator->getContents());
return true;
}
protected function interact(InputInterface $input, OutputInterface $output)
{
return;
}
public function revertComposerFile($hardExit = true)
{
$io = $this->getIO();
if ($this->newlyCreated) {
$io->writeError("\n".'<error>Installation failed, deleting '.$this->file.'.</error>');
unlink($this->json->getPath());
} else {
$io->writeError("\n".'<error>Installation failed, reverting '.$this->file.' to its original content.</error>');
file_put_contents($this->json->getPath(), $this->composerBackup);
}
if ($hardExit) {
exit(1);
}
}
}
@@ -0,0 +1,142 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Command;
use Composer\Script\Event as ScriptEvent;
use Composer\Script\ScriptEvents;
use Composer\Util\ProcessExecutor;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Helper\Table;
/**
* @author Fabien Potencier <fabien.potencier@gmail.com>
*/
class RunScriptCommand extends BaseCommand
{
/**
* @var array Array with command events
*/
protected $scriptEvents = array(
ScriptEvents::PRE_INSTALL_CMD,
ScriptEvents::POST_INSTALL_CMD,
ScriptEvents::PRE_UPDATE_CMD,
ScriptEvents::POST_UPDATE_CMD,
ScriptEvents::PRE_STATUS_CMD,
ScriptEvents::POST_STATUS_CMD,
ScriptEvents::POST_ROOT_PACKAGE_INSTALL,
ScriptEvents::POST_CREATE_PROJECT_CMD,
ScriptEvents::PRE_ARCHIVE_CMD,
ScriptEvents::POST_ARCHIVE_CMD,
ScriptEvents::PRE_AUTOLOAD_DUMP,
ScriptEvents::POST_AUTOLOAD_DUMP,
);
protected function configure()
{
$this
->setName('run-script')
->setAliases(array('run'))
->setDescription('Runs the scripts defined in composer.json.')
->setDefinition(array(
new InputArgument('script', InputArgument::OPTIONAL, 'Script name to run.'),
new InputArgument('args', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, ''),
new InputOption('timeout', null, InputOption::VALUE_REQUIRED, 'Sets script timeout in seconds, or 0 for never.'),
new InputOption('dev', null, InputOption::VALUE_NONE, 'Sets the dev mode.'),
new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables the dev mode.'),
new InputOption('list', 'l', InputOption::VALUE_NONE, 'List scripts.'),
))
->setHelp(
<<<EOT
The <info>run-script</info> command runs scripts defined in composer.json:
<info>php composer.phar run-script post-update-cmd</info>
Read more at https://getcomposer.org/doc/03-cli.md#run-script
EOT
)
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
if ($input->getOption('list')) {
return $this->listScripts($output);
} elseif (!$input->getArgument('script')) {
throw new \RuntimeException('Missing required argument "script"');
}
$script = $input->getArgument('script');
if (!in_array($script, $this->scriptEvents)) {
if (defined('Composer\Script\ScriptEvents::'.str_replace('-', '_', strtoupper($script)))) {
throw new \InvalidArgumentException(sprintf('Script "%s" cannot be run with this command', $script));
}
}
$composer = $this->getComposer();
$devMode = $input->getOption('dev') || !$input->getOption('no-dev');
$event = new ScriptEvent($script, $composer, $this->getIO(), $devMode);
$hasListeners = $composer->getEventDispatcher()->hasEventListeners($event);
if (!$hasListeners) {
throw new \InvalidArgumentException(sprintf('Script "%s" is not defined in this package', $script));
}
$args = $input->getArgument('args');
if (null !== $timeout = $input->getOption('timeout')) {
if (!ctype_digit($timeout)) {
throw new \RuntimeException('Timeout value must be numeric and positive if defined, or 0 for forever');
}
// Override global timeout set before in Composer by environment or config
ProcessExecutor::setTimeout((int) $timeout);
}
return $composer->getEventDispatcher()->dispatchScript($script, $devMode, $args);
}
protected function listScripts(OutputInterface $output)
{
$scripts = $this->getComposer()->getPackage()->getScripts();
if (!count($scripts)) {
return 0;
}
$io = $this->getIO();
$io->writeError('<info>scripts:</info>');
$table = array();
foreach ($scripts as $name => $script) {
$description = '';
try {
$cmd = $this->getApplication()->find($name);
if ($cmd instanceof ScriptAliasCommand) {
$description = $cmd->getDescription();
}
} catch (\Symfony\Component\Console\Exception\CommandNotFoundException $e) {
// ignore scripts that have no command associated, like native Composer script listeners
}
$table[] = array(' '.$name, $description);
}
$renderer = new Table($output);
$renderer->setStyle('compact');
$rendererStyle = $renderer->getStyle();
$rendererStyle->setVerticalBorderChar('');
$rendererStyle->setCellRowContentFormat('%s ');
$renderer->setRows($table)->render();
return 0;
}
}
@@ -0,0 +1,66 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Output\OutputInterface;
/**
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
class ScriptAliasCommand extends BaseCommand
{
private $script;
private $description;
public function __construct($script, $description)
{
$this->script = $script;
$this->description = empty($description) ? 'Runs the '.$script.' script as defined in composer.json.' : $description;
parent::__construct();
}
protected function configure()
{
$this
->setName($this->script)
->setDescription($this->description)
->setDefinition(array(
new InputOption('dev', null, InputOption::VALUE_NONE, 'Sets the dev mode.'),
new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables the dev mode.'),
new InputArgument('args', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, ''),
))
->setHelp(
<<<EOT
The <info>run-script</info> command runs scripts defined in composer.json:
<info>php composer.phar run-script post-update-cmd</info>
Read more at https://getcomposer.org/doc/03-cli.md#run-script
EOT
)
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$composer = $this->getComposer();
$args = $input->getArguments();
return $composer->getEventDispatcher()->dispatchScript($this->script, $input->getOption('dev') || !$input->getOption('no-dev'), $args['args']);
}
}
@@ -0,0 +1,85 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Command;
use Composer\Factory;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Composer\Repository\CompositeRepository;
use Composer\Repository\PlatformRepository;
use Composer\Repository\RepositoryInterface;
use Composer\Plugin\CommandEvent;
use Composer\Plugin\PluginEvents;
/**
* @author Robert Schönthal <seroscho@googlemail.com>
*/
class SearchCommand extends BaseCommand
{
protected $matches;
protected $lowMatches = array();
protected $tokens;
protected $output;
protected $onlyName;
protected function configure()
{
$this
->setName('search')
->setDescription('Searches for packages.')
->setDefinition(array(
new InputOption('only-name', 'N', InputOption::VALUE_NONE, 'Search only in name'),
new InputOption('type', 't', InputOption::VALUE_REQUIRED, 'Search for a specific package type'),
new InputArgument('tokens', InputArgument::IS_ARRAY | InputArgument::REQUIRED, 'tokens to search for'),
))
->setHelp(
<<<EOT
The search command searches for packages by its name
<info>php composer.phar search symfony composer</info>
Read more at https://getcomposer.org/doc/03-cli.md#search
EOT
)
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
// init repos
$platformRepo = new PlatformRepository;
$io = $this->getIO();
if (!($composer = $this->getComposer(false))) {
$composer = Factory::create($this->getIO(), array(), $input->hasParameterOption('--no-plugins'));
}
$localRepo = $composer->getRepositoryManager()->getLocalRepository();
$installedRepo = new CompositeRepository(array($localRepo, $platformRepo));
$repos = new CompositeRepository(array_merge(array($installedRepo), $composer->getRepositoryManager()->getRepositories()));
$commandEvent = new CommandEvent(PluginEvents::COMMAND, 'search', $input, $output);
$composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
$onlyName = $input->getOption('only-name');
$type = $input->getOption('type') ?: null;
$flags = $onlyName ? RepositoryInterface::SEARCH_NAME : RepositoryInterface::SEARCH_FULLTEXT;
$results = $repos->search(implode(' ', $input->getArgument('tokens')), $flags, $type);
foreach ($results as $result) {
$io->write($result['name'] . (isset($result['description']) ? ' '. $result['description'] : ''));
}
return 0;
}
}
@@ -0,0 +1,407 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Command;
use Composer\Composer;
use Composer\Factory;
use Composer\Config;
use Composer\Util\Filesystem;
use Composer\SelfUpdate\Keys;
use Composer\SelfUpdate\Versions;
use Composer\IO\IOInterface;
use Composer\Downloader\FilesystemException;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Finder\Finder;
/**
* @author Igor Wiedler <igor@wiedler.ch>
* @author Kevin Ran <kran@adobe.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
class SelfUpdateCommand extends BaseCommand
{
const HOMEPAGE = 'getcomposer.org';
const OLD_INSTALL_EXT = '-old.phar';
protected function configure()
{
$this
->setName('self-update')
->setAliases(array('selfupdate'))
->setDescription('Updates composer.phar to the latest version.')
->setDefinition(array(
new InputOption('rollback', 'r', InputOption::VALUE_NONE, 'Revert to an older installation of composer'),
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'),
new InputArgument('version', InputArgument::OPTIONAL, 'The version to update to'),
new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'),
new InputOption('update-keys', null, InputOption::VALUE_NONE, 'Prompt user for a key update'),
new InputOption('stable', null, InputOption::VALUE_NONE, 'Force an update to the stable channel'),
new InputOption('preview', null, InputOption::VALUE_NONE, 'Force an update to the preview channel'),
new InputOption('snapshot', null, InputOption::VALUE_NONE, 'Force an update to the snapshot channel'),
new InputOption('set-channel-only', null, InputOption::VALUE_NONE, 'Only store the channel as the default one and then exit'),
))
->setHelp(
<<<EOT
The <info>self-update</info> command checks getcomposer.org for newer
versions of composer and if found, installs the latest.
<info>php composer.phar self-update</info>
Read more at https://getcomposer.org/doc/03-cli.md#self-update-selfupdate-
EOT
)
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$config = Factory::createConfig();
if ($config->get('disable-tls') === true) {
$baseUrl = 'http://' . self::HOMEPAGE;
} else {
$baseUrl = 'https://' . self::HOMEPAGE;
}
$io = $this->getIO();
$remoteFilesystem = Factory::createRemoteFilesystem($io, $config);
$versionsUtil = new Versions($config, $remoteFilesystem);
// switch channel if requested
foreach (array('stable', 'preview', 'snapshot') as $channel) {
if ($input->getOption($channel)) {
$versionsUtil->setChannel($channel);
}
}
if ($input->getOption('set-channel-only')) {
return 0;
}
$cacheDir = $config->get('cache-dir');
$rollbackDir = $config->get('data-dir');
$home = $config->get('home');
$localFilename = realpath($_SERVER['argv'][0]) ?: $_SERVER['argv'][0];
if ($input->getOption('update-keys')) {
return $this->fetchKeys($io, $config);
}
// check if current dir is writable and if not try the cache dir from settings
$tmpDir = is_writable(dirname($localFilename)) ? dirname($localFilename) : $cacheDir;
// check for permissions in local filesystem before start connection process
if (!is_writable($tmpDir)) {
throw new FilesystemException('Composer update failed: the "'.$tmpDir.'" directory used to download the temp file could not be written');
}
// check if composer is running as the same user that owns the directory root, only if POSIX is defined and callable
if (function_exists('posix_getpwuid') && function_exists('posix_geteuid')) {
$composeUser = posix_getpwuid(posix_geteuid());
$homeOwner = posix_getpwuid(fileowner($home));
if (isset($composeUser['name']) && isset($homeOwner['name']) && $composeUser['name'] !== $homeOwner['name']) {
$io->writeError('<warning>You are running composer as "'.$composeUser['name'].'", while "'.$home.'" is owned by "'.$homeOwner['name'].'"</warning>');
}
}
if ($input->getOption('rollback')) {
return $this->rollback($output, $rollbackDir, $localFilename);
}
$latest = $versionsUtil->getLatest();
$latestVersion = $latest['version'];
$updateVersion = $input->getArgument('version') ?: $latestVersion;
if (preg_match('{^[0-9a-f]{40}$}', $updateVersion) && $updateVersion !== $latestVersion) {
$io->writeError('<error>You can not update to a specific SHA-1 as those phars are not available for download</error>');
return 1;
}
if (Composer::VERSION === $updateVersion) {
$io->writeError(sprintf('<info>You are already using composer version %s (%s channel).</info>', $updateVersion, $versionsUtil->getChannel()));
// remove all backups except for the most recent, if any
if ($input->getOption('clean-backups')) {
$this->cleanBackups($rollbackDir, $this->getLastBackupVersion($rollbackDir));
}
return 0;
}
$tempFilename = $tmpDir . '/' . basename($localFilename, '.phar').'-temp.phar';
$backupFile = sprintf(
'%s/%s-%s%s',
$rollbackDir,
strtr(Composer::RELEASE_DATE, ' :', '_-'),
preg_replace('{^([0-9a-f]{7})[0-9a-f]{33}$}', '$1', Composer::VERSION),
self::OLD_INSTALL_EXT
);
$updatingToTag = !preg_match('{^[0-9a-f]{40}$}', $updateVersion);
$io->write(sprintf("Updating to version <info>%s</info> (%s channel).", $updateVersion, $versionsUtil->getChannel()));
$remoteFilename = $baseUrl . ($updatingToTag ? "/download/{$updateVersion}/composer.phar" : '/composer.phar');
$signature = $remoteFilesystem->getContents(self::HOMEPAGE, $remoteFilename.'.sig', false);
$io->writeError(' ', false);
$remoteFilesystem->copy(self::HOMEPAGE, $remoteFilename, $tempFilename, !$input->getOption('no-progress'));
$io->writeError('');
if (!file_exists($tempFilename) || !$signature) {
$io->writeError('<error>The download of the new composer version failed for an unexpected reason</error>');
return 1;
}
// verify phar signature
if (!extension_loaded('openssl') && $config->get('disable-tls')) {
$io->writeError('<warning>Skipping phar signature verification as you have disabled OpenSSL via config.disable-tls</warning>');
} else {
if (!extension_loaded('openssl')) {
throw new \RuntimeException('The openssl extension is required for phar signatures to be verified but it is not available. '
. 'If you can not enable the openssl extension, you can disable this error, at your own risk, by setting the \'disable-tls\' option to true.');
}
$sigFile = 'file://'.$home.'/' . ($updatingToTag ? 'keys.tags.pub' : 'keys.dev.pub');
if (!file_exists($sigFile)) {
file_put_contents(
$home.'/keys.dev.pub',
<<<DEVPUBKEY
-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAnBDHjZS6e0ZMoK3xTD7f
FNCzlXjX/Aie2dit8QXA03pSrOTbaMnxON3hUL47Lz3g1SC6YJEMVHr0zYq4elWi
i3ecFEgzLcj+pZM5X6qWu2Ozz4vWx3JYo1/a/HYdOuW9e3lwS8VtS0AVJA+U8X0A
hZnBmGpltHhO8hPKHgkJtkTUxCheTcbqn4wGHl8Z2SediDcPTLwqezWKUfrYzu1f
o/j3WFwFs6GtK4wdYtiXr+yspBZHO3y1udf8eFFGcb2V3EaLOrtfur6XQVizjOuk
8lw5zzse1Qp/klHqbDRsjSzJ6iL6F4aynBc6Euqt/8ccNAIz0rLjLhOraeyj4eNn
8iokwMKiXpcrQLTKH+RH1JCuOVxQ436bJwbSsp1VwiqftPQieN+tzqy+EiHJJmGf
TBAbWcncicCk9q2md+AmhNbvHO4PWbbz9TzC7HJb460jyWeuMEvw3gNIpEo2jYa9
pMV6cVqnSa+wOc0D7pC9a6bne0bvLcm3S+w6I5iDB3lZsb3A9UtRiSP7aGSo7D72
8tC8+cIgZcI7k9vjvOqH+d7sdOU2yPCnRY6wFh62/g8bDnUpr56nZN1G89GwM4d4
r/TU7BQQIzsZgAiqOGXvVklIgAMiV0iucgf3rNBLjjeNEwNSTTG9F0CtQ+7JLwaE
wSEuAuRm+pRqi8BRnQ/GKUcCAwEAAQ==
-----END PUBLIC KEY-----
DEVPUBKEY
);
file_put_contents(
$home.'/keys.tags.pub',
<<<TAGSPUBKEY
-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0Vi/2K6apCVj76nCnCl2
MQUPdK+A9eqkYBacXo2wQBYmyVlXm2/n/ZsX6pCLYPQTHyr5jXbkQzBw8SKqPdlh
vA7NpbMeNCz7wP/AobvUXM8xQuXKbMDTY2uZ4O7sM+PfGbptKPBGLe8Z8d2sUnTO
bXtX6Lrj13wkRto7st/w/Yp33RHe9SlqkiiS4MsH1jBkcIkEHsRaveZzedUaxY0M
mba0uPhGUInpPzEHwrYqBBEtWvP97t2vtfx8I5qv28kh0Y6t+jnjL1Urid2iuQZf
noCMFIOu4vksK5HxJxxrN0GOmGmwVQjOOtxkwikNiotZGPR4KsVj8NnBrLX7oGuM
nQvGciiu+KoC2r3HDBrpDeBVdOWxDzT5R4iI0KoLzFh2pKqwbY+obNPS2bj+2dgJ
rV3V5Jjry42QOCBN3c88wU1PKftOLj2ECpewY6vnE478IipiEu7EAdK8Zwj2LmTr
RKQUSa9k7ggBkYZWAeO/2Ag0ey3g2bg7eqk+sHEq5ynIXd5lhv6tC5PBdHlWipDK
tl2IxiEnejnOmAzGVivE1YGduYBjN+mjxDVy8KGBrjnz1JPgAvgdwJ2dYw4Rsc/e
TzCFWGk/HM6a4f0IzBWbJ5ot0PIi4amk07IotBXDWwqDiQTwyuGCym5EqWQ2BD95
RGv89BPD+2DLnJysngsvVaUCAwEAAQ==
-----END PUBLIC KEY-----
TAGSPUBKEY
);
}
$pubkeyid = openssl_pkey_get_public($sigFile);
$algo = defined('OPENSSL_ALGO_SHA384') ? OPENSSL_ALGO_SHA384 : 'SHA384';
if (!in_array('sha384', array_map('strtolower', openssl_get_md_methods()))) {
throw new \RuntimeException('SHA384 is not supported by your openssl extension, could not verify the phar file integrity');
}
$signature = json_decode($signature, true);
$signature = base64_decode($signature['sha384']);
$verified = 1 === openssl_verify(file_get_contents($tempFilename), $signature, $pubkeyid, $algo);
openssl_free_key($pubkeyid);
if (!$verified) {
throw new \RuntimeException('The phar signature did not match the file you downloaded, this means your public keys are outdated or that the phar file is corrupt/has been modified');
}
}
// remove saved installations of composer
if ($input->getOption('clean-backups')) {
$this->cleanBackups($rollbackDir);
}
if ($err = $this->setLocalPhar($localFilename, $tempFilename, $backupFile)) {
@unlink($tempFilename);
$io->writeError('<error>The file is corrupted ('.$err->getMessage().').</error>');
$io->writeError('<error>Please re-run the self-update command to try again.</error>');
return 1;
}
if (file_exists($backupFile)) {
$io->writeError(sprintf(
'Use <info>composer self-update --rollback</info> to return to version <comment>%s</comment>',
Composer::VERSION
));
} else {
$io->writeError('<warning>A backup of the current version could not be written to '.$backupFile.', no rollback possible</warning>');
}
return 0;
}
protected function fetchKeys(IOInterface $io, Config $config)
{
if (!$io->isInteractive()) {
throw new \RuntimeException('Public keys can not be fetched in non-interactive mode, please run Composer interactively');
}
$io->write('Open <info>https://composer.github.io/pubkeys.html</info> to find the latest keys');
$validator = function ($value) {
if (!preg_match('{^-----BEGIN PUBLIC KEY-----$}', trim($value))) {
throw new \UnexpectedValueException('Invalid input');
}
return trim($value)."\n";
};
$devKey = '';
while (!preg_match('{(-----BEGIN PUBLIC KEY-----.+?-----END PUBLIC KEY-----)}s', $devKey, $match)) {
$devKey = $io->askAndValidate('Enter Dev / Snapshot Public Key (including lines with -----): ', $validator);
while ($line = $io->ask('')) {
$devKey .= trim($line)."\n";
if (trim($line) === '-----END PUBLIC KEY-----') {
break;
}
}
}
file_put_contents($keyPath = $config->get('home').'/keys.dev.pub', $match[0]);
$io->write('Stored key with fingerprint: ' . Keys::fingerprint($keyPath));
$tagsKey = '';
while (!preg_match('{(-----BEGIN PUBLIC KEY-----.+?-----END PUBLIC KEY-----)}s', $tagsKey, $match)) {
$tagsKey = $io->askAndValidate('Enter Tags Public Key (including lines with -----): ', $validator);
while ($line = $io->ask('')) {
$tagsKey .= trim($line)."\n";
if (trim($line) === '-----END PUBLIC KEY-----') {
break;
}
}
}
file_put_contents($keyPath = $config->get('home').'/keys.tags.pub', $match[0]);
$io->write('Stored key with fingerprint: ' . Keys::fingerprint($keyPath));
$io->write('Public keys stored in '.$config->get('home'));
}
protected function rollback(OutputInterface $output, $rollbackDir, $localFilename)
{
$rollbackVersion = $this->getLastBackupVersion($rollbackDir);
if (!$rollbackVersion) {
throw new \UnexpectedValueException('Composer rollback failed: no installation to roll back to in "'.$rollbackDir.'"');
}
$oldFile = $rollbackDir . '/' . $rollbackVersion . self::OLD_INSTALL_EXT;
if (!is_file($oldFile)) {
throw new FilesystemException('Composer rollback failed: "'.$oldFile.'" could not be found');
}
if (!is_readable($oldFile)) {
throw new FilesystemException('Composer rollback failed: "'.$oldFile.'" could not be read');
}
$io = $this->getIO();
$io->writeError(sprintf("Rolling back to version <info>%s</info>.", $rollbackVersion));
if ($err = $this->setLocalPhar($localFilename, $oldFile)) {
$io->writeError('<error>The backup file was corrupted ('.$err->getMessage().').</error>');
return 1;
}
return 0;
}
/**
* @param string $localFilename
* @param string $newFilename
* @param string $backupTarget
* @throws \Exception
* @return \UnexpectedValueException|\PharException|null
*/
protected function setLocalPhar($localFilename, $newFilename, $backupTarget = null)
{
try {
@chmod($newFilename, fileperms($localFilename));
if (!ini_get('phar.readonly')) {
// test the phar validity
$phar = new \Phar($newFilename);
// free the variable to unlock the file
unset($phar);
}
// copy current file into installations dir
if ($backupTarget && file_exists($localFilename)) {
@copy($localFilename, $backupTarget);
}
rename($newFilename, $localFilename);
return null;
} catch (\Exception $e) {
if (!$e instanceof \UnexpectedValueException && !$e instanceof \PharException) {
throw $e;
}
return $e;
}
}
protected function cleanBackups($rollbackDir, $except = null)
{
$finder = $this->getOldInstallationFinder($rollbackDir);
$io = $this->getIO();
$fs = new Filesystem;
foreach ($finder as $file) {
if ($except && $file->getBasename(self::OLD_INSTALL_EXT) === $except) {
continue;
}
$file = (string) $file;
$io->writeError('<info>Removing: '.$file.'</info>');
$fs->remove($file);
}
}
protected function getLastBackupVersion($rollbackDir)
{
$finder = $this->getOldInstallationFinder($rollbackDir);
$finder->sortByName();
$files = iterator_to_array($finder);
if (count($files)) {
return basename(end($files), self::OLD_INSTALL_EXT);
}
return false;
}
protected function getOldInstallationFinder($rollbackDir)
{
$finder = Finder::create()
->depth(0)
->files()
->name('*' . self::OLD_INSTALL_EXT)
->in($rollbackDir);
return $finder;
}
}
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,214 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Composer\Downloader\ChangeReportInterface;
use Composer\Downloader\DvcsDownloaderInterface;
use Composer\Downloader\VcsCapableDownloaderInterface;
use Composer\Package\Dumper\ArrayDumper;
use Composer\Package\Version\VersionGuesser;
use Composer\Package\Version\VersionParser;
use Composer\Plugin\CommandEvent;
use Composer\Plugin\PluginEvents;
use Composer\Script\ScriptEvents;
use Composer\Util\ProcessExecutor;
/**
* @author Tiago Ribeiro <tiago.ribeiro@seegno.com>
* @author Rui Marinho <rui.marinho@seegno.com>
*/
class StatusCommand extends BaseCommand
{
const EXIT_CODE_ERRORS = 1;
const EXIT_CODE_UNPUSHED_CHANGES = 2;
const EXIT_CODE_VERSION_CHANGES = 4;
/**
* @throws \Symfony\Component\Console\Exception\InvalidArgumentException
*/
protected function configure()
{
$this
->setName('status')
->setDescription('Shows a list of locally modified packages, for packages installed from source.')
->setDefinition(array(
new InputOption('verbose', 'v|vv|vvv', InputOption::VALUE_NONE, 'Show modified files for each directory that contains changes.'),
))
->setHelp(
<<<EOT
The status command displays a list of dependencies that have
been modified locally.
Read more at https://getcomposer.org/doc/03-cli.md#status
EOT
)
;
}
/**
* @param InputInterface $input
* @param OutputInterface $output
* @return int
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
// init repos
$composer = $this->getComposer();
$commandEvent = new CommandEvent(PluginEvents::COMMAND, 'status', $input, $output);
$composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
$installedRepo = $composer->getRepositoryManager()->getLocalRepository();
$dm = $composer->getDownloadManager();
$im = $composer->getInstallationManager();
// Dispatch pre-status-command
$composer->getEventDispatcher()->dispatchScript(ScriptEvents::PRE_STATUS_CMD, true);
$errors = array();
$io = $this->getIO();
$unpushedChanges = array();
$vcsVersionChanges = array();
$parser = new VersionParser;
$guesser = new VersionGuesser($composer->getConfig(), new ProcessExecutor($io), $parser);
$dumper = new ArrayDumper;
// list packages
foreach ($installedRepo->getCanonicalPackages() as $package) {
$downloader = $dm->getDownloaderForInstalledPackage($package);
$targetDir = $im->getInstallPath($package);
if ($downloader instanceof ChangeReportInterface) {
if (is_link($targetDir)) {
$errors[$targetDir] = $targetDir . ' is a symbolic link.';
}
if ($changes = $downloader->getLocalChanges($package, $targetDir)) {
$errors[$targetDir] = $changes;
}
}
if ($downloader instanceof VcsCapableDownloaderInterface) {
if ($currentRef = $downloader->getVcsReference($package, $targetDir)) {
switch ($package->getInstallationSource()) {
case 'source':
$previousRef = $package->getSourceReference();
break;
case 'dist':
$previousRef = $package->getDistReference();
break;
default:
$previousRef = null;
}
$currentVersion = $guesser->guessVersion($dumper->dump($package), $targetDir);
if ($previousRef && $currentVersion && $currentVersion['commit'] !== $previousRef) {
$vcsVersionChanges[$targetDir] = array(
'previous' => array(
'version' => $package->getPrettyVersion(),
'ref' => $previousRef,
),
'current' => array(
'version' => $currentVersion['pretty_version'],
'ref' => $currentVersion['commit'],
),
);
}
}
}
if ($downloader instanceof DvcsDownloaderInterface) {
if ($unpushed = $downloader->getUnpushedChanges($package, $targetDir)) {
$unpushedChanges[$targetDir] = $unpushed;
}
}
}
// output errors/warnings
if (!$errors && !$unpushedChanges && !$vcsVersionChanges) {
$io->writeError('<info>No local changes</info>');
return 0;
}
if ($errors) {
$io->writeError('<error>You have changes in the following dependencies:</error>');
foreach ($errors as $path => $changes) {
if ($input->getOption('verbose')) {
$indentedChanges = implode("\n", array_map(function ($line) {
return ' ' . ltrim($line);
}, explode("\n", $changes)));
$io->write('<info>'.$path.'</info>:');
$io->write($indentedChanges);
} else {
$io->write($path);
}
}
}
if ($unpushedChanges) {
$io->writeError('<warning>You have unpushed changes on the current branch in the following dependencies:</warning>');
foreach ($unpushedChanges as $path => $changes) {
if ($input->getOption('verbose')) {
$indentedChanges = implode("\n", array_map(function ($line) {
return ' ' . ltrim($line);
}, explode("\n", $changes)));
$io->write('<info>'.$path.'</info>:');
$io->write($indentedChanges);
} else {
$io->write($path);
}
}
}
if ($vcsVersionChanges) {
$io->writeError('<warning>You have version variations in the following dependencies:</warning>');
foreach ($vcsVersionChanges as $path => $changes) {
if ($input->getOption('verbose')) {
// If we don't can't find a version, use the ref instead.
$currentVersion = $changes['current']['version'] ?: $changes['current']['ref'];
$previousVersion = $changes['previous']['version'] ?: $changes['previous']['ref'];
if ($io->isVeryVerbose()) {
// Output the ref regardless of whether or not it's being used as the version
$currentVersion .= sprintf(' (%s)', $changes['current']['ref']);
$previousVersion .= sprintf(' (%s)', $changes['previous']['ref']);
}
$io->write('<info>'.$path.'</info>:');
$io->write(sprintf(' From <comment>%s</comment> to <comment>%s</comment>', $previousVersion, $currentVersion));
} else {
$io->write($path);
}
}
}
if (($errors || $unpushedChanges || $vcsVersionChanges) && !$input->getOption('verbose')) {
$io->writeError('Use --verbose (-v) to see a list of files');
}
// Dispatch post-status-command
$composer->getEventDispatcher()->dispatchScript(ScriptEvents::POST_STATUS_CMD, true);
return ($errors ? self::EXIT_CODE_ERRORS : 0) + ($unpushedChanges ? self::EXIT_CODE_UNPUSHED_CHANGES : 0) + ($vcsVersionChanges ? self::EXIT_CODE_VERSION_CHANGES : 0);
}
}
@@ -0,0 +1,154 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Command;
use Composer\Repository\PlatformRepository;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class SuggestsCommand extends BaseCommand
{
protected function configure()
{
$this
->setName('suggests')
->setDescription('Shows package suggestions.')
->setDefinition(array(
new InputOption('by-package', null, InputOption::VALUE_NONE, 'Groups output by suggesting package'),
new InputOption('by-suggestion', null, InputOption::VALUE_NONE, 'Groups output by suggested package'),
new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Exclude suggestions from require-dev packages'),
new InputArgument('packages', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, 'Packages that you want to list suggestions from.'),
))
->setHelp(
<<<EOT
The <info>%command.name%</info> command shows a sorted list of suggested packages.
Enabling <info>-v</info> implies <info>--by-package --by-suggestion</info>, showing both lists.
Read more at https://getcomposer.org/doc/03-cli.md#suggests
EOT
)
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$lock = $this->getComposer()->getLocker()->getLockData();
if (empty($lock)) {
throw new \RuntimeException('Lockfile seems to be empty?');
}
$packages = $lock['packages'];
if (!$input->getOption('no-dev')) {
$packages += $lock['packages-dev'];
}
$filter = $input->getArgument('packages');
// First assemble lookup list of packages that are installed, replaced or provided
$installed = array();
foreach ($packages as $package) {
$installed[] = $package['name'];
if (!empty($package['provide'])) {
$installed = array_merge($installed, array_keys($package['provide']));
}
if (!empty($package['replace'])) {
$installed = array_merge($installed, array_keys($package['replace']));
}
}
// Undub and sort the install list into a sorted lookup array
$installed = array_flip($installed);
ksort($installed);
// Init platform repo
$platform = new PlatformRepository(array(), $this->getComposer()->getConfig()->get('platform') ?: array());
// Next gather all suggestions that are not in that list
$suggesters = array();
$suggested = array();
foreach ($packages as $package) {
$packageName = $package['name'];
if ((!empty($filter) && !in_array($packageName, $filter)) || empty($package['suggest'])) {
continue;
}
foreach ($package['suggest'] as $suggestion => $reason) {
if (false === strpos('/', $suggestion) && null !== $platform->findPackage($suggestion, '*')) {
continue;
}
if (!isset($installed[$suggestion])) {
$suggesters[$packageName][$suggestion] = $reason;
$suggested[$suggestion][$packageName] = $reason;
}
}
}
ksort($suggesters);
ksort($suggested);
// Determine output mode
$mode = 0;
$io = $this->getIO();
if ($input->getOption('by-package') || $io->isVerbose()) {
$mode |= 1;
}
if ($input->getOption('by-suggestion')) {
$mode |= 2;
}
// Simple mode
if ($mode === 0) {
foreach (array_keys($suggested) as $suggestion) {
$io->write(sprintf('<info>%s</info>', $suggestion));
}
return 0;
}
// Grouped by package
if ($mode & 1) {
foreach ($suggesters as $suggester => $suggestions) {
$io->write(sprintf('<comment>%s</comment> suggests:', $suggester));
foreach ($suggestions as $suggestion => $reason) {
$io->write(sprintf(' - <info>%s</info>: %s', $suggestion, $reason ?: '*'));
}
$io->write('');
}
}
// Grouped by suggestion
if ($mode & 2) {
// Improve readability in full mode
if ($mode & 1) {
$io->write(str_repeat('-', 78));
}
foreach ($suggested as $suggestion => $suggesters) {
$io->write(sprintf('<comment>%s</comment> is suggested by:', $suggestion));
foreach ($suggesters as $suggester => $reason) {
$io->write(sprintf(' - <info>%s</info>: %s', $suggester, $reason ?: '*'));
}
$io->write('');
}
}
return 0;
}
}
@@ -0,0 +1,228 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Command;
use Composer\Composer;
use Composer\Installer;
use Composer\IO\IOInterface;
use Composer\Plugin\CommandEvent;
use Composer\Plugin\PluginEvents;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
/**
* @author Jordi Boggiano <j.boggiano@seld.be>
* @author Nils Adermann <naderman@naderman.de>
*/
class UpdateCommand extends BaseCommand
{
protected function configure()
{
$this
->setName('update')
->setAliases(array('u', 'upgrade'))
->setDescription('Upgrades your dependencies to the latest version according to composer.json, and updates the composer.lock file.')
->setDefinition(array(
new InputArgument('packages', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, 'Packages that should be updated, if not provided all packages are.'),
new InputOption('prefer-source', null, InputOption::VALUE_NONE, 'Forces installation from package sources when possible, including VCS information.'),
new InputOption('prefer-dist', null, InputOption::VALUE_NONE, 'Forces installation from package dist even for dev versions.'),
new InputOption('dry-run', null, InputOption::VALUE_NONE, 'Outputs the operations but will not execute anything (implicitly enables --verbose).'),
new InputOption('dev', null, InputOption::VALUE_NONE, 'Enables installation of require-dev packages (enabled by default, only present for BC).'),
new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables installation of require-dev packages.'),
new InputOption('lock', null, InputOption::VALUE_NONE, 'Only updates the lock file hash to suppress warning about the lock file being out of date.'),
new InputOption('no-custom-installers', null, InputOption::VALUE_NONE, 'DEPRECATED: Use no-plugins instead.'),
new InputOption('no-autoloader', null, InputOption::VALUE_NONE, 'Skips autoloader generation'),
new InputOption('no-scripts', null, InputOption::VALUE_NONE, 'Skips the execution of all scripts defined in composer.json file.'),
new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'),
new InputOption('no-suggest', null, InputOption::VALUE_NONE, 'Do not show package suggestions.'),
new InputOption('with-dependencies', null, InputOption::VALUE_NONE, 'Add also dependencies of whitelisted packages to the whitelist, except those defined in root package.'),
new InputOption('with-all-dependencies', null, InputOption::VALUE_NONE, 'Add also all dependencies of whitelisted packages to the whitelist, including those defined in root package.'),
new InputOption('verbose', 'v|vv|vvv', InputOption::VALUE_NONE, 'Shows more details including new commits pulled in when updating packages.'),
new InputOption('optimize-autoloader', 'o', InputOption::VALUE_NONE, 'Optimize autoloader during autoloader dump.'),
new InputOption('classmap-authoritative', 'a', InputOption::VALUE_NONE, 'Autoload classes from the classmap only. Implicitly enables `--optimize-autoloader`.'),
new InputOption('apcu-autoloader', null, InputOption::VALUE_NONE, 'Use APCu to cache found/not-found classes.'),
new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'),
new InputOption('prefer-stable', null, InputOption::VALUE_NONE, 'Prefer stable versions of dependencies.'),
new InputOption('prefer-lowest', null, InputOption::VALUE_NONE, 'Prefer lowest versions of dependencies.'),
new InputOption('interactive', 'i', InputOption::VALUE_NONE, 'Interactive interface with autocompletion to select the packages to update.'),
new InputOption('root-reqs', null, InputOption::VALUE_NONE, 'Restricts the update to your first degree dependencies.'),
))
->setHelp(
<<<EOT
The <info>update</info> command reads the composer.json file from the
current directory, processes it, and updates, removes or installs all the
dependencies.
<info>php composer.phar update</info>
To limit the update operation to a few packages, you can list the package(s)
you want to update as such:
<info>php composer.phar update vendor/package1 foo/mypackage [...]</info>
You may also use an asterisk (*) pattern to limit the update operation to package(s)
from a specific vendor:
<info>php composer.phar update vendor/package1 foo/* [...]</info>
To select packages names interactively with auto-completion use <info>-i</info>.
Read more at https://getcomposer.org/doc/03-cli.md#update-u
EOT
)
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$io = $this->getIO();
if ($input->getOption('no-custom-installers')) {
$io->writeError('<warning>You are using the deprecated option "no-custom-installers". Use "no-plugins" instead.</warning>');
$input->setOption('no-plugins', true);
}
if ($input->getOption('dev')) {
$io->writeError('<warning>You are using the deprecated option "dev". Dev packages are installed by default now.</warning>');
}
$composer = $this->getComposer(true, $input->getOption('no-plugins'));
$packages = $input->getArgument('packages');
if ($input->getOption('interactive')) {
$packages = $this->getPackagesInteractively($io, $input, $output, $composer, $packages);
}
if ($input->getOption('root-reqs')) {
$require = array_keys($composer->getPackage()->getRequires());
if (!$input->getOption('no-dev')) {
$requireDev = array_keys($composer->getPackage()->getDevRequires());
$require = array_merge($require, $requireDev);
}
if (!empty($packages)) {
$packages = array_intersect($packages, $require);
} else {
$packages = $require;
}
}
$composer->getDownloadManager()->setOutputProgress(!$input->getOption('no-progress'));
$commandEvent = new CommandEvent(PluginEvents::COMMAND, 'update', $input, $output);
$composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
$install = Installer::create($io, $composer);
$config = $composer->getConfig();
list($preferSource, $preferDist) = $this->getPreferredInstallOptions($config, $input);
$optimize = $input->getOption('optimize-autoloader') || $config->get('optimize-autoloader');
$authoritative = $input->getOption('classmap-authoritative') || $config->get('classmap-authoritative');
$apcu = $input->getOption('apcu-autoloader') || $config->get('apcu-autoloader');
$install
->setDryRun($input->getOption('dry-run'))
->setVerbose($input->getOption('verbose'))
->setPreferSource($preferSource)
->setPreferDist($preferDist)
->setDevMode(!$input->getOption('no-dev'))
->setDumpAutoloader(!$input->getOption('no-autoloader'))
->setRunScripts(!$input->getOption('no-scripts'))
->setSkipSuggest($input->getOption('no-suggest'))
->setOptimizeAutoloader($optimize)
->setClassMapAuthoritative($authoritative)
->setApcuAutoloader($apcu)
->setUpdate(true)
->setUpdateWhitelist($input->getOption('lock') ? array('lock') : $packages)
->setWhitelistTransitiveDependencies($input->getOption('with-dependencies'))
->setWhitelistAllDependencies($input->getOption('with-all-dependencies'))
->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs'))
->setPreferStable($input->getOption('prefer-stable'))
->setPreferLowest($input->getOption('prefer-lowest'))
;
if ($input->getOption('no-plugins')) {
$install->disablePlugins();
}
return $install->run();
}
private function getPackagesInteractively(IOInterface $io, InputInterface $input, OutputInterface $output, Composer $composer, array $packages)
{
if (!$input->isInteractive()) {
throw new \InvalidArgumentException('--interactive cannot be used in non-interactive terminals.');
}
$requires = array_merge(
$composer->getPackage()->getRequires(),
$composer->getPackage()->getDevRequires()
);
$autocompleterValues = array();
foreach ($requires as $require) {
$target = $require->getTarget();
$autocompleterValues[strtolower($target)] = $target;
}
$installedPackages = $composer->getRepositoryManager()->getLocalRepository()->getPackages();
foreach ($installedPackages as $package) {
$autocompleterValues[$package->getName()] = $package->getPrettyName();
}
$helper = $this->getHelper('question');
$question = new Question('<comment>Enter package name: </comment>', null);
$io->writeError('<info>Press enter without value to end submission</info>');
do {
$autocompleterValues = array_diff($autocompleterValues, $packages);
$question->setAutocompleterValues($autocompleterValues);
$addedPackage = $helper->ask($input, $output, $question);
if (!is_string($addedPackage) || empty($addedPackage)) {
break;
}
$addedPackage = strtolower($addedPackage);
if (!in_array($addedPackage, $packages)) {
$packages[] = $addedPackage;
}
} while (true);
$packages = array_filter($packages);
if (!$packages) {
throw new \InvalidArgumentException('You must enter minimum one package.');
}
$table = new Table($output);
$table->setHeaders(array('Selected packages'));
foreach ($packages as $package) {
$table->addRow(array($package));
}
$table->render();
if ($io->askConfirmation(sprintf(
'Would you like to continue and update the above package%s [<comment>yes</comment>]? ',
1 === count($packages) ? '' : 's'
), true)) {
return $packages;
}
throw new \RuntimeException('Installation aborted.');
}
}
@@ -0,0 +1,172 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Command;
use Composer\Factory;
use Composer\Package\Loader\ValidatingArrayLoader;
use Composer\Plugin\CommandEvent;
use Composer\Plugin\PluginEvents;
use Composer\Util\ConfigValidator;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* ValidateCommand
*
* @author Robert Schönthal <seroscho@googlemail.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
class ValidateCommand extends BaseCommand
{
/**
* configure
*/
protected function configure()
{
$this
->setName('validate')
->setDescription('Validates a composer.json and composer.lock.')
->setDefinition(array(
new InputOption('no-check-all', null, InputOption::VALUE_NONE, 'Do not validate requires for overly strict/loose constraints'),
new InputOption('no-check-lock', null, InputOption::VALUE_NONE, 'Do not check if lock file is up to date'),
new InputOption('no-check-publish', null, InputOption::VALUE_NONE, 'Do not check for publish errors'),
new InputOption('with-dependencies', 'A', InputOption::VALUE_NONE, 'Also validate the composer.json of all installed dependencies'),
new InputOption('strict', null, InputOption::VALUE_NONE, 'Return a non-zero exit code for warnings as well as errors'),
new InputArgument('file', InputArgument::OPTIONAL, 'path to composer.json file'),
))
->setHelp(
<<<EOT
The validate command validates a given composer.json and composer.lock
Exit codes in case of errors are:
1 validation warning(s), only when --strict is given
2 validation error(s)
3 file unreadable or missing
Read more at https://getcomposer.org/doc/03-cli.md#validate
EOT
);
}
/**
* @param InputInterface $input
* @param OutputInterface $output
*
* @return int
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$file = $input->getArgument('file') ?: Factory::getComposerFile();
$io = $this->getIO();
if (!file_exists($file)) {
$io->writeError('<error>' . $file . ' not found.</error>');
return 3;
}
if (!is_readable($file)) {
$io->writeError('<error>' . $file . ' is not readable.</error>');
return 3;
}
$validator = new ConfigValidator($io);
$checkAll = $input->getOption('no-check-all') ? 0 : ValidatingArrayLoader::CHECK_ALL;
$checkPublish = !$input->getOption('no-check-publish');
$checkLock = !$input->getOption('no-check-lock');
$isStrict = $input->getOption('strict');
list($errors, $publishErrors, $warnings) = $validator->validate($file, $checkAll);
$lockErrors = array();
$composer = Factory::create($io, $file, $input->hasParameterOption('--no-plugins'));
$locker = $composer->getLocker();
if ($locker->isLocked() && !$locker->isFresh()) {
$lockErrors[] = 'The lock file is not up to date with the latest changes in composer.json, it is recommended that you run `composer update` or `composer update <package name>`.';
}
$this->outputResult($io, $file, $errors, $warnings, $checkPublish, $publishErrors, $checkLock, $lockErrors, true, $isStrict);
// $errors include publish and lock errors when exists
$exitCode = $errors ? 2 : ($isStrict && $warnings ? 1 : 0);
if ($input->getOption('with-dependencies')) {
$localRepo = $composer->getRepositoryManager()->getLocalRepository();
foreach ($localRepo->getPackages() as $package) {
$path = $composer->getInstallationManager()->getInstallPath($package);
$file = $path . '/composer.json';
if (is_dir($path) && file_exists($file)) {
list($errors, $publishErrors, $warnings) = $validator->validate($file, $checkAll);
$this->outputResult($io, $package->getPrettyName(), $errors, $warnings, $checkPublish, $publishErrors);
$depCode = $errors ? 2 : ($isStrict && $warnings ? 1 : 0);
$exitCode = max($depCode, $exitCode);
}
}
}
$commandEvent = new CommandEvent(PluginEvents::COMMAND, 'validate', $input, $output);
$eventCode = $composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
$exitCode = max($eventCode, $exitCode);
return $exitCode;
}
private function outputResult($io, $name, &$errors, &$warnings, $checkPublish = false, $publishErrors = array(), $checkLock = false, $lockErrors = array(), $printSchemaUrl = false, $isStrict = false)
{
if (!$errors && !$publishErrors && !$warnings) {
$io->write('<info>' . $name . ' is valid</info>');
} elseif (!$errors && !$publishErrors) {
$io->writeError('<info>' . $name . ' is valid, but with a few warnings</info>');
if ($printSchemaUrl) {
$io->writeError('<warning>See https://getcomposer.org/doc/04-schema.md for details on the schema</warning>');
}
} elseif (!$errors) {
$io->writeError('<info>' . $name . ' is valid for simple usage with composer but has</info>');
$io->writeError('<info>strict errors that make it unable to be published as a package:</info>');
if ($printSchemaUrl) {
$io->writeError('<warning>See https://getcomposer.org/doc/04-schema.md for details on the schema</warning>');
}
} else {
$io->writeError('<error>' . $name . ' is invalid, the following errors/warnings were found:</error>');
}
// If checking publish errors, display them as errors, otherwise just show them as warnings
// Skip when it is a strict check and we don't want to check publish errors
if ($checkPublish) {
$errors = array_merge($errors, $publishErrors);
} elseif (!$isStrict) {
$warnings = array_merge($warnings, $publishErrors);
}
// If checking lock errors, display them as errors, otherwise just show them as warnings
// Skip when it is a strict check and we don't want to check lock errors
if ($checkLock) {
$errors = array_merge($errors, $lockErrors);
} elseif (!$isStrict) {
$warnings = array_merge($warnings, $lockErrors);
}
$messages = array(
'error' => $errors,
'warning' => $warnings,
);
foreach ($messages as $style => $msgs) {
foreach ($msgs as $msg) {
$io->writeError('<' . $style . '>' . $msg . '</' . $style . '>');
}
}
}
}
+284
View File
@@ -0,0 +1,284 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer;
use Composer\Json\JsonFile;
use Composer\Spdx\SpdxLicenses;
use Composer\CaBundle\CaBundle;
use Symfony\Component\Finder\Finder;
use Symfony\Component\Process\Process;
use Seld\PharUtils\Timestamps;
/**
* The Compiler class compiles composer into a phar
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
class Compiler
{
private $version;
private $branchAliasVersion = '';
private $versionDate;
/**
* Compiles composer into a single phar file
*
* @param string $pharFile The full path to the file to create
* @throws \RuntimeException
*/
public function compile($pharFile = 'composer.phar')
{
if (file_exists($pharFile)) {
unlink($pharFile);
}
$process = new Process('git log --pretty="%H" -n1 HEAD', __DIR__);
if ($process->run() != 0) {
throw new \RuntimeException('Can\'t run git log. You must ensure to run compile from composer git repository clone and that git binary is available.');
}
$this->version = trim($process->getOutput());
$process = new Process('git log -n1 --pretty=%ci HEAD', __DIR__);
if ($process->run() != 0) {
throw new \RuntimeException('Can\'t run git log. You must ensure to run compile from composer git repository clone and that git binary is available.');
}
$this->versionDate = new \DateTime(trim($process->getOutput()));
$this->versionDate->setTimezone(new \DateTimeZone('UTC'));
$process = new Process('git describe --tags --exact-match HEAD');
if ($process->run() == 0) {
$this->version = trim($process->getOutput());
} else {
// get branch-alias defined in composer.json for dev-master (if any)
$localConfig = __DIR__.'/../../composer.json';
$file = new JsonFile($localConfig);
$localConfig = $file->read();
if (isset($localConfig['extra']['branch-alias']['dev-master'])) {
$this->branchAliasVersion = $localConfig['extra']['branch-alias']['dev-master'];
}
}
$phar = new \Phar($pharFile, 0, 'composer.phar');
$phar->setSignatureAlgorithm(\Phar::SHA1);
$phar->startBuffering();
$finderSort = function ($a, $b) {
return strcmp(strtr($a->getRealPath(), '\\', '/'), strtr($b->getRealPath(), '\\', '/'));
};
$finder = new Finder();
$finder->files()
->ignoreVCS(true)
->name('*.php')
->notName('Compiler.php')
->notName('ClassLoader.php')
->in(__DIR__.'/..')
->sort($finderSort)
;
foreach ($finder as $file) {
$this->addFile($phar, $file);
}
$this->addFile($phar, new \SplFileInfo(__DIR__ . '/Autoload/ClassLoader.php'), false);
$finder = new Finder();
$finder->files()
->name('*.json')
->in(__DIR__.'/../../res')
->in(SpdxLicenses::getResourcesDir())
->sort($finderSort)
;
foreach ($finder as $file) {
$this->addFile($phar, $file, false);
}
$this->addFile($phar, new \SplFileInfo(__DIR__ . '/../../vendor/symfony/console/Resources/bin/hiddeninput.exe'), false);
$finder = new Finder();
$finder->files()
->ignoreVCS(true)
->name('*.php')
->name('LICENSE')
->exclude('Tests')
->exclude('tests')
->exclude('docs')
->in(__DIR__.'/../../vendor/symfony/')
->in(__DIR__.'/../../vendor/seld/jsonlint/')
->in(__DIR__.'/../../vendor/justinrainbow/json-schema/')
->in(__DIR__.'/../../vendor/composer/spdx-licenses/')
->in(__DIR__.'/../../vendor/composer/semver/')
->in(__DIR__.'/../../vendor/composer/ca-bundle/')
->in(__DIR__.'/../../vendor/composer/xdebug-handler/')
->in(__DIR__.'/../../vendor/psr/')
->sort($finderSort)
;
foreach ($finder as $file) {
$this->addFile($phar, $file);
}
$this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/autoload.php'));
$this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/autoload_namespaces.php'));
$this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/autoload_psr4.php'));
$this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/autoload_classmap.php'));
$this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/autoload_files.php'));
$this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/autoload_real.php'));
$this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/autoload_static.php'));
if (file_exists(__DIR__.'/../../vendor/composer/include_paths.php')) {
$this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/include_paths.php'));
}
$this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/ClassLoader.php'));
$this->addFile($phar, new \SplFileInfo(CaBundle::getBundledCaBundlePath()), false);
$this->addComposerBin($phar);
// Stubs
$phar->setStub($this->getStub());
$phar->stopBuffering();
// disabled for interoperability with systems without gzip ext
// $phar->compressFiles(\Phar::GZ);
$this->addFile($phar, new \SplFileInfo(__DIR__.'/../../LICENSE'), false);
unset($phar);
// re-sign the phar with reproducible timestamp / signature
$util = new Timestamps($pharFile);
$util->updateTimestamps($this->versionDate);
$util->save($pharFile, \Phar::SHA1);
}
/**
* @param \SplFileInfo $file
* @return string
*/
private function getRelativeFilePath($file)
{
$realPath = $file->getRealPath();
$pathPrefix = dirname(dirname(__DIR__)).DIRECTORY_SEPARATOR;
$pos = strpos($realPath, $pathPrefix);
$relativePath = ($pos !== false) ? substr_replace($realPath, '', $pos, strlen($pathPrefix)) : $realPath;
return strtr($relativePath, '\\', '/');
}
private function addFile($phar, $file, $strip = true)
{
$path = $this->getRelativeFilePath($file);
$content = file_get_contents($file);
if ($strip) {
$content = $this->stripWhitespace($content);
} elseif ('LICENSE' === basename($file)) {
$content = "\n".$content."\n";
}
if ($path === 'src/Composer/Composer.php') {
$content = str_replace('@package_version@', $this->version, $content);
$content = str_replace('@package_branch_alias_version@', $this->branchAliasVersion, $content);
$content = str_replace('@release_date@', $this->versionDate->format('Y-m-d H:i:s'), $content);
$content = preg_replace('{SOURCE_VERSION = \'[^\']+\';}', 'SOURCE_VERSION = \'\';', $content);
}
$phar->addFromString($path, $content);
}
private function addComposerBin($phar)
{
$content = file_get_contents(__DIR__.'/../../bin/composer');
$content = preg_replace('{^#!/usr/bin/env php\s*}', '', $content);
$phar->addFromString('bin/composer', $content);
}
/**
* Removes whitespace from a PHP source string while preserving line numbers.
*
* @param string $source A PHP string
* @return string The PHP string with the whitespace removed
*/
private function stripWhitespace($source)
{
if (!function_exists('token_get_all')) {
return $source;
}
$output = '';
foreach (token_get_all($source) as $token) {
if (is_string($token)) {
$output .= $token;
} elseif (in_array($token[0], array(T_COMMENT, T_DOC_COMMENT))) {
$output .= str_repeat("\n", substr_count($token[1], "\n"));
} elseif (T_WHITESPACE === $token[0]) {
// reduce wide spaces
$whitespace = preg_replace('{[ \t]+}', ' ', $token[1]);
// normalize newlines to \n
$whitespace = preg_replace('{(?:\r\n|\r|\n)}', "\n", $whitespace);
// trim leading spaces
$whitespace = preg_replace('{\n +}', "\n", $whitespace);
$output .= $whitespace;
} else {
$output .= $token[1];
}
}
return $output;
}
private function getStub()
{
$stub = <<<'EOF'
#!/usr/bin/env php
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view
* the license that is located at the bottom of this file.
*/
// Avoid APC causing random fatal errors per https://github.com/composer/composer/issues/264
if (extension_loaded('apc') && filter_var(ini_get('apc.enable_cli'), FILTER_VALIDATE_BOOLEAN) && filter_var(ini_get('apc.cache_by_default'), FILTER_VALIDATE_BOOLEAN)) {
if (version_compare(phpversion('apc'), '3.0.12', '>=')) {
ini_set('apc.cache_by_default', 0);
} else {
fwrite(STDERR, 'Warning: APC <= 3.0.12 may cause fatal errors when running composer commands.'.PHP_EOL);
fwrite(STDERR, 'Update APC, or set apc.enable_cli or apc.cache_by_default to 0 in your php.ini.'.PHP_EOL);
}
}
Phar::mapPhar('composer.phar');
EOF;
// add warning once the phar is older than 60 days
if (preg_match('{^[a-f0-9]+$}', $this->version)) {
$warningTime = $this->versionDate->format('U') + 60 * 86400;
$stub .= "define('COMPOSER_DEV_WARNING_TIME', $warningTime);\n";
}
return $stub . <<<'EOF'
require 'phar://composer.phar/bin/composer';
__HALT_COMPILER();
EOF;
}
}
+283
View File
@@ -0,0 +1,283 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer;
use Composer\Package\RootPackageInterface;
use Composer\Package\Locker;
use Composer\Repository\RepositoryManager;
use Composer\Installer\InstallationManager;
use Composer\Plugin\PluginManager;
use Composer\Downloader\DownloadManager;
use Composer\EventDispatcher\EventDispatcher;
use Composer\Autoload\AutoloadGenerator;
use Composer\Package\Archiver\ArchiveManager;
/**
* @author Jordi Boggiano <j.boggiano@seld.be>
* @author Konstantin Kudryashiv <ever.zet@gmail.com>
* @author Nils Adermann <naderman@naderman.de>
*/
class Composer
{
/*
* Examples of the following constants in the various configurations they can be in
*
* releases (phar):
* const VERSION = '1.8.2';
* const BRANCH_ALIAS_VERSION = '';
* const RELEASE_DATE = '2019-01-29 15:00:53';
* const SOURCE_VERSION = '';
*
* snapshot builds (phar):
* const VERSION = 'd3873a05650e168251067d9648845c220c50e2d7';
* const BRANCH_ALIAS_VERSION = '1.9-dev';
* const RELEASE_DATE = '2019-02-20 07:43:56';
* const SOURCE_VERSION = '';
*
* source (git clone):
* const VERSION = '1.9.3';
* const BRANCH_ALIAS_VERSION = '';
* const RELEASE_DATE = '2020-02-04 12:58:49';
* const SOURCE_VERSION = '1.8-dev+source';
*/
const VERSION = '1.9.3';
const BRANCH_ALIAS_VERSION = '';
const RELEASE_DATE = '2020-02-04 12:58:49';
const SOURCE_VERSION = '1.9-dev+source';
public static function getVersion()
{
// no replacement done, this must be a source checkout
if (self::VERSION === '@package_version'.'@') {
return self::SOURCE_VERSION;
}
// we have a branch alias and version is a commit id, this must be a snapshot build
if (self::BRANCH_ALIAS_VERSION !== '' && preg_match('{^[a-f0-9]{40}$}', self::VERSION)) {
return self::BRANCH_ALIAS_VERSION.'+'.self::VERSION;
}
return self::VERSION;
}
/**
* @var Package\RootPackageInterface
*/
private $package;
/**
* @var Locker
*/
private $locker;
/**
* @var Repository\RepositoryManager
*/
private $repositoryManager;
/**
* @var Downloader\DownloadManager
*/
private $downloadManager;
/**
* @var Installer\InstallationManager
*/
private $installationManager;
/**
* @var Plugin\PluginManager
*/
private $pluginManager;
/**
* @var Config
*/
private $config;
/**
* @var EventDispatcher
*/
private $eventDispatcher;
/**
* @var Autoload\AutoloadGenerator
*/
private $autoloadGenerator;
/**
* @var ArchiveManager
*/
private $archiveManager;
/**
* @param Package\RootPackageInterface $package
* @return void
*/
public function setPackage(RootPackageInterface $package)
{
$this->package = $package;
}
/**
* @return Package\RootPackageInterface
*/
public function getPackage()
{
return $this->package;
}
/**
* @param Config $config
*/
public function setConfig(Config $config)
{
$this->config = $config;
}
/**
* @return Config
*/
public function getConfig()
{
return $this->config;
}
/**
* @param Package\Locker $locker
*/
public function setLocker(Locker $locker)
{
$this->locker = $locker;
}
/**
* @return Package\Locker
*/
public function getLocker()
{
return $this->locker;
}
/**
* @param Repository\RepositoryManager $manager
*/
public function setRepositoryManager(RepositoryManager $manager)
{
$this->repositoryManager = $manager;
}
/**
* @return Repository\RepositoryManager
*/
public function getRepositoryManager()
{
return $this->repositoryManager;
}
/**
* @param Downloader\DownloadManager $manager
*/
public function setDownloadManager(DownloadManager $manager)
{
$this->downloadManager = $manager;
}
/**
* @return Downloader\DownloadManager
*/
public function getDownloadManager()
{
return $this->downloadManager;
}
/**
* @param ArchiveManager $manager
*/
public function setArchiveManager(ArchiveManager $manager)
{
$this->archiveManager = $manager;
}
/**
* @return ArchiveManager
*/
public function getArchiveManager()
{
return $this->archiveManager;
}
/**
* @param Installer\InstallationManager $manager
*/
public function setInstallationManager(InstallationManager $manager)
{
$this->installationManager = $manager;
}
/**
* @return Installer\InstallationManager
*/
public function getInstallationManager()
{
return $this->installationManager;
}
/**
* @param Plugin\PluginManager $manager
*/
public function setPluginManager(PluginManager $manager)
{
$this->pluginManager = $manager;
}
/**
* @return Plugin\PluginManager
*/
public function getPluginManager()
{
return $this->pluginManager;
}
/**
* @param EventDispatcher $eventDispatcher
*/
public function setEventDispatcher(EventDispatcher $eventDispatcher)
{
$this->eventDispatcher = $eventDispatcher;
}
/**
* @return EventDispatcher
*/
public function getEventDispatcher()
{
return $this->eventDispatcher;
}
/**
* @param Autoload\AutoloadGenerator $autoloadGenerator
*/
public function setAutoloadGenerator(AutoloadGenerator $autoloadGenerator)
{
$this->autoloadGenerator = $autoloadGenerator;
}
/**
* @return Autoload\AutoloadGenerator
*/
public function getAutoloadGenerator()
{
return $this->autoloadGenerator;
}
}
+479
View File
@@ -0,0 +1,479 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer;
use Composer\Config\ConfigSourceInterface;
use Composer\Downloader\TransportException;
use Composer\IO\IOInterface;
use Composer\Util\Platform;
use Composer\Util\ProcessExecutor;
/**
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
class Config
{
const RELATIVE_PATHS = 1;
public static $defaultConfig = array(
'process-timeout' => 300,
'use-include-path' => false,
'preferred-install' => 'auto',
'notify-on-install' => true,
'github-protocols' => array('https', 'ssh', 'git'),
'vendor-dir' => 'vendor',
'bin-dir' => '{$vendor-dir}/bin',
'cache-dir' => '{$home}/cache',
'data-dir' => '{$home}',
'cache-files-dir' => '{$cache-dir}/files',
'cache-repo-dir' => '{$cache-dir}/repo',
'cache-vcs-dir' => '{$cache-dir}/vcs',
'cache-ttl' => 15552000, // 6 months
'cache-files-ttl' => null, // fallback to cache-ttl
'cache-files-maxsize' => '300MiB',
'bin-compat' => 'auto',
'discard-changes' => false,
'autoloader-suffix' => null,
'sort-packages' => false,
'optimize-autoloader' => false,
'classmap-authoritative' => false,
'apcu-autoloader' => false,
'prepend-autoloader' => true,
'github-domains' => array('github.com'),
'bitbucket-expose-hostname' => true,
'disable-tls' => false,
'secure-http' => true,
'cafile' => null,
'capath' => null,
'github-expose-hostname' => true,
'gitlab-domains' => array('gitlab.com'),
'store-auths' => 'prompt',
'platform' => array(),
'archive-format' => 'tar',
'archive-dir' => '.',
'htaccess-protect' => true,
'use-github-api' => true,
// valid keys without defaults (auth config stuff):
// bitbucket-oauth
// github-oauth
// gitlab-oauth
// gitlab-token
// http-basic
);
public static $defaultRepositories = array(
'packagist.org' => array(
'type' => 'composer',
'url' => 'https?://repo.packagist.org',
'allow_ssl_downgrade' => true,
),
);
private $config;
private $baseDir;
private $repositories;
/** @var ConfigSourceInterface */
private $configSource;
/** @var ConfigSourceInterface */
private $authConfigSource;
private $useEnvironment;
private $warnedHosts = array();
/**
* @param bool $useEnvironment Use COMPOSER_ environment variables to replace config settings
* @param string $baseDir Optional base directory of the config
*/
public function __construct($useEnvironment = true, $baseDir = null)
{
// load defaults
$this->config = static::$defaultConfig;
$this->repositories = static::$defaultRepositories;
$this->useEnvironment = (bool) $useEnvironment;
$this->baseDir = $baseDir;
}
public function setConfigSource(ConfigSourceInterface $source)
{
$this->configSource = $source;
}
public function getConfigSource()
{
return $this->configSource;
}
public function setAuthConfigSource(ConfigSourceInterface $source)
{
$this->authConfigSource = $source;
}
public function getAuthConfigSource()
{
return $this->authConfigSource;
}
/**
* Merges new config values with the existing ones (overriding)
*
* @param array $config
*/
public function merge($config)
{
// override defaults with given config
if (!empty($config['config']) && is_array($config['config'])) {
foreach ($config['config'] as $key => $val) {
if (in_array($key, array('bitbucket-oauth', 'github-oauth', 'gitlab-oauth', 'gitlab-token', 'http-basic')) && isset($this->config[$key])) {
$this->config[$key] = array_merge($this->config[$key], $val);
} elseif ('preferred-install' === $key && isset($this->config[$key])) {
if (is_array($val) || is_array($this->config[$key])) {
if (is_string($val)) {
$val = array('*' => $val);
}
if (is_string($this->config[$key])) {
$this->config[$key] = array('*' => $this->config[$key]);
}
$this->config[$key] = array_merge($this->config[$key], $val);
// the full match pattern needs to be last
if (isset($this->config[$key]['*'])) {
$wildcard = $this->config[$key]['*'];
unset($this->config[$key]['*']);
$this->config[$key]['*'] = $wildcard;
}
} else {
$this->config[$key] = $val;
}
} else {
$this->config[$key] = $val;
}
}
}
if (!empty($config['repositories']) && is_array($config['repositories'])) {
$this->repositories = array_reverse($this->repositories, true);
$newRepos = array_reverse($config['repositories'], true);
foreach ($newRepos as $name => $repository) {
// disable a repository by name
if (false === $repository) {
$this->disableRepoByName($name);
continue;
}
// disable a repository with an anonymous {"name": false} repo
if (is_array($repository) && 1 === count($repository) && false === current($repository)) {
$this->disableRepoByName(key($repository));
continue;
}
// store repo
if (is_int($name)) {
$this->repositories[] = $repository;
} else {
if ($name === 'packagist') { // BC support for default "packagist" named repo
$this->repositories[$name . '.org'] = $repository;
} else {
$this->repositories[$name] = $repository;
}
}
}
$this->repositories = array_reverse($this->repositories, true);
}
}
/**
* @return array
*/
public function getRepositories()
{
return $this->repositories;
}
/**
* Returns a setting
*
* @param string $key
* @param int $flags Options (see class constants)
* @throws \RuntimeException
* @return mixed
*/
public function get($key, $flags = 0)
{
switch ($key) {
case 'vendor-dir':
case 'bin-dir':
case 'process-timeout':
case 'data-dir':
case 'cache-dir':
case 'cache-files-dir':
case 'cache-repo-dir':
case 'cache-vcs-dir':
case 'cafile':
case 'capath':
// convert foo-bar to COMPOSER_FOO_BAR and check if it exists since it overrides the local config
$env = 'COMPOSER_' . strtoupper(strtr($key, '-', '_'));
$val = $this->getComposerEnv($env);
$val = rtrim((string) $this->process(false !== $val ? $val : $this->config[$key], $flags), '/\\');
$val = Platform::expandPath($val);
if (substr($key, -4) !== '-dir') {
return $val;
}
return (($flags & self::RELATIVE_PATHS) == self::RELATIVE_PATHS) ? $val : $this->realpath($val);
case 'htaccess-protect':
$value = $this->getComposerEnv('COMPOSER_HTACCESS_PROTECT');
if (false === $value) {
$value = $this->config[$key];
}
return $value !== 'false' && (bool) $value;
case 'cache-ttl':
return (int) $this->config[$key];
case 'cache-files-maxsize':
if (!preg_match('/^\s*([0-9.]+)\s*(?:([kmg])(?:i?b)?)?\s*$/i', $this->config[$key], $matches)) {
throw new \RuntimeException(
"Could not parse the value of 'cache-files-maxsize': {$this->config[$key]}"
);
}
$size = $matches[1];
if (isset($matches[2])) {
switch (strtolower($matches[2])) {
case 'g':
$size *= 1024;
// intentional fallthrough
// no break
case 'm':
$size *= 1024;
// intentional fallthrough
// no break
case 'k':
$size *= 1024;
break;
}
}
return $size;
case 'cache-files-ttl':
if (isset($this->config[$key])) {
return (int) $this->config[$key];
}
return (int) $this->config['cache-ttl'];
case 'home':
$val = preg_replace('#^(\$HOME|~)(/|$)#', rtrim(getenv('HOME') ?: getenv('USERPROFILE'), '/\\') . '/', $this->config[$key]);
return rtrim($this->process($val, $flags), '/\\');
case 'bin-compat':
$value = $this->getComposerEnv('COMPOSER_BIN_COMPAT') ?: $this->config[$key];
if (!in_array($value, array('auto', 'full'))) {
throw new \RuntimeException(
"Invalid value for 'bin-compat': {$value}. Expected auto, full"
);
}
return $value;
case 'discard-changes':
if ($env = $this->getComposerEnv('COMPOSER_DISCARD_CHANGES')) {
if (!in_array($env, array('stash', 'true', 'false', '1', '0'), true)) {
throw new \RuntimeException(
"Invalid value for COMPOSER_DISCARD_CHANGES: {$env}. Expected 1, 0, true, false or stash"
);
}
if ('stash' === $env) {
return 'stash';
}
// convert string value to bool
return $env !== 'false' && (bool) $env;
}
if (!in_array($this->config[$key], array(true, false, 'stash'), true)) {
throw new \RuntimeException(
"Invalid value for 'discard-changes': {$this->config[$key]}. Expected true, false or stash"
);
}
return $this->config[$key];
case 'github-protocols':
$protos = $this->config['github-protocols'];
if ($this->config['secure-http'] && false !== ($index = array_search('git', $protos))) {
unset($protos[$index]);
}
if (reset($protos) === 'http') {
throw new \RuntimeException('The http protocol for github is not available anymore, update your config\'s github-protocols to use "https", "git" or "ssh"');
}
return $protos;
case 'disable-tls':
return $this->config[$key] !== 'false' && (bool) $this->config[$key];
case 'secure-http':
return $this->config[$key] !== 'false' && (bool) $this->config[$key];
case 'use-github-api':
return $this->config[$key] !== 'false' && (bool) $this->config[$key];
default:
if (!isset($this->config[$key])) {
return null;
}
return $this->process($this->config[$key], $flags);
}
}
public function all($flags = 0)
{
$all = array(
'repositories' => $this->getRepositories(),
);
foreach (array_keys($this->config) as $key) {
$all['config'][$key] = $this->get($key, $flags);
}
return $all;
}
public function raw()
{
return array(
'repositories' => $this->getRepositories(),
'config' => $this->config,
);
}
/**
* Checks whether a setting exists
*
* @param string $key
* @return bool
*/
public function has($key)
{
return array_key_exists($key, $this->config);
}
/**
* Replaces {$refs} inside a config string
*
* @param string|int|null $value a config string that can contain {$refs-to-other-config}
* @param int $flags Options (see class constants)
* @return string|int|null
*/
private function process($value, $flags)
{
$config = $this;
if (!is_string($value)) {
return $value;
}
return preg_replace_callback('#\{\$(.+)\}#', function ($match) use ($config, $flags) {
return $config->get($match[1], $flags);
}, $value);
}
/**
* Turns relative paths in absolute paths without realpath()
*
* Since the dirs might not exist yet we can not call realpath or it will fail.
*
* @param string $path
* @return string
*/
private function realpath($path)
{
if (preg_match('{^(?:/|[a-z]:|[a-z0-9.]+://)}i', $path)) {
return $path;
}
return $this->baseDir . '/' . $path;
}
/**
* Reads the value of a Composer environment variable
*
* This should be used to read COMPOSER_ environment variables
* that overload config values.
*
* @param string $var
* @return string|bool
*/
private function getComposerEnv($var)
{
if ($this->useEnvironment) {
return getenv($var);
}
return false;
}
private function disableRepoByName($name)
{
if (isset($this->repositories[$name])) {
unset($this->repositories[$name]);
} elseif ($name === 'packagist') { // BC support for default "packagist" named repo
unset($this->repositories['packagist.org']);
}
}
/**
* Validates that the passed URL is allowed to be used by current config, or throws an exception.
*
* @param string $url
* @param IOInterface $io
*/
public function prohibitUrlByConfig($url, IOInterface $io = null)
{
// Return right away if the URL is malformed or custom (see issue #5173)
if (false === filter_var($url, FILTER_VALIDATE_URL)) {
return;
}
// Extract scheme and throw exception on known insecure protocols
$scheme = parse_url($url, PHP_URL_SCHEME);
if (in_array($scheme, array('http', 'git', 'ftp', 'svn'))) {
if ($this->get('secure-http')) {
throw new TransportException("Your configuration does not allow connections to $url. See https://getcomposer.org/doc/06-config.md#secure-http for details.");
} elseif ($io) {
$host = parse_url($url, PHP_URL_HOST);
if (!isset($this->warnedHosts[$host])) {
$io->writeError("<warning>Warning: Accessing $host over $scheme which is an insecure protocol.</warning>");
}
$this->warnedHosts[$host] = true;
}
}
}
/**
* Used by long-running custom scripts in composer.json
*
* "scripts": {
* "watch": [
* "Composer\\Config::disableProcessTimeout",
* "vendor/bin/long-running-script --watch"
* ]
* }
*/
public static function disableProcessTimeout()
{
// Override global timeout set earlier by environment or config
ProcessExecutor::setTimeout(0);
}
}
@@ -0,0 +1,91 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Config;
/**
* Configuration Source Interface
*
* @author Jordi Boggiano <j.boggiano@seld.be>
* @author Beau Simensen <beau@dflydev.com>
*/
interface ConfigSourceInterface
{
/**
* Add a repository
*
* @param string $name Name
* @param array $config Configuration
*/
public function addRepository($name, $config);
/**
* Remove a repository
*
* @param string $name
*/
public function removeRepository($name);
/**
* Add a config setting
*
* @param string $name Name
* @param string $value Value
*/
public function addConfigSetting($name, $value);
/**
* Remove a config setting
*
* @param string $name
*/
public function removeConfigSetting($name);
/**
* Add a property
*
* @param string $name Name
* @param string $value Value
*/
public function addProperty($name, $value);
/**
* Remove a property
*
* @param string $name
*/
public function removeProperty($name);
/**
* Add a package link
*
* @param string $type Type (require, require-dev, provide, suggest, replace, conflict)
* @param string $name Name
* @param string $value Value
*/
public function addLink($type, $name, $value);
/**
* Remove a package link
*
* @param string $type Type (require, require-dev, provide, suggest, replace, conflict)
* @param string $name Name
*/
public function removeLink($type, $name);
/**
* Gives a user-friendly name to this source (file path or so)
*
* @return string
*/
public function getName();
}
@@ -0,0 +1,271 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Config;
use Composer\Json\JsonFile;
use Composer\Json\JsonManipulator;
use Composer\Util\Silencer;
/**
* JSON Configuration Source
*
* @author Jordi Boggiano <j.boggiano@seld.be>
* @author Beau Simensen <beau@dflydev.com>
*/
class JsonConfigSource implements ConfigSourceInterface
{
/**
* @var JsonFile
*/
private $file;
/**
* @var bool
*/
private $authConfig;
/**
* Constructor
*
* @param JsonFile $file
* @param bool $authConfig
*/
public function __construct(JsonFile $file, $authConfig = false)
{
$this->file = $file;
$this->authConfig = $authConfig;
}
/**
* {@inheritdoc}
*/
public function getName()
{
return $this->file->getPath();
}
/**
* {@inheritdoc}
*/
public function addRepository($name, $config)
{
$this->manipulateJson('addRepository', $name, $config, function (&$config, $repo, $repoConfig) {
// if converting from an array format to hashmap format, and there is a {"packagist.org":false} repo, we have
// to convert it to "packagist.org": false key on the hashmap otherwise it fails schema validation
if (isset($config['repositories'])) {
foreach ($config['repositories'] as $index => $val) {
if ($index === $repo) {
continue;
}
if (is_numeric($index) && ($val === array('packagist' => false) || $val === array('packagist.org' => false))) {
unset($config['repositories'][$index]);
$config['repositories']['packagist.org'] = false;
break;
}
}
}
$config['repositories'][$repo] = $repoConfig;
});
}
/**
* {@inheritdoc}
*/
public function removeRepository($name)
{
$this->manipulateJson('removeRepository', $name, function (&$config, $repo) {
unset($config['repositories'][$repo]);
});
}
/**
* {@inheritdoc}
*/
public function addConfigSetting($name, $value)
{
$authConfig = $this->authConfig;
$this->manipulateJson('addConfigSetting', $name, $value, function (&$config, $key, $val) use ($authConfig) {
if (preg_match('{^(bitbucket-oauth|github-oauth|gitlab-oauth|gitlab-token|http-basic|platform)\.}', $key)) {
list($key, $host) = explode('.', $key, 2);
if ($authConfig) {
$config[$key][$host] = $val;
} else {
$config['config'][$key][$host] = $val;
}
} else {
$config['config'][$key] = $val;
}
});
}
/**
* {@inheritdoc}
*/
public function removeConfigSetting($name)
{
$authConfig = $this->authConfig;
$this->manipulateJson('removeConfigSetting', $name, function (&$config, $key) use ($authConfig) {
if (preg_match('{^(bitbucket-oauth|github-oauth|gitlab-oauth|gitlab-token|http-basic|platform)\.}', $key)) {
list($key, $host) = explode('.', $key, 2);
if ($authConfig) {
unset($config[$key][$host]);
} else {
unset($config['config'][$key][$host]);
}
} else {
unset($config['config'][$key]);
}
});
}
/**
* {@inheritdoc}
*/
public function addProperty($name, $value)
{
$this->manipulateJson('addProperty', $name, $value, function (&$config, $key, $val) {
if (substr($key, 0, 6) === 'extra.' || substr($key, 0, 8) === 'scripts.') {
$bits = explode('.', $key);
$last = array_pop($bits);
$arr = &$config[reset($bits)];
foreach ($bits as $bit) {
if (!isset($arr[$bit])) {
$arr[$bit] = array();
}
$arr = &$arr[$bit];
}
$arr[$last] = $val;
} else {
$config[$key] = $val;
}
});
}
/**
* {@inheritdoc}
*/
public function removeProperty($name)
{
$authConfig = $this->authConfig;
$this->manipulateJson('removeProperty', $name, function (&$config, $key) {
if (substr($key, 0, 6) === 'extra.' || substr($key, 0, 8) === 'scripts.') {
$bits = explode('.', $key);
$last = array_pop($bits);
$arr = &$config[reset($bits)];
foreach ($bits as $bit) {
if (!isset($arr[$bit])) {
return;
}
$arr = &$arr[$bit];
}
unset($arr[$last]);
} else {
unset($config[$key]);
}
});
}
/**
* {@inheritdoc}
*/
public function addLink($type, $name, $value)
{
$this->manipulateJson('addLink', $type, $name, $value, function (&$config, $type, $name, $value) {
$config[$type][$name] = $value;
});
}
/**
* {@inheritdoc}
*/
public function removeLink($type, $name)
{
$this->manipulateJson('removeSubNode', $type, $name, function (&$config, $type, $name) {
unset($config[$type][$name]);
if (0 === count($config[$type])) {
unset($config[$type]);
}
});
}
protected function manipulateJson($method, $args, $fallback)
{
$args = func_get_args();
// remove method & fallback
array_shift($args);
$fallback = array_pop($args);
if ($this->file->exists()) {
if (!is_writable($this->file->getPath())) {
throw new \RuntimeException(sprintf('The file "%s" is not writable.', $this->file->getPath()));
}
if (!is_readable($this->file->getPath())) {
throw new \RuntimeException(sprintf('The file "%s" is not readable.', $this->file->getPath()));
}
$contents = file_get_contents($this->file->getPath());
} elseif ($this->authConfig) {
$contents = "{\n}\n";
} else {
$contents = "{\n \"config\": {\n }\n}\n";
}
$manipulator = new JsonManipulator($contents);
$newFile = !$this->file->exists();
// override manipulator method for auth config files
if ($this->authConfig && $method === 'addConfigSetting') {
$method = 'addSubNode';
list($mainNode, $name) = explode('.', $args[0], 2);
$args = array($mainNode, $name, $args[1]);
} elseif ($this->authConfig && $method === 'removeConfigSetting') {
$method = 'removeSubNode';
list($mainNode, $name) = explode('.', $args[0], 2);
$args = array($mainNode, $name);
}
// try to update cleanly
if (call_user_func_array(array($manipulator, $method), $args)) {
file_put_contents($this->file->getPath(), $manipulator->getContents());
} else {
// on failed clean update, call the fallback and rewrite the whole file
$config = $this->file->read();
$this->arrayUnshiftRef($args, $config);
call_user_func_array($fallback, $args);
$this->file->write($config);
}
if ($newFile) {
Silencer::call('chmod', $this->file->getPath(), 0600);
}
}
/**
* Prepend a reference to an element to the beginning of an array.
*
* @param array $array
* @param mixed $value
* @return array
*/
private function arrayUnshiftRef(&$array, &$value)
{
$return = array_unshift($array, '');
$array[0] = &$value;
return $return;
}
}
Loaded 100 of 6566 files, more files were not shown because too many files have changed in this diff. Show more