frontend and backend
This commit is contained in:
22
backend/.env
Normal file
22
backend/.env
Normal file
@ -0,0 +1,22 @@
|
||||
# In all environments, the following files are loaded if they exist,
|
||||
# the latter taking precedence over the former:
|
||||
#
|
||||
# * .env contains default values for the environment variables needed by the app
|
||||
# * .env.local uncommitted file with local overrides
|
||||
# * .env.$APP_ENV committed environment-specific defaults
|
||||
# * .env.$APP_ENV.local uncommitted environment-specific overrides
|
||||
#
|
||||
# Real environment variables win over .env files.
|
||||
#
|
||||
# DO NOT DEFINE PRODUCTION SECRETS IN THIS FILE NOR IN ANY OTHER COMMITTED FILES.
|
||||
#
|
||||
# Run "composer dump-env prod" to compile .env files for production use (requires symfony/flex >=1.2).
|
||||
# https://symfony.com/doc/current/best_practices.html#use-environment-variables-for-infrastructure-configuration
|
||||
|
||||
REDIS_HOST=xrges-redis
|
||||
REDIS_PORT=6379
|
||||
|
||||
###> symfony/framework-bundle ###
|
||||
APP_ENV=dev
|
||||
APP_SECRET=8ffa51f664399110f3cdc2ffa39e9705
|
||||
###< symfony/framework-bundle ###
|
10
backend/.gitignore
vendored
Normal file
10
backend/.gitignore
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
|
||||
###> symfony/framework-bundle ###
|
||||
/.env.local
|
||||
/.env.local.php
|
||||
/.env.*.local
|
||||
/config/secrets/prod/prod.decrypt.private.php
|
||||
/public/bundles/
|
||||
/var/
|
||||
/vendor/
|
||||
###< symfony/framework-bundle ###
|
17
backend/bin/console
Executable file
17
backend/bin/console
Executable file
@ -0,0 +1,17 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
use App\Kernel;
|
||||
use Symfony\Bundle\FrameworkBundle\Console\Application;
|
||||
|
||||
if (!is_file(dirname(__DIR__).'/vendor/autoload_runtime.php')) {
|
||||
throw new LogicException('Symfony Runtime is missing. Try running "composer require symfony/runtime".');
|
||||
}
|
||||
|
||||
require_once dirname(__DIR__).'/vendor/autoload_runtime.php';
|
||||
|
||||
return function (array $context) {
|
||||
$kernel = new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']);
|
||||
|
||||
return new Application($kernel);
|
||||
};
|
77
backend/composer.json
Normal file
77
backend/composer.json
Normal file
@ -0,0 +1,77 @@
|
||||
{
|
||||
"type": "project",
|
||||
"license": "proprietary",
|
||||
"minimum-stability": "stable",
|
||||
"prefer-stable": true,
|
||||
"require": {
|
||||
"php": ">=8.0.2",
|
||||
"ext-ctype": "*",
|
||||
"ext-iconv": "*",
|
||||
"doctrine/annotations": "^1.13",
|
||||
"phpdocumentor/reflection-docblock": "^5.3",
|
||||
"phpstan/phpdoc-parser": "^1.2",
|
||||
"symfony/console": "6.0.*",
|
||||
"symfony/dotenv": "6.0.*",
|
||||
"symfony/flex": "^2",
|
||||
"symfony/framework-bundle": "6.0.*",
|
||||
"symfony/property-access": "6.0.*",
|
||||
"symfony/property-info": "6.0.*",
|
||||
"symfony/runtime": "6.0.*",
|
||||
"symfony/serializer": "6.0.*",
|
||||
"symfony/yaml": "6.0.*"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/maker-bundle": "^1.36"
|
||||
},
|
||||
"config": {
|
||||
"allow-plugins": {
|
||||
"composer/package-versions-deprecated": true,
|
||||
"symfony/flex": true,
|
||||
"symfony/runtime": true
|
||||
},
|
||||
"optimize-autoloader": true,
|
||||
"preferred-install": {
|
||||
"*": "dist"
|
||||
},
|
||||
"sort-packages": true
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"App\\": "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"App\\Tests\\": "tests/"
|
||||
}
|
||||
},
|
||||
"replace": {
|
||||
"symfony/polyfill-ctype": "*",
|
||||
"symfony/polyfill-iconv": "*",
|
||||
"symfony/polyfill-php72": "*",
|
||||
"symfony/polyfill-php73": "*",
|
||||
"symfony/polyfill-php74": "*",
|
||||
"symfony/polyfill-php80": "*"
|
||||
},
|
||||
"scripts": {
|
||||
"auto-scripts": {
|
||||
"cache:clear": "symfony-cmd",
|
||||
"assets:install %PUBLIC_DIR%": "symfony-cmd"
|
||||
},
|
||||
"post-install-cmd": [
|
||||
"@auto-scripts"
|
||||
],
|
||||
"post-update-cmd": [
|
||||
"@auto-scripts"
|
||||
]
|
||||
},
|
||||
"conflict": {
|
||||
"symfony/symfony": "*"
|
||||
},
|
||||
"extra": {
|
||||
"symfony": {
|
||||
"allow-contrib": false,
|
||||
"require": "6.0.*"
|
||||
}
|
||||
}
|
||||
}
|
3360
backend/composer.lock
generated
Normal file
3360
backend/composer.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
6
backend/config/bundles.php
Normal file
6
backend/config/bundles.php
Normal file
@ -0,0 +1,6 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true],
|
||||
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
|
||||
];
|
19
backend/config/packages/cache.yaml
Normal file
19
backend/config/packages/cache.yaml
Normal file
@ -0,0 +1,19 @@
|
||||
framework:
|
||||
cache:
|
||||
# Unique name of your app: used to compute stable namespaces for cache keys.
|
||||
#prefix_seed: your_vendor_name/app_name
|
||||
|
||||
# The "app" cache stores to the filesystem by default.
|
||||
# The data in this cache should persist between deploys.
|
||||
# Other options include:
|
||||
|
||||
# Redis
|
||||
#app: cache.adapter.redis
|
||||
#default_redis_provider: redis://localhost
|
||||
|
||||
# APCu (not recommended with heavy random-write workloads as memory fragmentation can cause perf issues)
|
||||
#app: cache.adapter.apcu
|
||||
|
||||
# Namespaced pools use the above "app" backend by default
|
||||
#pools:
|
||||
#my.dedicated.cache: null
|
26
backend/config/packages/framework.yaml
Normal file
26
backend/config/packages/framework.yaml
Normal file
@ -0,0 +1,26 @@
|
||||
# see https://symfony.com/doc/current/reference/configuration/framework.html
|
||||
framework:
|
||||
secret: '%env(APP_SECRET)%'
|
||||
#csrf_protection: true
|
||||
http_method_override: false
|
||||
|
||||
# Enables session support. Note that the session will ONLY be started if you read or write from it.
|
||||
# Remove or comment this section to explicitly disable session support.
|
||||
session:
|
||||
handler_id: null
|
||||
cookie_secure: auto
|
||||
cookie_samesite: lax
|
||||
storage_factory_id: session.storage.factory.native
|
||||
|
||||
#esi: true
|
||||
#fragments: true
|
||||
php_errors:
|
||||
log: true
|
||||
|
||||
error_controller: App\Controller\ErrorController::show
|
||||
|
||||
when@test:
|
||||
framework:
|
||||
test: true
|
||||
session:
|
||||
storage_factory_id: session.storage.factory.mock_file
|
12
backend/config/packages/routing.yaml
Normal file
12
backend/config/packages/routing.yaml
Normal file
@ -0,0 +1,12 @@
|
||||
framework:
|
||||
router:
|
||||
utf8: true
|
||||
|
||||
# Configure how to generate URLs in non-HTTP contexts, such as CLI commands.
|
||||
# See https://symfony.com/doc/current/routing.html#generating-urls-in-commands
|
||||
#default_uri: http://localhost
|
||||
|
||||
when@prod:
|
||||
framework:
|
||||
router:
|
||||
strict_requirements: null
|
5
backend/config/preload.php
Normal file
5
backend/config/preload.php
Normal file
@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
if (file_exists(dirname(__DIR__).'/var/cache/prod/App_KernelProdContainer.preload.php')) {
|
||||
require dirname(__DIR__).'/var/cache/prod/App_KernelProdContainer.preload.php';
|
||||
}
|
7
backend/config/routes.yaml
Normal file
7
backend/config/routes.yaml
Normal file
@ -0,0 +1,7 @@
|
||||
controllers:
|
||||
resource: ../src/Controller/
|
||||
type: annotation
|
||||
|
||||
kernel:
|
||||
resource: ../src/Kernel.php
|
||||
type: annotation
|
7
backend/config/routes/annotations.yaml
Normal file
7
backend/config/routes/annotations.yaml
Normal file
@ -0,0 +1,7 @@
|
||||
controllers:
|
||||
resource: ../../src/Controller/
|
||||
type: annotation
|
||||
|
||||
kernel:
|
||||
resource: ../../src/Kernel.php
|
||||
type: annotation
|
4
backend/config/routes/framework.yaml
Normal file
4
backend/config/routes/framework.yaml
Normal file
@ -0,0 +1,4 @@
|
||||
when@dev:
|
||||
_errors:
|
||||
resource: '@FrameworkBundle/Resources/config/routing/errors.xml'
|
||||
prefix: /_error
|
41
backend/config/services.yaml
Normal file
41
backend/config/services.yaml
Normal file
@ -0,0 +1,41 @@
|
||||
# This file is the entry point to configure your own services.
|
||||
# Files in the packages/ subdirectory configure your dependencies.
|
||||
|
||||
# Put parameters here that don't need to change on each machine where the app is deployed
|
||||
# https://symfony.com/doc/current/best_practices.html#use-parameters-for-application-configuration
|
||||
parameters:
|
||||
|
||||
services:
|
||||
# default configuration for services in *this* file
|
||||
_defaults:
|
||||
autowire: true # Automatically injects dependencies in your services.
|
||||
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
|
||||
|
||||
# makes classes in src/ available to be used as services
|
||||
# this creates a service per class whose id is the fully-qualified class name
|
||||
App\:
|
||||
resource: '../src/'
|
||||
exclude:
|
||||
- '../src/DependencyInjection/'
|
||||
- '../src/Entity/'
|
||||
- '../src/Kernel.php'
|
||||
- '../src/Tests/'
|
||||
|
||||
# add more service definitions when explicit configuration is needed
|
||||
# please note that last definitions always *replace* previous ones
|
||||
|
||||
Redis:
|
||||
# you can also use \RedisArray, \RedisCluster or \Predis\Client classes
|
||||
class: Redis
|
||||
calls:
|
||||
- connect:
|
||||
- '%env(REDIS_HOST)%'
|
||||
- '%env(int:REDIS_PORT)%'
|
||||
|
||||
# uncomment the following if your Redis server requires a password
|
||||
# - auth:
|
||||
# - '%env(REDIS_PASSWORD)%'
|
||||
|
||||
logger:
|
||||
class: Symfony\Component\HttpKernel\Log\Logger
|
||||
arguments: [ 'info', 'php://stdout' ]
|
11
backend/public/index.php
Normal file
11
backend/public/index.php
Normal file
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Kernel;
|
||||
|
||||
require_once dirname(__DIR__).'/vendor/autoload_runtime.php';
|
||||
|
||||
return static function (array $context) {
|
||||
return new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']);
|
||||
};
|
0
backend/src/Controller/.gitignore
vendored
Normal file
0
backend/src/Controller/.gitignore
vendored
Normal file
19
backend/src/Controller/DencodeController.php
Normal file
19
backend/src/Controller/DencodeController.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
|
||||
class DencodeController extends AbstractController
|
||||
{
|
||||
#[Route('/api/dencode', name: 'api_dencode', methods: "GET")]
|
||||
public function index(): Response
|
||||
{
|
||||
return $this->json([
|
||||
'message' => 'Welcome to your new controller!',
|
||||
'path' => 'src/Controller/DencodeController.php',
|
||||
]);
|
||||
}
|
||||
}
|
20
backend/src/Controller/ErrorController.php
Normal file
20
backend/src/Controller/ErrorController.php
Normal file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* Exceptions will end here.
|
||||
*/
|
||||
class ErrorController extends AbstractController
|
||||
{
|
||||
public function show(\Throwable $exception): Response
|
||||
{
|
||||
//return $this->json(['fatal' => sprintf('%s: %s [%s:%d]', $exception::class, $exception->getMessage(), basename($exception->getFile()), $exception->getLine())]);
|
||||
return $this->json(['fatal' => sprintf('%s: %s', $exception::class, $exception->getMessage())]);
|
||||
}
|
||||
}
|
55
backend/src/Controller/PregController.php
Normal file
55
backend/src/Controller/PregController.php
Normal file
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Entity\PregDTO;
|
||||
use App\Service\Preg;
|
||||
use Exception;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\Serializer\SerializerInterface;
|
||||
|
||||
/**
|
||||
* PREGs controller.
|
||||
*/
|
||||
class PregController extends AbstractController
|
||||
{
|
||||
private const STORE_KEY = 'xrg.es2022:%s';
|
||||
private const STORE_TTL = 60*60*24*7;
|
||||
|
||||
#[Route('/api/preg', name: 'api_preg_post', methods: ["POST"])]
|
||||
public function index(Request $request, SerializerInterface $serializer, \Redis $redis): Response
|
||||
{
|
||||
$dto = $serializer->deserialize($request->getContent(), PregDTO::class, 'json');
|
||||
|
||||
if ($dto->pattern === '') {
|
||||
throw new Exception('Empty regular expression');
|
||||
}
|
||||
|
||||
$preg = new Preg($dto);
|
||||
$result = $preg->exec();
|
||||
$key = $preg->getHash();
|
||||
|
||||
$redis->setex(sprintf(self::STORE_KEY, $key), self::STORE_TTL, $serializer->serialize($dto, 'json'));
|
||||
|
||||
return $this->json(array_merge((array)$result, ['hash' => $key]));
|
||||
}
|
||||
|
||||
#[Route('/api/preg', name: 'api_preg_hash', methods: ["GET"])]
|
||||
public function hash(Request $request, \Redis $redis): Response
|
||||
{
|
||||
$json = $redis->get(sprintf(self::STORE_KEY, $request->query->get('hash')));
|
||||
|
||||
if ($json === false) {
|
||||
throw new NotFoundHttpException('Key not found.');
|
||||
}
|
||||
|
||||
return JsonResponse::fromJsonString($json);
|
||||
}
|
||||
}
|
29
backend/src/Entity/PregDTO.php
Normal file
29
backend/src/Entity/PregDTO.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Resource\Functions;
|
||||
|
||||
/**
|
||||
* Basic data comming from form.
|
||||
*/
|
||||
class PregDTO
|
||||
{
|
||||
public function __construct(
|
||||
public readonly Functions $method,
|
||||
public readonly string $pattern,
|
||||
public readonly string $replacement,
|
||||
public readonly string $subject,
|
||||
public readonly bool $PREG_OFFSET_CAPTURE,
|
||||
public readonly bool $PREG_SET_ORDER,
|
||||
public readonly bool $PREG_SPLIT_DELIM_CAPTURE,
|
||||
public readonly bool $PREG_SPLIT_NO_EMPTY,
|
||||
public readonly bool $PREG_SPLIT_OFFSET_CAPTURE,
|
||||
public readonly bool $PREG_UNMATCHED_AS_NULL,
|
||||
public readonly ?int $offset,
|
||||
public readonly ?int $limit,
|
||||
public readonly ?string $delimiter
|
||||
) {}
|
||||
}
|
24
backend/src/Entity/PregResponse.php
Normal file
24
backend/src/Entity/PregResponse.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Data returned to web interface. Will be converted to JSON most likely.
|
||||
*/
|
||||
class PregResponse
|
||||
{
|
||||
public function __construct(
|
||||
public readonly mixed $dump,
|
||||
public readonly string $code,
|
||||
public readonly ?string $returnType,
|
||||
public readonly ?string $returnValue,
|
||||
) {
|
||||
if (preg_last_error() !== PREG_NO_ERROR) {
|
||||
throw new Exception('preg_last_error() = '. preg_last_error_msg());
|
||||
}
|
||||
}
|
||||
}
|
13
backend/src/Kernel.php
Normal file
13
backend/src/Kernel.php
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
|
||||
use Symfony\Component\HttpKernel\Kernel as BaseKernel;
|
||||
|
||||
class Kernel extends BaseKernel
|
||||
{
|
||||
use MicroKernelTrait;
|
||||
}
|
17
backend/src/Resource/Flags.php
Normal file
17
backend/src/Resource/Flags.php
Normal file
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Resource;
|
||||
|
||||
/**
|
||||
* Implemented PREG flags.
|
||||
*/
|
||||
enum Flags {
|
||||
case PREG_OFFSET_CAPTURE;
|
||||
case PREG_SET_ORDER;
|
||||
case PREG_SPLIT_DELIM_CAPTURE;
|
||||
case PREG_SPLIT_NO_EMPTY;
|
||||
case PREG_SPLIT_OFFSET_CAPTURE;
|
||||
case PREG_UNMATCHED_AS_NULL;
|
||||
}
|
16
backend/src/Resource/Functions.php
Normal file
16
backend/src/Resource/Functions.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Resource;
|
||||
|
||||
/**
|
||||
* Implemented PREG functions.
|
||||
*/
|
||||
enum Functions: string {
|
||||
case Match = 'preg_match';
|
||||
case MatchAll = 'preg_match_all';
|
||||
case Split = 'preg_split';
|
||||
case Replace = 'preg_replace';
|
||||
case Quote = 'preg_quote';
|
||||
}
|
98
backend/src/Service/Preg.php
Normal file
98
backend/src/Service/Preg.php
Normal file
@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
use App\Entity\PregResponse;
|
||||
use App\Entity\PregDTO;
|
||||
|
||||
class Preg
|
||||
{
|
||||
private string $hash;
|
||||
|
||||
public function __construct(private readonly PregDTO $dto)
|
||||
{
|
||||
$this->hash = base_convert(substr(hash('sha256', serialize($this->dto)), 0, 10), 16, 36);
|
||||
}
|
||||
|
||||
public function exec(): PregResponse
|
||||
{
|
||||
//if ($this->dto->pattern === '') {
|
||||
// return new PregResponse();
|
||||
//}
|
||||
|
||||
$cn = 'App\Service\Preg'.$this->dto->method->name;
|
||||
return (new $cn($this->dto))->exec();
|
||||
}
|
||||
|
||||
protected function exec_preg_match(): array
|
||||
{
|
||||
$ret = @preg_match($this->dto->pattern, $this->dto->subject, $matches, $this->flags, $this->dto->offset);
|
||||
|
||||
$error = $this->last_error();
|
||||
|
||||
return array_merge($this->result($ret), [
|
||||
"dump" => $error?: $matches,
|
||||
"error" => (bool)$error,
|
||||
"code" => $this->syntax()
|
||||
]);
|
||||
}
|
||||
|
||||
protected function exec_preg_match_all(): array
|
||||
{
|
||||
$ret = @preg_match_all($this->pattern, $this->subject, $matches, $this->flags, $this->offset);
|
||||
|
||||
$error = $this->last_error();
|
||||
|
||||
return array_merge($this->result($ret), [
|
||||
"dump" => $error?: $matches,
|
||||
"error" => (bool)$error,
|
||||
"code" => $this->syntax()
|
||||
]);
|
||||
}
|
||||
|
||||
protected function exec_preg_split()
|
||||
{
|
||||
$ret = @preg_split($this->pattern, $this->subject, $this->limit, $this->flags);
|
||||
|
||||
$error = $this->last_error();
|
||||
|
||||
return array_merge($this->result($ret), [
|
||||
"dump" => $error?: $ret,
|
||||
"error" => (bool)$error,
|
||||
"code" => $this->syntax()
|
||||
]);
|
||||
}
|
||||
|
||||
protected function exec_preg_replace()
|
||||
{
|
||||
$ret = @preg_replace($this->pattern, $this->replacement, $this->subject, $this->limit);
|
||||
|
||||
$error = $this->last_error();
|
||||
|
||||
return array_merge($this->result($ret), [
|
||||
"dump" => $error?: $ret,
|
||||
"error" => (bool)$error,
|
||||
"code" => $this->syntax()
|
||||
]);
|
||||
}
|
||||
|
||||
protected function exec_preg_quote()
|
||||
{
|
||||
$ret = @preg_quote($this->pattern, $this->delimeter);
|
||||
|
||||
$error = $this->last_error();
|
||||
|
||||
return array_merge($this->result($ret), [
|
||||
"dump" => $error?: $ret,
|
||||
"error" => (bool)$error,
|
||||
"code" => $this->syntax()
|
||||
]);
|
||||
}
|
||||
|
||||
public function getHash(): string
|
||||
{
|
||||
return $this->hash;
|
||||
}
|
||||
}
|
84
backend/src/Service/PregFunction.php
Normal file
84
backend/src/Service/PregFunction.php
Normal file
@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
use App\Entity\PregDTO;
|
||||
use App\Entity\PregResponse;
|
||||
use App\Resource\Flags;
|
||||
|
||||
/**
|
||||
* Base class for all Preg* methods.
|
||||
*/
|
||||
abstract class PregFunction
|
||||
{
|
||||
/**
|
||||
* Build on construction to pass the flags to each preg function.
|
||||
*/
|
||||
protected int $flags = 0;
|
||||
|
||||
/**
|
||||
* Flag list as string for future representation, based on $flags.
|
||||
*/
|
||||
protected array $flagsStr = [];
|
||||
|
||||
/**
|
||||
* Available flags for each function.
|
||||
* @var array<Flags>
|
||||
*/
|
||||
protected array $availableFlags = [];
|
||||
|
||||
abstract public function exec(): PregResponse;
|
||||
abstract protected function syntax(): string;
|
||||
|
||||
public function __construct(protected readonly PregDTO $dto)
|
||||
{
|
||||
foreach ($this->availableFlags as $flag) {
|
||||
if ($this->dto->{$flag->name}) {
|
||||
$this->flags |= \constant($flag->name);
|
||||
$this->flagsStr[] = $flag->name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function result(mixed $ret): array
|
||||
{
|
||||
$gettype = \gettype($ret);
|
||||
$return = [$gettype];
|
||||
|
||||
if (\in_array($gettype, ["boolean", "integer", "NULL"])) {
|
||||
$return[] = json_encode($ret);
|
||||
|
||||
} elseif ($gettype === 'string') {
|
||||
$return[] = "len(". mb_strlen($ret). ")";
|
||||
|
||||
} elseif ($gettype === 'array') {
|
||||
$return[] = "count(". count($ret). ")";
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can be used to remove some parts from the syntax() output.
|
||||
* @see syntax()
|
||||
*/
|
||||
protected function syntaxCleanup(string $code): string
|
||||
{
|
||||
return str_replace(
|
||||
['?>', '<?php ', ' ', '<code>', '</code>'],
|
||||
['', '', ' ', '', ''],
|
||||
highlight_string('<?php '. $code .' ?'.'>', true)
|
||||
);
|
||||
}
|
||||
|
||||
protected function cropString(string $str, int $len = 50, bool $quote = true, string $longText = '$subject'): string
|
||||
{
|
||||
if (mb_strlen($str, "utf-8") < $len) {
|
||||
return $quote? '"'. addslashes($str) .'"': $str;
|
||||
}
|
||||
return $longText;
|
||||
//return str_replace(["\r", "\n"], ['\r', '\n'], addslashes(mb_substr($str, 0, $len, "utf-8")). "...");
|
||||
}
|
||||
}
|
49
backend/src/Service/PregMatch.php
Normal file
49
backend/src/Service/PregMatch.php
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
use App\Entity\PregResponse;
|
||||
use App\Resource\Flags;
|
||||
|
||||
/**
|
||||
* Handler form preg_match() function.
|
||||
* @see preg_match()
|
||||
*/
|
||||
class PregMatch extends PregFunction
|
||||
{
|
||||
protected array $availableFlags = [Flags::PREG_OFFSET_CAPTURE, Flags::PREG_UNMATCHED_AS_NULL];
|
||||
|
||||
public function exec(): PregResponse
|
||||
{
|
||||
$ret = preg_match($this->dto->pattern, $this->dto->subject, $matches, $this->flags, $this->dto->offset ?? 0);
|
||||
|
||||
return new PregResponse(
|
||||
$matches,
|
||||
$this->syntax(),
|
||||
...$this->result($ret)
|
||||
);
|
||||
}
|
||||
|
||||
protected function syntax(): string
|
||||
{
|
||||
$method = $this->dto->method->value;
|
||||
$args = [$method .'("'. $this->dto->pattern.'"'];
|
||||
|
||||
$args[] = $this->cropString($this->dto->subject);
|
||||
$args[] = '$matches';
|
||||
|
||||
if (\count($this->flagsStr)) {
|
||||
$args[] = implode(' | ', $this->flagsStr);
|
||||
}
|
||||
|
||||
if (!\is_null($this->dto->offset)) {
|
||||
$args[] = (!\count($this->flagsStr)? 'null, ': ''). $this->dto->offset;
|
||||
}
|
||||
|
||||
$code = implode(", ", $args). ");";
|
||||
|
||||
return $this->syntaxCleanup($code);
|
||||
}
|
||||
}
|
28
backend/src/Service/PregMatchAll.php
Normal file
28
backend/src/Service/PregMatchAll.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
use App\Entity\PregResponse;
|
||||
use App\Resource\Flags;
|
||||
|
||||
/**
|
||||
* Handler form preg_match_all() function. This handler is very similar to PregMatch, same syntax.
|
||||
* @see preg_match_all()
|
||||
*/
|
||||
class PregMatchAll extends PregMatch
|
||||
{
|
||||
protected array $availableFlags = [Flags::PREG_SET_ORDER, Flags::PREG_OFFSET_CAPTURE, Flags::PREG_UNMATCHED_AS_NULL];
|
||||
|
||||
public function exec(): PregResponse
|
||||
{
|
||||
$ret = preg_match_all($this->dto->pattern, $this->dto->subject, $matches, $this->flags, $this->dto->offset ?? 0);
|
||||
|
||||
return new PregResponse(
|
||||
$matches,
|
||||
$this->syntax(),
|
||||
...$this->result($ret)
|
||||
);
|
||||
}
|
||||
}
|
38
backend/src/Service/PregQuote.php
Normal file
38
backend/src/Service/PregQuote.php
Normal file
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
use App\Entity\PregResponse;
|
||||
|
||||
/**
|
||||
* Handler form preg_quote() function.
|
||||
* @see preg_quote()
|
||||
*/
|
||||
class PregQuote extends PregFunction
|
||||
{
|
||||
public function exec(): PregResponse
|
||||
{
|
||||
$ret = preg_quote($this->dto->pattern, $this->dto->delimiter);
|
||||
|
||||
return new PregResponse(
|
||||
$ret,
|
||||
$this->syntax(),
|
||||
...$this->result($ret)
|
||||
);
|
||||
}
|
||||
|
||||
protected function syntax(): string
|
||||
{
|
||||
$args = [$this->dto->method->value .'("'. $this->dto->pattern.'"'];
|
||||
|
||||
if (!\is_null($this->dto->delimiter) && $this->dto->delimiter !== '') {
|
||||
$args[] = $this->cropString($this->dto->delimiter);
|
||||
}
|
||||
|
||||
$code = implode(", ", $args). ");";
|
||||
|
||||
return $this->syntaxCleanup($code);
|
||||
}
|
||||
}
|
41
backend/src/Service/PregReplace.php
Normal file
41
backend/src/Service/PregReplace.php
Normal file
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
use App\Entity\PregResponse;
|
||||
|
||||
/**
|
||||
* Handler form preg_replace() function.
|
||||
* @see preg_replace()
|
||||
*/
|
||||
class PregReplace extends PregFunction
|
||||
{
|
||||
public function exec(): PregResponse
|
||||
{
|
||||
$ret = preg_replace($this->dto->pattern, $this->dto->replacement, $this->dto->subject, $this->dto->limit ?? -1);
|
||||
|
||||
return new PregResponse(
|
||||
$ret,
|
||||
$this->syntax(),
|
||||
...$this->result($ret)
|
||||
);
|
||||
}
|
||||
|
||||
protected function syntax(): string
|
||||
{
|
||||
$args = [$this->dto->method->value .'("'. $this->dto->pattern.'"'];
|
||||
|
||||
$args[] = $this->cropString($this->dto->replacement, longText: '$replacement');
|
||||
$args[] = $this->cropString($this->dto->subject);
|
||||
|
||||
if (!\is_null($this->dto->limit)) {
|
||||
$args[] = $this->dto->limit;
|
||||
}
|
||||
|
||||
$code = implode(", ", $args). ");";
|
||||
|
||||
return $this->syntaxCleanup($code);
|
||||
}
|
||||
}
|
49
backend/src/Service/PregSplit.php
Normal file
49
backend/src/Service/PregSplit.php
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
use App\Entity\PregResponse;
|
||||
use App\Resource\Flags;
|
||||
|
||||
/**
|
||||
* Handler form preg_split() function.
|
||||
* @see preg_split()
|
||||
*/
|
||||
class PregSplit extends PregFunction
|
||||
{
|
||||
protected array $availableFlags = [Flags::PREG_SPLIT_NO_EMPTY, Flags::PREG_SPLIT_DELIM_CAPTURE, Flags::PREG_SPLIT_OFFSET_CAPTURE];
|
||||
|
||||
public function exec(): PregResponse
|
||||
{
|
||||
$ret = preg_split($this->dto->pattern, $this->dto->subject, $this->dto->limit ?? -1, $this->flags);
|
||||
|
||||
return new PregResponse(
|
||||
$ret,
|
||||
$this->syntax(),
|
||||
...$this->result($ret)
|
||||
);
|
||||
}
|
||||
|
||||
protected function syntax(): string
|
||||
{
|
||||
$method = $this->dto->method->value;
|
||||
$args = [$method .'("'. $this->dto->pattern.'"'];
|
||||
|
||||
$args[] = $this->cropString($this->dto->subject);
|
||||
|
||||
if (!\is_null($this->dto->limit)) {
|
||||
$args[] = $this->dto->limit;
|
||||
}
|
||||
|
||||
if (\count($this->flagsStr)) {
|
||||
$args[] = (\is_null($this->dto->limit)? 'null, ': ''). implode(' | ', $this->flagsStr);
|
||||
}
|
||||
|
||||
|
||||
$code = implode(", ", $args). ");";
|
||||
|
||||
return $this->syntaxCleanup($code);
|
||||
}
|
||||
}
|
193
backend/symfony.lock
Normal file
193
backend/symfony.lock
Normal file
@ -0,0 +1,193 @@
|
||||
{
|
||||
"doctrine/annotations": {
|
||||
"version": "1.13",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "master",
|
||||
"version": "1.0",
|
||||
"ref": "a2759dd6123694c8d901d0ec80006e044c2e6457"
|
||||
},
|
||||
"files": [
|
||||
"config/routes/annotations.yaml"
|
||||
]
|
||||
},
|
||||
"doctrine/inflector": {
|
||||
"version": "2.0.4"
|
||||
},
|
||||
"doctrine/lexer": {
|
||||
"version": "1.2.1"
|
||||
},
|
||||
"nikic/php-parser": {
|
||||
"version": "v4.13.2"
|
||||
},
|
||||
"phpdocumentor/reflection-common": {
|
||||
"version": "2.2.0"
|
||||
},
|
||||
"phpdocumentor/reflection-docblock": {
|
||||
"version": "5.3.0"
|
||||
},
|
||||
"phpdocumentor/type-resolver": {
|
||||
"version": "1.5.1"
|
||||
},
|
||||
"phpstan/phpdoc-parser": {
|
||||
"version": "1.2.0"
|
||||
},
|
||||
"psr/cache": {
|
||||
"version": "3.0.0"
|
||||
},
|
||||
"psr/container": {
|
||||
"version": "2.0.2"
|
||||
},
|
||||
"psr/event-dispatcher": {
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"psr/log": {
|
||||
"version": "3.0.0"
|
||||
},
|
||||
"symfony/cache": {
|
||||
"version": "v6.0.1"
|
||||
},
|
||||
"symfony/cache-contracts": {
|
||||
"version": "v3.0.0"
|
||||
},
|
||||
"symfony/config": {
|
||||
"version": "v6.0.0"
|
||||
},
|
||||
"symfony/console": {
|
||||
"version": "6.0",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "master",
|
||||
"version": "5.3",
|
||||
"ref": "da0c8be8157600ad34f10ff0c9cc91232522e047"
|
||||
},
|
||||
"files": [
|
||||
"bin/console"
|
||||
]
|
||||
},
|
||||
"symfony/dependency-injection": {
|
||||
"version": "v6.0.1"
|
||||
},
|
||||
"symfony/deprecation-contracts": {
|
||||
"version": "v3.0.0"
|
||||
},
|
||||
"symfony/dotenv": {
|
||||
"version": "v6.0.1"
|
||||
},
|
||||
"symfony/error-handler": {
|
||||
"version": "v6.0.1"
|
||||
},
|
||||
"symfony/event-dispatcher": {
|
||||
"version": "v6.0.1"
|
||||
},
|
||||
"symfony/event-dispatcher-contracts": {
|
||||
"version": "v3.0.0"
|
||||
},
|
||||
"symfony/filesystem": {
|
||||
"version": "v6.0.0"
|
||||
},
|
||||
"symfony/finder": {
|
||||
"version": "v6.0.0"
|
||||
},
|
||||
"symfony/flex": {
|
||||
"version": "2.0",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "master",
|
||||
"version": "1.0",
|
||||
"ref": "c0eeb50665f0f77226616b6038a9b06c03752d8e"
|
||||
},
|
||||
"files": [
|
||||
".env"
|
||||
]
|
||||
},
|
||||
"symfony/framework-bundle": {
|
||||
"version": "6.0",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "master",
|
||||
"version": "5.4",
|
||||
"ref": "d4131812e20853626928e73d3effef44014944c0"
|
||||
},
|
||||
"files": [
|
||||
"config/packages/cache.yaml",
|
||||
"config/packages/framework.yaml",
|
||||
"config/preload.php",
|
||||
"config/routes/framework.yaml",
|
||||
"config/services.yaml",
|
||||
"public/index.php",
|
||||
"src/Controller/.gitignore",
|
||||
"src/Kernel.php"
|
||||
]
|
||||
},
|
||||
"symfony/http-foundation": {
|
||||
"version": "v6.0.1"
|
||||
},
|
||||
"symfony/http-kernel": {
|
||||
"version": "v6.0.1"
|
||||
},
|
||||
"symfony/maker-bundle": {
|
||||
"version": "1.36",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "master",
|
||||
"version": "1.0",
|
||||
"ref": "fadbfe33303a76e25cb63401050439aa9b1a9c7f"
|
||||
}
|
||||
},
|
||||
"symfony/polyfill-intl-grapheme": {
|
||||
"version": "v1.23.1"
|
||||
},
|
||||
"symfony/polyfill-intl-normalizer": {
|
||||
"version": "v1.23.0"
|
||||
},
|
||||
"symfony/polyfill-mbstring": {
|
||||
"version": "v1.23.1"
|
||||
},
|
||||
"symfony/polyfill-php81": {
|
||||
"version": "v1.23.0"
|
||||
},
|
||||
"symfony/property-access": {
|
||||
"version": "v6.0.0"
|
||||
},
|
||||
"symfony/property-info": {
|
||||
"version": "v6.0.0"
|
||||
},
|
||||
"symfony/routing": {
|
||||
"version": "6.0",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "master",
|
||||
"version": "6.0",
|
||||
"ref": "ab9ad892b7bba7ac584f6dc2ccdb659d358c63c5"
|
||||
},
|
||||
"files": [
|
||||
"config/packages/routing.yaml",
|
||||
"config/routes.yaml"
|
||||
]
|
||||
},
|
||||
"symfony/runtime": {
|
||||
"version": "v6.0.0"
|
||||
},
|
||||
"symfony/serializer": {
|
||||
"version": "v6.0.1"
|
||||
},
|
||||
"symfony/service-contracts": {
|
||||
"version": "v3.0.0"
|
||||
},
|
||||
"symfony/string": {
|
||||
"version": "v6.0.1"
|
||||
},
|
||||
"symfony/var-dumper": {
|
||||
"version": "v6.0.1"
|
||||
},
|
||||
"symfony/var-exporter": {
|
||||
"version": "v6.0.0"
|
||||
},
|
||||
"symfony/yaml": {
|
||||
"version": "v6.0.1"
|
||||
},
|
||||
"webmozart/assert": {
|
||||
"version": "1.10.0"
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user