@ -0,0 +1,14 @@
@@ -0,0 +1,14 @@
|
||||
kind: pipeline |
||||
type: docker |
||||
name: default |
||||
|
||||
steps: |
||||
- name: backend |
||||
image: composer |
||||
commands: |
||||
- cd backend && composer install |
||||
|
||||
- name: frontend |
||||
image: node |
||||
commands: |
||||
- cd frontend && npm install |
@ -0,0 +1,22 @@
@@ -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 ### |
@ -0,0 +1,10 @@
@@ -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 ### |
@ -0,0 +1,17 @@
@@ -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); |
||||
}; |
@ -0,0 +1,77 @@
@@ -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.*" |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,6 @@
@@ -0,0 +1,6 @@
|
||||
<?php |
||||
|
||||
return [ |
||||
Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true], |
||||
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true], |
||||
]; |
@ -0,0 +1,19 @@
@@ -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 |
@ -0,0 +1,26 @@
@@ -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 |
@ -0,0 +1,12 @@
@@ -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 |
@ -0,0 +1,5 @@
@@ -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'; |
||||
} |
@ -0,0 +1,7 @@
@@ -0,0 +1,7 @@
|
||||
controllers: |
||||
resource: ../src/Controller/ |
||||
type: annotation |
||||
|
||||
kernel: |
||||
resource: ../src/Kernel.php |
||||
type: annotation |
@ -0,0 +1,7 @@
@@ -0,0 +1,7 @@
|
||||
controllers: |
||||
resource: ../../src/Controller/ |
||||
type: annotation |
||||
|
||||
kernel: |
||||
resource: ../../src/Kernel.php |
||||
type: annotation |
@ -0,0 +1,4 @@
@@ -0,0 +1,4 @@
|
||||
when@dev: |
||||
_errors: |
||||
resource: '@FrameworkBundle/Resources/config/routing/errors.xml' |
||||
prefix: /_error |
@ -0,0 +1,41 @@
@@ -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' ] |
@ -0,0 +1,11 @@
@@ -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,0 +1,19 @@
@@ -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', |
||||
]); |
||||
} |
||||
} |
@ -0,0 +1,20 @@
@@ -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())]); |
||||
} |
||||
} |
@ -0,0 +1,55 @@
@@ -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); |
||||
} |
||||
} |
@ -0,0 +1,29 @@
@@ -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 |
||||
) {} |
||||
} |
@ -0,0 +1,24 @@
@@ -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()); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,13 @@
@@ -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; |
||||
} |
@ -0,0 +1,17 @@
@@ -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; |
||||
} |
@ -0,0 +1,16 @@
@@ -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'; |
||||
} |
@ -0,0 +1,98 @@
@@ -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; |
||||
} |
||||
} |
@ -0,0 +1,84 @@
@@ -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")). "..."); |
||||
} |
||||
} |
@ -0,0 +1,49 @@
@@ -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); |
||||
} |
||||
} |
@ -0,0 +1,28 @@
@@ -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) |
||||
); |
||||
} |
||||
} |
@ -0,0 +1,38 @@
@@ -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); |
||||
} |
||||
} |
@ -0,0 +1,41 @@
@@ -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); |
||||
} |
||||
} |
@ -0,0 +1,49 @@
@@ -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); |
||||
} |
||||
} |
@ -0,0 +1,193 @@
@@ -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" |
||||
} |
||||
} |
@ -1,5 +1,5 @@
@@ -1,5 +1,5 @@
|
||||
``` |
||||
docker build -t php_symfony --progress=plain . |
||||
docker run --rm --name phptest php_symfony |
||||
docker run --rm --name phptest -v $(pwd):/var/www/html --user 1000:1000 php_symfony |
||||
docker exec -it phptest bash |
||||
``` |
@ -1,8 +1,31 @@
@@ -1,8 +1,31 @@
|
||||
version: '3.8' |
||||
|
||||
services: |
||||
php: |
||||
build: . |
||||
restart: unless-stopped |
||||
volumes: |
||||
- ..:/var/www/html |
||||
xrges-nginx: |
||||
image: nginx:latest |
||||
restart: unless-stopped |
||||
ports: |
||||
- "8086:80" |
||||
volumes: |
||||
- ../frontend/build:/var/www/frontend |
||||
- ./zz-default-nginx.conf:/etc/nginx/conf.d/default.conf |
||||
links: |
||||
- xrges-php |
||||
|
||||
xrges-php: |
||||
build: . |
||||
restart: unless-stopped |
||||
volumes: |
||||
- ../backend:/var/www/backend |
||||
|
||||
xrges-redis: |
||||
image: redis:latest |
||||
restart: unless-stopped |
||||
volumes: |
||||
- ../home/redis:/data |
||||
- ./redis.conf:/usr/local/etc/redis/redis.conf |
||||
command: [ "redis-server", "/usr/local/etc/redis/redis.conf" ] |
||||
|
||||
networks: |
||||
default: |
||||
name: xrges-network |
||||
|
@ -0,0 +1,9 @@
@@ -0,0 +1,9 @@
|
||||
; cat /usr/local/etc/php-fpm.d/zz-docker.conf |
||||
[global] |
||||
daemonize = no |
||||
|
||||
[www] |
||||
listen = 9000 |
||||
|
||||
; /dev/stdout -> /proc/self/fd/1 |
||||
access.log = /proc/self/fd/1 |
@ -0,0 +1,19 @@
@@ -0,0 +1,19 @@
|
||||
|
||||
limit_req_zone $binary_remote_addr zone=rlimit:10m rate=10r/s; |
||||
|
||||
server { |
||||
access_log /proc/self/fd/1; |
||||
error_log /proc/self/fd/2; |
||||
|
||||
index index.html index.php; |
||||
root /var/www/frontend; |
||||
|
||||
location /api { |
||||
include fastcgi_params; |
||||
|
||||
fastcgi_pass xrges-php:9000; |
||||
fastcgi_param SCRIPT_FILENAME /var/www/backend/public/index.php; |
||||
|
||||
limit_req zone=rlimit burst=5 nodelay; |
||||
} |
||||
} |
@ -0,0 +1,23 @@
@@ -0,0 +1,23 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. |
||||
|
||||
# dependencies |
||||
/node_modules |
||||
/.pnp |
||||
.pnp.js |
||||
|
||||
# testing |
||||
/coverage |
||||
|
||||
# production |
||||
/build |
||||
|
||||
# misc |
||||
.DS_Store |
||||
.env.local |
||||
.env.development.local |
||||
.env.test.local |
||||
.env.production.local |
||||
|
||||
npm-debug.log* |
||||
yarn-debug.log* |
||||
yarn-error.log* |
@ -0,0 +1,70 @@
@@ -0,0 +1,70 @@
|
||||
# Getting Started with Create React App |
||||
|
||||
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). |
||||
|
||||
## Available Scripts |
||||
|
||||
In the project directory, you can run: |
||||
|
||||
### `npm start` |
||||
|
||||
Runs the app in the development mode.\ |
||||
Open [http://localhost:3000](http://localhost:3000) to view it in your browser. |
||||
|
||||
The page will reload when you make changes.\ |
||||
You may also see any lint errors in the console. |
||||
|
||||
### `npm test` |
||||
|
||||
Launches the test runner in the interactive watch mode.\ |
||||
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. |
||||
|
||||
### `npm run build` |
||||
|
||||
Builds the app for production to the `build` folder.\ |
||||
It correctly bundles React in production mode and optimizes the build for the best performance. |
||||
|
||||
The build is minified and the filenames include the hashes.\ |
||||
Your app is ready to be deployed! |
||||
|
||||
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. |
||||
|
||||
### `npm run eject` |
||||
|
||||
**Note: this is a one-way operation. Once you `eject`, you can't go back!** |
||||
|
||||
If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. |
||||
|
||||
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own. |
||||
|
||||
You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it. |
||||
|
||||
## Learn More |
||||
|
||||
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). |
||||
|
||||
To learn React, check out the [React documentation](https://reactjs.org/). |
||||
|
||||
### Code Splitting |
||||
|
||||
This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting) |
||||
|
||||
### Analyzing the Bundle Size |
||||
|
||||
This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size) |
||||
|
||||
### Making a Progressive Web App |
||||
|
||||
This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app) |
||||
|
||||
### Advanced Configuration |
||||
|
||||
This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration) |
||||
|
||||
### Deployment |
||||
|
||||
This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment) |
||||
|
||||
### `npm run build` fails to minify |
||||
|
||||
This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify) |
@ -0,0 +1,45 @@
@@ -0,0 +1,45 @@
|
||||
{ |
||||
"name": "frontend", |
||||
"version": "0.1.0", |
||||
"private": true, |
||||
"dependencies": { |
||||
"@testing-library/jest-dom": "^5.16.1", |
||||
"@testing-library/react": "^12.1.2", |
||||
"@testing-library/user-event": "^13.5.0", |
||||
"axiom": "^0.1.6", |
||||
"axios": "^0.24.0", |
||||
"bootstrap": "^5.1.3", |
||||
"react": "^17.0.2", |
||||
"react-bootstrap": "^2.0.4", |
||||
"react-copy-to-clipboard": "^5.0.4", |
||||
"react-dom": "^17.0.2", |
||||
"react-icons": "^4.3.1", |
||||
"react-scripts": "5.0.0", |
||||
"sass": "^1.45.1", |
||||
"web-vitals": "^2.1.2" |
||||
}, |
||||
"scripts": { |
||||
"start": "react-scripts start", |
||||
"build": "react-scripts build", |
||||
"test": "react-scripts test", |
||||
"eject": "react-scripts eject" |
||||
}, |
||||
"eslintConfig": { |
||||
"extends": [ |
||||
"react-app", |
||||
"react-app/jest" |
||||
] |
||||
}, |
||||
"browserslist": { |
||||
"production": [ |
||||
">0.2%", |
||||
"not dead", |
||||
"not op_mini all" |
||||
], |
||||
"development": [ |
||||
"last 1 chrome version", |
||||
"last 1 firefox version", |
||||
"last 1 safari version" |
||||
] |
||||
} |
||||
} |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 4.7 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 3.2 KiB |
@ -0,0 +1,9 @@
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<browserconfig> |
||||
<msapplication> |
||||
<tile> |
||||
<square150x150logo src="/img/icons/mstile-150x150.png"/> |
||||
<TileColor>#5755d9</TileColor> |
||||
</tile> |
||||
</msapplication> |
||||
</browserconfig> |
After Width: | Height: | Size: 616 B |
After Width: | Height: | Size: 945 B |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 3.6 KiB |