2011-08-03 21:57:27 +00:00
< ? 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 ;
2014-10-17 01:13:17 +00:00
use Composer\Json\JsonFile ;
2016-04-11 15:51:29 +00:00
use Composer\CaBundle\CaBundle ;
2011-08-03 21:57:27 +00:00
use Symfony\Component\Finder\Finder ;
2011-11-12 18:44:24 +00:00
use Symfony\Component\Process\Process ;
2015-05-01 12:44:24 +00:00
use Seld\PharUtils\Timestamps ;
2020-02-14 15:36:11 +00:00
use Seld\PharUtils\Linter ;
2011-08-03 21:57:27 +00:00
/**
2011-09-14 00:34:48 +00:00
* The Compiler class compiles composer into a phar
2011-08-03 21:57:27 +00:00
*
* @ author Fabien Potencier < fabien @ symfony . com >
2011-10-02 21:38:54 +00:00
* @ author Jordi Boggiano < j . boggiano @ seld . be >
2011-08-03 21:57:27 +00:00
*/
class Compiler
{
2013-05-24 04:36:48 +00:00
private $version ;
2014-10-17 01:13:17 +00:00
private $branchAliasVersion = '' ;
2013-09-16 12:08:24 +00:00
private $versionDate ;
2013-05-24 04:36:48 +00:00
2011-09-14 11:52:21 +00:00
/**
* Compiles composer into a single phar file
*
2012-05-22 10:07:08 +00:00
* @ param string $pharFile The full path to the file to create
2015-09-28 09:51:14 +00:00
* @ throws \RuntimeException
2011-09-14 11:52:21 +00:00
*/
2011-08-03 21:57:27 +00:00
public function compile ( $pharFile = 'composer.phar' )
{
if ( file_exists ( $pharFile )) {
unlink ( $pharFile );
}
2013-02-13 13:32:50 +00:00
$process = new Process ( 'git log --pretty="%H" -n1 HEAD' , __DIR__ );
2011-11-12 23:22:52 +00:00
if ( $process -> run () != 0 ) {
2012-03-07 08:06:03 +00:00
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.' );
2011-11-12 18:44:24 +00:00
}
$this -> version = trim ( $process -> getOutput ());
2013-09-16 12:08:24 +00:00
$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.' );
}
2014-10-17 01:13:17 +00:00
2015-05-01 10:54:55 +00:00
$this -> versionDate = new \DateTime ( trim ( $process -> getOutput ()));
$this -> versionDate -> setTimezone ( new \DateTimeZone ( 'UTC' ));
2013-09-16 12:08:24 +00:00
2014-12-09 22:39:59 +00:00
$process = new Process ( 'git describe --tags --exact-match HEAD' );
2012-03-04 18:03:54 +00:00
if ( $process -> run () == 0 ) {
$this -> version = trim ( $process -> getOutput ());
2014-10-17 18:07:26 +00:00
} 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' ];
}
2012-03-04 18:03:54 +00:00
}
2011-08-03 21:57:27 +00:00
$phar = new \Phar ( $pharFile , 0 , 'composer.phar' );
2021-08-17 13:50:50 +00:00
$phar -> setSignatureAlgorithm ( \Phar :: SHA512 );
2011-08-03 21:57:27 +00:00
$phar -> startBuffering ();
2015-05-01 10:21:21 +00:00
$finderSort = function ( $a , $b ) {
2015-05-01 10:54:55 +00:00
return strcmp ( strtr ( $a -> getRealPath (), '\\' , '/' ), strtr ( $b -> getRealPath (), '\\' , '/' ));
2015-05-01 10:21:21 +00:00
};
2020-10-24 18:41:49 +00:00
// Add Composer sources
2011-08-03 21:57:27 +00:00
$finder = new Finder ();
$finder -> files ()
-> ignoreVCS ( true )
-> name ( '*.php' )
-> notName ( 'Compiler.php' )
2012-03-05 22:33:31 +00:00
-> notName ( 'ClassLoader.php' )
2021-06-02 13:19:09 +00:00
-> notName ( 'InstalledVersions.php' )
2011-10-02 21:38:54 +00:00
-> in ( __DIR__ . '/..' )
2015-05-01 10:21:21 +00:00
-> sort ( $finderSort )
2011-08-03 21:57:27 +00:00
;
foreach ( $finder as $file ) {
$this -> addFile ( $phar , $file );
}
2021-06-02 13:19:09 +00:00
// Add runtime utilities separately to make sure they retains the docblocks as these will get copied into projects
2012-05-07 19:34:25 +00:00
$this -> addFile ( $phar , new \SplFileInfo ( __DIR__ . '/Autoload/ClassLoader.php' ), false );
2021-06-02 13:19:09 +00:00
$this -> addFile ( $phar , new \SplFileInfo ( __DIR__ . '/InstalledVersions.php' ), false );
2012-05-07 19:34:25 +00:00
2020-10-24 18:41:49 +00:00
// Add Composer resources
2012-05-07 19:34:25 +00:00
$finder = new Finder ();
$finder -> files ()
2015-07-16 09:48:30 +00:00
-> in ( __DIR__ . '/../../res' )
2015-05-01 10:21:21 +00:00
-> sort ( $finderSort )
2012-05-07 19:34:25 +00:00
;
foreach ( $finder as $file ) {
$this -> addFile ( $phar , $file , false );
}
2011-08-03 21:57:27 +00:00
2020-10-24 18:41:49 +00:00
// Add vendor files
2011-11-04 10:11:06 +00:00
$finder = new Finder ();
$finder -> files ()
-> ignoreVCS ( true )
2020-11-19 08:39:03 +00:00
-> notPath ( '/\/(composer\.(json|lock)|[A-Z]+\.md|\.gitignore|appveyor.yml|phpunit\.xml\.dist|phpstan\.neon\.dist|phpstan-config\.neon)$/' )
2020-11-05 14:00:54 +00:00
-> notPath ( '/bin\/(jsonlint|validate-json|simple-phpunit)(\.bat)?$/' )
2020-10-24 18:41:49 +00:00
-> notPath ( 'symfony/debug/Resources/ext/' )
-> notPath ( 'justinrainbow/json-schema/demo/' )
-> notPath ( 'justinrainbow/json-schema/dist/' )
-> notPath ( 'composer/installed.json' )
-> notPath ( 'composer/LICENSE' )
2012-04-02 06:32:24 +00:00
-> exclude ( 'Tests' )
2015-02-25 17:48:01 +00:00
-> exclude ( 'tests' )
-> exclude ( 'docs' )
2020-10-24 18:41:49 +00:00
-> in ( __DIR__ . '/../../vendor/' )
2015-05-01 10:21:21 +00:00
-> sort ( $finderSort )
2011-11-04 10:11:06 +00:00
;
2020-10-24 18:41:49 +00:00
$extraFiles = array (
realpath ( __DIR__ . '/../../vendor/composer/spdx-licenses/res/spdx-exceptions.json' ),
realpath ( __DIR__ . '/../../vendor/composer/spdx-licenses/res/spdx-licenses.json' ),
realpath ( CaBundle :: getBundledCaBundlePath ()),
realpath ( __DIR__ . '/../../vendor/symfony/console/Resources/bin/hiddeninput.exe' ),
realpath ( __DIR__ . '/../../vendor/symfony/polyfill-mbstring/Resources/mb_convert_variables.php8' ),
);
2020-11-05 13:55:06 +00:00
$unexpectedFiles = array ();
2020-10-24 18:41:49 +00:00
2011-11-04 10:11:06 +00:00
foreach ( $finder as $file ) {
2020-11-05 13:55:06 +00:00
if ( in_array ( realpath ( $file ), $extraFiles , true )) {
unset ( $extraFiles [ array_search ( realpath ( $file ), $extraFiles , true )]);
2021-02-26 15:26:47 +00:00
} elseif ( ! preg_match ( '{([/\\\\]LICENSE|\.php)$}' , $file )) {
2020-11-05 13:55:06 +00:00
$unexpectedFiles [] = ( string ) $file ;
2020-10-24 18:41:49 +00:00
}
2011-11-04 10:11:06 +00:00
2020-10-24 18:41:49 +00:00
if ( preg_match ( '{\.php[\d.]*$}' , $file )) {
$this -> addFile ( $phar , $file );
} else {
$this -> addFile ( $phar , $file , false );
}
2013-02-21 17:53:39 +00:00
}
2014-03-02 14:06:47 +00:00
2020-11-05 13:55:06 +00:00
if ( $extraFiles ) {
throw new \RuntimeException ( 'These files were expected but not added to the phar, they might be excluded or gone from the source package:' . PHP_EOL . implode ( PHP_EOL , $extraFiles ));
}
if ( $unexpectedFiles ) {
throw new \RuntimeException ( 'These files were unexpectedly added to the phar, make sure they are excluded or listed in $extraFiles:' . PHP_EOL . implode ( PHP_EOL , $unexpectedFiles ));
}
2020-10-24 18:41:49 +00:00
// Add bin/composer
2011-10-02 21:38:54 +00:00
$this -> addComposerBin ( $phar );
2011-08-03 21:57:27 +00:00
// Stubs
$phar -> setStub ( $this -> getStub ());
$phar -> stopBuffering ();
2012-02-15 17:21:17 +00:00
// disabled for interoperability with systems without gzip ext
// $phar->compressFiles(\Phar::GZ);
2011-08-03 21:57:27 +00:00
$this -> addFile ( $phar , new \SplFileInfo ( __DIR__ . '/../../LICENSE' ), false );
unset ( $phar );
2015-05-01 12:44:24 +00:00
2015-05-01 13:32:32 +00:00
// re-sign the phar with reproducible timestamp / signature
2015-05-01 12:44:24 +00:00
$util = new Timestamps ( $pharFile );
$util -> updateTimestamps ( $this -> versionDate );
2021-08-17 13:50:50 +00:00
$util -> save ( $pharFile , \Phar :: SHA512 );
2020-02-14 15:36:11 +00:00
Linter :: lint ( $pharFile );
2011-08-03 21:57:27 +00:00
}
2017-09-06 19:25:12 +00:00
/**
2017-12-18 15:02:48 +00:00
* @ param \SplFileInfo $file
2017-09-06 19:25:12 +00:00
* @ return string
*/
private function getRelativeFilePath ( $file )
2011-08-03 21:57:27 +00:00
{
2017-09-06 19:25:12 +00:00
$realPath = $file -> getRealPath ();
$pathPrefix = dirname ( dirname ( __DIR__ )) . DIRECTORY_SEPARATOR ;
$pos = strpos ( $realPath , $pathPrefix );
$relativePath = ( $pos !== false ) ? substr_replace ( $realPath , '' , $pos , strlen ( $pathPrefix )) : $realPath ;
2011-08-03 21:57:27 +00:00
2017-09-06 19:25:12 +00:00
return strtr ( $relativePath , '\\' , '/' );
}
private function addFile ( $phar , $file , $strip = true )
{
$path = $this -> getRelativeFilePath ( $file );
2012-03-14 14:23:11 +00:00
$content = file_get_contents ( $file );
2011-08-03 21:57:27 +00:00
if ( $strip ) {
2012-03-14 14:35:08 +00:00
$content = $this -> stripWhitespace ( $content );
2012-03-06 07:44:41 +00:00
} elseif ( 'LICENSE' === basename ( $file )) {
2012-03-14 14:23:11 +00:00
$content = " \n " . $content . " \n " ;
2011-08-03 21:57:27 +00:00
}
2013-11-22 10:06:22 +00:00
if ( $path === 'src/Composer/Composer.php' ) {
2020-09-11 21:51:55 +00:00
$content = strtr (
$content ,
array (
'@package_version@' => $this -> version ,
'@package_branch_alias_version@' => $this -> branchAliasVersion ,
2020-11-22 13:48:56 +00:00
'@release_date@' => $this -> versionDate -> format ( 'Y-m-d H:i:s' ),
2020-09-11 21:51:55 +00:00
)
);
2019-02-21 12:39:12 +00:00
$content = preg_replace ( '{SOURCE_VERSION = \'[^\']+\';}' , 'SOURCE_VERSION = \'\';' , $content );
2013-11-22 10:06:22 +00:00
}
2011-11-12 18:44:24 +00:00
2011-08-03 21:57:27 +00:00
$phar -> addFromString ( $path , $content );
}
2011-10-02 21:38:54 +00:00
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 );
}
2012-03-14 14:23:11 +00:00
/**
2012-03-14 14:35:08 +00:00
* Removes whitespace from a PHP source string while preserving line numbers .
2012-03-14 14:23:11 +00:00
*
2012-05-22 10:07:08 +00:00
* @ param string $source A PHP string
2012-03-14 14:35:08 +00:00
* @ return string The PHP string with the whitespace removed
2012-03-14 14:23:11 +00:00
*/
2012-03-14 14:35:08 +00:00
private function stripWhitespace ( $source )
2012-03-14 14:23:11 +00:00
{
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 " ));
2012-03-14 14:35:08 +00:00
} 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 ;
2012-03-14 14:23:11 +00:00
} else {
$output .= $token [ 1 ];
}
}
return $output ;
}
2011-10-02 21:38:54 +00:00
private function getStub ()
2011-08-03 21:57:27 +00:00
{
2012-05-13 09:28:19 +00:00
$stub = <<< 'EOF'
2012-01-22 20:42:50 +00:00
#!/usr/bin/env php
2011-08-03 21:57:27 +00:00
< ? 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 .
*/
2014-12-03 13:58:23 +00:00
// Avoid APC causing random fatal errors per https://github.com/composer/composer/issues/264
2018-10-31 08:55:53 +00:00
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 )) {
2014-12-03 13:58:23 +00:00
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 );
}
}
2011-08-03 21:57:27 +00:00
Phar :: mapPhar ( 'composer.phar' );
2012-05-13 09:28:19 +00:00
EOF ;
2015-05-01 10:54:55 +00:00
// add warning once the phar is older than 60 days
2012-05-13 09:28:19 +00:00
if ( preg_match ( '{^[a-f0-9]+$}' , $this -> version )) {
2015-05-01 10:54:55 +00:00
$warningTime = $this -> versionDate -> format ( 'U' ) + 60 * 86400 ;
2012-06-24 10:18:07 +00:00
$stub .= " define('COMPOSER_DEV_WARNING_TIME', $warningTime ); \n " ;
2012-05-13 09:28:19 +00:00
}
2012-06-24 10:18:07 +00:00
return $stub . <<< 'EOF'
2011-08-03 21:57:27 +00:00
require 'phar://composer.phar/bin/composer' ;
__HALT_COMPILER ();
EOF ;
}
}