2023-03-18 17:32:21 +08:00
< ? php
declare ( strict_types = 1 );
namespace SPC\command ;
2023-08-08 20:23:38 +08:00
use SPC\store\FileSystem ;
2023-04-22 17:45:43 +08:00
use Symfony\Component\Console\Attribute\AsCommand ;
2023-03-18 17:32:21 +08:00
use Symfony\Component\Console\Helper\ProgressBar ;
use Symfony\Component\Console\Input\InputArgument ;
use Symfony\Component\Console\Input\InputOption ;
2023-08-08 20:23:38 +08:00
use function Laravel\Prompts\confirm ;
use function Laravel\Prompts\text ;
2023-04-22 17:45:43 +08:00
#[AsCommand('deploy', 'Deploy static-php-cli self to an .phar application')]
2023-03-18 17:32:21 +08:00
class DeployCommand extends BaseCommand
{
2023-08-20 19:51:45 +08:00
public function configure () : void
2023-03-18 17:32:21 +08:00
{
$this -> addArgument ( 'target' , InputArgument :: OPTIONAL , 'The file or directory to pack.' );
$this -> addOption ( 'auto-phar-fix' , null , InputOption :: VALUE_NONE , 'Automatically fix ini option.' );
$this -> addOption ( 'overwrite' , 'W' , InputOption :: VALUE_NONE , 'Overwrite existing files.' );
2023-08-08 20:23:38 +08:00
$this -> addOption ( 'with-no-dev' , 'D' , InputOption :: VALUE_NONE , 'Automatically use non-dev composer dependencies to reduce size' );
$this -> addOption ( 'with-dev' , 'd' , InputOption :: VALUE_NONE , 'Automatically use dev composer dependencies' );
2023-03-18 17:32:21 +08:00
}
2023-08-20 19:51:45 +08:00
/**
* @ throws \PharException
*/
2023-04-22 17:45:43 +08:00
public function handle () : int
2023-03-18 17:32:21 +08:00
{
2023-08-08 20:23:38 +08:00
$composer = require ROOT_DIR . '/vendor/composer/installed.php' ;
if (( $composer [ 'root' ][ 'dev' ] ? ? false ) === true ) {
if ( ! $this -> getOption ( 'with-no-dev' )) {
$this -> output -> writeln ( '<comment>Current static-php-cli dependencies have installed dev-dependencies</comment>' );
$this -> output -> writeln ( '<comment>If you want to remove, you can choose "Yes" to run command "composer update --no-dev" to remove.</comment>' );
$this -> output -> writeln ( '<comment>Or choose "No", just pack, deploy.</comment>' );
$ask = confirm ( 'Do you want to remove dev-dependencies to reduce size of phar file?' );
} elseif ( ! $this -> getOption ( 'with-dev' )) {
$ask = true ;
} else {
$ask = false ;
}
if ( $ask ) {
[ $code ] = shell () -> execWithResult ( 'composer update --no-dev' );
if ( $code !== 0 ) {
$this -> output -> writeln ( '<error>"composer update --no-dev" failed with exit code [' . $code . ']</error>' );
$this -> output -> writeln ( '<error>You may need to run this command by your own.</error>' );
return static :: FAILURE ;
}
$this -> output -> writeln ( '<info>Update successfully, you need to re-run deploy command to pack.</info>' );
return static :: SUCCESS ;
}
}
2023-03-18 17:32:21 +08:00
// 首先得确认是不是关闭了readonly模式
if ( ini_get ( 'phar.readonly' ) == 1 ) {
2023-04-22 17:45:43 +08:00
if ( $this -> getOption ( 'auto-phar-fix' )) {
2023-03-18 17:32:21 +08:00
$ask = true ;
} else {
2023-08-08 20:23:38 +08:00
$this -> output -> writeln ( '<comment>pack command needs "phar.readonly" = "Off" !</comment>' );
$ask = confirm ( 'Do you want to automatically set it and continue ?' );
// $ask = $prompt->requireBool('<comment>pack command needs "phar.readonly" = "Off" !</comment>' . PHP_EOL . 'If you want to automatically set it and continue, just Enter', true);
2023-03-18 17:32:21 +08:00
}
if ( $ask ) {
global $argv ;
2023-08-08 20:23:38 +08:00
$args = array_merge ([ '-d' , 'phar.readonly=0' ], $_SERVER [ 'argv' ], [ '--no-motd' ]);
2023-03-27 00:11:00 +08:00
if ( function_exists ( 'pcntl_exec' )) {
2023-04-22 17:45:43 +08:00
$this -> output -> writeln ( '<info>Changing to phar.readonly=0 mode ...</info>' );
2023-03-27 00:11:00 +08:00
if ( pcntl_exec ( PHP_BINARY , $args ) === false ) {
2023-08-08 20:23:38 +08:00
throw new \PharException ( 'Switching to read write mode failed, please check the environment.' );
2023-03-27 00:11:00 +08:00
}
} else {
2023-04-22 17:45:43 +08:00
$this -> output -> writeln ( '<info>Now running command in child process.</info>' );
2023-03-27 00:11:00 +08:00
passthru ( PHP_BINARY . ' -d phar.readonly=0 ' . implode ( ' ' , $argv ), $retcode );
exit ( $retcode );
}
2023-03-18 17:32:21 +08:00
}
}
// 获取路径
$path = WORKING_DIR ;
// 如果是目录,则将目录下的所有文件打包
2023-08-08 20:23:38 +08:00
$phar_path = text ( 'Please input the phar target filename' , default : '/tmp/static-php-cli.phar' );
// $phar_path = $prompt->requireArgument('target', 'Please input the phar target filename', 'static-php-cli.phar');
2023-03-18 17:32:21 +08:00
2023-08-08 20:23:38 +08:00
if ( FileSystem :: isRelativePath ( $phar_path )) {
$phar_path = WORKING_DIR . '/' . $phar_path ;
2023-03-18 17:32:21 +08:00
}
if ( file_exists ( $phar_path )) {
2023-08-08 20:23:38 +08:00
if ( ! $this -> getOption ( 'overwrite' )) {
$this -> output -> writeln ( '<comment>The file "' . $phar_path . '" already exists.</comment>' );
$ask = confirm ( 'Do you want to overwrite it?' );
} else {
$ask = true ;
}
2023-03-18 17:32:21 +08:00
if ( ! $ask ) {
2023-04-22 17:45:43 +08:00
$this -> output -> writeln ( '<comment>User canceled.</comment>' );
2023-08-06 10:43:20 +08:00
return static :: FAILURE ;
2023-03-18 17:32:21 +08:00
}
@ unlink ( $phar_path );
}
$phar = new \Phar ( $phar_path );
$phar -> startBuffering ();
2023-08-08 20:23:38 +08:00
$all = FileSystem :: scanDirFiles ( $path , true , true );
2023-03-18 17:32:21 +08:00
$all = array_filter ( $all , function ( $x ) {
2023-03-26 23:59:34 +08:00
$dirs = preg_match ( '/(^(config|src|vendor)\\/|^(composer\\.json|README\\.md|source\\.json|LICENSE|README-en\\.md)$)/' , $x );
2023-03-18 17:32:21 +08:00
return ! ( $dirs !== 1 );
});
sort ( $all );
$map = [];
foreach ( $all as $v ) {
$map [ $v ] = $path . '/' . $v ;
}
2023-04-22 17:45:43 +08:00
$this -> output -> writeln ( '<info>Start packing files...</info>' );
2023-03-18 17:32:21 +08:00
try {
2023-04-22 17:45:43 +08:00
foreach ( $this -> progress () -> iterate ( $map ) as $file => $origin_file ) {
2023-03-26 23:59:34 +08:00
$phar -> addFromString ( $file , php_strip_whitespace ( $origin_file ));
}
// $phar->buildFromIterator(new SeekableArrayIterator($map, new ProgressBar($output)));
2023-03-18 17:32:21 +08:00
$phar -> addFromString (
'.phar-entry.php' ,
str_replace (
'/../vendor/autoload.php' ,
'/vendor/autoload.php' ,
2023-03-18 17:39:08 +08:00
file_get_contents ( ROOT_DIR . '/bin/spc' )
2023-03-18 17:32:21 +08:00
)
);
$stub = '.phar-entry.php' ;
$phar -> setStub ( $phar -> createDefaultStub ( $stub ));
} catch ( \Throwable $e ) {
2023-04-22 17:45:43 +08:00
$this -> output -> writeln ( $e );
2023-08-06 10:43:20 +08:00
return static :: FAILURE ;
2023-03-18 17:32:21 +08:00
}
$phar -> addFromString ( '.prod' , 'true' );
$phar -> stopBuffering ();
2023-04-22 17:45:43 +08:00
$this -> output -> writeln ( PHP_EOL . 'Done! Phar file is generated at "' . $phar_path . '".' );
2023-03-18 17:32:21 +08:00
if ( file_exists ( SOURCE_PATH . '/php-src/sapi/micro/micro.sfx' )) {
2023-04-22 17:45:43 +08:00
$this -> output -> writeln ( 'Detected you have already compiled micro binary, I will make executable now for you!' );
2023-03-18 17:32:21 +08:00
file_put_contents (
2023-03-27 00:11:00 +08:00
pathinfo ( $phar_path , PATHINFO_DIRNAME ) . '/spc' ,
2023-03-18 17:32:21 +08:00
file_get_contents ( SOURCE_PATH . '/php-src/sapi/micro/micro.sfx' ) .
file_get_contents ( $phar_path )
);
2023-03-27 00:11:00 +08:00
chmod ( pathinfo ( $phar_path , PATHINFO_DIRNAME ) . '/spc' , 0755 );
2023-04-22 17:45:43 +08:00
$this -> output -> writeln ( '<info>Binary Executable: ' . pathinfo ( $phar_path , PATHINFO_DIRNAME ) . '/spc</info>' );
2023-03-18 17:32:21 +08:00
}
chmod ( $phar_path , 0755 );
2023-04-22 17:45:43 +08:00
$this -> output -> writeln ( '<info>Phar Executable: ' . $phar_path . '</info>' );
2023-08-06 10:43:20 +08:00
return static :: SUCCESS ;
2023-03-18 17:32:21 +08:00
}
2023-03-26 23:59:34 +08:00
2023-08-20 19:51:45 +08:00
private function progress () : ProgressBar
2023-03-26 23:59:34 +08:00
{
2023-08-20 19:51:45 +08:00
$progress = new ProgressBar ( $this -> output , 0 );
2023-03-26 23:59:34 +08:00
$progress -> setBarCharacter ( '<fg=green>⚬</>' );
$progress -> setEmptyBarCharacter ( '<fg=red>⚬</>' );
$progress -> setProgressCharacter ( '<fg=green>➤</>' );
$progress -> setFormat (
" %current%/%max% [%bar%] %percent:3s%% \n 🪅 %estimated:-20s% %memory:20s% " . PHP_EOL
);
return $progress ;
}
2023-03-18 17:32:21 +08:00
}