fix(generator): update exception throwing mechanism and add progress tracking

- Replace php::throwException with php::throwExceptionEx for better error handling
- Add return php::null statements after exception throws in closure generators
- Update argument count error messages to use format strings with proper escaping
- Modify type error messages to use format strings instead of concatenation
- Add completed callback parameter to dispatchParallel method in NativeBuilder
- Track and report completed task count during parallel compilation
- Add progress bar functionality using AnsiKit components
- Include failure reason reporting when compilation fails
- Add test case for parallel dispatcher completion tracking
- Add test for foreach indentation restoration in object iteration
- Increment version from 0.3.0 to 0.4.0
pull/18/head
韩天峰 1 month ago
parent bc8e4a685b
commit cf8bfdef06
  1. 11
      phpunit/code/object-foreach-indent.php
  2. 23
      phpunit/src/ForeachIndentTest.php
  3. 34
      phpunit/src/ParallelCompileTest.php
  4. 8
      src/Build/NativeBuilder.php
  5. 12
      src/Generator/ClosureGenerator.php
  6. 57
      src/Generator/TypeCheckGenerator.php
  7. 13
      src/Parser/PropertyAccessTrait.php
  8. 62
      src/Translator.php

@ -0,0 +1,11 @@
<?php
function visitObjects(object $first, object $second): void
{
foreach ($first as $value) {
echo $value;
}
foreach ($second as $value) {
echo $value;
}
}

@ -0,0 +1,23 @@
<?php
namespace TypePhp\Tests;
use PHPUnit\Framework\TestCase;
use TypePhp\CompilerTest;
class ForeachIndentTest extends TestCase
{
public function testObjectForeachRestoresCompilerIndentation(): void
{
global $translator;
$compiler = CompilerTest::create(ROOT_PATH);
$translator = $compiler;
$file = ROOT_PATH . '/phpunit/code/object-foreach-indent.php';
$compiler->addFiles([$file]);
$compiler->prepareFile($file);
$compiler->convertFile($file);
$reflection = new \ReflectionClass($compiler);
$this->assertSame(0, $reflection->getProperty('indentLevel')->getValue($compiler));
}
}

@ -3,6 +3,8 @@
namespace TypePhp\Tests; namespace TypePhp\Tests;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use TypePhp\Backend\CompilerBackend;
use TypePhp\Build\NativeBuilder;
use TypePhp\CompilerTest; use TypePhp\CompilerTest;
class ParallelCompileTest extends TestCase class ParallelCompileTest extends TestCase
@ -55,6 +57,38 @@ class ParallelCompileTest extends TestCase
$this->assertSame(1, $compiler->getWaitCallCount()); $this->assertSame(1, $compiler->getWaitCallCount());
} }
public function testParallelDispatcherReportsEachCompletedTask(): void
{
$builder = new NativeBuilder($this->createMock(CompilerBackend::class));
$forkResults = [101, 102];
$waitResults = [[102, 0], [101, 1 << 8]];
$completed = [];
$result = $builder->dispatchParallel(
['first.cc', 'second.cc'],
2,
static fn(string $source): string => $source . '.o',
static function (): void {},
static function () use (&$forkResults): int {
return array_shift($forkResults);
},
static function () use (&$waitResults): array {
return array_shift($waitResults);
},
static fn(int $status): bool => $status === 0,
static function (string $source, string $object, int $status, bool $success, int $count) use (&$completed): void {
$completed[] = [$source, $object, $status, $success, $count];
},
);
$this->assertSame(['second.cc.o'], $result['objects']);
$this->assertSame(['first.cc'], $result['failures']);
$this->assertSame([
['second.cc', 'second.cc.o', 0, true, 1],
['first.cc', 'first.cc.o', 1 << 8, false, 2],
], $completed);
}
} }
class ScriptedWaitCompiler extends CompilerTest class ScriptedWaitCompiler extends CompilerTest

