Find this useful? Enter your email to receive occasional updates for securing PHP code.
Signing you up...
Thank you for signing up!
PHP Decode
<?php namespace SwooleCli; use MJS\TopSort\CircularDependencyException; use MJS\TopSort\..
Decoded Output download
<?php
namespace SwooleCli;
use MJS\TopSort\CircularDependencyException;
use MJS\TopSort\ElementNotFoundException;
use MJS\TopSort\Implementations\StringSort;
use RuntimeException;
class Preprocessor
{
public const VERSION = '1.7';
public const IMAGE_NAME = 'phpswoole/swoole-cli-builder';
public const CONTAINER_NAME = 'swoole-cli-builder';
protected static ?Preprocessor $instance = null;
protected array $prepareArgs = [];
protected string $osType = 'linux';
protected array $libraryList = [];
protected array $extensionList = [];
protected string $cCompiler = 'clang';
protected string $cppCompiler = 'clang++';
protected string $lld = 'ld.lld';
protected array $downloadExtensionList = [];
protected array $libraryMap = [];
protected array $extensionMap = [];
/**
*
* @var string
*/
protected string $rootDir;
protected string $libraryDir;
protected string $extensionDir;
protected array $pkgConfigPaths = [];
protected string $phpSrcDir;
protected string $dockerVersion = 'latest';
/**
* swoole-cli
* $workDir/pool/ext
* $workDir/pool/lib
*/
protected string $workDir = '/work';
/**
*
* @var string
*/
protected string $buildDir = '/work/thirdparty';
/**
* .a
* @var string
*/
protected string $globalPrefix = '/usr/local/swoole-cli';
protected string $extraLdflags = '';
protected string $extraOptions = '';
protected string $extraCflags = '';
protected array $variables = [];
protected array $exportVariables = [];
/**
* default value : CPU logical processors
* @var string
*/
protected string $maxJob = '${LOGICAL_PROCESSORS}';
/**
* CPU logical processors
* @var string
*/
protected string $logicalProcessors = '';
protected bool $installLibrary = true;
protected array $inputOptions = [];
protected array $binPaths = [];
/**
* Extensions enabled by default
* @var array|string[]
*/
protected array $extEnabled = [
'opcache',
'curl',
'iconv',
'bz2',
'bcmath',
'pcntl',
'filter',
'session',
'tokenizer',
'mbstring',
'ctype',
'zlib',
'zip',
'posix',
'sockets',
'pdo',
'sqlite3',
'phar',
'mysqlnd',
'mysqli',
'intl',
'fileinfo',
'pdo_mysql',
//'pdo_sqlite',
'soap',
'xsl',
'gmp',
'exif',
'sodium',
'openssl',
'readline',
'xml',
'gd',
'redis',
'swoole',
'yaml',
'imagick',
'mongodb',
];
protected array $endCallbacks = [];
protected array $extCallbacks = [];
protected array $beforeConfigure = [];
protected string $configureVarables;
protected string $buildType = 'release';
protected bool $inVirtualMachine = false;
protected function __construct()
{
$this->setOsType($this->getRealOsType());
}
public function setLinker(string $ld): static
{
$this->lld = $ld;
return $this;
}
public static function getInstance(): static
{
if (!self::$instance) {
self::$instance = new static();
}
return self::$instance;
}
protected function setOsType(string $osType): void
{
$this->osType = $osType;
}
public function getOsType(): string
{
return $this->osType;
}
public function getSystemArch(): string
{
$uname = posix_uname();
switch ($uname['machine']) {
case 'x86_64':
return 'x64';
case 'aarch64':
return 'arm64';
default:
return $uname['machine'];
}
}
public function getImageTag(): string
{
$arch = $this->getSystemArch();
if ($arch == 'x64') {
return self::VERSION;
} else {
return self::VERSION . '-' . $arch;
}
}
public function getBaseImageTag(): string
{
$arch = $this->getSystemArch();
if ($arch == 'x64') {
return 'base';
} else {
return 'base' . '-' . $arch;
}
}
public function getBaseImageDockerFile(): string
{
$arch = $this->getSystemArch();
if ($arch == 'x64') {
return 'Dockerfile';
} else {
return 'Dockerfile' . '-' . $arch;
}
}
public function setPhpSrcDir(string $phpSrcDir)
{
$this->phpSrcDir = $phpSrcDir;
}
public function getPhpSrcDir(): string
{
return $this->phpSrcDir;
}
public function setGlobalPrefix(string $prefix)
{
$this->globalPrefix = $prefix;
}
public function getGlobalPrefix(): string
{
return $this->globalPrefix;
}
public function setRootDir(string $rootDir)
{
$this->rootDir = $rootDir;
}
public function getRootDir(): string
{
return $this->rootDir;
}
public function getPrepareArgs(): array
{
return $this->prepareArgs;
}
public function setLibraryDir(string $libraryDir)
{
$this->libraryDir = $libraryDir;
}
public function setExtensionDir(string $extensionDir)
{
$this->extensionDir = $extensionDir;
}
public function setWorkDir(string $workDir)
{
$this->workDir = $workDir;
}
public function setBuildDir(string $buildDir)
{
$this->buildDir = $buildDir;
}
public function getBuildDir(): string
{
return $this->buildDir;
}
public function getWorkDir(): string
{
return $this->workDir;
}
public function setExtraLdflags(string $flags)
{
$this->extraLdflags = $flags;
}
public function setExtraCflags(string $flags)
{
$this->extraCflags = $flags;
}
public function setConfigureVarables(string $varables)
{
$this->configureVarables = $varables;
}
public function setExtraOptions(string $options)
{
$this->extraOptions = $options;
}
/**
* make -j {$n}
* @param string $n
*/
public function setMaxJob(int $n): static
{
$this->maxJob = $n;
return $this;
}
/**
* set CPU logical processors
* @param string $logicalProcessors
* @return $this
*/
public function setLogicalProcessors(string $logicalProcessors): static
{
$this->logicalProcessors = $logicalProcessors;
return $this;
}
public function setBuildType(string $buildType): static
{
$this->buildType = $buildType;
return $this;
}
public function getBuildType(): string
{
return $this->buildType;
}
public function donotInstallLibrary()
{
$this->installLibrary = false;
}
/**
* @param string $url
* @param string $file
* @param string $md5sum
* @throws Exception
*/
protected function downloadFile(string $url, string $file, object $project = null)
{
$retry_number = DOWNLOAD_FILE_RETRY_NUMBE;
$wait_retry = DOWNLOAD_FILE_WAIT_RETRY;
$connect_timeout = DOWNLOAD_FILE_CONNECTION_TIMEOUT;
echo PHP_EOL;
if ($this->getInputOption('with-downloader') === 'wget') {
$cmd = "wget {$url} -O {$file} -t {$retry_number} --wait={$wait_retry} -T {$connect_timeout} ";
} else {
$cmd = "curl --connect-timeout {$connect_timeout} --retry {$retry_number} --retry-delay {$wait_retry} -Lo '{$file}' '{$url}' ";
}
echo $cmd;
echo PHP_EOL;
echo `$cmd`;
echo PHP_EOL;
if (is_file($file) && (filesize($file) == 0)) {
unlink($file);
}
//
if (!is_file($file) or filesize($file) == 0) {
throw new Exception("Downloading file[" . basename($file) . "] from url[$url] failed");
}
// hash
if ($project->enableHashVerify) {
if (!$project->hashVerify($file)) {
throw new Exception("The {$project->hashAlgo} of downloaded file[$file] is inconsistent with the configuration");
}
}
}
/**
* @param Library $lib
* @throws Exception
*/
public function addLibrary(Library $lib): void
{
if (empty($lib->file)) {
$lib->file = basename($lib->url);
}
if (!empty($this->getInputOption('with-download-mirror-url'))) {
$lib->url = $this->getInputOption('with-download-mirror-url') . '/lib/' . $lib->file;
}
$lib->path = $this->libraryDir . '/' . $lib->file;
if ($lib->enableHashVerify) {
// hash
$lib->hashVerify($lib->path);
}
$skip_download = ($this->getInputOption('skip-download'));
if (!$skip_download) {
if (!is_file($lib->path) or filesize($lib->path) === 0) {
echo "[Library] {$lib->file} not found, downloading: " . $lib->url . PHP_EOL;
$this->downloadFile($lib->url, $lib->path, $lib);
} else {
echo "[Library] file cached: " . $lib->file . PHP_EOL;
}
if ($this->getInputOption('show-tarball-hash')) {
echo "[Library] {$lib->name} " . PHP_EOL;
echo "md5: " . hash_file('md5', $lib->path) . PHP_EOL;
echo "sha1: " . hash_file('sha1', $lib->path) . PHP_EOL;
echo "sha256: " . hash_file('sha256', $lib->path) . PHP_EOL;
echo PHP_EOL;
}
}
if (!empty($lib->pkgConfig)) {
$this->pkgConfigPaths[] = $lib->pkgConfig;
}
if (!empty($lib->binPath)) {
$this->binPaths[] = $lib->binPath;
}
if (empty($lib->license)) {
throw new Exception("require license");
}
$this->libraryList[] = $lib;
$this->libraryMap[$lib->name] = $lib;
}
public function addExtension(Extension $ext): void
{
if ($ext->peclVersion) {
$ext->file = $ext->name . '-' . $ext->peclVersion . '.tgz';
$ext->path = $this->extensionDir . '/' . $ext->file;
$ext->url = "https://pecl.php.net/get/{$ext->file}";
if (!empty($this->getInputOption('with-download-mirror-url'))) {
$ext->url = $this->getInputOption('with-download-mirror-url') . '/ext/' . $ext->file;
}
if ($ext->enableHashVerify) {
// hash
$ext->hashVerify($ext->path);
}
if (!$this->getInputOption('skip-download')) {
if (!is_file($ext->path) or filesize($ext->path) === 0) {
echo "[Extension] {$ext->file} not found, downloading: " . $ext->url . PHP_EOL;
$this->downloadFile($ext->url, $ext->path, $ext);
} else {
echo "[Extension] file cached: " . $ext->file . PHP_EOL;
}
if ($this->getInputOption('show-tarball-hash')) {
echo "[Extension] {$ext->name} " . PHP_EOL;
echo "md5: " . hash_file('md5', $ext->path) . PHP_EOL;
echo "sha1: " . hash_file('sha1', $ext->path) . PHP_EOL;
echo "sha256: " . hash_file('sha256', $ext->path) . PHP_EOL;
echo PHP_EOL;
}
$dst_dir = "{$this->rootDir}/ext/{$ext->name}";
$this->mkdirIfNotExists($dst_dir, 0777, true);
echo `tar --strip-components=1 -C $dst_dir -xf {$ext->path}`;
}
$this->downloadExtensionList[] = ['url' => $ext->url, 'file' => $ext->file];
}
$this->extensionList[] = $ext;
$this->extensionMap[$ext->name] = $ext;
}
public function getLibrary(string $name): ?Library
{
if (!isset($this->libraryMap[$name])) {
return null;
}
return $this->libraryMap[$name];
}
public function getLibraryPackages(): array
{
$packages = [];
/**
* @var $item Library
*/
foreach ($this->libraryList as $item) {
if (!empty($item->pkgNames)) {
$packages = array_merge($packages, $item->pkgNames);
}
}
return $packages;
}
public function withBinPath(string $path): static
{
$this->binPaths[] = $path;
return $this;
}
public function withVariable(string $key, string $value): static
{
$this->variables[] = [$key => $value];
return $this;
}
public function withExportVariable(string $key, string $value): static
{
$this->exportVariables[] = [$key => $value];
return $this;
}
public function getExtension(string $name): ?Extension
{
if (!isset($this->extensionMap[$name])) {
return null;
}
return $this->extensionMap[$name];
}
public function existsLibrary(string $name): bool
{
return isset($this->libraryMap[$name]);
}
public function existsExtension(string $name): bool
{
return isset($this->extensionMap[$name]);
}
public array $extensionDependentLibraryMap = [];
public array $extensionDependentPackageNameMap = [];
public array $extensionDependentPackageNames = [];
public function setExtensionDependency(): void
{
$extensionDepsMap = [];
foreach ($this->extensionList as $extension) {
if (empty($extension->deps)) {
$this->extensionDependentLibraryMap[$extension->name] = [];
} else {
$extensionDepsMap[$extension->name] = $extension->deps;
}
}
foreach ($extensionDepsMap as $extensionName => $deps) {
$pkgNames = [];
foreach ($deps as $libraryName) {
$packages = [];
$this->getLibraryDependenciesByName($libraryName, $packages);
foreach ($packages as $item) {
if (empty($item)) {
continue;
} else {
$pkgNames[] = trim($item);
}
}
$this->extensionDependentLibraryMap[$extensionName][] = $libraryName;
}
$this->extensionDependentPackageNameMap[$extensionName] = $pkgNames;
}
$pkgNames = [];
foreach ($this->extensionDependentPackageNameMap as $extensionName => $value) {
$pkgNames = array_merge($pkgNames, $value);
$this->extensionDependentPackageNameMap[$extensionName] = array_values(array_unique($value));
}
$this->extensionDependentPackageNames = array_values(array_unique($pkgNames));
}
private function getLibraryDependenciesByName($libraryName, &$packages): void
{
if (!isset($this->libraryMap[$libraryName])) {
throw new RuntimeException('library ' . $libraryName . ' no found');
}
$lib = $this->libraryMap[$libraryName];
if (!empty($lib->pkgNames)) {
$packages = array_merge($packages, $lib->pkgNames);
}
if (!empty($lib->deps)) {
foreach ($lib->deps as $name) {
$this->getLibraryDependenciesByName($name, $packages);
}
}
}
public function addEndCallback($fn)
{
$this->endCallbacks[] = $fn;
}
public function setExtCallback($name, $fn)
{
$this->extCallbacks[$name] = $fn;
}
public function withBeforeConfigureScript($name, $fn): void
{
$this->beforeConfigure[$name] = $fn;
}
public function parseArguments(int $argc, array $argv): void
{
$this->prepareArgs = $argv;
// parse the parameters passed in by the user
for ($i = 1; $i < $argc; $i++) {
$arg = $argv[$i];
$op = $arg[0];
$value = substr($argv[$i], 1);
if ($op == '+') {
$this->extEnabled[] = $value;
} elseif ($op == '-') {
if ($arg[1] == '-') {
$_ = explode('=', substr($arg, 2));
$this->inputOptions[$_[0]] = $_[1] ?? true;
} else {
$key = array_search($value, $this->extEnabled);
if ($key !== false) {
unset($this->extEnabled[$key]);
}
}
} elseif ($op == '@') {
$this->inVirtualMachine = $value != $this->getRealOsType();
$this->setOsType($value);
}
}
}
/**
* Get the value of an input option, attempting to read from command-line arguments and environment variables,
* and returning the default value if not set
* @param string $key
* @param string $default
* @return string
*/
public function getInputOption(string $key, string $default = ''): string
{
if (isset($this->inputOptions[$key])) {
return $this->inputOptions[$key];
}
$env = getenv('SWOOLE_CLI_' . str_replace('-', '_', strtoupper($key)));
if ($env !== false) {
return $env;
}
return $default;
}
/**
* @throws CircularDependencyException
* @throws ElementNotFoundException
*/
protected function sortLibrary(): void
{
$libs = [];
$sorter = new StringSort();
foreach ($this->libraryList as $item) {
$libs[$item->name] = $item;
$sorter->add($item->name, $item->deps);
}
$sorted_list = $sorter->sort();
foreach ($this->extensionList as $item) {
if ($item->deps) {
foreach ($item->deps as $lib) {
if (!isset($libs[$lib])) {
throw new Exception("The ext-{$item->name} depends on $lib, but it does not exist");
}
}
}
}
$libraryList = [];
foreach ($sorted_list as $name) {
if (empty($name)) {
continue;
}
$libraryList[] = $libs[$name];
}
$this->libraryList = $libraryList;
}
protected function mkdirIfNotExists(string $dir, int $permissions = 0777, bool $recursive = false)
{
if (!is_dir($dir)) {
mkdir($dir, $permissions, $recursive);
}
}
/**
* Scan and load config files in directory
*/
protected function scanConfigFiles(string $dir, array &$extAvailabled)
{
$files = scandir($dir);
foreach ($files as $f) {
if ($f == '.' or $f == '..' or substr($f, -4, 4) != '.php') {
continue;
}
$path = $dir . '/' . $f;
if (is_dir($path)) {
$this->scanConfigFiles($path, $extAvailabled);
} else {
$extAvailabled[basename($f, '.php')] = require $path;
}
}
}
public function loadDependentExtension($extension_name)
{
if (!isset($this->extensionMap[$extension_name])) {
$file = realpath(__DIR__ . '/builder/extension/' . $extension_name . '.php');
if (!is_file($file)) {
throw new Exception("The ext-$extension_name does not exist");
}
$func = require $file;
$func($this);
}
if (isset($this->extensionMap[$extension_name])) {
$deps = $this->extensionMap[$extension_name]->dependentExtensions;
if (!empty($deps)) {
foreach ($deps as $extension_name) {
$this->loadDependentExtension($extension_name);
}
}
}
}
public function loadDependentLibrary($library_name)
{
if (!isset($this->libraryMap[$library_name])) {
$file = realpath(__DIR__ . '/builder/library/' . $library_name . '.php');
if (!is_file($file)) {
throw new Exception("The library-$library_name does not exist");
}
$func = require $file;
$func($this);
}
if (isset($this->libraryMap[$library_name])) {
$deps = $this->libraryMap[$library_name]->deps;
if (!empty($deps)) {
foreach ($deps as $library_name) {
$this->loadDependentLibrary($library_name);
}
}
}
}
public function generateFile(string $templateFile, string $outFile): bool
{
if (!is_file($templateFile)) {
return false;
}
ob_start();
include $templateFile;
return file_put_contents($outFile, ob_get_clean());
}
/**
* @throws CircularDependencyException
* @throws ElementNotFoundException
*/
public function execute(): void
{
if (empty($this->rootDir)) {
$this->rootDir = dirname(__DIR__, 2);
}
if (empty($this->libraryDir)) {
$this->libraryDir = $this->rootDir . '/pool/lib';
}
if (empty($this->extensionDir)) {
$this->extensionDir = $this->rootDir . '/pool/ext';
}
$this->mkdirIfNotExists($this->libraryDir, 0777, true);
$this->mkdirIfNotExists($this->extensionDir, 0777, true);
include __DIR__ . '/constants.php';
$extAvailabled = [];
$this->scanConfigFiles(__DIR__ . '/builder/extension', $extAvailabled);
$confPath = $this->getInputOption('conf-path');
if ($confPath) {
$confDirList = explode(':', $confPath);
foreach ($confDirList as $dir) {
if (!is_dir($dir)) {
continue;
}
$this->scanConfigFiles($dir, $extAvailabled);
}
}
$this->extEnabled = array_unique($this->extEnabled);
foreach ($this->extEnabled as $ext) {
if (!isset($extAvailabled[$ext])) {
echo "unsupported extension[$ext]
";
continue;
}
($extAvailabled[$ext])($this);
if (isset($this->extCallbacks[$ext])) {
($this->extCallbacks[$ext])($this);
}
}
if ($this->isMacos()) {
if (is_file('/usr/local/opt/bison/bin/bison')) {
$this->withBinPath('/usr/local/opt/bison/bin');
} elseif (is_file('/opt/homebrew/opt/bison/bin/bison')) { // github action
$this->withBinPath('/opt/homebrew/opt/bison/bin/');
} else {
$this->loadDependentLibrary("bison");
}
}
// autoload extension depend extension
foreach ($this->extensionMap as $ext) {
foreach ($ext->dependentExtensions as $extension_name) {
$this->loadDependentExtension($extension_name);
}
}
// autoload library depend library
foreach ($this->extensionMap as $ext) {
foreach ($ext->deps as $library_name) {
$this->loadDependentLibrary($library_name);
}
}
$this->pkgConfigPaths[] = '$PKG_CONFIG_PATH';
$this->pkgConfigPaths = array_unique($this->pkgConfigPaths);
$packagesArr = $this->getLibraryPackages();
if (!empty($packagesArr)) {
$packages = implode(' ', $packagesArr);
$this->withVariable('PACKAGES', $packages);
$this->withVariable('CPPFLAGS', '$CPPFLAGS $(pkg-config --cflags-only-I --static $PACKAGES ) ');
$this->withVariable('LDFLAGS', '$LDFLAGS $(pkg-config --libs-only-L --static $PACKAGES ) ');
$this->withVariable('LIBS', '$LIBS $(pkg-config --libs-only-l --static $PACKAGES ) ');
}
if (!empty($this->varables) || !empty($packagesArr)) {
$this->withExportVariable('CPPFLAGS', '$CPPFLAGS');
$this->withExportVariable('LDFLAGS', '$LDFLAGS');
$this->withExportVariable('LIBS', '$LIBS');
}
$this->binPaths[] = '$PATH';
$this->binPaths = array_unique($this->binPaths);
$this->sortLibrary();
$this->setExtensionDependency();
if ($this->getInputOption('skip-download')) {
$this->generateLibraryDownloadLinks();
}
$this->generateFile(__DIR__ . '/template/make.php', $this->rootDir . '/make.sh');
$this->mkdirIfNotExists($this->rootDir . '/bin');
$this->generateFile(__DIR__ . '/template/license.php', $this->rootDir . '/bin/LICENSE');
$this->generateFile(__DIR__ . '/template/credits.php', $this->rootDir . '/bin/credits.html');
copy($this->rootDir . '/sapi/scripts/pack-sfx.php', $this->rootDir . '/bin/pack-sfx.php');
if ($this->getInputOption('with-dependency-graph')) {
$this->generateFile(
__DIR__ . '/template/extension_ependency_graph.php',
$this->rootDir . '/bin/ext-dependency-graph.graphviz.dot'
);
}
foreach ($this->endCallbacks as $endCallback) {
$endCallback($this);
}
echo '==========================================================' . PHP_EOL;
echo "Extension count: " . count($this->extensionList) . PHP_EOL;
echo '==========================================================' . PHP_EOL;
foreach ($this->extensionList as $item) {
echo $item->name . PHP_EOL;
}
echo '==========================================================' . PHP_EOL;
echo "Library count: " . count($this->libraryList) . PHP_EOL;
echo '==========================================================' . PHP_EOL;
foreach ($this->libraryList as $item) {
echo "{$item->name}
";
}
}
protected function generateLibraryDownloadLinks(): void
{
$this->mkdirIfNotExists($this->getRootDir() . '/var/download-box/', 0755, true);
$download_urls = [];
foreach ($this->libraryList as $item) {
if (empty($item->url)) {
continue;
}
$url = '';
$item->mirrorUrls[] = $item->url;
if (!empty($item->mirrorUrls)) {
$newMirrorUrls = [];
foreach ($item->mirrorUrls as $value) {
$newMirrorUrls[] = trim($value);
}
$url = implode(" ", $newMirrorUrls);
}
$download_urls[] = $url . PHP_EOL . " out=" . $item->file;
}
file_put_contents(
$this->getRootDir() . '/var/download-box/download_library_urls.txt',
implode(PHP_EOL, $download_urls)
);
$download_urls = [];
foreach ($this->downloadExtensionList as $item) {
$download_urls[] = $item['url'] . PHP_EOL . " out=" . $item['file'];
}
file_put_contents(
$this->getRootDir() . '/var/download-box/download_extension_urls.txt',
implode(PHP_EOL, $download_urls)
);
}
public function getRealOsType(): string
{
switch (PHP_OS) {
default:
case 'Linux':
return 'linux';
case 'Darwin':
return 'macos';
case 'WINNT':
return 'win';
}
}
public function isLinux(): bool
{
return $this->osType === 'linux';
}
public function isMacos(): bool
{
return $this->osType === 'macos';
}
public function hasLibrary(string $lib): bool
{
return isset($this->libraryMap[$lib]);
}
public function hasExtension(string $ext): bool
{
return isset($this->extensionMap[$ext]);
}
}
?>
Did this file decode correctly?
Original Code
<?php
namespace SwooleCli;
use MJS\TopSort\CircularDependencyException;
use MJS\TopSort\ElementNotFoundException;
use MJS\TopSort\Implementations\StringSort;
use RuntimeException;
class Preprocessor
{
public const VERSION = '1.7';
public const IMAGE_NAME = 'phpswoole/swoole-cli-builder';
public const CONTAINER_NAME = 'swoole-cli-builder';
protected static ?Preprocessor $instance = null;
protected array $prepareArgs = [];
protected string $osType = 'linux';
protected array $libraryList = [];
protected array $extensionList = [];
protected string $cCompiler = 'clang';
protected string $cppCompiler = 'clang++';
protected string $lld = 'ld.lld';
protected array $downloadExtensionList = [];
protected array $libraryMap = [];
protected array $extensionMap = [];
/**
*
* @var string
*/
protected string $rootDir;
protected string $libraryDir;
protected string $extensionDir;
protected array $pkgConfigPaths = [];
protected string $phpSrcDir;
protected string $dockerVersion = 'latest';
/**
* swoole-cli
* $workDir/pool/ext
* $workDir/pool/lib
*/
protected string $workDir = '/work';
/**
*
* @var string
*/
protected string $buildDir = '/work/thirdparty';
/**
* .a
* @var string
*/
protected string $globalPrefix = '/usr/local/swoole-cli';
protected string $extraLdflags = '';
protected string $extraOptions = '';
protected string $extraCflags = '';
protected array $variables = [];
protected array $exportVariables = [];
/**
* default value : CPU logical processors
* @var string
*/
protected string $maxJob = '${LOGICAL_PROCESSORS}';
/**
* CPU logical processors
* @var string
*/
protected string $logicalProcessors = '';
protected bool $installLibrary = true;
protected array $inputOptions = [];
protected array $binPaths = [];
/**
* Extensions enabled by default
* @var array|string[]
*/
protected array $extEnabled = [
'opcache',
'curl',
'iconv',
'bz2',
'bcmath',
'pcntl',
'filter',
'session',
'tokenizer',
'mbstring',
'ctype',
'zlib',
'zip',
'posix',
'sockets',
'pdo',
'sqlite3',
'phar',
'mysqlnd',
'mysqli',
'intl',
'fileinfo',
'pdo_mysql',
//'pdo_sqlite',
'soap',
'xsl',
'gmp',
'exif',
'sodium',
'openssl',
'readline',
'xml',
'gd',
'redis',
'swoole',
'yaml',
'imagick',
'mongodb',
];
protected array $endCallbacks = [];
protected array $extCallbacks = [];
protected array $beforeConfigure = [];
protected string $configureVarables;
protected string $buildType = 'release';
protected bool $inVirtualMachine = false;
protected function __construct()
{
$this->setOsType($this->getRealOsType());
}
public function setLinker(string $ld): static
{
$this->lld = $ld;
return $this;
}
public static function getInstance(): static
{
if (!self::$instance) {
self::$instance = new static();
}
return self::$instance;
}
protected function setOsType(string $osType): void
{
$this->osType = $osType;
}
public function getOsType(): string
{
return $this->osType;
}
public function getSystemArch(): string
{
$uname = posix_uname();
switch ($uname['machine']) {
case 'x86_64':
return 'x64';
case 'aarch64':
return 'arm64';
default:
return $uname['machine'];
}
}
public function getImageTag(): string
{
$arch = $this->getSystemArch();
if ($arch == 'x64') {
return self::VERSION;
} else {
return self::VERSION . '-' . $arch;
}
}
public function getBaseImageTag(): string
{
$arch = $this->getSystemArch();
if ($arch == 'x64') {
return 'base';
} else {
return 'base' . '-' . $arch;
}
}
public function getBaseImageDockerFile(): string
{
$arch = $this->getSystemArch();
if ($arch == 'x64') {
return 'Dockerfile';
} else {
return 'Dockerfile' . '-' . $arch;
}
}
public function setPhpSrcDir(string $phpSrcDir)
{
$this->phpSrcDir = $phpSrcDir;
}
public function getPhpSrcDir(): string
{
return $this->phpSrcDir;
}
public function setGlobalPrefix(string $prefix)
{
$this->globalPrefix = $prefix;
}
public function getGlobalPrefix(): string
{
return $this->globalPrefix;
}
public function setRootDir(string $rootDir)
{
$this->rootDir = $rootDir;
}
public function getRootDir(): string
{
return $this->rootDir;
}
public function getPrepareArgs(): array
{
return $this->prepareArgs;
}
public function setLibraryDir(string $libraryDir)
{
$this->libraryDir = $libraryDir;
}
public function setExtensionDir(string $extensionDir)
{
$this->extensionDir = $extensionDir;
}
public function setWorkDir(string $workDir)
{
$this->workDir = $workDir;
}
public function setBuildDir(string $buildDir)
{
$this->buildDir = $buildDir;
}
public function getBuildDir(): string
{
return $this->buildDir;
}
public function getWorkDir(): string
{
return $this->workDir;
}
public function setExtraLdflags(string $flags)
{
$this->extraLdflags = $flags;
}
public function setExtraCflags(string $flags)
{
$this->extraCflags = $flags;
}
public function setConfigureVarables(string $varables)
{
$this->configureVarables = $varables;
}
public function setExtraOptions(string $options)
{
$this->extraOptions = $options;
}
/**
* make -j {$n}
* @param string $n
*/
public function setMaxJob(int $n): static
{
$this->maxJob = $n;
return $this;
}
/**
* set CPU logical processors
* @param string $logicalProcessors
* @return $this
*/
public function setLogicalProcessors(string $logicalProcessors): static
{
$this->logicalProcessors = $logicalProcessors;
return $this;
}
public function setBuildType(string $buildType): static
{
$this->buildType = $buildType;
return $this;
}
public function getBuildType(): string
{
return $this->buildType;
}
public function donotInstallLibrary()
{
$this->installLibrary = false;
}
/**
* @param string $url
* @param string $file
* @param string $md5sum
* @throws Exception
*/
protected function downloadFile(string $url, string $file, object $project = null)
{
$retry_number = DOWNLOAD_FILE_RETRY_NUMBE;
$wait_retry = DOWNLOAD_FILE_WAIT_RETRY;
$connect_timeout = DOWNLOAD_FILE_CONNECTION_TIMEOUT;
echo PHP_EOL;
if ($this->getInputOption('with-downloader') === 'wget') {
$cmd = "wget {$url} -O {$file} -t {$retry_number} --wait={$wait_retry} -T {$connect_timeout} ";
} else {
$cmd = "curl --connect-timeout {$connect_timeout} --retry {$retry_number} --retry-delay {$wait_retry} -Lo '{$file}' '{$url}' ";
}
echo $cmd;
echo PHP_EOL;
echo `$cmd`;
echo PHP_EOL;
if (is_file($file) && (filesize($file) == 0)) {
unlink($file);
}
//
if (!is_file($file) or filesize($file) == 0) {
throw new Exception("Downloading file[" . basename($file) . "] from url[$url] failed");
}
// hash
if ($project->enableHashVerify) {
if (!$project->hashVerify($file)) {
throw new Exception("The {$project->hashAlgo} of downloaded file[$file] is inconsistent with the configuration");
}
}
}
/**
* @param Library $lib
* @throws Exception
*/
public function addLibrary(Library $lib): void
{
if (empty($lib->file)) {
$lib->file = basename($lib->url);
}
if (!empty($this->getInputOption('with-download-mirror-url'))) {
$lib->url = $this->getInputOption('with-download-mirror-url') . '/lib/' . $lib->file;
}
$lib->path = $this->libraryDir . '/' . $lib->file;
if ($lib->enableHashVerify) {
// hash
$lib->hashVerify($lib->path);
}
$skip_download = ($this->getInputOption('skip-download'));
if (!$skip_download) {
if (!is_file($lib->path) or filesize($lib->path) === 0) {
echo "[Library] {$lib->file} not found, downloading: " . $lib->url . PHP_EOL;
$this->downloadFile($lib->url, $lib->path, $lib);
} else {
echo "[Library] file cached: " . $lib->file . PHP_EOL;
}
if ($this->getInputOption('show-tarball-hash')) {
echo "[Library] {$lib->name} " . PHP_EOL;
echo "md5: " . hash_file('md5', $lib->path) . PHP_EOL;
echo "sha1: " . hash_file('sha1', $lib->path) . PHP_EOL;
echo "sha256: " . hash_file('sha256', $lib->path) . PHP_EOL;
echo PHP_EOL;
}
}
if (!empty($lib->pkgConfig)) {
$this->pkgConfigPaths[] = $lib->pkgConfig;
}
if (!empty($lib->binPath)) {
$this->binPaths[] = $lib->binPath;
}
if (empty($lib->license)) {
throw new Exception("require license");
}
$this->libraryList[] = $lib;
$this->libraryMap[$lib->name] = $lib;
}
public function addExtension(Extension $ext): void
{
if ($ext->peclVersion) {
$ext->file = $ext->name . '-' . $ext->peclVersion . '.tgz';
$ext->path = $this->extensionDir . '/' . $ext->file;
$ext->url = "https://pecl.php.net/get/{$ext->file}";
if (!empty($this->getInputOption('with-download-mirror-url'))) {
$ext->url = $this->getInputOption('with-download-mirror-url') . '/ext/' . $ext->file;
}
if ($ext->enableHashVerify) {
// hash
$ext->hashVerify($ext->path);
}
if (!$this->getInputOption('skip-download')) {
if (!is_file($ext->path) or filesize($ext->path) === 0) {
echo "[Extension] {$ext->file} not found, downloading: " . $ext->url . PHP_EOL;
$this->downloadFile($ext->url, $ext->path, $ext);
} else {
echo "[Extension] file cached: " . $ext->file . PHP_EOL;
}
if ($this->getInputOption('show-tarball-hash')) {
echo "[Extension] {$ext->name} " . PHP_EOL;
echo "md5: " . hash_file('md5', $ext->path) . PHP_EOL;
echo "sha1: " . hash_file('sha1', $ext->path) . PHP_EOL;
echo "sha256: " . hash_file('sha256', $ext->path) . PHP_EOL;
echo PHP_EOL;
}
$dst_dir = "{$this->rootDir}/ext/{$ext->name}";
$this->mkdirIfNotExists($dst_dir, 0777, true);
echo `tar --strip-components=1 -C $dst_dir -xf {$ext->path}`;
}
$this->downloadExtensionList[] = ['url' => $ext->url, 'file' => $ext->file];
}
$this->extensionList[] = $ext;
$this->extensionMap[$ext->name] = $ext;
}
public function getLibrary(string $name): ?Library
{
if (!isset($this->libraryMap[$name])) {
return null;
}
return $this->libraryMap[$name];
}
public function getLibraryPackages(): array
{
$packages = [];
/**
* @var $item Library
*/
foreach ($this->libraryList as $item) {
if (!empty($item->pkgNames)) {
$packages = array_merge($packages, $item->pkgNames);
}
}
return $packages;
}
public function withBinPath(string $path): static
{
$this->binPaths[] = $path;
return $this;
}
public function withVariable(string $key, string $value): static
{
$this->variables[] = [$key => $value];
return $this;
}
public function withExportVariable(string $key, string $value): static
{
$this->exportVariables[] = [$key => $value];
return $this;
}
public function getExtension(string $name): ?Extension
{
if (!isset($this->extensionMap[$name])) {
return null;
}
return $this->extensionMap[$name];
}
public function existsLibrary(string $name): bool
{
return isset($this->libraryMap[$name]);
}
public function existsExtension(string $name): bool
{
return isset($this->extensionMap[$name]);
}
public array $extensionDependentLibraryMap = [];
public array $extensionDependentPackageNameMap = [];
public array $extensionDependentPackageNames = [];
public function setExtensionDependency(): void
{
$extensionDepsMap = [];
foreach ($this->extensionList as $extension) {
if (empty($extension->deps)) {
$this->extensionDependentLibraryMap[$extension->name] = [];
} else {
$extensionDepsMap[$extension->name] = $extension->deps;
}
}
foreach ($extensionDepsMap as $extensionName => $deps) {
$pkgNames = [];
foreach ($deps as $libraryName) {
$packages = [];
$this->getLibraryDependenciesByName($libraryName, $packages);
foreach ($packages as $item) {
if (empty($item)) {
continue;
} else {
$pkgNames[] = trim($item);
}
}
$this->extensionDependentLibraryMap[$extensionName][] = $libraryName;
}
$this->extensionDependentPackageNameMap[$extensionName] = $pkgNames;
}
$pkgNames = [];
foreach ($this->extensionDependentPackageNameMap as $extensionName => $value) {
$pkgNames = array_merge($pkgNames, $value);
$this->extensionDependentPackageNameMap[$extensionName] = array_values(array_unique($value));
}
$this->extensionDependentPackageNames = array_values(array_unique($pkgNames));
}
private function getLibraryDependenciesByName($libraryName, &$packages): void
{
if (!isset($this->libraryMap[$libraryName])) {
throw new RuntimeException('library ' . $libraryName . ' no found');
}
$lib = $this->libraryMap[$libraryName];
if (!empty($lib->pkgNames)) {
$packages = array_merge($packages, $lib->pkgNames);
}
if (!empty($lib->deps)) {
foreach ($lib->deps as $name) {
$this->getLibraryDependenciesByName($name, $packages);
}
}
}
public function addEndCallback($fn)
{
$this->endCallbacks[] = $fn;
}
public function setExtCallback($name, $fn)
{
$this->extCallbacks[$name] = $fn;
}
public function withBeforeConfigureScript($name, $fn): void
{
$this->beforeConfigure[$name] = $fn;
}
public function parseArguments(int $argc, array $argv): void
{
$this->prepareArgs = $argv;
// parse the parameters passed in by the user
for ($i = 1; $i < $argc; $i++) {
$arg = $argv[$i];
$op = $arg[0];
$value = substr($argv[$i], 1);
if ($op == '+') {
$this->extEnabled[] = $value;
} elseif ($op == '-') {
if ($arg[1] == '-') {
$_ = explode('=', substr($arg, 2));
$this->inputOptions[$_[0]] = $_[1] ?? true;
} else {
$key = array_search($value, $this->extEnabled);
if ($key !== false) {
unset($this->extEnabled[$key]);
}
}
} elseif ($op == '@') {
$this->inVirtualMachine = $value != $this->getRealOsType();
$this->setOsType($value);
}
}
}
/**
* Get the value of an input option, attempting to read from command-line arguments and environment variables,
* and returning the default value if not set
* @param string $key
* @param string $default
* @return string
*/
public function getInputOption(string $key, string $default = ''): string
{
if (isset($this->inputOptions[$key])) {
return $this->inputOptions[$key];
}
$env = getenv('SWOOLE_CLI_' . str_replace('-', '_', strtoupper($key)));
if ($env !== false) {
return $env;
}
return $default;
}
/**
* @throws CircularDependencyException
* @throws ElementNotFoundException
*/
protected function sortLibrary(): void
{
$libs = [];
$sorter = new StringSort();
foreach ($this->libraryList as $item) {
$libs[$item->name] = $item;
$sorter->add($item->name, $item->deps);
}
$sorted_list = $sorter->sort();
foreach ($this->extensionList as $item) {
if ($item->deps) {
foreach ($item->deps as $lib) {
if (!isset($libs[$lib])) {
throw new Exception("The ext-{$item->name} depends on $lib, but it does not exist");
}
}
}
}
$libraryList = [];
foreach ($sorted_list as $name) {
if (empty($name)) {
continue;
}
$libraryList[] = $libs[$name];
}
$this->libraryList = $libraryList;
}
protected function mkdirIfNotExists(string $dir, int $permissions = 0777, bool $recursive = false)
{
if (!is_dir($dir)) {
mkdir($dir, $permissions, $recursive);
}
}
/**
* Scan and load config files in directory
*/
protected function scanConfigFiles(string $dir, array &$extAvailabled)
{
$files = scandir($dir);
foreach ($files as $f) {
if ($f == '.' or $f == '..' or substr($f, -4, 4) != '.php') {
continue;
}
$path = $dir . '/' . $f;
if (is_dir($path)) {
$this->scanConfigFiles($path, $extAvailabled);
} else {
$extAvailabled[basename($f, '.php')] = require $path;
}
}
}
public function loadDependentExtension($extension_name)
{
if (!isset($this->extensionMap[$extension_name])) {
$file = realpath(__DIR__ . '/builder/extension/' . $extension_name . '.php');
if (!is_file($file)) {
throw new Exception("The ext-$extension_name does not exist");
}
$func = require $file;
$func($this);
}
if (isset($this->extensionMap[$extension_name])) {
$deps = $this->extensionMap[$extension_name]->dependentExtensions;
if (!empty($deps)) {
foreach ($deps as $extension_name) {
$this->loadDependentExtension($extension_name);
}
}
}
}
public function loadDependentLibrary($library_name)
{
if (!isset($this->libraryMap[$library_name])) {
$file = realpath(__DIR__ . '/builder/library/' . $library_name . '.php');
if (!is_file($file)) {
throw new Exception("The library-$library_name does not exist");
}
$func = require $file;
$func($this);
}
if (isset($this->libraryMap[$library_name])) {
$deps = $this->libraryMap[$library_name]->deps;
if (!empty($deps)) {
foreach ($deps as $library_name) {
$this->loadDependentLibrary($library_name);
}
}
}
}
public function generateFile(string $templateFile, string $outFile): bool
{
if (!is_file($templateFile)) {
return false;
}
ob_start();
include $templateFile;
return file_put_contents($outFile, ob_get_clean());
}
/**
* @throws CircularDependencyException
* @throws ElementNotFoundException
*/
public function execute(): void
{
if (empty($this->rootDir)) {
$this->rootDir = dirname(__DIR__, 2);
}
if (empty($this->libraryDir)) {
$this->libraryDir = $this->rootDir . '/pool/lib';
}
if (empty($this->extensionDir)) {
$this->extensionDir = $this->rootDir . '/pool/ext';
}
$this->mkdirIfNotExists($this->libraryDir, 0777, true);
$this->mkdirIfNotExists($this->extensionDir, 0777, true);
include __DIR__ . '/constants.php';
$extAvailabled = [];
$this->scanConfigFiles(__DIR__ . '/builder/extension', $extAvailabled);
$confPath = $this->getInputOption('conf-path');
if ($confPath) {
$confDirList = explode(':', $confPath);
foreach ($confDirList as $dir) {
if (!is_dir($dir)) {
continue;
}
$this->scanConfigFiles($dir, $extAvailabled);
}
}
$this->extEnabled = array_unique($this->extEnabled);
foreach ($this->extEnabled as $ext) {
if (!isset($extAvailabled[$ext])) {
echo "unsupported extension[$ext]\n";
continue;
}
($extAvailabled[$ext])($this);
if (isset($this->extCallbacks[$ext])) {
($this->extCallbacks[$ext])($this);
}
}
if ($this->isMacos()) {
if (is_file('/usr/local/opt/bison/bin/bison')) {
$this->withBinPath('/usr/local/opt/bison/bin');
} elseif (is_file('/opt/homebrew/opt/bison/bin/bison')) { // github action
$this->withBinPath('/opt/homebrew/opt/bison/bin/');
} else {
$this->loadDependentLibrary("bison");
}
}
// autoload extension depend extension
foreach ($this->extensionMap as $ext) {
foreach ($ext->dependentExtensions as $extension_name) {
$this->loadDependentExtension($extension_name);
}
}
// autoload library depend library
foreach ($this->extensionMap as $ext) {
foreach ($ext->deps as $library_name) {
$this->loadDependentLibrary($library_name);
}
}
$this->pkgConfigPaths[] = '$PKG_CONFIG_PATH';
$this->pkgConfigPaths = array_unique($this->pkgConfigPaths);
$packagesArr = $this->getLibraryPackages();
if (!empty($packagesArr)) {
$packages = implode(' ', $packagesArr);
$this->withVariable('PACKAGES', $packages);
$this->withVariable('CPPFLAGS', '$CPPFLAGS $(pkg-config --cflags-only-I --static $PACKAGES ) ');
$this->withVariable('LDFLAGS', '$LDFLAGS $(pkg-config --libs-only-L --static $PACKAGES ) ');
$this->withVariable('LIBS', '$LIBS $(pkg-config --libs-only-l --static $PACKAGES ) ');
}
if (!empty($this->varables) || !empty($packagesArr)) {
$this->withExportVariable('CPPFLAGS', '$CPPFLAGS');
$this->withExportVariable('LDFLAGS', '$LDFLAGS');
$this->withExportVariable('LIBS', '$LIBS');
}
$this->binPaths[] = '$PATH';
$this->binPaths = array_unique($this->binPaths);
$this->sortLibrary();
$this->setExtensionDependency();
if ($this->getInputOption('skip-download')) {
$this->generateLibraryDownloadLinks();
}
$this->generateFile(__DIR__ . '/template/make.php', $this->rootDir . '/make.sh');
$this->mkdirIfNotExists($this->rootDir . '/bin');
$this->generateFile(__DIR__ . '/template/license.php', $this->rootDir . '/bin/LICENSE');
$this->generateFile(__DIR__ . '/template/credits.php', $this->rootDir . '/bin/credits.html');
copy($this->rootDir . '/sapi/scripts/pack-sfx.php', $this->rootDir . '/bin/pack-sfx.php');
if ($this->getInputOption('with-dependency-graph')) {
$this->generateFile(
__DIR__ . '/template/extension_ependency_graph.php',
$this->rootDir . '/bin/ext-dependency-graph.graphviz.dot'
);
}
foreach ($this->endCallbacks as $endCallback) {
$endCallback($this);
}
echo '==========================================================' . PHP_EOL;
echo "Extension count: " . count($this->extensionList) . PHP_EOL;
echo '==========================================================' . PHP_EOL;
foreach ($this->extensionList as $item) {
echo $item->name . PHP_EOL;
}
echo '==========================================================' . PHP_EOL;
echo "Library count: " . count($this->libraryList) . PHP_EOL;
echo '==========================================================' . PHP_EOL;
foreach ($this->libraryList as $item) {
echo "{$item->name}\n";
}
}
protected function generateLibraryDownloadLinks(): void
{
$this->mkdirIfNotExists($this->getRootDir() . '/var/download-box/', 0755, true);
$download_urls = [];
foreach ($this->libraryList as $item) {
if (empty($item->url)) {
continue;
}
$url = '';
$item->mirrorUrls[] = $item->url;
if (!empty($item->mirrorUrls)) {
$newMirrorUrls = [];
foreach ($item->mirrorUrls as $value) {
$newMirrorUrls[] = trim($value);
}
$url = implode("\t", $newMirrorUrls);
}
$download_urls[] = $url . PHP_EOL . " out=" . $item->file;
}
file_put_contents(
$this->getRootDir() . '/var/download-box/download_library_urls.txt',
implode(PHP_EOL, $download_urls)
);
$download_urls = [];
foreach ($this->downloadExtensionList as $item) {
$download_urls[] = $item['url'] . PHP_EOL . " out=" . $item['file'];
}
file_put_contents(
$this->getRootDir() . '/var/download-box/download_extension_urls.txt',
implode(PHP_EOL, $download_urls)
);
}
public function getRealOsType(): string
{
switch (PHP_OS) {
default:
case 'Linux':
return 'linux';
case 'Darwin':
return 'macos';
case 'WINNT':
return 'win';
}
}
public function isLinux(): bool
{
return $this->osType === 'linux';
}
public function isMacos(): bool
{
return $this->osType === 'macos';
}
public function hasLibrary(string $lib): bool
{
return isset($this->libraryMap[$lib]);
}
public function hasExtension(string $ext): bool
{
return isset($this->extensionMap[$ext]);
}
}
Function Calls
None |
Stats
MD5 | ad1e983a04f3629c078d54f19ff834a9 |
Eval Count | 0 |
Decode Time | 99 ms |