Skip to content

Commit

Permalink
Init
Browse files Browse the repository at this point in the history
  • Loading branch information
luzrain committed Feb 2, 2024
0 parents commit cd215d7
Show file tree
Hide file tree
Showing 20 changed files with 1,347 additions and 0 deletions.
6 changes: 6 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/.gitattributes export-ignore
/.gitignore export-ignore
/.github export-ignore
/tests export-ignore
/phpunit.xml export-ignore
/.php-cs-fixer.dist.php export-ignore
Empty file added .github/workflows/tests.yaml
Empty file.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
composer.lock
vendor/
var/
78 changes: 78 additions & 0 deletions .php-cs-fixer.dist.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<?php

$finder = PhpCsFixer\Finder::create()
->in(__DIR__ . '/src')
->in(__DIR__ . '/tests')
;

$rules = [
// Rules that follow PSR-12 standard.
'@PER-CS2.0' => true,

// Rules that follow PSR-12 standard. This set contains rules that are risky.
'@PER-CS2.0:risky' => true,

// PHP arrays should be declared using the short syntax.
'array_syntax' => ['syntax' => 'short'],

// Each line of multi-line DocComments must have an asterisk [PSR-5] and must be aligned with the first one.
'align_multiline_comment' => true,

// A single space or none should be between cast and variable.
'cast_spaces' => true,

// There should not be any empty comments.
'no_empty_comment' => true,

// Unused use statements must be removed.
'no_unused_imports' => true,

// Scalar types should always be written in the same form. int not integer, bool not boolean, float not real or double.
'phpdoc_scalar' => true,

// Single line @var PHPDoc should have proper spacing.
'phpdoc_single_line_var_spacing' => true,

// Removes extra blank lines after summary and after description in PHPDoc.
'phpdoc_trim' => true,

// @var and @type annotations must have type and name in the correct order.
'phpdoc_var_annotation_correct_order' => true,

// Remove useless (semicolon) statements.
'no_empty_statement' => true,

// There MUST NOT be spaces around offset braces.
'no_spaces_around_offset' => true,

// Force strict types declaration in all files.
'declare_strict_types' => true,

// Comparisons should be strict.
'strict_comparison' => true,

// Ordering use statements
'ordered_imports' => true,

// Replace get_class calls on object variables with class keyword syntax.
'get_class_to_class_keyword' => true,

// Removes @param, @return and @var tags that don’t provide any useful information.
'no_superfluous_phpdoc_tags' => true,

// Multi-line arrays, arguments list, parameters list and match expressions must have a trailing comma.
'trailing_comma_in_multiline' => [
'after_heredoc' => true,
'elements' => ['arrays', 'match', 'arguments', 'parameters'],
],

// Empty body of class, interface, trait, enum or function must be abbreviated as {} and placed on the same line
'single_line_empty_body' => false,
];

