Skip to content

Commit

Permalink
Merge pull request #423 from bearsunday/compileapp
Browse files Browse the repository at this point in the history
CompileApp
  • Loading branch information
koriym authored Jun 9, 2024
2 parents 9e7de07 + f2730b7 commit 1eb7e10
Show file tree
Hide file tree
Showing 7 changed files with 232 additions and 1 deletion.
5 changes: 4 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,13 @@
"tests/Fake/"
],
"FakeVendor\\HelloWorld\\": [
"tests/Fake/fake-app/src/"
"tests/Fake/fake-app/src"
],
"Import\\HelloWorld\\": [
"tests/Fake/import-app/src"
],
"FakeVendor\\MinApp\\": [
"tests/Fake/fake-min-app/src"
]
},
"files": [
Expand Down
124 changes: 124 additions & 0 deletions src/Compiler/CompileApp.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
<?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 $resource) {
$this->logs['class']++;
[$className] = $resource;
$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();
foreach ($methods as $method) {
$methodName = $method->getName();

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

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

$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, []);
// @codeCoverageIgnoreStart
} catch (ParameterException) {
return; // It is OK to ignore exceptions. The objective is to obtain meta-information.

// @codeCoverageIgnoreEnd
}
}

/** @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']);
}
}
10 changes: 10 additions & 0 deletions tests/Fake/fake-min-app/composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"require": {
"bear/package": "^1.16"
},
"autoload": {
"psr-4": {
"FakeVendor\\Compile\\": "src"
}
}
}
27 changes: 27 additions & 0 deletions tests/Fake/fake-min-app/src/Module/App.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

namespace FakeVendor\MinApp\Module;

use BEAR\Resource\ResourceInterface;
use BEAR\Sunday\Extension\Application\AbstractApp;
use BEAR\Sunday\Extension\Application\AppInterface;
use BEAR\Sunday\Extension\Error\ErrorInterface;
use BEAR\Sunday\Extension\Error\ThrowableHandlerInterface;
use BEAR\Sunday\Extension\Router\RouterInterface;
use BEAR\Sunday\Extension\Transfer\HttpCacheInterface;
use BEAR\Sunday\Extension\Transfer\TransferInterface;
use Ray\Di\Di\Inject;

class App implements AppInterface
{
public function __construct(
public HttpCacheInterface $httpCache,
public RouterInterface $router,
public TransferInterface $responder,
public ResourceInterface $resource,
public ErrorInterface $error
){
}
}
26 changes: 26 additions & 0 deletions tests/Fake/fake-min-app/src/Module/AppModule.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

declare(strict_types=1);

namespace FakeVendor\MinApp\Module;

use BEAR\Package\PackageModule;
use FakeVendor\HelloWorld\Auth;
use FakeVendor\HelloWorld\FakeDep;
use FakeVendor\HelloWorld\FakeDepInterface;
use FakeVendor\HelloWorld\FakeFoo;
use FakeVendor\HelloWorld\Module\Provider\AuthProvider;
use FakeVendor\HelloWorld\NullInterceptor;
use FakeVendor\HelloWorld\Resource\Page\Dep;
use Ray\Di\AbstractModule;

class AppModule extends AbstractModule
{
/**
* {@inheritdoc}
*/
protected function configure()
{
$this->install(new PackageModule());
}
}
19 changes: 19 additions & 0 deletions tests/Fake/fake-min-app/src/Resource/Page/Index.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

declare(strict_types=1);

namespace FakeVendor\MinApp\Resource\Page;

use BEAR\Resource\ResourceObject;
use Psr\Log\LoggerInterface;

class Index extends ResourceObject
{
public function __construct(public LoggerInterface $logger)
{
}
public function onGet(): static
{
return $this;
}
}

0 comments on commit 1eb7e10

Please sign in to comment.