@ -63,6 +63,7 @@ final readonly class NativeBuilder
* @param Closure(): int $fork * @param Closure(): int $fork
* @param Closure(): array{int, int} $wait * @param Closure(): array{int, int} $wait
* @param Closure(int): bool $succeeded * @param Closure(int): bool $succeeded
* @param null|Closure(string, string, int, bool, int): void $completed
* @return array{objects: list<string>, failures: list<string>} * @return array{objects: list<string>, failures: list<string>}
*/ */
public function dispatchParallel( public function dispatchParallel(
@ -73,11 +74,13 @@ final readonly class NativeBuilder
Closure $fork, Closure $fork,
Closure $wait, Closure $wait,
Closure $succeeded, Closure $succeeded,
?Closure $completed = null,
): array { ): array {
$queue = array_values($sources); $queue = array_values($sources);
$running = []; $running = [];
$objects = []; $objects = [];
$failures = []; $failures = [];
$completedCount = 0;
while ($queue !== [] || $running !== []) { while ($queue !== [] || $running !== []) {
while (count($running) < $jobs && $queue !== []) { while (count($running) < $jobs && $queue !== []) {
@ -109,11 +112,14 @@ final readonly class NativeBuilder
if ($task === null) { if ($task === null) {
continue; continue;
} }
if ($succeeded($status)) { $success = $succeeded($status);
if ($success) {
$objects[] = $task['object']; $objects[] = $task['object'];
} else { } else {
$failures[] = $task['source']; $failures[] = $task['source'];
} }
$completedCount++;
$completed?->__invoke($task['source'], $task['object'], $status, $success, $completedCount);
} }
return ['objects' => $objects, 'failures' => $failures]; return ['objects' => $objects, 'failures' => $failures];
} }

@ -108,14 +108,14 @@ trait ClosureGenerator
} }
if ($requiredArgCount > 0) { if ($requiredArgCount > 0) {
$expected = $requiredArgCount === count($params) ? 'exactly' : 'at least'; $expected = $requiredArgCount === count($params) ? 'exactly' : 'at least';
$message = 'php::concat({' $message = $this->genCharPtr(
. 'php::Str(' . $this->genCharPtr('Too few arguments to function {closure}(), ', true) . '), ' 'Too few arguments to function {closure}(), %u passed and ' . $expected . ' ' . $requiredArgCount . ' expected',
. 'php::toString(php::getCallArgNum()), ' true
. 'php::Str(' . $this->genCharPtr(' passed and ' . $expected . ' ' . $requiredArgCount . ' expected', true) . ')' );
. '})';
$code .= $this->getIndent() . 'if (UNEXPECTED(php::getCallArgNum() < ' . $requiredArgCount . ')) {' . PHP_EOL; $code .= $this->getIndent() . 'if (UNEXPECTED(php::getCallArgNum() < ' . $requiredArgCount . ')) {' . PHP_EOL;
$this->indentLevel++; $this->indentLevel++;
$code .= $this->getIndent() . 'return php::throwException(zend_ce_argument_count_error, (' . $message . ').toCString());' . PHP_EOL; $code .= $this->getIndent() . 'php::throwExceptionEx(zend_ce_argument_count_error, 0, ' . $message . ', php::getCallArgNum());' . PHP_EOL;
$code .= $this->getIndent() . 'return php::null;' . PHP_EOL;
$this->indentLevel--; $this->indentLevel--;
$code .= $this->getIndent() . '}' . PHP_EOL; $code .= $this->getIndent() . '}' . PHP_EOL;
} }