return (new PhpCsFixer\Config())
->setCacheFile(__DIR__ . '/var/.php-cs-fixer.cache')
->setFinder($finder)
->setRules($rules)
->setRiskyAllowed(true)
;
19 changes: 19 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Copyright (c) 2024 [email protected]

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
137 changes: 137 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
# PhpRunner runtime for symfony applications
![PHP >=8.2](https://img.shields.io/badge/PHP->=8.2-777bb3.svg?style=flat)
![Symfony ^6.4|^7.0](https://img.shields.io/badge/Symfony-^7.0-374151.svg?style=flat)
[![Version](https://img.shields.io/github/v/tag/luzrain/phprunner-bundle?label=Version&filter=v*.*.*&sort=semver&color=374151)](../../releases)
[![Tests Status](https://img.shields.io/github/actions/workflow/status/luzrain/phprunner-bundle/tests.yaml?label=Tests&branch=master)](../../actions/workflows/tests.yaml)

This bundle provides a [PhpRunner](https://github.com/luzrain/phprunner) integration with Symfony framework to run your application in a highly efficient event-loop based runtime.

> [!NOTE]
> This tool is in development now
## Getting started
### Install composer packages
```bash
$ composer require luzrain/phprunner-bundle
```

### Enable the bundle
```php
<?php
// config/bundles.php

return [
// ...
Luzrain\PhpRunnerBundle\PhpRunnerBundle::class => ['all' => true],
];
```

### Configure the bundle
A minimal configuration might look like this.
For all available options with documentation, see the command output.
```bash
$ bin/console config:dump-reference phprunner
```

```yaml
# config/packages/phprunner.yaml

phprunner:
servers:
- name: 'Symfony webserver'
listen: http://0.0.0.0:80
processes: 4

reload_strategy:
exception:
active: true

file_monitor:
active: true
```
### Start application
```bash
$ APP_RUNTIME=Luzrain\\PhpRunnerBundle\\Runtime php public/index.php start
```

\* For better performance, install the _php-uv_ extension.

## Reload strategies
Because of the asynchronous nature of the server, the workers reuse loaded resources on each request. This means that in some cases we need to restart workers.
For example, after an exception is thrown, to prevent services from being in an unrecoverable state. Or every time you change the code in the IDE.
There are a few restart strategies that are implemented and can be enabled or disabled depending on the environment.

- **exception**
Reload worker each time that an exception is thrown during the request handling.
- **max_requests**
Reload worker on every N request to prevent memory leaks.
- **file_monitor**
Reload all workers each time you change the files**.
- **always**
Reload worker after each request.

** It is highly recommended to install the _php-inotify_ extension for file monitoring. Without it, monitoring will work in polling mode, which can be very cpu and disk intensive for large projects.

See all available options for each strategy in the command output.
```bash
$ bin/console config:dump-reference phprunner reload_strategy
```

## Scheduler
Periodic tasks can be configured with attributes or with tags in configuration files.
Schedule string can be formatted in several ways:
- An integer to define the frequency as a number of seconds. Example: _60_
- An ISO8601 datetime format. Example: _2023-08-01T01:00:00+08:00_
- An ISO8601 duration format. Example: _PT1M_
- A relative date format as supported by DateInterval. Example: _1 minutes_
- A cron expression**. Example: _*/1 * * * *_

** Note that you need to install the [dragonmantank/cron-expression](https://github.com/dragonmantank/cron-expression) package if you want to use cron expressions as schedule strings

```php
<?php

use Luzrain\PhpRunnerBundle\Attribute\AsTask;

/**
* Attribute parameters
* name: Task name
* schedule: Task schedule in any format
* method: method to call, __invoke by default
* jitter: Maximum jitter in seconds that adds a random time offset to the schedule. Use to prevent multiple tasks from running at the same time
*/
#[AsTask(name: 'My scheduled task', schedule: '1 minutes')]
final class TaskService
{
public function __invoke()
{
// ...
}
}
```

## Supervisor
Supervisor can be configured with attributes or with tags in configuration files.
Processes are kept alive and wake up if one of them dies.

```php
<?php

use Luzrain\PhpRunnerBundle\Attribute\AsProcess;

/**
* Attribute parameters
* name: Process name
* processes: number of processes
* method: method to call, __invoke by default
*/
#[AsProcess(name: 'My worker', processes: 1)]
final class ProcessService
{
public function __invoke()
{
// ...
}
}
```
54 changes: 54 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"name": "luzrain/phprunner-bundle",
"description": "Phprunner runtime for symfony applications",
"keywords": ["phprunner", "symfony", "runtime", "php-runtime"],
"homepage": "https://github.com/luzrain/phprunner-bundle",
"type": "symfony-bundle",
"license": "MIT",
"authors": [
{
"name": "Anton Zenkov",
"email": "[email protected]"
}
],
"require": {
"php": "^8.2",
"ext-pcntl": "*",
"ext-posix": "*",
"luzrain/phprunner": "dev-master",
"psr/http-factory": "^1.0",
"symfony/config": "^7.0",
"symfony/dependency-injection": "^7.0",
"symfony/error-handler": "^7.0",
"symfony/http-kernel": "^7.0",
"symfony/psr-http-message-bridge": "^7.0",
"symfony/runtime": "^7.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.48",
"guzzlehttp/guzzle": "^7.8",
"phpunit/phpunit": "^10.5",
"symfony/framework-bundle": "^7.0"
},
"suggest": {
"ext-uv": "For better performance",
"ext-inotify": "For effective file monitoring",
"dragonmantank/cron-expression": "For parse cron expressions"
},
"autoload": {
"psr-4": {
"Luzrain\\PhpRunnerBundle\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Luzrain\\PhpRunnerBundle\\Test\\": "tests/"
}
},
"config": {
"sort-packages": true,
"allow-plugins": {
"symfony/runtime": true
}
}
}
Empty file added phpunit.xml
Empty file.
87 changes: 87 additions & 0 deletions src/ConfigLoader.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<?php

declare(strict_types=1);

namespace Luzrain\PhpRunnerBundle;

use Symfony\Component\Config\ConfigCache;
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface;

final class ConfigLoader implements CacheWarmerInterface
{
private array $config;
private ConfigCache $cache;
private string $yamlConfigFilePath;

public function __construct(string $projectDir, string $cacheDir, bool $isDebug)
{
$this->yamlConfigFilePath = \sprintf('%s/config/packages/phprunner.yaml', $projectDir);
$cacheConfigFilePath = \sprintf('%s/phprunner_config.cache.php', $cacheDir);
$this->cache = new ConfigCache($cacheConfigFilePath, $isDebug);
}

public function isOptional(): bool
{
return false;
}

public function warmUp(string $cacheDir, string $buildDir = null): array
{
$resources = \is_file($this->yamlConfigFilePath) ? [new FileResource($this->yamlConfigFilePath)] : [];
$this->cache->write(\sprintf('<?php return %s;', \var_export($this->config, true)), $resources);

return [];
}

public function warmUpInFork(KernelFactory $kernelFactory): void
{
if (\pcntl_fork() === 0) {
$kernelFactory->createKernel()->boot();
exit;
} else {
pcntl_wait($status);
unset($status);
}
}

public function isFresh(): bool
{
return $this->cache->isFresh();
}

private function getConfigCache(): array
{
return $this->config ??= require $this->cache->getPath();
}

public function setConfig(array $config): void
{
$this->config[0] = $config;
}

public function setProcessConfig(array $config): void
{
$this->config[1] = $config;
}

public function setSchedulerConfig(array $config): void
{
$this->config[2] = $config;
}

public function getConfig(): array
{
return $this->getConfigCache()[0];
}

public function getProcessConfig(): array
{
return $this->getConfigCache()[1];
}

public function getSchedulerConfig(): array
{
return $this->getConfigCache()[2];
}
}
Loading

0 comments on commit cd215d7

Please sign in to comment.