Вход Регистрация
Файл: concrete5.7.5.6/concrete/src/Console/Command/PackPackageCommand.php
Строк: 668
<?php
namespace ConcreteCoreConsoleCommand;

use 
SymfonyComponentConsoleCommandCommand;
use 
SymfonyComponentConsoleInputInputOption;
use 
SymfonyComponentConsoleInputInputArgument;
use 
SymfonyComponentConsoleInputInputInterface;
use 
SymfonyComponentConsoleOutputOutputInterface;
use 
Core;
use 
Exception;
use 
Package;
use 
stdClass;
use 
ZipArchive;
use 
GettextTranslations;

final class 
PackPackageCommand extends Command
{
    const 
PACKAGEFORMAT_LEGACY 'legacy';
    const 
PACKAGEFORMAT_CURRENT 'current';

    const 
SHORTTAGS_ALL 'all';
    const 
SHORTTAGS_KEEPECHO 'keep-echo';
    const 
SHORTTAGS_NO 'no';

    const 
KEEP_DOT 'dot';
    const 
KEEP_SOURCES 'sources';

    const 
YNA_YES 'yes';
    const 
YNA_AUTO 'auto';
    const 
YNA_NO 'no';

    const 
ZIPOUT_AUTO '-';

    protected function 
configure()
    {
        
$zipAuto = static::ZIPOUT_AUTO;
        
$keepDot = static::KEEP_DOT;
        
$keepSources = static::KEEP_SOURCES;
        
$this
            
->setName('c5:package-pack')
            ->
setDescription('Process a package (expand PHP short tags, compile icons and translations, create zip archive)')
            ->
addArgument('package'InputArgument::REQUIRED'The handle of the package to work on (or the path to a directory containing a concrete5 package)')
            ->
addOption('short-tags''s'InputOption::VALUE_REQUIRED'Expand PHP short tags ['.static::SHORTTAGS_ALL.'|'.static::SHORTTAGS_KEEPECHO.'|'.static::SHORTTAGS_NO.']', static::SHORTTAGS_ALL)
            ->
addOption('compile-icons''i'InputOption::VALUE_REQUIRED'Compile SVG icons to PNG icons ['.static::YNA_YES.'|'.static::YNA_NO.'|'.static::YNA_AUTO.']?', static::YNA_AUTO)
            ->
addOption('compile-translations''t'InputOption::VALUE_REQUIRED'Compile source .po translation files to the .mo binary format ['.static::YNA_YES.'|'.static::YNA_NO.'|'.static::YNA_AUTO.']?', static::YNA_AUTO)
            ->
addOption('keep''k'InputOption::VALUE_REQUIRED InputOption::VALUE_IS_ARRAY'Which files should be stored in the zip archive ['.static::KEEP_DOT.'|'.static::KEEP_SOURCES.']')
            ->
addOption('update-source-directory''u'InputOption::VALUE_NONE'Update the files of the source directory (otherwise it remains untouched)')
            ->
addOption('zip''z'InputOption::VALUE_OPTIONAL'Create a zip archive of the package (and optionally set its path)', static::ZIPOUT_AUTO)
            ->
setHelp(<<<EOT
You have to specify at least the -z option (to create a zip file) and/or the -u option (to update the package directory).

If the -u option is not specified, the package directory is not touched.

If the -z option is specified without a value (or with a value of "
$zipAuto"), the zip file will be created in the directory containing the package folder.
If the -z option is set and the zip file already exists, it will be overwritten.

To include in the zip archive the files and directories starting with a dot, use the "-k 
$keepDot" option.
To include in the zip archive the source files for translations (.po files) and icons (.svg files) use the "-k 
$keepSources" option.

Please remark that this command can also parse legacy (pre-5.7) packages.

Returns codes:
  0 operation completed successfully
  1 errors occurred
EOT
            )
        ;
    }

    protected function 
execute(InputInterface $inputOutputInterface $output)
    {
        
$rc 0;
        try {
            
$state = static::parseInput($input);
            if (
$state->updateSourceDirectory === false && $state->zipFilename === null) {
                throw new 
Exception('No operation will be performed: neither a zip archive will be created nor the source directory will be updated');
            }
            
$state->zip null;
            if (
$state->zipFilename !== null) {
                if (@
file_exists($state->zipFilename)) {
                    @
unlink($state->zipFilename);
                    if (@
file_exists($state->zipFilename)) {
                        throw new 
Exception("Unable to delete the file {$state->zipFilename}");
                    }
                }
                
$state->zip = new ZipArchive();
                if (@
$state->zip->open($state->zipFilenameZipArchive::CREATE ZipArchive::CHECKCONS) !== true) {
                    
$err = @$state->zip->getStatusString();
                    if (!
$err) {
                        
$err "Failed to create the file {$state->zipFilename}";
                    }
                    throw new 
Exception($err);
                }
            }
            static::
processDirectory($state$output$state->packageDirectory'');
            if (
$state->zip !== null) {
                
$state->zip->close();
                
$state->zip null;
            }
            if (
$state->updateSourceDirectory) {
                
$output->writeln('Package folder has been updated');
            }
            if (
$state->zipFilename !== null) {
                
$output->writeln('Zip archive created: '.$state->zipFilename);
            }
        } catch (
Exception $x) {
            if (
$state->zip !== null) {
                
$state->zip->close();
                
$state->zip null;
                @
unlink($state->zipFilename);
            }
            
$output->writeln('<error>'.$x->getMessage().'</error>');
            
$rc 1;
        }

        return 
$rc;
    }

    
/**
     * @param InputInterface $input
     *
     * @return stdClass {
     *
     *  @var string $packageHandle The package handle
     *  @var string $packageDirectory The package directory
     *  @var string|null $packageVersion The package version (if retrieved)
     *  @var string|null $packageFormat The package format (if retrieved) - One of the PackPackageCommand::PACKAGEFORMAT_ constants
     *  @var string $shortTags One of the PackPackageCommand::SHORTTAGS_ constants
     *  @var string $compileIcons One of the PackPackageCommand::YNA_ constants
     *  @var string $compileTranslations One of the PackPackageCommand::YNA_ constants
     *  @var array $keep List of PackPackageCommand::KEEP_ constants
     *  @var bool $updateSourceDirectory
     *  @var string|null $zipFilename
     * }
     */
    
protected static function parseInput(InputInterface $input)
    {
        
$result = new stdClass();

        
$vsh Core::make('helper/validation/strings');
        
/* @var ConcreteCoreUtilityServiceValidationStrings $vsh */
        
$fh Core::make('helper/file');
        
/* @var ConcreteCoreFileServiceFile $fh */

        
$result->packageHandle null;
        
$result->packageDirectory null;
        
$result->packageVersion null;
        
$result->packageFormat null;
        
$p $input->getArgument('package');
        if (
is_dir($p) || !$vsh->handle($p)) {
            
$result->packageDirectory = @realpath($p);
            if (
$result->packageDirectory === false) {
                throw new 
Exception("Unable to find the directory '$p'");
            }
            
$controllerFile $result->packageDirectory.'/'.FILENAME_CONTROLLER;
            if (!
is_file($controllerFile)) {
                throw new 
Exception("The directory '{$result->packageDirectory}' does not seems to contain a valid concrete5 package");
            }
            
$controllerContents $fh->getContents($controllerFile);
            if (
$controllerContents) {
                
$allTokens = @token_get_all($controllerContents);
                if (
$allTokens) {
                    
$tokens array_values(
                        
array_filter(
                            
$allTokens,
                            function (
$token) {
                                
$keep true;
                                if (
is_array($token)) {
                                    switch (
$token[0]) {
                                        case 
T_DOC_COMMENT:
                                        case 
T_WHITESPACE:
                                        case 
T_COMMENT;
                                        
$keep false;
                                        break;
                                    }
                                }

                                return 
$keep;
                            }
                            )
                        );
                    
$packageAppVersionRequired null;
                    
// Look for package info
                    
for ($i 0$i count($tokens) - 2; ++$i) {
                        if (
                            
$result->packageHandle === null
                            
&& is_array($tokens[$i 0]) && $tokens[$i 0][0] === T_VARIABLE && $tokens[$i 0][1] === '$pkgHandle'
                            
&& is_string($tokens[$i 1]) && $tokens[$i 1] === '='
                            
&& is_array($tokens[$i 2]) && $tokens[$i 2][0] === T_CONSTANT_ENCAPSED_STRING
                        
) {
                            
$result->packageHandle = @eval('return '.$tokens[$i 2][1].';');
                            if (!
is_string($result->packageHandle) || $result->packageHandle === '') {
                                
$result->packageHandle null;
                            }
                        }
                        if (
                            
$result->packageVersion === null
                            
&& is_array($tokens[$i 0]) && $tokens[$i 0][0] === T_VARIABLE && $tokens[$i 0][1] === '$pkgVersion'
                            
&& is_string($tokens[$i 1]) && $tokens[$i 1] === '='
                            
&& is_array($tokens[$i 2]) && $tokens[$i 2][0] === T_CONSTANT_ENCAPSED_STRING
                        
) {
                            
$result->packageVersion = @eval('return '.$tokens[$i 2][1].';');
                            if (!
is_string($result->packageVersion) || $result->packageVersion === '') {
                                
$result->packageVersion null;
                            }
                        }
                        if (
                            
$packageAppVersionRequired === null
                            
&& is_array($tokens[$i 0]) && $tokens[$i 0][0] === T_VARIABLE && $tokens[$i 0][1] === '$appVersionRequired'
                            
&& is_string($tokens[$i 1]) && $tokens[$i 1] === '='
                            
&& is_array($tokens[$i 2]) && $tokens[$i 2][0] === T_CONSTANT_ENCAPSED_STRING
                        
) {
                            
$packageAppVersionRequired = @eval('return '.$tokens[$i 2][1].';');
                            if (!
is_string($packageAppVersionRequired) || $packageAppVersionRequired === '') {
                                
$packageAppVersionRequired null;
                            }
                        }
                    }
                    if (
$packageAppVersionRequired !== null) {
                        if (
version_compare($packageAppVersionRequired'5.7') < 0) {
                            
$result->packageFormat self::PACKAGEFORMAT_LEGACY;
                        } else {
                            
$result->packageFormat self::PACKAGEFORMAT_CURRENT;
                        }
                    }
                }
            }
            if (
$result->packageHandle === null) {
                
$result->packageHandle basename($result->packageDirectory);
            }
        } else {
            foreach (
Package::getAvailablePackages(false) as $pkg) {
                if (
strcasecmp($p$pkg->getPackageHandle()) === 0) {
                    
$result->packageHandle $pkg->getPackageHandle();
                    
$result->packageDirectory $pkg->getPackagePath();
                    
$result->packageVersion $pkg->getPackageVersion();
                    
$result->packageFormat self::PACKAGEFORMAT_CURRENT;
                    break;
                }
            }
            if (
$result->packageHandle === null) {
                throw new 
Exception("Unable to find a package with handle '$p'");
            }
        }

        
$v $input->getOption('short-tags');
        switch (
$v) {
            case static::
SHORTTAGS_ALL:
            case static::
SHORTTAGS_KEEPECHO:
            case static::
SHORTTAGS_NO:
                
$result->shortTags $v;
                break;
            default:
                throw new 
Exception('Invalid value of the --short-tags option: '.$v);
        }
        
$v $input->getOption('compile-icons');
        switch (
$v) {
            case static::
YNA_YES:
            case static::
YNA_AUTO:
            case static::
YNA_NO:
                
$result->compileIcons $v;
                break;
            default:
                throw new 
Exception('Invalid value of the --compile-icons option: '.$v);
        }
        
$v $input->getOption('compile-translations');
        switch (
$v) {
            case static::
YNA_YES:
            case static::
YNA_AUTO:
            case static::
YNA_NO:
                
$result->compileTranslations $v;
                break;
            default:
                throw new 
Exception('Invalid value of the --compile-translations option: '.$v);
        }
        
$result->keep = array();
        foreach (
$input->getOption('keep') as $keep) {
            if (!
in_array($keep$result->keep)) {
                switch (
$keep) {
                    case static::
KEEP_DOT:
                    case static::
KEEP_SOURCES:
                        
$result->keep[] = $keep;
                        break;
                    default:
                        throw new 
Exception('Invalid value of the --keep option: '.$keep);
                }
            }
        }
        
$result->updateSourceDirectory = (bool) $input->getOption('update-source-directory');

        
$result->zipFilename null;
        
$zipOption $input->getOption('zip');
        if (
$zipOption === static::ZIPOUT_AUTO) {
            if (
$input->getParameterOption(array('--zip''-z')) === false) {
                
$zipOption null;
            }
        }
        if (
$zipOption !== null) {
            if (
$zipOption === static::ZIPOUT_AUTO) {
                
$zipOption dirname($result->packageDirectory);
            }
            if (
is_dir($zipOption)) {
                
$dir = @realpath($zipOption);
                if (
$dir === false) {
                    throw new 
Exception('Unable to normalize the directory '.$zipOption);
                }
                
$result->zipFilename $dir.DIRECTORY_SEPARATOR.$result->packageHandle;
                if (
$result->packageVersion !== null) {
                    
$result->zipFilename .= '-' $result->packageVersion;
                }
                
$result->zipFilename .= '.zip';
            } else {
                
$result->zipFilename $zipOption;
            }
        }

        return 
$result;
    }

    private static function 
processDirectory($stateOutputInterface $output$dirAbs$dirRel)
    {
        
$hDir = @opendir($dirAbs);
        if (
$hDir === false) {
            throw new 
Exception("Failed to open directory $dirAbs");
        }
        try {
            
$storePrefix $state->packageHandle;
            if (
$dirRel !== '') {
                
$storePrefix .= "/$dirRel";
            }
            if (
$state->zip !== null) {
                if (@
$state->zip->addEmptyDir($storePrefix) === false) {
                    
$err "Failed to create directory entry $storePrefix in zip file";
                    
$reason $state->zip->getStatusString();
                    if (
$reason) {
                        
$err .= ": $reason";
                    }
                    throw new 
Exception($err);
                }
            }
            
$store = array();
            
$subDirs = array();
            while ((
$item readdir($hDir)) !== false) {
                if ((
$item === '.') || ($item === '..')) {
                    continue;
                }
                if (
$item[0] === '.' && !in_array(static::KEEP_DOT$state->keep)) {
                    continue;
                }
                
$itemAbs $dirAbs.DIRECTORY_SEPARATOR.$item;
                if (
is_dir($itemAbs)) {
                    
$subDirs[] = $item;
                    continue;
                }
                if (isset(
$store[$item])) {
                    continue;
                }
                
$store[$item] = array('kind' => 'file''file' => $itemAbs);
                
$p strrpos($item'.');
                
$ext = ($p === false || $p === 0) ? '' strtolower(substr($item$p 1));
                switch (
$ext) {
                    case 
'php':
                        if (
$state->shortTags !== static::SHORTTAGS_NO) {
                            
$newContents = static::expandShortTags($itemAbs$state->shortTags !== static::SHORTTAGS_KEEPECHO);
                            if (
$newContents !== null) {
                                if (
$state->updateSourceDirectory) {
                                    if (@
file_put_contents($itemAbs$newContents) === false) {
                                        throw new 
Exception("Failed to update PHP file $itemAbs");
                                    }
                                } elseif (
$state->zip !== null) {
                                    
$store[$item] = array('kind' => 'contents''contents' => $newContents);
                                }
                                
$output->writeln("Expanded short tags in: $dirRel/$item");
                            }
                        }
                        break;
                    case 
'pot':
                        if (
$dirRel === 'languages') {
                            if (
$state->zip !== null && !in_array(static::KEEP_SOURCES$state->keep)) {
                                
$output->writeln("Skipped source file: $dirRel/$item");
                                unset(
$store[$item]);
                            }
                        }
                        break;
                    case 
'po':
                        if (
preg_match('%^languages/[^/]+/LC_MESSAGES$%'$dirRel)) {
                            if (
$state->zip !== null && !in_array(static::KEEP_SOURCES$state->keep)) {
                                
$output->writeln("Skipped source file: $dirRel/$item");
                                unset(
$store[$item]);
                            }
                            
$compile false;
                            
$compiledAbs substr($itemAbs0, -2).'mo';
                            switch (
$state->compileTranslations) {
                                case static::
YNA_YES:
                                    
$compile true;
                                    break;
                                case static::
YNA_AUTO:
                                    if (
is_file($compiledAbs)) {
                                        
$sourceTime = @filemtime($itemAbs);
                                        
$compiledTime = @filemtime($compiledAbs);
                                        if (
$sourceTime && $compiledTime && $sourceTime $compiledTime) {
                                            
$compile true;
                                        }
                                    } else {
                                        
$compile true;
                                    }
                                    break;
                            }
                            if (
$compile) {
                                
$compiledItem substr($item0, -2).'mo';
                                
$output->writeln("Compiling language : $dirRel/$item => $dirRel/$compiledItem");
                                
$translations Translations::fromPoFile($itemAbs);
                                if (
$state->updateSourceDirectory) {
                                    if (
$translations->toMoFile($compiledAbs) === false) {
                                        throw new 
Exception("Failed to write compiled translations file to $compiledAbs");
                                    }
                                    
$store[$compiledItem] = array('kind' => 'file''file' => $compiledAbs);
                                } elseif (
$state->zip !== null) {
                                    
$store[$compiledItem] = array('kind' => 'contents''contents' => $translations->toMoString());
                                }
                            }
                        }
                        break;
                    case 
'svg':
                        if (
$item === 'icon.svg') {
                            
$iconSize null;
                            if (
$dirRel === '') {
                                
// Package icon
                                
$iconSize = ($state->packageFormat === static::PACKAGEFORMAT_CURRENT) ? array('width' => 200'height' => 200) : array('width' => 97'height' => 97);
                            } elseif (
preg_match('%^blocks/[^/]+$%'$dirRel)) {
                                
$iconSize = ($state->packageFormat === static::PACKAGEFORMAT_CURRENT) ? array('width' => 50'height' => 50) : array('width' => 16'height' => 16);
                            }
                            if (
$iconSize !== null) {
                                if (
$state->zip !== null && !in_array(static::KEEP_SOURCES$state->keep)) {
                                    
$output->writeln("Skipped source file: $dirRel/$item");
                                    unset(
$store[$item]);
                                }
                                
$compile false;
                                
$compiledAbs substr($itemAbs0, -3).'png';
                                switch (
$state->compileIcons) {
                                    case static::
YNA_YES:
                                        
$compile true;
                                        break;
                                    case static::
YNA_AUTO:
                                        if (
is_file($compiledAbs)) {
                                            
$sourceTime = @filemtime($itemAbs);
                                            
$compiledTime = @filemtime($compiledAbs);
                                            if (
$sourceTime && $compiledTime && $sourceTime $compiledTime) {
                                                
$compile true;
                                            }
                                        } else {
                                            
$compile true;
                                        }
                                        break;
                                }
                                if (
$compile) {
                                    
$compiledItem substr($item0, -3).'png';
                                    
$output->writeln("Generating png icon: $dirRel/$item => $dirRel/$compiledItem");
                                    
$tmpDir Core::make('helper/file')->getTemporaryDirectory();
                                    if (!
is_dir($tmpDir)) {
                                        throw new 
Exception('Failed to retrieve the temporary directory');
                                    }
                                    
$tmpFile = @tempnam($tmpDir'c5p');
                                    if (
$tmpFile === false) {
                                        throw new 
Exception('Failed to create a temporary file in directory '.$tmpDir);
                                    }
                                    try {
                                        
$cmd 'inkscape';
                                        
$cmd .= ' --file=' escapeshellarg($itemAbs);
                                        
$cmd .= ' --export-png=' escapeshellarg($tmpFile);
                                        
$cmd .= ' --export-area-page';
                                        
$cmd .= ' --export-width='.$iconSize['width'];
                                        
$cmd .= ' --export-height='.$iconSize['height'];
                                        
$cmd .= ' 2>&1';
                                        
$execOutput = array();
                                        @
exec($cmd$execOutput$rc);
                                        if (!
is_int($rc)) {
                                            
$rc = -1;
                                        }
                                        if (
$rc !== 0) {
                                            throw new 
Exception(implode("n"$execOutput));
                                        }
                                        
$pngData = @file_get_contents($tmpFile);
                                        if (
$pngData === false) {
                                            throw new 
Exception('Failed to read the generated PNG file');
                                        }
                                    } catch (
Exception $x) {
                                        @
unlink($tmpFile);
                                        throw 
$x;
                                    }
                                    @
unlink($tmpFile);
                                    if (
$pngData === '') {
                                        throw new 
Exception("Inkscape failed to generate the PNG file:n".implode("n"$execOutput));
                                    }
                                    if (
$state->updateSourceDirectory) {
                                        if (@
file_put_contents($compiledAbs$pngData) === false) {
                                            throw new 
Exception("Failed to write rendered SVN icon to $compiledAbs");
                                        }
                                        
$store[$compiledItem] = array('kind' => 'file''file' => $compiledAbs);
                                    } elseif (
$state->zip !== null) {
                                        
$store[$compiledItem] = array('kind' => 'contents''contents' => $pngData);
                                    }
                                }
                            }
                        }
                        break;
                }
            }
            @
closedir($hDir);
            if (
$state->zip !== null) {
                foreach (
$store as $storeItem => $storeWhat) {
                    
$storeName $storePrefix.'/'.$storeItem;
                    switch (
$storeWhat['kind']) {
                        case 
'file':
                            if (
$state->zip->addFile($storeWhat['file'], $storeName) === false) {
                                
$err "Failed to store file $storeName to zip file";
                                
$reason $state->zip->getStatusString();
                                if (
$reason) {
                                    
$err .= ": $reason";
                                }
                                throw new 
Exception($err);
                            }
                            break;
                        case 
'contents':
                            if (
$state->zip->addFromString($storeName$storeWhat['contents']) === false) {
                                
$err "Failed to store file $storeName to zip file";
                                
$reason $state->zip->getStatusString();
                                if (
$reason) {
                                    
$err .= ": $reason";
                                }
                                throw new 
Exception($err);
                            }
                            break;
                        default:
                            throw new 
Exception('?');
                    }
                }
            }
            unset(
$store);
            foreach (
$subDirs as $subDir) {
                static::
processDirectory($state$output$dirAbs.DIRECTORY_SEPARATOR.$subDir, ($dirRel === '') ? $subDir "$dirRel/$subDir");
            }
        } catch (
Exception $x) {
            @
closedir($hDir);
            throw 
$x;
        }
    }

    
/**
     * @param string $file
     * @param bool $shortEcho
     *
     * @return string|null
     */
    
protected static function expandShortTags($phpFile$expandShortEcho)
    {
        
$phpCode = @file_get_contents($phpFile);
        if (
$phpCode === false) {
            throw new 
Exception("Failed to read contents of file $phpFile");
        }
        
$result '';
        
$tokens = @token_get_all($phpCode);
        
$numTokens count($tokens);
        
$someExpanded false;
        for (
$i 0$i $numTokens; ++$i) {
            
$token $tokens[$i];
            if (
is_array($token)) {
                
$expanded null;
                switch (
$token[0]) {
                    case 
T_OPEN_TAG_WITH_ECHO:
                        if (
$expandShortEcho) {
                            
$expanded '<?php echo';
                        }
                        break;
                    case 
T_OPEN_TAG:
                        
$expanded '<?php';
                        break;
                }
                if (
$expanded === null) {
                    
$result .= $token[1];
                } else {
                    
$someExpanded true;
                    
$result .= $expanded;
                    
// Let's see if we have to add some white space after the expanded token
                    
if (preg_match('/([ trn]+)$/'$token[1], $m)) {
                        
$result .= $m[1];
                    } elseif (
$i $numTokens && (!is_array($tokens[$i 1]) || $tokens[$i 1][0] !== T_WHITESPACE)) {
                        
$result .= ' ';
                    }
                }
            } else {
                
$result .= $token;
            }
        }

        return (
$someExpanded && $result !== $phpCode) ? $result null;
    }
}
Онлайн: 4
Реклама