frontend and backend

This commit is contained in:
2022-01-09 12:08:42 +01:00
parent cbff7c5d22
commit dde65761e5
75 changed files with 37830 additions and 19 deletions

0
backend/src/Controller/.gitignore vendored Normal file
View File

View 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',
]);
}
}

View 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())]);
}
}

View 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);
}
}

View 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
) {}
}

View 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
View 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;
}

View 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;
}

View 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';
}

View 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;
}
}

View 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(
['?&gt;', '&lt;?php&nbsp;', '&nbsp;', '<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")). "...");
}
}

View 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);
}
}

View 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)
);
}
}

View 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);
}
}

View 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);
}
}

View 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);
}
}