@ -252,12 +252,12 @@ trait TypeCheckGenerator
} }
$orExpr = implode(' || ', $conditions); $orExpr = implode(' || ', $conditions);
$msgExpr = $this->genUnionParamTypeErrorExpr($argInfo, $varName, (string) ($argIndex + 1)); $throwExpr = $this->genUnionParamTypeErrorExpr($argInfo, $varName, (string) ($argIndex + 1));
$code = $this->genCompositeIntToFloatCoercion($varName, $argInfo->typeCheck); $code = $this->genCompositeIntToFloatCoercion($varName, $argInfo->typeCheck);
$code .= $this->getIndent() . 'if (UNEXPECTED(!(' . $orExpr . '))) {' . PHP_EOL; $code .= $this->getIndent() . 'if (UNEXPECTED(!(' . $orExpr . '))) {' . PHP_EOL;
$this->indentLevel++; $this->indentLevel++;
$code .= $this->getIndent() . 'php::throwException(zend_ce_type_error, (' . $msgExpr . ').toCString());' . PHP_EOL; $code .= $this->getIndent() . $throwExpr . ';' . PHP_EOL;
$this->indentLevel--; $this->indentLevel--;
$code .= $this->getIndent() . '}' . PHP_EOL; $code .= $this->getIndent() . '}' . PHP_EOL;
@ -282,7 +282,7 @@ trait TypeCheckGenerator
} }
$orExpr = implode(' || ', $conditions); $orExpr = implode(' || ', $conditions);
$msgExpr = $this->genUnionParamTypeErrorExpr($argInfo, $valueVar, $argNoVar); $throwExpr = $this->genUnionParamTypeErrorExpr($argInfo, $valueVar, $argNoVar);
$code = $this->getIndent() . 'for (auto ' . $iterVar . ' = ' . $argInfo->name . '.begin(); ' . $iterVar . ' != ' . $argInfo->name . '.end(); ++' . $iterVar . ') {' . PHP_EOL; $code = $this->getIndent() . 'for (auto ' . $iterVar . ' = ' . $argInfo->name . '.begin(); ' . $iterVar . ' != ' . $argInfo->name . '.end(); ++' . $iterVar . ') {' . PHP_EOL;
$this->indentLevel++; $this->indentLevel++;
@ -298,7 +298,7 @@ trait TypeCheckGenerator
$code .= $this->getIndent() . Type::INT . ' ' . $argNoVar . ' = ' . ($argIndex + 1) . ' + ' . $iterVar . '.index();' . PHP_EOL; $code .= $this->getIndent() . Type::INT . ' ' . $argNoVar . ' = ' . ($argIndex + 1) . ' + ' . $iterVar . '.index();' . PHP_EOL;
$code .= $this->getIndent() . 'if (UNEXPECTED(!(' . $orExpr . '))) {' . PHP_EOL; $code .= $this->getIndent() . 'if (UNEXPECTED(!(' . $orExpr . '))) {' . PHP_EOL;
$this->indentLevel++; $this->indentLevel++;
$code .= $this->getIndent() . 'php::throwException(zend_ce_type_error, (' . $msgExpr . ').toCString());' . PHP_EOL; $code .= $this->getIndent() . $throwExpr . ';' . PHP_EOL;
$this->indentLevel--; $this->indentLevel--;
$code .= $this->getIndent() . '}' . PHP_EOL; $code .= $this->getIndent() . '}' . PHP_EOL;
$this->indentLevel--; $this->indentLevel--;
@ -311,15 +311,11 @@ trait TypeCheckGenerator
{ {
$fnName = $this->getTypeCheckCallableName(); $fnName = $this->getTypeCheckCallableName();
$paramName = $argInfo->phpName ?: $this->unescapeVarName($argInfo->name); $paramName = $argInfo->phpName ?: $this->unescapeVarName($argInfo->name);
return 'php::concat({' $format = $this->genCharPtr($fnName . '(): Argument #', true)
. 'php::Str(' . $this->genCharPtr($fnName . '(): Argument #', true) . '), ' . ' ZEND_LONG_FMT '
. 'php::toString(' . $argNoExpr . '), ' . $this->genCharPtr(' ($' . $paramName . ') must be of type ' . $argInfo->typeStr . ', %s given', true);
. 'php::Str(' . $this->genCharPtr(' ($' . $paramName . ') must be of type ', true) . '), ' return 'php::throwExceptionEx(zend_ce_type_error, 0, ' . $format . ', '
. 'php::Str(' . $this->genCharPtr($argInfo->typeStr, true) . '), ' . $argNoExpr . ', ' . $valueExpr . '.typeStr())';
. 'php::Str(", "), '
. $valueExpr . '.typeStr(), '
. 'php::Str(" given")'
. '})';
} }
protected function genUnionReturnCheck(string $varName): string protected function genUnionReturnCheck(string $varName): string
@ -344,13 +340,12 @@ trait TypeCheckGenerator
$fnName = $this->getTypeCheckCallableName(); $fnName = $this->getTypeCheckCallableName();
$typeStr = $this->functionDef->returnTypeStr; $typeStr = $this->functionDef->returnTypeStr;
$msgExpr = 'php::concat(php::concat(php::Str(' . $this->genCharPtr($fnName, true) . ' "(): Return value must be of type " ' $format = $this->genCharPtr($fnName . '(): Return value must be of type ' . $typeStr . ', %s given', true);
. $this->genCharPtr($typeStr, true) . ' ", "), ' . $varName . '.typeStr()), php::Str(" given"))';
$code = $this->genCompositeIntToFloatCoercion($varName, $typeCheck); $code = $this->genCompositeIntToFloatCoercion($varName, $typeCheck);
$code .= $this->getIndent() . 'if (UNEXPECTED(!(' . $orExpr . '))) {' . PHP_EOL; $code .= $this->getIndent() . 'if (UNEXPECTED(!(' . $orExpr . '))) {' . PHP_EOL;
$this->indentLevel++; $this->indentLevel++;
$code .= $this->getIndent() . 'php::throwException(zend_ce_type_error, (' . $msgExpr . ').toCString());' . PHP_EOL; $code .= $this->getIndent() . 'php::throwExceptionEx(zend_ce_type_error, 0, ' . $format . ', ' . $varName . '.typeStr());' . PHP_EOL;
$this->indentLevel--; $this->indentLevel--;
$code .= $this->getIndent() . '}' . PHP_EOL; $code .= $this->getIndent() . '}' . PHP_EOL;
@ -379,12 +374,13 @@ trait TypeCheckGenerator
} }
$orExpr = implode(' || ', $conditions); $orExpr = implode(' || ', $conditions);
$msgExpr = $this->genClosureParamTypeErrorExpr($argInfo, $argInfo->name, (string) ($argIndex + 1)); $throwExpr = $this->genClosureParamTypeErrorExpr($argInfo, $argInfo->name, (string) ($argIndex + 1));
$code = $this->genCompositeIntToFloatCoercion($argInfo->name, $argInfo->typeCheck); $code = $this->genCompositeIntToFloatCoercion($argInfo->name, $argInfo->typeCheck);
$code .= $this->getIndent() . 'if (UNEXPECTED(!(' . $orExpr . '))) {' . PHP_EOL; $code .= $this->getIndent() . 'if (UNEXPECTED(!(' . $orExpr . '))) {' . PHP_EOL;
$this->indentLevel++; $this->indentLevel++;
$code .= $this->getIndent() . 'return php::throwException(zend_ce_type_error, (' . $msgExpr . ').toCString());' . PHP_EOL; $code .= $this->getIndent() . $throwExpr . ';' . PHP_EOL;
$code .= $this->getIndent() . 'return php::null;' . PHP_EOL;
$this->indentLevel--; $this->indentLevel--;
$code .= $this->getIndent() . '}' . PHP_EOL; $code .= $this->getIndent() . '}' . PHP_EOL;
@ -409,7 +405,7 @@ trait TypeCheckGenerator
} }
$orExpr = implode(' || ', $conditions); $orExpr = implode(' || ', $conditions);
$msgExpr = $this->genClosureParamTypeErrorExpr($argInfo, $valueVar, $argNoVar); $throwExpr = $this->genClosureParamTypeErrorExpr($argInfo, $valueVar, $argNoVar);
$code = $this->getIndent() . 'for (auto ' . $iterVar . ' = ' . $argInfo->name . '.begin(); ' . $iterVar . ' != ' . $argInfo->name . '.end(); ++' . $iterVar . ') {' . PHP_EOL; $code = $this->getIndent() . 'for (auto ' . $iterVar . ' = ' . $argInfo->name . '.begin(); ' . $iterVar . ' != ' . $argInfo->name . '.end(); ++' . $iterVar . ') {' . PHP_EOL;
$this->indentLevel++; $this->indentLevel++;
@ -425,7 +421,8 @@ trait TypeCheckGenerator
$code .= $this->getIndent() . Type::INT . ' ' . $argNoVar . ' = ' . ($argIndex + 1) . ' + ' . $iterVar . '.index();' . PHP_EOL; $code .= $this->getIndent() . Type::INT . ' ' . $argNoVar . ' = ' . ($argIndex + 1) . ' + ' . $iterVar . '.index();' . PHP_EOL;
$code .= $this->getIndent() . 'if (UNEXPECTED(!(' . $orExpr . '))) {' . PHP_EOL; $code .= $this->getIndent() . 'if (UNEXPECTED(!(' . $orExpr . '))) {' . PHP_EOL;
$this->indentLevel++; $this->indentLevel++;
$code .= $this->getIndent() . 'return php::throwException(zend_ce_type_error, (' . $msgExpr . ').toCString());' . PHP_EOL; $code .= $this->getIndent() . $throwExpr . ';' . PHP_EOL;
$code .= $this->getIndent() . 'return php::null;' . PHP_EOL;
$this->indentLevel--; $this->indentLevel--;
$code .= $this->getIndent() . '}' . PHP_EOL; $code .= $this->getIndent() . '}' . PHP_EOL;
$this->indentLevel--; $this->indentLevel--;
@ -437,15 +434,11 @@ trait TypeCheckGenerator
protected function genClosureParamTypeErrorExpr(ArgInfo $argInfo, string $valueExpr, string $argNoExpr): string protected function genClosureParamTypeErrorExpr(ArgInfo $argInfo, string $valueExpr, string $argNoExpr): string
{ {
$paramName = $argInfo->phpName ?: $this->unescapeVarName($argInfo->name); $paramName = $argInfo->phpName ?: $this->unescapeVarName($argInfo->name);
return 'php::concat({' $format = $this->genCharPtr('{closure}(): Argument #', true)
. 'php::Str(' . $this->genCharPtr('{closure}(): Argument #', true) . '), ' . ' ZEND_LONG_FMT '
. 'php::toString(' . $argNoExpr . '), ' . $this->genCharPtr(' ($' . $paramName . ') must be of type ' . $argInfo->typeStr . ', %s given', true);
. 'php::Str(' . $this->genCharPtr(' ($' . $paramName . ') must be of type ', true) . '), ' return 'php::throwExceptionEx(zend_ce_type_error, 0, ' . $format . ', '
. 'php::Str(' . $this->genCharPtr($argInfo->typeStr, true) . '), ' . $argNoExpr . ', ' . $valueExpr . '.typeStr())';
. 'php::Str(", "), '
. $valueExpr . '.typeStr(), '
. 'php::Str(" given")'
. '})';
} }
protected function genClosureReturnCheck(string $varName): string protected function genClosureReturnCheck(string $varName): string
@ -468,13 +461,13 @@ trait TypeCheckGenerator
$orExpr = implode(' || ', $conditions); $orExpr = implode(' || ', $conditions);
$typeStr = $this->context->closureReturnTypeStr; $typeStr = $this->context->closureReturnTypeStr;
$msgExpr = 'php::concat(php::concat(php::Str(' . $this->genCharPtr('{closure}', true) . ' "(): Return value must be of type " ' $format = $this->genCharPtr('{closure}(): Return value must be of type ' . $typeStr . ', %s given', true);
. $this->genCharPtr($typeStr, true) . ' ", "), ' . $varName . '.typeStr()), php::Str(" given"))';
$code = $this->genCompositeIntToFloatCoercion($varName, $typeCheck); $code = $this->genCompositeIntToFloatCoercion($varName, $typeCheck);
$code .= $this->getIndent() . 'if (UNEXPECTED(!(' . $orExpr . '))) {' . PHP_EOL; $code .= $this->getIndent() . 'if (UNEXPECTED(!(' . $orExpr . '))) {' . PHP_EOL;
$this->indentLevel++; $this->indentLevel++;
$code .= $this->getIndent() . 'return php::throwException(zend_ce_type_error, (' . $msgExpr . ').toCString());' . PHP_EOL; $code .= $this->getIndent() . 'php::throwExceptionEx(zend_ce_type_error, 0, ' . $format . ', ' . $varName . '.typeStr());' . PHP_EOL;
$code .= $this->getIndent() . 'return php::null;' . PHP_EOL;
$this->indentLevel--; $this->indentLevel--;
$code .= $this->getIndent() . '}' . PHP_EOL; $code .= $this->getIndent() . '}' . PHP_EOL;

