frontend and backend
This commit is contained in:
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);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user