Skip to content

Commit

Permalink
Create CompileApp
Browse files Browse the repository at this point in the history
  • Loading branch information
koriym committed Sep 13, 2023
1 parent 14f5b36 commit b359888
Show file tree
Hide file tree
Showing 2 changed files with 145 additions and 0 deletions.
123 changes: 123 additions & 0 deletions src/Compiler/CompileApp.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
<?php

declare(strict_types=1);

namespace BEAR\Package\Compiler;

use BEAR\AppMeta\AbstractAppMeta;
use BEAR\AppMeta\Meta;
use BEAR\Package\Injector\PackageInjector;
use BEAR\Resource\Exception\ParameterException;
use BEAR\Resource\NamedParameterInterface;
use BEAR\Sunday\Extension\Application\AppInterface;
use Doctrine\Common\Annotations\Reader;
use Ray\Compiler\CompileInjector;
use Ray\PsrCacheModule\LocalCacheProvider;
use ReflectionClass;

use function assert;
use function in_array;
use function is_callable;
use function microtime;
use function sprintf;
use function str_starts_with;

final class CompileApp
{
/** @var array{class: int, method: int, time: float} */
private array $logs = [
'class' => 0,
'method' => 0,
'time' => 0
];

/**
* Compile application
*
* DI+AOP script file
* Parameter meta information
* (No annotation cached)
*
* @param list<string> $extraContexts
*
* @return array{class: int, method: int, time: float}
*/
public function compile(CompileInjector $injector, array $extraContexts = []): array
{
$start = microtime(true);
$reader = $injector->getInstance(Reader::class);
assert($reader instanceof Reader);
$namedParams = $injector->getInstance(NamedParameterInterface::class);
assert($namedParams instanceof NamedParameterInterface);
// create DI factory class and AOP compiled class for all resources and save $app cache.
$app = $injector->getInstance(AppInterface::class);
assert($app instanceof AppInterface);
$meta = $injector->getInstance(AbstractAppMeta::class);
// check resource injection and create annotation cache
$resources = $meta->getResourceListGenerator();
foreach ($resources as $resourc) {
$this->logs['class']++;
[$className] = $resourc;
$this->saveMeta($namedParams, new ReflectionClass($className));
}

$this->compileExtraContexts($extraContexts, $meta);
$this->logs['time'] = (float) sprintf('%.3f', microtime(true) - $start);

return $this->logs;
}

/**
* Save annotation and method meta information
*
* @param ReflectionClass<object> $class
*/
private function saveMeta(NamedParameterInterface $namedParams, ReflectionClass $class): void
{
$instance = $class->newInstanceWithoutConstructor();

$methods = $class->getMethods();
$log = sprintf('%s: ', $class->getName());
foreach ($methods as $method) {
$methodName = $method->getName();

if (! str_starts_with($methodName, 'on')) {
continue;
}

$this->logs['method']++;

$log .= sprintf(' %s()', $methodName);
$this->saveNamedParam($namedParams, $instance, $methodName);
}
}

private function saveNamedParam(NamedParameterInterface $namedParameter, object $instance, string $method): void
{
// named parameter
if (! in_array($method, ['onGet', 'onPost', 'onPut', 'onPatch', 'onDelete', 'onHead'], true)) {
return; // @codeCoverageIgnore
}

$callable = [$instance, $method];
if (! is_callable($callable)) {
return; // @codeCoverageIgnore
}

try {
$namedParameter->getParameters($callable, []);
} catch (ParameterException) {
return;
}
}

/** @param list<string> $extraContexts */
public function compileExtraContexts(array $extraContexts, AbstractAppMeta $meta): void
{
$cache = (new LocalCacheProvider())->get();
foreach ($extraContexts as $context) {
$contextualMeta = new Meta($meta->name, $context, $meta->appDir);
PackageInjector::getInstance($contextualMeta, $context, $cache)->getInstance(AppInterface::class);
}
}
}
22 changes: 22 additions & 0 deletions tests/CompileAppTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

namespace BEAR\Package;

use BEAR\Package\Compiler\CompileApp;
use PHPUnit\Framework\TestCase;
use Ray\Compiler\CompileInjector;

use function assert;

class CompileAppTest extends TestCase
{
public function testCompile(): void
{
$injector = Injector::getInstance('FakeVendor\MinApp', 'prod-app', __DIR__ . '/Fake/fake-min-app');
assert($injector instanceof CompileInjector);
$logs = (new CompileApp())->compile($injector, ['prod-api-app']);
$this->assertSame(1, $logs['method']);
}
}

0 comments on commit b359888

Please # to comment.