@ -567,12 +567,13 @@ trait PropertyAccessTrait
$propDisplay = $this->getObjectPropertyTypeCheckDisplayName($left); $propDisplay = $this->getObjectPropertyTypeCheckDisplayName($left);
$typeStr = $this->getObjectPropertyTypeCheckTypeString($def); $typeStr = $this->getObjectPropertyTypeCheckTypeString($def);
if ($this->usesPhpStylePropertyAssignTypeError($def)) { if ($this->usesPhpStylePropertyAssignTypeError($def)) {
$msgExpr = 'php::concat({php::Str("Cannot assign "), ' . $tmpVar . '.typeStr(), php::Str(" to property "), ' $throwExpr = 'php::throwExceptionEx(zend_ce_type_error, 0, '
. 'php::Str(' . $this->genCharPtr($propDisplay, true) . '), php::Str(" of type "), ' . $this->genCharPtr('Cannot assign %s to property ' . $propDisplay . ' of type ' . $typeStr, true)
. 'php::Str(' . $this->genCharPtr($typeStr, true) . ')})'; . ', ' . $tmpVar . '.typeStr())';
} else { } else {
$msgExpr = 'php::concat(php::concat(php::Str(' . $this->genCharPtr($propDisplay, true) . ' " must be of type " ' $throwExpr = 'php::throwExceptionEx(zend_ce_type_error, 0, '
. $this->genCharPtr($typeStr, true) . ' ", "), ' . $tmpVar . '.typeStr()), php::Str(" given"))'; . $this->genCharPtr($propDisplay . ' must be of type ' . $typeStr . ', %s given', true)
. ', ' . $tmpVar . '.typeStr())';
} }
$coercion = $this->compositeTypeNeedsIntToFloatCoercion($typeCheck) $coercion = $this->compositeTypeNeedsIntToFloatCoercion($typeCheck)
@ -583,7 +584,7 @@ trait PropertyAccessTrait
. $tmpVar . ' = ' . $rightExpr . '; ' . $tmpVar . ' = ' . $rightExpr . '; '
. $coercion . $coercion
. 'if (UNEXPECTED(!(' . implode(' || ', $conditions) . '))) { ' . 'if (UNEXPECTED(!(' . implode(' || ', $conditions) . '))) { '
. 'php::throwException(zend_ce_type_error, (' . $msgExpr . ').toCString()); ' . $throwExpr . '; '
. '} ' . '} '
. 'return ' . $tmpVar . '; ' . 'return ' . $tmpVar . '; '
. '}())'; . '}())';

@ -8,6 +8,8 @@
namespace TypePhp; namespace TypePhp;
use Ajaxray\AnsiKit\AnsiTerminal;
use Ajaxray\AnsiKit\Components\Progressbar;
use MJS\TopSort\Implementations\StringSort; use MJS\TopSort\Implementations\StringSort;
use TypePhp\Analysis\SsaBuilder; use TypePhp\Analysis\SsaBuilder;
use TypePhp\Backend\CompilerFactory; use TypePhp\Backend\CompilerFactory;
@ -49,7 +51,7 @@ class Translator extends Preprocessor
use ResourceCompilationTrait; use ResourceCompilationTrait;
use ClassConstantValueTrait; use ClassConstantValueTrait;
public const string VERSION = '0.3.0'; public const string VERSION = '0.4.0';
public const string APP_NAME = 'TypePHP Compiler (AOT)'; public const string APP_NAME = 'TypePHP Compiler (AOT)';
protected const string MODULE_NAME_PREFIX = 'app_'; protected const string MODULE_NAME_PREFIX = 'app_';
@ -1312,6 +1314,17 @@ CODE;
return pcntl_wifexited($status) && pcntl_wexitstatus($status) === 0; return pcntl_wifexited($status) && pcntl_wexitstatus($status) === 0;
} }
protected function getCompileChildFailureReason(int $status): string
{
if (pcntl_wifsignaled($status)) {
return 'terminated by signal ' . pcntl_wtermsig($status);
}
if (pcntl_wifexited($status)) {
return 'exited with status ' . pcntl_wexitstatus($status);
}
return 'terminated abnormally';
}
protected function compileWithPcntl(array $sourceFiles, int $job): array protected function compileWithPcntl(array $sourceFiles, int $job): array
{ {
if (!function_exists('pcntl_fork')) { if (!function_exists('pcntl_fork')) {
@ -1321,6 +1334,14 @@ CODE;
$totalFiles = count($sourceFiles); $totalFiles = count($sourceFiles);
$this->climate->lightBlue("Starting parallel compilation with {$job} jobs for {$totalFiles} files"); $this->climate->lightBlue("Starting parallel compilation with {$job} jobs for {$totalFiles} files");
$progress = null;
if (!$this->noProgress) {
$progress = new Progressbar();
$progress->barStyle([AnsiTerminal::FG_GREEN])
->percentageStyle([AnsiTerminal::TEXT_BOLD])
->labelStyle([AnsiTerminal::FG_CYAN]);
$progress->renderInPlace(0, $totalFiles, 'Compiling');
}
$result = $this->getNativeBuilder()->dispatchParallel( $result = $this->getNativeBuilder()->dispatchParallel(
$sourceFiles, $sourceFiles,
$job, $job,
@ -1331,8 +1352,25 @@ CODE;
fn(): int => $this->pcntlFork(), fn(): int => $this->pcntlFork(),
fn(): array => $this->waitForCompileChild(), fn(): array => $this->waitForCompileChild(),
fn(int $status): bool => $this->compileChildSucceeded($status), fn(int $status): bool => $this->compileChildSucceeded($status),
function (string $source, string $object, int $status, bool $success, int $completed) use ($progress, $totalFiles): void {
if (!$success) {
echo PHP_EOL;
$this->climate->red("Compilation failed: {$source} ({$this->getCompileChildFailureReason($status)})");
}
if ($this->noProgress) {
$percent = (int) ($completed / $totalFiles * 100);
$shortSource = $this->removeCommonPrefix($this->buildDir, $source);
$this->climate->white("[{$completed}/{$totalFiles}] {$percent}% {$shortSource}");
} else {
$progress->renderInPlace($completed, $totalFiles, 'Compiling');
}
},
); );
if (!$this->noProgress) {
echo PHP_EOL;
}
if ($result['failures'] !== []) { if ($result['failures'] !== []) {
throw new \Exception('Compilation failed for: ' . implode(', ', $result['failures'])); throw new \Exception('Compilation failed for: ' . implode(', ', $result['failures']));
} }
@ -2653,15 +2691,14 @@ CODE;
{ {
$required = $functionDef->argCountRequired; $required = $functionDef->argCountRequired;
$expected = $required === count($functionDef->argInfoList) ? 'exactly' : 'at least'; $expected = $required === count($functionDef->argInfoList) ? 'exactly' : 'at least';
$message = 'php::concat({' $message = $this->genCharPtr(
. 'php::Str(' . $this->genCharPtr('Too few arguments to function ' . $displayName . '(), ', true) . '), ' 'Too few arguments to function ' . $displayName . '(), %u passed and ' . $expected . ' ' . $required . ' expected',
. 'php::toString(php::getCallArgNum()), ' true
. 'php::Str(' . $this->genCharPtr(' passed and ' . $expected . ' ' . $required . ' expected', true) . ')' );
. '})';
$code = $this->getIndent() . 'if (UNEXPECTED(php::getCallArgNum() < ' . $required . ')) {' . PHP_EOL; $code = $this->getIndent() . 'if (UNEXPECTED(php::getCallArgNum() < ' . $required . ')) {' . PHP_EOL;
$this->indentLevel++; $this->indentLevel++;
$code .= $this->getIndent() . 'php::throwException(zend_ce_argument_count_error, (' . $message . ').toCString());' . PHP_EOL; $code .= $this->getIndent() . 'php::throwExceptionEx(zend_ce_argument_count_error, 0, ' . $message . ', php::getCallArgNum());' . PHP_EOL;
$code .= $this->getIndent() . 'return;' . PHP_EOL; $code .= $this->getIndent() . 'return;' . PHP_EOL;
$this->indentLevel--; $this->indentLevel--;
$code .= $this->getIndent() . '}' . PHP_EOL; $code .= $this->getIndent() . '}' . PHP_EOL;
@ -3560,11 +3597,9 @@ CODE;
$nextStr = $this->getLiteralString('next'); $nextStr = $this->getLiteralString('next');
$rewindStr = $this->getLiteralString('rewind'); $rewindStr = $this->getLiteralString('rewind');
$invalidAggregateReturn = static function (string $aggregateObj): string { $invalidAggregateReturn = static function (string $aggregateObj): string {
return 'php::throwException(zend_ce_exception, (php::concat({' return 'php::throwExceptionEx(zend_ce_exception, 0, '
. 'php::Str("Objects returned by "), ' . '"Objects returned by %s::getIterator() must be traversable or implement interface Iterator", '
. $aggregateObj . '.getClassName(), ' . $aggregateObj . '.getClassName().toCString());';
. 'php::Str("::getIterator() must be traversable or implement interface Iterator")'
. '})).toCString());';
}; };
$code = $iterableVar . ' = ' . $obj . ';' . PHP_EOL; $code = $iterableVar . ' = ' . $obj . ';' . PHP_EOL;
@ -3608,7 +3643,8 @@ CODE;
$code .= $this->getIndent() . $tmpArrayVar . ' = php::call(' . $this->getFuncPtr('get_object_vars') . ', {' . $obj . '});' . PHP_EOL; $code .= $this->getIndent() . $tmpArrayVar . ' = php::call(' . $this->getFuncPtr('get_object_vars') . ', {' . $obj . '});' . PHP_EOL;
$code .= $this->parseForeachArray($node, $tmpArrayVar); $code .= $this->parseForeachArray($node, $tmpArrayVar);
$this->indentLevel--; $this->indentLevel--;
$code .= '}' . PHP_EOL; $this->indentLevel--;
$code .= $this->getIndent() . '}' . PHP_EOL;
return $code; return $code;
} }

Loading…
Cancel
Save