first commit
This commit is contained in:
5
.gitignore
vendored
Executable file
5
.gitignore
vendored
Executable file
@@ -0,0 +1,5 @@
|
|||||||
|
# Arquivo de variáveis de ambiente
|
||||||
|
.env
|
||||||
|
|
||||||
|
# Pasta de Logs
|
||||||
|
app/Storage/logs
|
||||||
42
app/Common/Config/Config.php
Executable file
42
app/Common/Config/Config.php
Executable file
@@ -0,0 +1,42 @@
|
|||||||
|
<?php
|
||||||
|
namespace Zampet\Config;
|
||||||
|
|
||||||
|
use Dotenv\Dotenv;
|
||||||
|
|
||||||
|
class Config {
|
||||||
|
private $config = [];
|
||||||
|
private static $instance = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Método construtor
|
||||||
|
*/
|
||||||
|
public function __construct() {
|
||||||
|
// Carrega apenas para $_ENV, não para $_SERVER
|
||||||
|
$dotEnv = Dotenv::createUnsafeImmutable(
|
||||||
|
paths: realpath(path: __DIR__ . "/../../../"),
|
||||||
|
names: '.env'
|
||||||
|
);
|
||||||
|
$dotEnv->safeLoad(); // não dá erro se .env não existir
|
||||||
|
|
||||||
|
// Carrega as configurações
|
||||||
|
$this->config = require_once realpath(path: __DIR__ . "/Consts.php");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retorna a instância única da classe
|
||||||
|
*/
|
||||||
|
public static function getInstance() {
|
||||||
|
if (self::$instance === null) {
|
||||||
|
self::$instance = new Config();
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retorna uma configuração
|
||||||
|
*/
|
||||||
|
public function get(string $key) {
|
||||||
|
return $this->config[$key] ?? null;
|
||||||
|
}
|
||||||
|
}
|
||||||
21
app/Common/Config/Consts.php
Executable file
21
app/Common/Config/Consts.php
Executable file
@@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
return [
|
||||||
|
'SYSTEM_URL' => (string) getenv(name: 'SYSTEM_URL'),
|
||||||
|
'SYSTEM_VERSION' => (string) getenv(name: 'SYSTEM_VERSION'),
|
||||||
|
'SYSTEM_TIMEZONE' => (string) getenv(name: 'SYSTEM_TIMEZONE'),
|
||||||
|
'SYSTEM_ENVIRONMENT_ID' => (int) getenv(name: 'SYSTEM_ENVIRONMENT_ID'),
|
||||||
|
'SYSTEM_FRONTEND_URL' => (string) getenv(name: 'SYSTEM_FRONTEND_URL'),
|
||||||
|
|
||||||
|
'JWT_ALGO' => getenv(name: 'JWT_ALGO'),
|
||||||
|
'JWT_ISSUER' => getenv(name: 'JWT_ISSUER'),
|
||||||
|
'JWT_SECRET' => getenv(name: 'JWT_SECRET'),
|
||||||
|
'JWT_EXPIRATION' => (int) getenv(name: 'JWT_EXPIRATION'),
|
||||||
|
|
||||||
|
'DEFAULT_DATABASE_HOST' => getenv(name: 'DEFAULT_DATABASE_HOST'),
|
||||||
|
'DEFAULT_DATABASE_PORT' => getenv(name: 'DEFAULT_DATABASE_PORT'),
|
||||||
|
'DEFAULT_DATABASE_DRIVER' => getenv(name: 'DEFAULT_DATABASE_DRIVER'),
|
||||||
|
'DEFAULT_DATABASE_SCHEMA' => getenv(name: 'DEFAULT_DATABASE_SCHEMA'),
|
||||||
|
'DEFAULT_DATABASE_CHARSET' => getenv(name: 'DEFAULT_DATABASE_CHARSET'),
|
||||||
|
'DEFAULT_DATABASE_USERNAME' => getenv(name: 'DEFAULT_DATABASE_USERNAME'),
|
||||||
|
'DEFAULT_DATABASE_PASSWORD' => getenv(name: 'DEFAULT_DATABASE_PASSWORD'),
|
||||||
|
];
|
||||||
174
app/Common/Helpers/DataValidator.php
Executable file
174
app/Common/Helpers/DataValidator.php
Executable file
@@ -0,0 +1,174 @@
|
|||||||
|
<?php
|
||||||
|
namespace Zampet\Helpers;
|
||||||
|
|
||||||
|
class DataValidator {
|
||||||
|
private array $inputs;
|
||||||
|
private array $errors = [];
|
||||||
|
|
||||||
|
public function __construct(array $inputs) {
|
||||||
|
$this->inputs = $inputs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Valida um campo usando uma regra específica.
|
||||||
|
*/
|
||||||
|
public function validate(string $field, callable|string $rule, string $message = '', array $additionalParams = []): static {
|
||||||
|
$value = $this->inputs[$field] ?? null;
|
||||||
|
|
||||||
|
// Se for string, monta callable usando a própria classe
|
||||||
|
if (is_string(value: $rule) && method_exists(object_or_class: self::class, method: $rule)) {
|
||||||
|
$rule = [self::class, $rule];
|
||||||
|
}
|
||||||
|
|
||||||
|
$params = array_merge([$value], $additionalParams);
|
||||||
|
|
||||||
|
$result = call_user_func_array(callback: $rule, args: $params);
|
||||||
|
|
||||||
|
// Caso seja validação de senha (retorna array)
|
||||||
|
if (is_array(value: $result) && isset($result['valid'])) {
|
||||||
|
if (!$result['valid']) {
|
||||||
|
$this->errors[$field] = [
|
||||||
|
'status' => 'is_invalid',
|
||||||
|
'message' => $result['errors']
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Para outras validações booleanas
|
||||||
|
elseif ($result === false) {
|
||||||
|
$this->errors[$field] = [
|
||||||
|
'status' => 'is_invalid',
|
||||||
|
'message' => $message
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getErrors(): array {
|
||||||
|
return $this->errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function passes(): bool {
|
||||||
|
return empty($this->errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------- Regras comuns ----------
|
||||||
|
|
||||||
|
public static function validateEmail(string $value): bool {
|
||||||
|
return (bool) filter_var(value: $value, filter: FILTER_VALIDATE_EMAIL);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function validatePhone(string $value, int $minLength = 10, int $maxLength = 13): bool {
|
||||||
|
$digits = preg_replace(pattern: '/\D+/', replacement: '', subject: $value);
|
||||||
|
$len = strlen(string: $digits);
|
||||||
|
return $len >= $minLength && $len <= $maxLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function validateCpf(string $value): bool {
|
||||||
|
$cpf = preg_replace(pattern: '/\D+/', replacement: '', subject: $value);
|
||||||
|
|
||||||
|
if (strlen(string: $cpf) !== 11 || preg_match(pattern: '/(\d)\1{10}/', subject: $cpf)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for ($t = 9; $t < 11; $t++) {
|
||||||
|
$d = 0;
|
||||||
|
for ($c = 0; $c < $t; $c++) {
|
||||||
|
$d += $cpf[$c] * (($t + 1) - $c);
|
||||||
|
}
|
||||||
|
$d = ((10 * $d) % 11) % 10;
|
||||||
|
if ($cpf[$c] != $d) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function validateCnpj(string $value): bool {
|
||||||
|
$cnpj = preg_replace(pattern: '/\D+/', replacement: '', subject: $value);
|
||||||
|
|
||||||
|
if (strlen(string: $cnpj) != 14) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preg_match(pattern: '/(\d)\1{13}/', subject: $cnpj)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$tamanho = 12;
|
||||||
|
$multiplicadores = [5,4,3,2,9,8,7,6,5,4,3,2];
|
||||||
|
|
||||||
|
for ($i = 0; $i < 2; $i++) {
|
||||||
|
$soma = 0;
|
||||||
|
for ($j = 0; $j < $tamanho; $j++) {
|
||||||
|
$soma += $cnpj[$j] * $multiplicadores[$j + ($i == 1 ? 1 : 0)];
|
||||||
|
}
|
||||||
|
$digito = ($soma % 11 < 2) ? 0 : 11 - ($soma % 11);
|
||||||
|
if ($cnpj[$tamanho] != $digito) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$tamanho++;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function notEmpty(mixed $value): bool {
|
||||||
|
if (is_array(value: $value)) {
|
||||||
|
return !empty(array_filter(array: $value, callback: fn($v) => $v !== null && $v !== ''));
|
||||||
|
}
|
||||||
|
return !empty(trim(string: (string) $value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function validatePassword(string $password, array $rules = []): array {
|
||||||
|
$errors = [];
|
||||||
|
|
||||||
|
$minLength = $rules['minLength'] ?? 8;
|
||||||
|
$maxLength = $rules['maxLength'] ?? 64;
|
||||||
|
$requireUpper = $rules['upper'] ?? true;
|
||||||
|
$requireLower = $rules['lower'] ?? true;
|
||||||
|
$requireDigit = $rules['digit'] ?? true;
|
||||||
|
$requireSymbol = $rules['symbol'] ?? true;
|
||||||
|
|
||||||
|
if (strlen(string: $password) < $minLength) {
|
||||||
|
$errors[] = "Senha deve ter no mínimo {$minLength} caracteres.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strlen(string: $password) > $maxLength) {
|
||||||
|
$errors[] = "Senha deve ter no máximo {$maxLength} caracteres.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($requireUpper && !preg_match(pattern: '/[A-Z]/', subject: $password)) {
|
||||||
|
$errors[] = "Senha deve conter pelo menos uma letra maiúscula.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($requireLower && !preg_match(pattern: '/[a-z]/', subject: $password)) {
|
||||||
|
$errors[] = "Senha deve conter pelo menos uma letra minúscula.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($requireDigit && !preg_match(pattern: '/\d/', subject: $password)) {
|
||||||
|
$errors[] = "Senha deve conter pelo menos um número.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($requireSymbol && !preg_match(pattern: '/[\W_]/', subject: $password)) {
|
||||||
|
$errors[] = "Senha deve conter pelo menos um símbolo.";
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'valid' => empty($errors),
|
||||||
|
'errors' => $errors
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function validateList(mixed $value, array $validOptions): bool {
|
||||||
|
if (is_array(value: $value)) {
|
||||||
|
foreach ($value as $item) {
|
||||||
|
if (!in_array(needle: $item, haystack: $validOptions, strict: true)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return in_array(needle: $value, haystack: $validOptions, strict: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
69
app/Common/Helpers/Sanitizer.php
Executable file
69
app/Common/Helpers/Sanitizer.php
Executable file
@@ -0,0 +1,69 @@
|
|||||||
|
<?php
|
||||||
|
namespace Zampet\Helpers;
|
||||||
|
|
||||||
|
use DateTime;
|
||||||
|
|
||||||
|
class Sanitizer {
|
||||||
|
public static function string(mixed $value): string|null {
|
||||||
|
if(is_null($value)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return trim(string: strip_tags(string: $value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function int(mixed $value): int {
|
||||||
|
return filter_var(value: $value, filter: FILTER_VALIDATE_INT) ?? 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function float(mixed $value): float {
|
||||||
|
return filter_var(value: $value, filter: FILTER_VALIDATE_FLOAT) ?? 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function email(string $value): string {
|
||||||
|
$value = trim(string: strtolower(string: $value));
|
||||||
|
return filter_var(value: $value, filter: FILTER_VALIDATE_EMAIL) ?: '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function phone(string $value): string {
|
||||||
|
// remove tudo que não for número
|
||||||
|
return preg_replace(pattern: '/\D+/', replacement: '', subject: $value) ?? '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function document(string $value): string {
|
||||||
|
// CPF, CNPJ, RG etc. só números
|
||||||
|
return preg_replace(pattern: '/\D+/', replacement: '', subject: $value) ?? '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function date(string $value, string $format = 'Y-m-d'): string {
|
||||||
|
$value = trim($value);
|
||||||
|
|
||||||
|
if ($value === '') {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$date = DateTime::createFromFormat('!'.$format, $value);
|
||||||
|
|
||||||
|
if (!$date) {
|
||||||
|
// tenta converter qualquer formato válido
|
||||||
|
$date = date_create($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $date ? $date->format($format) : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function datetime(string $value, string $format = 'Y-m-d H:i:s'): string {
|
||||||
|
$value = trim($value);
|
||||||
|
|
||||||
|
if ($value === '') {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$date = DateTime::createFromFormat('!'.$format, $value);
|
||||||
|
|
||||||
|
if (!$date) {
|
||||||
|
$date = date_create($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $date ? $date->format($format) : '';
|
||||||
|
}
|
||||||
|
}
|
||||||
8
app/Common/Services/DB.php
Executable file
8
app/Common/Services/DB.php
Executable file
@@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
namespace Zampet\Services;
|
||||||
|
|
||||||
|
use AxiumPHP\Core\Database;
|
||||||
|
|
||||||
|
class DB extends Database {
|
||||||
|
|
||||||
|
}
|
||||||
172
app/Common/Services/JWTService.php
Executable file
172
app/Common/Services/JWTService.php
Executable file
@@ -0,0 +1,172 @@
|
|||||||
|
<?php
|
||||||
|
namespace Zampet\Services;
|
||||||
|
|
||||||
|
use DateTime;
|
||||||
|
use Firebase\JWT\JWT;
|
||||||
|
use Firebase\JWT\Key;
|
||||||
|
use Zampet\Config\Config;
|
||||||
|
|
||||||
|
class JWTService {
|
||||||
|
private static string $issuer;
|
||||||
|
private static int $expiration;
|
||||||
|
private static string $algorithm;
|
||||||
|
private static string $secretKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inicializa as configurações para a geração de tokens JWT.
|
||||||
|
*
|
||||||
|
* Este método estático carrega as informações necessárias para a criação e validação de tokens
|
||||||
|
* JWT (JSON Web Tokens) a partir de uma classe de configuração (`Config`). Ele busca a chave secreta,
|
||||||
|
* o algoritmo de criptografia, o emissor e o tempo de expiração definidos para a aplicação
|
||||||
|
* e os armazena como propriedades estáticas da classe.
|
||||||
|
*
|
||||||
|
* Este método deve ser chamado antes de qualquer operação que envolva a geração ou
|
||||||
|
* verificação de tokens, garantindo que as configurações estejam prontas para uso.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function init(): void {
|
||||||
|
$Config = Config::getInstance();
|
||||||
|
self::$issuer = $Config->get(key: 'JWT_ISSUER');
|
||||||
|
self::$algorithm = $Config->get(key: 'JWT_ALGO');
|
||||||
|
self::$secretKey = $Config->get(key: 'JWT_SECRET');
|
||||||
|
self::$expiration = $Config->get(key: 'JWT_EXPIRATION');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gera um token JWT com base em um payload e nas configurações da aplicação.
|
||||||
|
*
|
||||||
|
* Este método cria um token JWT válido a partir de um array de dados fornecido (`$payload`),
|
||||||
|
* adicionando automaticamente metadados essenciais como a data de emissão, a data de expiração e o emissor.
|
||||||
|
*
|
||||||
|
* O processo detalhado inclui:
|
||||||
|
* 1. **Inicialização:** Chama o método `init()` para garantir que todas as configurações do JWT
|
||||||
|
* (chave secreta, algoritmo, etc.) estejam carregadas.
|
||||||
|
* 2. **Definição dos Timestamps:** Calcula o timestamp de emissão (`iat`) e o de expiração (`exp`)
|
||||||
|
* com base no tempo de expiração definido nas configurações.
|
||||||
|
* 3. **Montagem do Payload:** Adiciona as chaves `iat`, `exp` e `iss` (emissor) ao array `$payload`.
|
||||||
|
* 4. **Codificação do Token:** Utiliza a biblioteca `JWT::encode()` para codificar o payload
|
||||||
|
* com a chave secreta e o algoritmo definidos nas configurações.
|
||||||
|
*
|
||||||
|
* @param array $payload Um array associativo contendo os dados personalizados que serão incluídos no token.
|
||||||
|
* @return string O token JWT gerado como uma string.
|
||||||
|
*/
|
||||||
|
public static function generateToken(array $payload): string {
|
||||||
|
// Carrega as informações do arquivo de configuração
|
||||||
|
self::init();
|
||||||
|
|
||||||
|
// Define data de emissão e calcula a data de expiração do token
|
||||||
|
$expiration = self::$expiration;
|
||||||
|
$createdAt = (new DateTime())->getTimestamp();
|
||||||
|
$expireAt = (new DateTime())->modify(modifier: "+{$expiration} seconds")->getTimestamp();
|
||||||
|
|
||||||
|
// Adiciona data de criação ao payload
|
||||||
|
$payload['iat'] = $createdAt;
|
||||||
|
// Adiciona a expiração ao payload
|
||||||
|
$payload['exp'] = $expireAt;
|
||||||
|
// Adiciona o emissor ao payload
|
||||||
|
$payload['iss'] = self::$issuer;
|
||||||
|
|
||||||
|
return JWT::encode(payload: $payload, key: self::$secretKey, alg: self::$algorithm);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Valida um token JWT e retorna o objeto de payload decodificado.
|
||||||
|
*
|
||||||
|
* Este método estático decodifica e valida um token JWT fornecido, utilizando
|
||||||
|
* a chave secreta e o algoritmo de criptografia configurados na classe.
|
||||||
|
*
|
||||||
|
* #### Como funciona:
|
||||||
|
* 1. Recebe o token (`$token`) que precisa ser validado.
|
||||||
|
* 2. Usa a biblioteca `JWT::decode()` para decodificar o token.
|
||||||
|
* 3. Para a decodificação, ele cria uma nova instância de `Key`, passando
|
||||||
|
* a chave secreta (`self::$secretKey`) e o algoritmo (`self::$algorithm`).
|
||||||
|
* Isso garante que o token só será decodificado se tiver sido assinado com a chave e algoritmo corretos.
|
||||||
|
* 4. Se o token for válido e a assinatura corresponder, a função retorna o payload do token
|
||||||
|
* como um objeto.
|
||||||
|
* 5. Se o token for inválido (por exemplo, assinatura incorreta, expirado ou formato incorreto),
|
||||||
|
* a biblioteca JWT lançará uma exceção, que a função que chama este método deve capturar.
|
||||||
|
*
|
||||||
|
* @param string $token O token JWT a ser validado e decodificado.
|
||||||
|
* @return object O objeto de payload decodificado do token.
|
||||||
|
*/
|
||||||
|
public static function validateToken(string $token): object {
|
||||||
|
// Inicializa as configurações se ainda não estiverem definidas
|
||||||
|
if (!isset(self::$secretKey)) {
|
||||||
|
self::init();
|
||||||
|
}
|
||||||
|
|
||||||
|
return JWT::decode(jwt: $token, keyOrKeyArray: new Key(keyMaterial: self::$secretKey, algorithm: self::$algorithm));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decodifica um token JWT e retorna seu payload como um array associativo.
|
||||||
|
*
|
||||||
|
* Este método estático é responsável por decodificar e validar um token JWT. Antes de decodificar, ele garante que as configurações de chave secreta e algoritmo (`self::$secretKey` e `self::$algorithm`) já foram inicializadas chamando o método `init()` se necessário.
|
||||||
|
*
|
||||||
|
* #### Como Funciona:
|
||||||
|
* 1. **Inicialização:** Primeiro, verifica se a chave secreta (`self::$secretKey`) já foi carregada. Se não, ele chama `self::init()` para carregar as configurações de um arquivo externo.
|
||||||
|
* 2. **Decodificação:** Usa a biblioteca `Firebase\JWT\JWT::decode()` para decodificar o token fornecido. O método passa a chave secreta e o algoritmo para verificar a assinatura do token.
|
||||||
|
* 3. **Conversão:** O payload decodificado, que por padrão é um objeto, é convertido para uma string JSON e depois para um array associativo, o que facilita o acesso aos dados.
|
||||||
|
* 4. **Retorno:** Retorna o payload do token como um array associativo. Se o token for inválido (assinatura incorreta, expirado, etc.), a biblioteca JWT lançará uma exceção, que a função que chama este método deve capturar.
|
||||||
|
*
|
||||||
|
* @param string $token O token JWT a ser decodificado.
|
||||||
|
* @return array O payload do token decodificado como um array associativo.
|
||||||
|
*/
|
||||||
|
public static function decode(string $token): array {
|
||||||
|
// Inicializa as configurações se ainda não estiverem definidas
|
||||||
|
if (!isset(self::$secretKey)) {
|
||||||
|
self::init();
|
||||||
|
}
|
||||||
|
|
||||||
|
$decoded = JWT::decode(jwt: $token, keyOrKeyArray: new Key(keyMaterial: self::$secretKey, algorithm: self::$algorithm));
|
||||||
|
return json_decode(json: json_encode(value: $decoded), associative: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extrai o token de autenticação do tipo 'Bearer' do cabeçalho da requisição HTTP.
|
||||||
|
*
|
||||||
|
* Este método estático é um utilitário projetado para buscar um token JWT (JSON Web Token)
|
||||||
|
* que é enviado no cabeçalho `Authorization`. Ele é robusto o suficiente para verificar
|
||||||
|
* o token em diferentes superglobais do PHP, dependendo da configuração do servidor web.
|
||||||
|
*
|
||||||
|
* #### Fluxo de Busca:
|
||||||
|
* 1. Primeiro, tenta obter o cabeçalho `Authorization` diretamente da superglobal `$_SERVER`.
|
||||||
|
* 2. Em seguida, tenta a chave `HTTP_AUTHORIZATION`, que é um nome de variável comum em alguns ambientes.
|
||||||
|
* 3. Por último, usa a função `apache_request_headers()` para tentar obter o cabeçalho, caso as
|
||||||
|
* superglobais não estejam preenchidas. Isso garante compatibilidade com servidores Apache onde o cabeçalho pode não ser exposto.
|
||||||
|
* 4. Se o cabeçalho for encontrado, o valor é sanitizado (removendo espaços em branco) e a busca continua.
|
||||||
|
* 5. Se nenhum cabeçalho de autenticação for encontrado, o método retorna `null`.
|
||||||
|
*
|
||||||
|
* #### Extração do Token:
|
||||||
|
* - Depois de encontrar o cabeçalho, o método usa uma expressão regular (`/Bearer\s(\S+)/`)
|
||||||
|
* para verificar se o valor do cabeçalho está no formato `Bearer <token>`.
|
||||||
|
* - Se o padrão corresponder, ele extrai e retorna a string do token.
|
||||||
|
* - Se o padrão não corresponder, o método retorna `null`.
|
||||||
|
*
|
||||||
|
* @return string|null O token de autenticação do tipo 'Bearer' como uma string, ou `null`
|
||||||
|
* se o cabeçalho não for encontrado ou não estiver no formato esperado.
|
||||||
|
*/
|
||||||
|
public static function getBearerToken(): ?string {
|
||||||
|
if (!isset(self::$secretKey)) {
|
||||||
|
self::init();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tenta pegar o header de todas as fontes possíveis
|
||||||
|
$headers = $_SERVER['Authorization'] ?? $_SERVER['HTTP_AUTHORIZATION'] ?? $_SERVER['REDIRECT_HTTP_AUTHORIZATION'] ?? (function_exists(function: 'apache_request_headers') ? apache_request_headers() : []);
|
||||||
|
|
||||||
|
// Se veio do apache_request_headers, normaliza chaves
|
||||||
|
if (is_array(value: $headers)) {
|
||||||
|
$headers = array_change_key_case(array: $headers, case: CASE_LOWER);
|
||||||
|
$headers = $headers['authorization'] ?? '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$headers = trim(string: $headers);
|
||||||
|
|
||||||
|
if ($headers && preg_match(pattern: '/Bearer\s(\S+)/', subject: $headers, matches: $matches)) {
|
||||||
|
return $matches[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
0
app/Module/Auth/blank_file
Executable file
0
app/Module/Auth/blank_file
Executable file
158
app/Module/Auth/v0/Controllers/AuthController.php
Executable file
158
app/Module/Auth/v0/Controllers/AuthController.php
Executable file
@@ -0,0 +1,158 @@
|
|||||||
|
<?php
|
||||||
|
namespace Zampet\Module\Auth\v0\Controllers;
|
||||||
|
|
||||||
|
use Zampet\Helpers\Sanitizer;
|
||||||
|
use Zampet\Services\JWTService;
|
||||||
|
use AxiumPHP\Helpers\RequestHelper;
|
||||||
|
use Zampet\Module\Auth\v0\DTO\UsuarioDTO;
|
||||||
|
use Zampet\Module\Auth\v0\Services\AuthService;
|
||||||
|
|
||||||
|
class AuthController {
|
||||||
|
/**
|
||||||
|
* Lida com a requisição de registro (criação) de um novo usuário.
|
||||||
|
*
|
||||||
|
* Este método atua como um **controlador** para o endpoint de registro. Ele recebe os dados do formulário, realiza uma validação de presença básica nos campos obrigatórios, sanitiza os dados e delega a lógica de negócio e persistência para o `AuthService`. A resposta final é formatada e enviada como JSON.
|
||||||
|
*
|
||||||
|
* #### Fluxo de Operação:
|
||||||
|
* 1. **Coleta de Dados:** Obtém os dados do formulário (`fullName`, `email`, `password`) via requisição POST.
|
||||||
|
* 2. **Validação de Presença:** Itera sobre os campos obrigatórios (`fullName`, `email`, `password`). Se qualquer um estiver vazio, o método envia uma resposta JSON com o código **400 Bad Request** e encerra a execução.
|
||||||
|
* 3. **Sanitização e DTO:** Os dados são sanitizados (string, email) e encapsulados em um **`UsuarioDTO`**.
|
||||||
|
* 4. **Delegação ao Serviço:** O método `register` do `AuthService` é chamado com o DTO. O serviço é responsável por validações de complexidade, checagem de duplicidade, criptografia de senha e persistência no banco de dados.
|
||||||
|
* 5. **Envio da Resposta JSON:** O resultado do serviço (`serviceResult`) é formatado e enviado de volta ao cliente. O código de resposta HTTP e o status JSON são extraídos do array retornado pelo serviço.
|
||||||
|
*
|
||||||
|
* @return void Este método não retorna um valor, pois seu objetivo é enviar uma resposta JSON e encerrar a execução do script.
|
||||||
|
*/
|
||||||
|
public function register(): void {
|
||||||
|
// Receber dados do formulário de registro
|
||||||
|
$form = RequestHelper::getFilteredInput(form_type: INPUT_POST);
|
||||||
|
|
||||||
|
// Dados Obrigatórios
|
||||||
|
$required_fields = [
|
||||||
|
'fullName',
|
||||||
|
'email',
|
||||||
|
'cpf',
|
||||||
|
'birthDate',
|
||||||
|
'password'
|
||||||
|
];
|
||||||
|
|
||||||
|
// Verificar se todos os campos obrigatórios estão presentes
|
||||||
|
foreach ($required_fields as $field) {
|
||||||
|
if (empty($form[$field])) {
|
||||||
|
RequestHelper::sendJsonResponse(
|
||||||
|
response_code: 400,
|
||||||
|
status: 'error',
|
||||||
|
message: "Field '{$field}' is required"
|
||||||
|
);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Chama o serviço de autenticação para registrar o usuário
|
||||||
|
$serviceResult = (new AuthService())->register(usuarioDTO:
|
||||||
|
new UsuarioDTO(data: [
|
||||||
|
'nome_completo' => Sanitizer::string(value: $form['fullName']),
|
||||||
|
'email' => Sanitizer::email(value: $form['email']),
|
||||||
|
'senha' => Sanitizer::string(value: $form['password']),
|
||||||
|
'documentcpf' => Sanitizer::document(value: $form['cpf']),
|
||||||
|
'data_nascimento' => Sanitizer::string(value: $form['birthDate'])
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
// Enviar a resposta JSON
|
||||||
|
RequestHelper::sendJsonResponse(
|
||||||
|
response_code: $serviceResult['response_code'],
|
||||||
|
status: $serviceResult['status'],
|
||||||
|
message: $serviceResult['message'],
|
||||||
|
output: $serviceResult['output'] ?? []
|
||||||
|
);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lida com a requisição de login de um usuário via POST.
|
||||||
|
*
|
||||||
|
* Este método atua como um **controlador** para o endpoint de login. Ele processa as credenciais enviadas pelo usuário, realiza uma validação de presença básica e delega o processo de autenticação a um serviço. A resposta da autenticação é então formatada e enviada de volta ao cliente como um JSON.
|
||||||
|
*
|
||||||
|
* #### Fluxo de Operação:
|
||||||
|
* 1. **Coleta de Dados:** Obtém os dados do formulário (`email` e `password`) enviados via requisição POST.
|
||||||
|
* 2. **Validação de Presença:** Itera sobre os campos obrigatórios. Se qualquer 3campo estiver vazio, uma resposta JSON com o código **400 Bad Request** é enviada e a execução é encerrada.
|
||||||
|
* 3. **Delegação ao Serviço:** O método `login` do **`AuthService`** é chamado com o e-mail e a senha fornecidos. O serviço é responsável por toda a lógica de autenticação (busca, verificação de senha, revogação de tokens e geração de novo JWT).
|
||||||
|
* 4. **Envio da Resposta JSON:** O resultado do serviço (`serviceResult`) é formatado para uma resposta JSON padronizada usando **`RequestHelper::sendJsonResponse`**, incluindo o código HTTP, status, mensagem e os dados de saída (como o token JWT). A execução é encerrada.
|
||||||
|
*
|
||||||
|
* @return void Este método não retorna um valor, pois seu objetivo é enviar uma resposta JSON e encerrar a execução do script.
|
||||||
|
*/
|
||||||
|
public function login(): void {
|
||||||
|
// Receber dados do formulário de login
|
||||||
|
$form = RequestHelper::getFilteredInput(form_type: INPUT_POST);
|
||||||
|
|
||||||
|
// Dados Obrigatórios
|
||||||
|
$required_fields = [
|
||||||
|
'email',
|
||||||
|
'password'
|
||||||
|
];
|
||||||
|
|
||||||
|
// Verificar se todos os campos obrigatórios estão presentes
|
||||||
|
foreach ($required_fields as $field) {
|
||||||
|
if (empty($form[$field])) {
|
||||||
|
RequestHelper::sendJsonResponse(
|
||||||
|
response_code: 400,
|
||||||
|
status: 'error',
|
||||||
|
message: "Field '{$field}' is required"
|
||||||
|
);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Chamar o serviço de autenticação para fazer login
|
||||||
|
$serviceResult = (new AuthService())->login(email: $form['email'], password: $form['password']);
|
||||||
|
|
||||||
|
// Enviar a resposta JSON
|
||||||
|
RequestHelper::sendJsonResponse(
|
||||||
|
response_code: $serviceResult['response_code'],
|
||||||
|
status: $serviceResult['status'],
|
||||||
|
message: $serviceResult['message'],
|
||||||
|
output: $serviceResult['output'] ?? []
|
||||||
|
);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lida com a requisição de logout, revogando o token de autenticação do usuário.
|
||||||
|
*
|
||||||
|
* Este método atua como um **controlador** para o endpoint de logout. Ele extrai o token JWT do cabeçalho da requisição, delega a lógica de revogação para a camada de serviço e envia uma resposta JSON formatada de volta ao cliente.
|
||||||
|
*
|
||||||
|
* #### Fluxo de Operação:
|
||||||
|
* 1. **Obtenção do Token:** O token Bearer é extraído do cabeçalho da requisição usando `JWTService::getBearerToken()`.
|
||||||
|
* 2. **Delegação ao Serviço:** O método `logout` do **`AuthService`** é chamado com o token. O serviço é responsável por revogar o token no banco de dados e gerenciar a transação.
|
||||||
|
* 3. **Envio da Resposta JSON:** O resultado da operação do serviço (`serviceResult`) é formatado para uma resposta JSON padronizada usando **`RequestHelper::sendJsonResponse`**, garantindo que o código HTTP, status, mensagem e quaisquer dados de saída sejam comunicados ao cliente.
|
||||||
|
*
|
||||||
|
* @return void Este método não retorna um valor, pois seu objetivo é enviar uma resposta JSON e encerrar a execução do script.
|
||||||
|
*/
|
||||||
|
public function logout(): void {
|
||||||
|
// Chamar o serviço de autenticação para fazer logout
|
||||||
|
$serviceResult = (new AuthService())->logout(token: JWTService::getBearerToken());
|
||||||
|
|
||||||
|
// Enviar a resposta JSON
|
||||||
|
RequestHelper::sendJsonResponse(
|
||||||
|
response_code: $serviceResult['response_code'],
|
||||||
|
status: $serviceResult['status'],
|
||||||
|
message: $serviceResult['message'],
|
||||||
|
output: $serviceResult['output'] ?? []
|
||||||
|
);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getUserData(): void {
|
||||||
|
// Chamar o serviço de autenticação para obter os dados do usuário
|
||||||
|
$serviceResult = (new AuthService())->getUserData(token: JWTService::getBearerToken());
|
||||||
|
|
||||||
|
// Enviar a resposta JSON
|
||||||
|
RequestHelper::sendJsonResponse(
|
||||||
|
response_code: $serviceResult['response_code'],
|
||||||
|
status: $serviceResult['status'],
|
||||||
|
message: $serviceResult['message'],
|
||||||
|
output: $serviceResult['output'] ?? []
|
||||||
|
);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
237
app/Module/Auth/v0/DTO/UsuarioDTO.php
Executable file
237
app/Module/Auth/v0/DTO/UsuarioDTO.php
Executable file
@@ -0,0 +1,237 @@
|
|||||||
|
<?php
|
||||||
|
namespace Zampet\Module\Auth\v0\DTO;
|
||||||
|
|
||||||
|
class UsuarioDTO {
|
||||||
|
private ?int $id = null;
|
||||||
|
private ?string $uuid = null;
|
||||||
|
private ?string $nome_completo = null;
|
||||||
|
private ?int $status_id = 1;
|
||||||
|
private ?string $documentcpf = null;
|
||||||
|
private ?string $documentcrmv = null;
|
||||||
|
private ?string $email = null;
|
||||||
|
private ?string $senha = null;
|
||||||
|
private ?string $telefone = null;
|
||||||
|
private ?string $data_nascimento = null;
|
||||||
|
private ?string $endereco_rua = null;
|
||||||
|
private ?string $endereco_numero = null;
|
||||||
|
private ?string $endereco_complemento = null;
|
||||||
|
private ?string $endereco_bairro = null;
|
||||||
|
private ?string $endereco_cidade = null;
|
||||||
|
private ?string $endereco_uf = null;
|
||||||
|
private ?string $endereco_cep = null;
|
||||||
|
private ?string $created_at = null;
|
||||||
|
private ?string $updated_at = null;
|
||||||
|
private ?string $deleted_at = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construtor do DTO.
|
||||||
|
*
|
||||||
|
* Permite inicializar os atributos do objeto a partir de um array associativo.
|
||||||
|
*
|
||||||
|
* @param array<string, mixed> $data Array associativo opcional para popular os atributos.
|
||||||
|
*/
|
||||||
|
public function __construct(array $data = []) {
|
||||||
|
if (!empty($data)) {
|
||||||
|
$this->fromArray($data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Popula o DTO a partir de um array.
|
||||||
|
*
|
||||||
|
* @param array<string, mixed> $data
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function fromArray(array $data): void {
|
||||||
|
$this->id = $data['id'] ?? $this->id;
|
||||||
|
$this->uuid = $data['uuid'] ?? $this->uuid;
|
||||||
|
$this->nome_completo = $data['nome_completo'] ?? $this->nome_completo;
|
||||||
|
$this->status_id = $data['status_id'] ?? $this->status_id;
|
||||||
|
$this->documentcpf = $data['documentcpf'] ?? $this->documentcpf;
|
||||||
|
$this->documentcrmv = $data['documentcrmv'] ?? $this->documentcrmv;
|
||||||
|
$this->email = $data['email'] ?? $this->email;
|
||||||
|
$this->senha = $data['senha'] ?? $this->senha;
|
||||||
|
$this->telefone = $data['telefone'] ?? $this->telefone;
|
||||||
|
$this->data_nascimento = $data['data_nascimento'] ?? $this->data_nascimento;
|
||||||
|
$this->endereco_rua = $data['endereco_rua'] ?? $this->endereco_rua;
|
||||||
|
$this->endereco_numero = $data['endereco_numero'] ?? $this->endereco_numero;
|
||||||
|
$this->endereco_complemento = $data['endereco_complemento'] ?? $this->endereco_complemento;
|
||||||
|
$this->endereco_bairro = $data['endereco_bairro'] ?? $this->endereco_bairro;
|
||||||
|
$this->endereco_cidade = $data['endereco_cidade'] ?? $this->endereco_cidade;
|
||||||
|
$this->endereco_uf = $data['endereco_uf'] ?? $this->endereco_uf;
|
||||||
|
$this->endereco_cep = $data['endereco_cep'] ?? $this->endereco_cep;
|
||||||
|
$this->created_at = $data['created_at'] ?? $this->created_at;
|
||||||
|
$this->updated_at = $data['updated_at'] ?? $this->updated_at;
|
||||||
|
$this->deleted_at = $data['deleted_at'] ?? $this->deleted_at;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converte o DTO para um array associativo.
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function toArray(): array {
|
||||||
|
return [
|
||||||
|
'id' => $this->id,
|
||||||
|
'uuid' => $this->uuid,
|
||||||
|
'nome_completo' => $this->nome_completo,
|
||||||
|
'status_id' => $this->status_id,
|
||||||
|
'documentcpf' => $this->documentcpf,
|
||||||
|
'documentcrmv' => $this->documentcrmv,
|
||||||
|
'email' => $this->email,
|
||||||
|
'senha' => $this->senha,
|
||||||
|
'telefone' => $this->telefone,
|
||||||
|
'data_nascimento' => $this->data_nascimento,
|
||||||
|
'endereco_rua' => $this->endereco_rua,
|
||||||
|
'endereco_numero' => $this->endereco_numero,
|
||||||
|
'endereco_complemento' => $this->endereco_complemento,
|
||||||
|
'endereco_bairro' => $this->endereco_bairro,
|
||||||
|
'endereco_cidade' => $this->endereco_cidade,
|
||||||
|
'endereco_uf' => $this->endereco_uf,
|
||||||
|
'endereco_cep' => $this->endereco_cep,
|
||||||
|
'created_at' => $this->created_at,
|
||||||
|
'updated_at' => $this->updated_at,
|
||||||
|
'deleted_at' => $this->deleted_at,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getId(): ?int {
|
||||||
|
return $this->id;
|
||||||
|
}
|
||||||
|
public function setId(?int $id): void {
|
||||||
|
$this->id = $id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getUuid(): ?string {
|
||||||
|
return $this->uuid;
|
||||||
|
}
|
||||||
|
public function setUuid(?string $uuid): void {
|
||||||
|
$this->uuid = $uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getNomeCompleto(): ?string {
|
||||||
|
return $this->nome_completo;
|
||||||
|
}
|
||||||
|
public function setNomeCompleto(?string $nome_completo): void {
|
||||||
|
$this->nome_completo = $nome_completo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getStatusId(): ?int {
|
||||||
|
return $this->status_id;
|
||||||
|
}
|
||||||
|
public function setStatusId(?int $status_id): void {
|
||||||
|
$this->status_id = $status_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDocumentCpf(): ?string {
|
||||||
|
return $this->documentcpf;
|
||||||
|
}
|
||||||
|
public function setDocumentCpf(?string $documentcpf): void {
|
||||||
|
$this->documentcpf = $documentcpf;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDocumentCrmv(): ?string {
|
||||||
|
return $this->documentcrmv;
|
||||||
|
}
|
||||||
|
public function setDocumentCrmv(?string $documentcrmv): void {
|
||||||
|
$this->documentcrmv = $documentcrmv;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getEmail(): ?string {
|
||||||
|
return $this->email;
|
||||||
|
}
|
||||||
|
public function setEmail(?string $email): void {
|
||||||
|
$this->email = $email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSenha(): ?string {
|
||||||
|
return $this->senha;
|
||||||
|
}
|
||||||
|
public function setSenha(?string $senha): void {
|
||||||
|
$this->senha = $senha;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTelefone(): ?string {
|
||||||
|
return $this->telefone;
|
||||||
|
}
|
||||||
|
public function setTelefone(?string $telefone): void {
|
||||||
|
$this->telefone = $telefone;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDataNascimento(): ?string {
|
||||||
|
return $this->data_nascimento;
|
||||||
|
}
|
||||||
|
public function setDataNascimento(?string $data_nascimento): void {
|
||||||
|
$this->data_nascimento = $data_nascimento;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getEnderecoRua(): ?string {
|
||||||
|
return $this->endereco_rua;
|
||||||
|
}
|
||||||
|
public function setEnderecoRua(?string $endereco_rua): void {
|
||||||
|
$this->endereco_rua = $endereco_rua;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getEnderecoNumero(): ?string {
|
||||||
|
return $this->endereco_numero;
|
||||||
|
}
|
||||||
|
public function setEnderecoNumero(?string $endereco_numero): void {
|
||||||
|
$this->endereco_numero = $endereco_numero;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getEnderecoComplemento(): ?string {
|
||||||
|
return $this->endereco_complemento;
|
||||||
|
}
|
||||||
|
public function setEnderecoComplemento(?string $endereco_complemento): void {
|
||||||
|
$this->endereco_complemento = $endereco_complemento;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getEnderecoBairro(): ?string {
|
||||||
|
return $this->endereco_bairro;
|
||||||
|
}
|
||||||
|
public function setEnderecoBairro(?string $endereco_bairro): void {
|
||||||
|
$this->endereco_bairro = $endereco_bairro;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getEnderecoCidade(): ?string {
|
||||||
|
return $this->endereco_cidade;
|
||||||
|
}
|
||||||
|
public function setEnderecoCidade(?string $endereco_cidade): void {
|
||||||
|
$this->endereco_cidade = $endereco_cidade;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getEnderecoUf(): ?string {
|
||||||
|
return $this->endereco_uf;
|
||||||
|
}
|
||||||
|
public function setEnderecoUf(?string $endereco_uf): void {
|
||||||
|
$this->endereco_uf = $endereco_uf;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getEnderecoCep(): ?string {
|
||||||
|
return $this->endereco_cep;
|
||||||
|
}
|
||||||
|
public function setEnderecoCep(?string $endereco_cep): void {
|
||||||
|
$this->endereco_cep = $endereco_cep;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCreatedAt(): ?string {
|
||||||
|
return $this->created_at;
|
||||||
|
}
|
||||||
|
public function setCreatedAt(?string $created_at): void {
|
||||||
|
$this->created_at = $created_at;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getUpdatedAt(): ?string {
|
||||||
|
return $this->updated_at;
|
||||||
|
}
|
||||||
|
public function setUpdatedAt(?string $updated_at): void {
|
||||||
|
$this->updated_at = $updated_at;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDeletedAt(): ?string {
|
||||||
|
return $this->deleted_at;
|
||||||
|
}
|
||||||
|
public function setDeletedAt(?string $deleted_at): void {
|
||||||
|
$this->deleted_at = $deleted_at;
|
||||||
|
}
|
||||||
|
}
|
||||||
52
app/Module/Auth/v0/Middlewares/AuthMiddleware.php
Executable file
52
app/Module/Auth/v0/Middlewares/AuthMiddleware.php
Executable file
@@ -0,0 +1,52 @@
|
|||||||
|
<?php
|
||||||
|
namespace Zampet\Module\Auth\v0\Middlewares;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
use Zampet\Services\JWTService;
|
||||||
|
use Zampet\Module\Auth\v0\Services\AuthService;
|
||||||
|
|
||||||
|
class AuthMiddleware {
|
||||||
|
/**
|
||||||
|
* Gerencia o processo de autenticação de uma requisição de API via token Bearer.
|
||||||
|
*
|
||||||
|
* Este método estático atua como um **middleware de autenticação**, verificando a presença e a validade de um token JWT em uma requisição HTTP.
|
||||||
|
*
|
||||||
|
* #### Fluxo de Operação:
|
||||||
|
* 1. **Obtenção do Token:** Tenta extrair o token Bearer do cabeçalho da requisição usando `JWTService::getBearerToken()`.
|
||||||
|
* 2. **Verificação de Presença:** Se o token não for encontrado, uma `Exception` é lançada com o código `401 Unauthorized`.
|
||||||
|
* 3. **Validação do Token:** Chama `AuthService::validateToken()` para verificar a validade do token (assinatura, expiração e status no banco de dados).
|
||||||
|
* 4. **Conclusão:**
|
||||||
|
* * Se a validação for bem-sucedida, o método retorna `true`, permitindo que o processamento da rota continue.
|
||||||
|
*
|
||||||
|
* #### Tratamento de Erros:
|
||||||
|
* - Qualquer `Exception` lançada durante o processo (token ausente, falha de validação JWT, token expirado) é capturada.
|
||||||
|
* - O método retorna um array de erro formatado, contendo o código HTTP e a mensagem de erro da exceção, o que é útil para a camada de controle enviar uma resposta JSON padronizada ao cliente.
|
||||||
|
*
|
||||||
|
* @return array|bool Retorna `true` se o token for válido; caso contrário, retorna um array associativo com o status da operação, o código de resposta HTTP e uma mensagem de erro.
|
||||||
|
*/
|
||||||
|
public static function handle(): array|bool {
|
||||||
|
// Recebe o token JWT do cabeçalho Authorization
|
||||||
|
$bearerToken = JWTService::getBearerToken();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Verifica se o token está presente
|
||||||
|
if (!$bearerToken) {
|
||||||
|
throw new Exception(message: "Token not provided", code: 401);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verifica se o token é válido
|
||||||
|
$validateTokenService = (new AuthService())->validateToken(token: $bearerToken);
|
||||||
|
if ($validateTokenService['status'] !== 'success') {
|
||||||
|
throw new Exception(message: $validateTokenService['message'], code: 401);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch(Exception $e) {
|
||||||
|
return [
|
||||||
|
'response_code' => $e->getCode(),
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => $e->getMessage()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
396
app/Module/Auth/v0/Models/UsuarioModel.php
Executable file
396
app/Module/Auth/v0/Models/UsuarioModel.php
Executable file
@@ -0,0 +1,396 @@
|
|||||||
|
<?php
|
||||||
|
namespace Zampet\Module\Auth\v0\Models;
|
||||||
|
|
||||||
|
use DateTime;
|
||||||
|
use Exception;
|
||||||
|
use Zampet\Services\DB;
|
||||||
|
use Zampet\Module\Auth\v0\DTO\UsuarioDTO;
|
||||||
|
|
||||||
|
class UsuarioModel {
|
||||||
|
protected string $usuarioTable = 'usuario';
|
||||||
|
protected string $usuarioTokenTable = 'usuario_token';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cria e armazena um novo usuário no banco de dados.
|
||||||
|
*
|
||||||
|
* Este método é responsável por inserir os dados completos de um novo usuário, incluindo informações pessoais, documentos, credenciais e endereço, na tabela `{$this->usuarioTable}`. A operação é atômica e utiliza uma transação de banco de dados para garantir a consistência dos dados.
|
||||||
|
*
|
||||||
|
* #### Fluxo de Operação:
|
||||||
|
* 1. **Início da Transação:** Inicia uma transação no banco de dados (`DB::beginTransaction()`).
|
||||||
|
* 2. **Execução da Query:** Prepara e executa a instrução `INSERT` com todos os campos preenchidos a partir do `UsuarioDTO`.
|
||||||
|
* 3. **Confirmação:** Se a inserção for bem-sucedida, a transação é confirmada (`DB::commit()`).
|
||||||
|
* 4. **Retorno de Sucesso:** Retorna um array com `status` 'success', o código `201 Created`, uma mensagem de êxito e os dados do usuário criado (ID e UUID).
|
||||||
|
*
|
||||||
|
* #### Tratamento de Erros:
|
||||||
|
* - Qualquer `Exception` capturada durante o processo faz com que a transação seja revertida (`DB::rollBack()`).
|
||||||
|
* - Retorna um array com `status` 'error', código `500 Internal Server Error` e uma mensagem genérica de erro.
|
||||||
|
*
|
||||||
|
* @param UsuarioDTO $usuarioDTO O objeto DTO contendo os dados do usuário a ser criado.
|
||||||
|
* @return array Um array associativo com o status da operação, um código de resposta HTTP e uma mensagem.
|
||||||
|
*/
|
||||||
|
public function store(UsuarioDTO $usuarioDTO): array {
|
||||||
|
try {
|
||||||
|
$sql =
|
||||||
|
"INSERT INTO {$this->usuarioTable} (
|
||||||
|
uuid,
|
||||||
|
nome_completo,
|
||||||
|
status_id,
|
||||||
|
documentcpf,
|
||||||
|
documentcrmv,
|
||||||
|
email,
|
||||||
|
senha,
|
||||||
|
telefone,
|
||||||
|
data_nascimento,
|
||||||
|
endereco_rua,
|
||||||
|
endereco_numero,
|
||||||
|
endereco_complemento,
|
||||||
|
endereco_bairro,
|
||||||
|
endereco_cidade,
|
||||||
|
endereco_uf,
|
||||||
|
endereco_cep,
|
||||||
|
created_at,
|
||||||
|
updated_at,
|
||||||
|
deleted_at
|
||||||
|
) VALUES (
|
||||||
|
:uuid,
|
||||||
|
:nome_completo,
|
||||||
|
:status_id,
|
||||||
|
:documentcpf,
|
||||||
|
:documentcrmv,
|
||||||
|
:email,
|
||||||
|
:senha,
|
||||||
|
:telefone,
|
||||||
|
:data_nascimento,
|
||||||
|
:endereco_rua,
|
||||||
|
:endereco_numero,
|
||||||
|
:endereco_complemento,
|
||||||
|
:endereco_bairro,
|
||||||
|
:endereco_cidade,
|
||||||
|
:endereco_uf,
|
||||||
|
:endereco_cep,
|
||||||
|
:created_at,
|
||||||
|
:updated_at,
|
||||||
|
:deleted_at
|
||||||
|
)";
|
||||||
|
|
||||||
|
$params = [
|
||||||
|
":uuid" => $usuarioDTO->getUuid(),
|
||||||
|
":nome_completo" => $usuarioDTO->getNomeCompleto(),
|
||||||
|
":status_id" => $usuarioDTO->getStatusId(),
|
||||||
|
":documentcpf" => $usuarioDTO->getDocumentCpf(),
|
||||||
|
":documentcrmv" => $usuarioDTO->getDocumentCrmv(),
|
||||||
|
":email" => $usuarioDTO->getEmail(),
|
||||||
|
":senha" => $usuarioDTO->getSenha(),
|
||||||
|
":telefone" => $usuarioDTO->getTelefone(),
|
||||||
|
":data_nascimento" => $usuarioDTO->getDataNascimento(),
|
||||||
|
":endereco_rua" => $usuarioDTO->getEnderecoRua(),
|
||||||
|
":endereco_numero" => $usuarioDTO->getEnderecoNumero(),
|
||||||
|
":endereco_complemento" => $usuarioDTO->getEnderecoComplemento(),
|
||||||
|
":endereco_bairro" => $usuarioDTO->getEnderecoBairro(),
|
||||||
|
":endereco_cidade" => $usuarioDTO->getEnderecoCidade(),
|
||||||
|
":endereco_uf" => $usuarioDTO->getEnderecoUf(),
|
||||||
|
":endereco_cep" => $usuarioDTO->getEnderecoCep(),
|
||||||
|
":created_at" => $usuarioDTO->getCreatedAt(),
|
||||||
|
":updated_at" => $usuarioDTO->getUpdatedAt(),
|
||||||
|
":deleted_at" => $usuarioDTO->getDeletedAt()
|
||||||
|
];
|
||||||
|
|
||||||
|
// Executa a query de inserção
|
||||||
|
DB::execute(sql: $sql, params: $params);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'response_code' => 201,
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Usuário registrado com sucesso.',
|
||||||
|
'output' => [
|
||||||
|
'data' => [
|
||||||
|
'user_id' => DB::lastInsertId(),
|
||||||
|
'uuid' => $usuarioDTO->getUuid()
|
||||||
|
]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return [
|
||||||
|
'response_code' => 500,
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => 'Ocorreu um erro ao registrar o usuário. Tente novamente mais tarde.',
|
||||||
|
'output' => ['errors' => $e->getMessage()]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Busca um único usuário ativo no banco de dados por um identificador específico.
|
||||||
|
*
|
||||||
|
* Este método recupera todos os detalhes de um usuário na tabela `{$this->usuarioTable}`. A busca é flexível e permite usar qualquer coluna como identificador (`$identifier`), mas é estritamente restrita a registros que não foram logicamente excluídos (`deleted_at IS NULL`).
|
||||||
|
*
|
||||||
|
* @param string $identifier A coluna da tabela a ser usada como critério de busca (ex: 'uuid', 'email', 'documentcpf').
|
||||||
|
* @param mixed $value O valor correspondente ao identificador que está sendo buscado.
|
||||||
|
* @return array Um array associativo contendo o status da operação, um código de resposta HTTP e os dados do usuário, se encontrado.
|
||||||
|
*/
|
||||||
|
public function getUsuarioByIdentifier(string $identifier, mixed $value): array {
|
||||||
|
try {
|
||||||
|
$sql =
|
||||||
|
"SELECT
|
||||||
|
id,
|
||||||
|
uuid,
|
||||||
|
nome_completo,
|
||||||
|
status_id,
|
||||||
|
documentcpf,
|
||||||
|
documentcrmv,
|
||||||
|
email,
|
||||||
|
senha,
|
||||||
|
telefone,
|
||||||
|
data_nascimento,
|
||||||
|
endereco_rua,
|
||||||
|
endereco_numero,
|
||||||
|
endereco_complemento,
|
||||||
|
endereco_bairro,
|
||||||
|
endereco_cidade,
|
||||||
|
endereco_uf,
|
||||||
|
endereco_cep,
|
||||||
|
created_at,
|
||||||
|
updated_at,
|
||||||
|
deleted_at
|
||||||
|
FROM {$this->usuarioTable}
|
||||||
|
WHERE {$identifier} = :value
|
||||||
|
AND deleted_at IS NULL";
|
||||||
|
|
||||||
|
$params = [
|
||||||
|
':value' => $value
|
||||||
|
];
|
||||||
|
|
||||||
|
$result = DB::fetchOne(sql: $sql, params: $params);
|
||||||
|
|
||||||
|
if (!$result) {
|
||||||
|
return [
|
||||||
|
'response_code' => 404,
|
||||||
|
'status' => 'fail',
|
||||||
|
'message' => 'Usuário não encontrado.',
|
||||||
|
'output' => [
|
||||||
|
'data' => []
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'response_code' => 200,
|
||||||
|
'status' => 'success',
|
||||||
|
'output' => ['data' => $result]
|
||||||
|
];
|
||||||
|
} catch(Exception $e) {
|
||||||
|
return [
|
||||||
|
'response_code' => 500,
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => 'Ocorreu um erro ao buscar usuário. Tente novamente mais tarde.',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Armazena um novo token de autenticação no banco de dados para um usuário específico.
|
||||||
|
*
|
||||||
|
* Este método é responsável por registrar um token na tabela `{$this->usuarioTokenTable}`. Ele associa o token a um ID de usuário, define o status inicial como não revogado (`revoked = 0`) e registra o timestamp de criação.
|
||||||
|
*
|
||||||
|
* #### Fluxo de Operação:
|
||||||
|
* 1. **Início da Transação:** O método inicia uma transação no banco de dados (`DB::beginTransaction()`).
|
||||||
|
* 2. **Execução da Query:** Prepara e executa a instrução `INSERT` com o ID do usuário, a string do token, o status inicial de revogação e o timestamp atual.
|
||||||
|
* 3. **Confirmação:** Se a inserção for bem-sucedida, a transação é confirmada (`DB::commit()`).
|
||||||
|
* 4. **Retorno de Sucesso:** Retorna um array com `status` 'success', o código `201 Created` e uma mensagem de êxito.
|
||||||
|
*
|
||||||
|
* #### Tratamento de Erros:
|
||||||
|
* - Qualquer `Exception` capturada durante o processo faz com que a transação seja revertida (`DB::rollback()`).
|
||||||
|
* - Retorna um array com `status` 'error', o código `500 Internal Server Error` e uma mensagem genérica de erro.
|
||||||
|
*
|
||||||
|
* @param int $user_id O ID do usuário ao qual o token pertence.
|
||||||
|
* @param string $token A string do token de autenticação (JWT ou similar).
|
||||||
|
* @return array Um array associativo contendo o status da operação ("success" ou "error"), um código de resposta HTTP e uma mensagem.
|
||||||
|
*/
|
||||||
|
public function storeToken(int $user_id, string $token): array {
|
||||||
|
try {
|
||||||
|
$sql =
|
||||||
|
"INSERT INTO {$this->usuarioTokenTable} (
|
||||||
|
usuario_id,
|
||||||
|
token,
|
||||||
|
revoked,
|
||||||
|
created_at
|
||||||
|
) VALUES (
|
||||||
|
:usuario_id,
|
||||||
|
:token,
|
||||||
|
:revoked,
|
||||||
|
:created_at
|
||||||
|
)";
|
||||||
|
|
||||||
|
$params = [
|
||||||
|
":usuario_id" => $user_id,
|
||||||
|
":token" => $token,
|
||||||
|
":revoked" => 0,
|
||||||
|
":created_at" => (new DateTime())->format(format: 'Y-m-d H:i:s')
|
||||||
|
];
|
||||||
|
|
||||||
|
DB::execute(sql: $sql, params: $params);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'response_code' => 201,
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Token registrado com sucesso.'
|
||||||
|
];
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return [
|
||||||
|
'response_code' => 500,
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => 'Erro ao registrar token. Tente novamente mais tarde.'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Revoga todos os tokens de autenticação ativos de um usuário específico.
|
||||||
|
*
|
||||||
|
* Este método atualiza a coluna `revoked` para `1` (revogado) para todos os tokens não revogados (`revoked = 0`) associados a um `user_id` específico na tabela `{$this->usuarioTokenTable}`. Isso invalida todas as sessões ativas do usuário, forçando-o a fazer login novamente.
|
||||||
|
*
|
||||||
|
* #### Fluxo de Operação:
|
||||||
|
* 1. **Execução da Query:** Prepara e executa a instrução `UPDATE` para revogar os tokens.
|
||||||
|
* 2. **Confirmação:** Se a atualização for bem-sucedida, retorna um array com `status` 'success' e código `200 OK`.
|
||||||
|
*
|
||||||
|
* #### Tratamento de Erros:
|
||||||
|
* - Qualquer `Exception` capturada durante o processo reverte qualquer transação de banco de dados e retorna um array com `status` 'error' e código `500 Internal Server Error`, com uma mensagem de erro genérica.
|
||||||
|
*
|
||||||
|
* @param int $user_id O ID do usuário para o qual os tokens serão revogados.
|
||||||
|
* @return array Um array associativo contendo o status da operação ("success" ou "error"), um código de resposta HTTP e uma mensagem descritiva.
|
||||||
|
*/
|
||||||
|
public function revokeOldTokens(int $user_id): array {
|
||||||
|
try {
|
||||||
|
$sql =
|
||||||
|
"UPDATE {$this->usuarioTokenTable} SET
|
||||||
|
revoked = 1,
|
||||||
|
updated_at = CURRENT_TIMESTAMP
|
||||||
|
WHERE usuario_id = :usuario_id
|
||||||
|
AND revoked = 0";
|
||||||
|
|
||||||
|
$params = [
|
||||||
|
":usuario_id" => $user_id
|
||||||
|
];
|
||||||
|
|
||||||
|
DB::execute(sql: $sql, params: $params);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'response_code' => 200,
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Old tokens revoked successfully.'
|
||||||
|
];
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return [
|
||||||
|
'response_code' => 500,
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => 'Error revoking old tokens. Please try again later.'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Busca um token de usuário no banco de dados por um identificador específico.
|
||||||
|
*
|
||||||
|
* Este método recupera os dados de um único token de autenticação da tabela `{$this->usuarioTokenTable}` de forma flexível, permitindo a busca por qualquer coluna (`$identifier`) e o valor correspondente.
|
||||||
|
*
|
||||||
|
* #### Fluxo de Operação:
|
||||||
|
* 1. **Execução da Query**: Monta e executa uma consulta `SELECT` que usa o `$identifier` de forma dinâmica na cláusula `WHERE` e um parâmetro nomeado (`:value`) para o valor.
|
||||||
|
* 2. **Verificação de Resultados**:
|
||||||
|
* - Se o token for encontrado, retorna um array com **`status` 'success'**, código **200 OK** e os dados do token.
|
||||||
|
* - Se o token **não for encontrado**, retorna um array com **`status` 'fail'** e código **404 Not Found**.
|
||||||
|
*
|
||||||
|
* #### Tratamento de Erros:
|
||||||
|
* - Qualquer `Exception` capturada durante a consulta retorna um array com **`status` 'error'** e código **500 Internal Server Error**, com uma mensagem genérica de erro.
|
||||||
|
*
|
||||||
|
* @param string $identifier A coluna da tabela a ser usada como critério de busca (ex: 'id', 'token').
|
||||||
|
* @param mixed $value O valor correspondente ao identificador que está sendo buscado.
|
||||||
|
* @return array Um array associativo contendo o status da operação, um código de resposta HTTP e os dados do token, se encontrado.
|
||||||
|
*/
|
||||||
|
public function getTokenByidentifier(string $identifier, mixed $value): array {
|
||||||
|
try {
|
||||||
|
$sql =
|
||||||
|
"SELECT
|
||||||
|
id,
|
||||||
|
usuario_id,
|
||||||
|
token,
|
||||||
|
revoked,
|
||||||
|
created_at,
|
||||||
|
updated_at
|
||||||
|
FROM {$this->usuarioTokenTable}
|
||||||
|
WHERE {$identifier} = :value";
|
||||||
|
|
||||||
|
$params = [
|
||||||
|
':value' => $value
|
||||||
|
];
|
||||||
|
|
||||||
|
$result = DB::fetchOne(sql: $sql, params: $params);
|
||||||
|
|
||||||
|
if (!$result) {
|
||||||
|
return [
|
||||||
|
'response_code' => 404,
|
||||||
|
'status' => 'fail',
|
||||||
|
'message' => 'Token não encontrado.',
|
||||||
|
'output' => [
|
||||||
|
'data' => []
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'response_code' => 200,
|
||||||
|
'status' => 'success',
|
||||||
|
'output' => ['data' => $result]
|
||||||
|
];
|
||||||
|
} catch(Exception $e) {
|
||||||
|
return [
|
||||||
|
'response_code' => 500,
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => 'Ocorreu um erro ao buscar token. Tente novamente mais tarde.',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Revoga um token de autenticação de usuário no banco de dados.
|
||||||
|
*
|
||||||
|
* Este método marca um token específico como revogado, atualizando a coluna `revoked` para `1`. Ele garante que apenas tokens que ainda não foram revogados (`revoked = 0`) sejam atualizados.
|
||||||
|
*
|
||||||
|
* #### Fluxo de Operação:
|
||||||
|
* 1. **Execução da Query**: Prepara e executa a instrução `UPDATE` para definir `revoked = 1` para o token correspondente.
|
||||||
|
* 2. **Confirmação**: Se a atualização for bem-sucedida, retorna um array com **`status` 'success'** e código **200 OK**.
|
||||||
|
*
|
||||||
|
* #### Tratamento de Erros:
|
||||||
|
* - Qualquer `Exception` capturada durante o processo retorna um array com **`status` 'error'** e código **500 Internal Server Error**, com uma mensagem de erro genérica.
|
||||||
|
*
|
||||||
|
* @param string $token O token de autenticação a ser revogado.
|
||||||
|
* @return array Um array associativo contendo o status da operação ("success" ou "error"), um código de resposta HTTP e uma mensagem.
|
||||||
|
*/
|
||||||
|
public function revokeToken(string $token): array {
|
||||||
|
try {
|
||||||
|
$sql =
|
||||||
|
"UPDATE {$this->usuarioTokenTable} SET
|
||||||
|
revoked = 1,
|
||||||
|
updated_at = CURRENT_TIMESTAMP
|
||||||
|
WHERE token = :token
|
||||||
|
AND revoked = 0";
|
||||||
|
|
||||||
|
$params = [
|
||||||
|
":token" => $token
|
||||||
|
];
|
||||||
|
|
||||||
|
DB::execute(sql: $sql, params: $params);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'response_code' => 200,
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Token revoked successfully.'
|
||||||
|
];
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return [
|
||||||
|
'response_code' => 500,
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => 'Error revoking token. Please try again later.'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
30
app/Module/Auth/v0/Routes/AuthRoutes.php
Executable file
30
app/Module/Auth/v0/Routes/AuthRoutes.php
Executable file
@@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
use AxiumPHP\Core\Router;
|
||||||
|
use Zampet\Module\Auth\v0\Controllers\AuthController;
|
||||||
|
use Zampet\Module\Auth\v0\Middlewares\AuthMiddleware;
|
||||||
|
|
||||||
|
Router::group(
|
||||||
|
prefix: '/auth',
|
||||||
|
callback: function () {
|
||||||
|
// Criar novo usuário
|
||||||
|
Router::POST(
|
||||||
|
uri: '/register',
|
||||||
|
handler: [AuthController::class, 'register']
|
||||||
|
);
|
||||||
|
|
||||||
|
// Login no Sistema
|
||||||
|
Router::POST(
|
||||||
|
uri: '/login',
|
||||||
|
handler: [AuthController::class, 'login']
|
||||||
|
);
|
||||||
|
|
||||||
|
// Logout do Sistema
|
||||||
|
Router::POST(
|
||||||
|
uri: '/logout',
|
||||||
|
handler: [AuthController::class, 'logout'],
|
||||||
|
middlewares: [
|
||||||
|
AuthMiddleware::class.'::handle'
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
12
app/Module/Auth/v0/Routes/Routes.php
Executable file
12
app/Module/Auth/v0/Routes/Routes.php
Executable file
@@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
use AxiumPHP\Core\Router;
|
||||||
|
|
||||||
|
Router::group(
|
||||||
|
prefix: '/v0',
|
||||||
|
callback: function() {
|
||||||
|
require 'AuthRoutes.php';
|
||||||
|
},
|
||||||
|
middlewares: [
|
||||||
|
|
||||||
|
]
|
||||||
|
);
|
||||||
346
app/Module/Auth/v0/Services/AuthService.php
Executable file
346
app/Module/Auth/v0/Services/AuthService.php
Executable file
@@ -0,0 +1,346 @@
|
|||||||
|
<?php
|
||||||
|
namespace Zampet\Module\Auth\v0\Services;
|
||||||
|
|
||||||
|
use DateTime;
|
||||||
|
use Exception;
|
||||||
|
use Ramsey\Uuid\Uuid;
|
||||||
|
use Zampet\Services\DB;
|
||||||
|
use Zampet\Services\JWTService;
|
||||||
|
use Zampet\Helpers\DataValidator;
|
||||||
|
use Zampet\Module\Auth\v0\DTO\UsuarioDTO;
|
||||||
|
use Zampet\Module\Auth\v0\Models\UsuarioModel;
|
||||||
|
|
||||||
|
class AuthService {
|
||||||
|
/**
|
||||||
|
* Registra um novo usuário no sistema após realizar validações rigorosas.
|
||||||
|
*
|
||||||
|
* Este método orquestra o processo de criação de um novo usuário, incluindo validação de dados, checagem de duplicidade de e-mail e persistência no banco de dados. A operação é atômica e utiliza uma transação de banco de dados (`DB::beginTransaction()`).
|
||||||
|
*
|
||||||
|
* #### Fluxo de Operação:
|
||||||
|
* 1. **Início da Transação:** Inicia uma transação no banco de dados.
|
||||||
|
* 2. **Validação de Dados:** Um `DataValidator` é usado para validar campos obrigatórios (`nome_completo`, `email`, `senha`) e a complexidade da senha (mínimo de 8 caracteres, maiúsculas, números e símbolos).
|
||||||
|
* 3. **Checagem de Duplicidade:** O método `getUsuarioByIdentifier` é chamado para verificar se o e-mail já está em uso por outro usuário.
|
||||||
|
* 4. **Encerramento em Caso de Falha na Validação/Duplicidade:** Se houver erros de validação (código **400**) ou se o e-mail já estiver em uso (código **409**), a transação é revertida (`DB::rollBack()`) e o erro é retornado.
|
||||||
|
* 5. **Preparação Final dos Dados:** Se a validação passar, o **UUID** do usuário e o timestamp de criação são definidos. A senha é criptografada usando **`PASSWORD_ARGON2ID`** antes de ser persistida.
|
||||||
|
* 6. **Persistência:** O método `store` do `UsuarioModel` é chamado para inserir o novo registro.
|
||||||
|
* 7. **Confirmação:** Se a persistência for bem-sucedida (código **201**), a transação é confirmada (`DB::commit()`) e o resultado de sucesso é retornado.
|
||||||
|
* 8. **Tratamento de Erro de Persistência:** Se o `store` falhar por um motivo inesperado (código **500**), a transação é revertida.
|
||||||
|
*
|
||||||
|
* @param UsuarioDTO $usuarioDTO O objeto DTO contendo os dados do novo usuário, incluindo nome, email e senha.
|
||||||
|
* @return array Um array associativo contendo o código HTTP, status da operação, mensagem e dados de saída.
|
||||||
|
*/
|
||||||
|
public function register(UsuarioDTO $usuarioDTO): array {
|
||||||
|
// Inicia conexão com o banco de dados
|
||||||
|
DB::beginTransaction();
|
||||||
|
|
||||||
|
// Valida os dados do DTO
|
||||||
|
$dataValidador = new DataValidator(inputs: $usuarioDTO->toArray());
|
||||||
|
$dataValidador->validate(field: 'nome_completo', rule: 'notEmpty', message: 'Nome completo é obrigatório');
|
||||||
|
$dataValidador->validate(field: 'email', rule: 'notEmpty', message: 'Email é obrigatório');
|
||||||
|
$dataValidador->validate(field: 'email', rule: 'validateEmail', message: 'Email inválido');
|
||||||
|
$dataValidador->validate(field: 'senha', rule: 'notEmpty', message: 'Senha é obrigatória');
|
||||||
|
$dataValidador->validate(field: 'documentcpf', rule: 'notEmpty', message: 'CPF é obrigatório');
|
||||||
|
$dataValidador->validate(field: 'documentcpf', rule: 'validateCpf', message: 'CPF informado é inválido.');
|
||||||
|
$dataValidador->validate(field: 'senha', rule: 'validatePassword', message: 'Senha inválida', additionalParams: [
|
||||||
|
[
|
||||||
|
'minLength' => 8,
|
||||||
|
'upper' => true,
|
||||||
|
'digit' => true,
|
||||||
|
'symbol' => true
|
||||||
|
]
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Se houver erros de validação, retornar resposta de erro
|
||||||
|
if(!$dataValidador->passes()) {
|
||||||
|
DB::rollBack();
|
||||||
|
return [
|
||||||
|
'response_code' => 400,
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => 'Validation errors',
|
||||||
|
'output' => [
|
||||||
|
'errors' => $dataValidador->getErrors()
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verifica se o email já está em uso
|
||||||
|
$existingEmailUser = (new UsuarioModel())->getUsuarioByIdentifier(identifier: 'email', value: $usuarioDTO->getEmail());
|
||||||
|
if($existingEmailUser['status'] === 'success' && !empty($existingEmailUser['output']['data'])) {
|
||||||
|
DB::rollBack();
|
||||||
|
return [
|
||||||
|
'response_code' => 409,
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => 'E-mail já está em uso por outro usuário.',
|
||||||
|
'output' => []
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verifica se o CPF já está em uso
|
||||||
|
$existingCpfUser = (new UsuarioModel())->getUsuarioByIdentifier(identifier: 'documentcpf', value: $usuarioDTO->getDocumentCpf());
|
||||||
|
if($existingCpfUser['status'] === 'success' && !empty($existingCpfUser['output']['data'])) {
|
||||||
|
DB::rollBack();
|
||||||
|
return [
|
||||||
|
'response_code' => 409,
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => 'CPF já está em uso por outro usuário.',
|
||||||
|
'output' => []
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define dados adicionais
|
||||||
|
$usuarioDTO->setUuid(uuid: Uuid::uuid7()->toString());
|
||||||
|
$usuarioDTO->setCreatedAt(created_at: (new DateTime())->format(format: 'Y-m-d H:i:s'));
|
||||||
|
$usuarioDTO->setSenha(senha: password_hash(password: $usuarioDTO->getSenha(), algo: PASSWORD_ARGON2ID));
|
||||||
|
|
||||||
|
// Persiste o novo usuário no banco de dados
|
||||||
|
$storeUsuario = (new UsuarioModel())->store(usuarioDTO: $usuarioDTO);
|
||||||
|
if($storeUsuario['status'] !== 'success') {
|
||||||
|
DB::rollBack();
|
||||||
|
return [
|
||||||
|
'response_code' => 500,
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => 'Erro ao registrar usuário. Tente novamente mais tarde.',
|
||||||
|
'output' => $storeUsuario['output'] ?? []
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
DB::commit();
|
||||||
|
return [
|
||||||
|
'response_code' => $storeUsuario['response_code'],
|
||||||
|
'status' => $storeUsuario['status'],
|
||||||
|
'message' => $storeUsuario['message'],
|
||||||
|
'output' => $storeUsuario['output']
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Realiza a autenticação de um usuário por e-mail e senha, gerando e registrando um token JWT.
|
||||||
|
*
|
||||||
|
* Este método gerencia todo o fluxo de login, incluindo a verificação de credenciais, revogação de tokens antigos, geração de um novo JWT e persistência desse novo token no banco de dados. A operação é atômica e utiliza uma transação de banco de dados para garantir a consistência dos dados.
|
||||||
|
*
|
||||||
|
* #### Fluxo de Operação:
|
||||||
|
* 1. **Início da Transação:** Inicia uma transação no banco de dados (`DB::beginTransaction()`).
|
||||||
|
* 2. **Consulta do Usuário:** Busca o usuário pelo e-mail. Se o usuário não for encontrado, uma `Exception` é lançada com o código `401 Unauthorized`.
|
||||||
|
* 3. **Validação da Senha:** Verifica a senha fornecida contra o hash armazenado. Se a senha for inválida, uma `Exception` é lançada com o código `401 Unauthorized`.
|
||||||
|
* 4. **Revogação de Tokens Antigos:** Chama o método `revokeOldTokens` do `UsuarioModel` para invalidar todas as sessões anteriores do usuário.
|
||||||
|
* 5. **Geração do JWT:** Monta o payload do JWT com os dados essenciais do usuário e os timestamps `iat` (emitido em) e `exp` (expira em), gerando o novo token.
|
||||||
|
* 6. **Registro do Token:** Armazena o novo token no banco de dados usando `storeToken`. Se a operação falhar, uma `Exception` é lançada com o código `500 Internal Server Error`.
|
||||||
|
* 7. **Confirmação e Retorno:** Se todas as etapas forem bem-sucedidas, a transação é confirmada (`DB::commit()`), e o token gerado é retornado com `status` 'success' e código `200 OK`.
|
||||||
|
*
|
||||||
|
* #### Tratamento de Erros:
|
||||||
|
* - Qualquer `Exception` capturada durante o processo faz com que a transação seja revertida (`DB::rollBack()`).
|
||||||
|
* - Retorna um array com `status` 'error', o código de erro da exceção (ou 500 como fallback) e uma mensagem descritiva.
|
||||||
|
*
|
||||||
|
* @param string $email O e-mail do usuário para autenticação.
|
||||||
|
* @param string $password A senha em texto plano fornecida pelo usuário.
|
||||||
|
* @return array Um array associativo contendo o status da operação, o código de resposta HTTP e o token JWT em caso de sucesso.
|
||||||
|
*/
|
||||||
|
public function login(string $email, string $password): array {
|
||||||
|
try {
|
||||||
|
// Inicia Transação no Banco de dados
|
||||||
|
DB::beginTransaction();
|
||||||
|
|
||||||
|
// Consulta as informações do usuário pelo email
|
||||||
|
$userResult = (new UsuarioModel())->getUsuarioByIdentifier(identifier: 'email', value: $email);
|
||||||
|
if($userResult['status'] !== 'success' || empty($userResult['output']['data'])) {
|
||||||
|
throw new Exception(message: 'E-mail ou Senha incorretos.', code: 401);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valida a senha fornecida com a armazenada
|
||||||
|
if(!password_verify(password: $password, hash: $userResult['output']['data']['senha'])) {
|
||||||
|
throw new Exception(message: 'E-mail ou Senha incorretos.', code: 401);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Revoga os tokens antigos do usuário
|
||||||
|
$revokeOldTokens = (new UsuarioModel())->revokeOldTokens(user_id: $userResult['output']['data']['id']);
|
||||||
|
if($revokeOldTokens['status'] !== 'success') {
|
||||||
|
throw new Exception(message: 'Erro ao revogar tokens antigos.', code: 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepara o Payload do JWT
|
||||||
|
$jwtPayload = [
|
||||||
|
'user_uuid' => $userResult['output']['data']['uuid'],
|
||||||
|
'email' => $userResult['output']['data']['email'],
|
||||||
|
'nome_completo' => $userResult['output']['data']['nome_completo'],
|
||||||
|
'iat' => (new DateTime())->getTimestamp(),
|
||||||
|
'exp' => (new DateTime())->modify(modifier: '+8 hour')->getTimestamp()
|
||||||
|
];
|
||||||
|
|
||||||
|
$token = JWTService::generateToken(payload: $jwtPayload);
|
||||||
|
|
||||||
|
// Armazena o token no banco de dados
|
||||||
|
$storeToken = (new UsuarioModel())->storeToken(user_id: $userResult['output']['data']['id'], token: $token);
|
||||||
|
if($storeToken['status'] !== 'success') {
|
||||||
|
throw new Exception(message: 'Erro ao armazenar token de autenticação.', code: 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
DB::commit();
|
||||||
|
return [
|
||||||
|
'response_code' => 200,
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Login successful.',
|
||||||
|
'output' => [
|
||||||
|
'data' => [
|
||||||
|
'token' => $token
|
||||||
|
]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
} catch(Exception $e) {
|
||||||
|
DB::rollBack();
|
||||||
|
return [
|
||||||
|
'response_code' => $e->getCode() ?: 500,
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => $e->getMessage() ?: 'An unexpected error occurred during login.'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finaliza a sessão do usuário revogando um token JWT específico.
|
||||||
|
*
|
||||||
|
* Este método é o responsável por orquestrar o processo de logout seguro, garantindo que o token de autenticação seja validado e, em seguida, invalidado no banco de dados.
|
||||||
|
*
|
||||||
|
* #### Fluxo de Operação:
|
||||||
|
* 1. **Validação do Token:** O método chama `validateToken` para verificar se o token existe e ainda é válido (não expirado ou revogado no DB). Se a validação falhar, uma `Exception` é lançada com o código de erro apropriado.
|
||||||
|
* 2. **Revogação no Banco de Dados:** Tenta revogar o token usando `UsuarioModel::revokeToken()`. Se a revogação falhar (ex: erro de banco de dados), uma `Exception` é lançada.
|
||||||
|
* 3. **Retorno de Sucesso:** Se ambas as operações (validação e revogação) forem bem-sucedidas, retorna um array com **`status` 'success'**, código **200 OK** e uma mensagem de êxito.
|
||||||
|
*
|
||||||
|
* #### Tratamento de Erros:
|
||||||
|
* - Qualquer `Exception` lançada durante o processo é capturada, e o método retorna um array de erro formatado, contendo o código HTTP e a mensagem da exceção.
|
||||||
|
*
|
||||||
|
* @param string $token O token JWT a ser revogado.
|
||||||
|
* @return array Um array associativo contendo o status da operação ("success" ou "error"), o código de resposta HTTP e uma mensagem.
|
||||||
|
*/
|
||||||
|
public function logout(string $token): array {
|
||||||
|
try {
|
||||||
|
// Validar se o token existe e não está revogado
|
||||||
|
$validateToken = $this->validateToken(token: $token);
|
||||||
|
if($validateToken['status'] !== 'success') {
|
||||||
|
throw new Exception(message: $validateToken['message'], code: $validateToken['response_code']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Revoga o token fornecido
|
||||||
|
$revokeToken = (new UsuarioModel())->revokeToken(token: $token);
|
||||||
|
if($revokeToken['status'] !== 'success') {
|
||||||
|
throw new Exception(message: 'Erro ao revogar token.', code: $revokeToken['response_code']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'response_code' => 200,
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Logout successful.',
|
||||||
|
'output' => []
|
||||||
|
];
|
||||||
|
} catch(Exception $e) {
|
||||||
|
return [
|
||||||
|
'response_code' => $e->getCode() ?: 500,
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => $e->getMessage() ?: 'An unexpected error occurred during logout.'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Valida um token de autenticação verificando sua presença no banco de dados e seu status de expiração.
|
||||||
|
*
|
||||||
|
* Este método realiza uma validação de duas etapas: 1) Checa se o token existe e não está revogado no banco de dados, e 2) verifica se o timestamp de expiração (exp) no payload do token já passou. Se o token estiver expirado, ele é revogado no banco de dados.
|
||||||
|
*
|
||||||
|
* #### Fluxo de Operação:
|
||||||
|
* 1. **Início da Transação:** Inicia uma transação no banco de dados (`DB::beginTransaction()`) para garantir que a revogação, se necessária, seja atômica.
|
||||||
|
* 2. **Busca no DB:** Tenta encontrar o token usando o `UsuarioModel`. Se a busca falhar (token não encontrado ou revogado), uma `Exception` é lançada com o código `401 Unauthorized`.
|
||||||
|
* 3. **Decodificação:** O token é decodificado usando `JWTService::decode()` para acessar o payload e o timestamp de expiração (`exp`).
|
||||||
|
* 4. **Verificação de Expiração:** Compara o timestamp atual com o `exp` do payload.
|
||||||
|
* * **Se Expirado:** O método tenta revogar o token no DB. Se a revogação for bem-sucedida, a transação é confirmada e o método retorna um erro com código `401`. Se a revogação falhar, a transação é revertida e um erro `500` é retornado.
|
||||||
|
* * **Se Válido:** A transação é confirmada e o método retorna `status` 'success' com o código `200 OK` e os dados do payload.
|
||||||
|
*
|
||||||
|
* #### Tratamento de Erros:
|
||||||
|
* - Qualquer `Exception` lançada durante o processo (falha de busca, falha de decodificação JWT, falha de revogação) é capturada. Se a falha ocorrer após o início da transação, um `DB::rollBack()` é implícito ou explicitamente chamado antes do retorno.
|
||||||
|
* - Retorna um array com `status` 'error', o código de erro (ou `500` como fallback) e uma mensagem descritiva.
|
||||||
|
*
|
||||||
|
* @param string $token O token JWT a ser validado.
|
||||||
|
* @return array Um array associativo contendo o status da operação, um código de resposta HTTP e os dados do token, se válido.
|
||||||
|
*/
|
||||||
|
public function validateToken(string $token): array {
|
||||||
|
// Inicia transação no banco de dados
|
||||||
|
DB::beginTransaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
$dbTokenData = (new UsuarioModel())->getTokenByidentifier(identifier: 'token', value: $token);
|
||||||
|
if($dbTokenData['status'] !== 'success' || empty($dbTokenData['output']['data'])) {
|
||||||
|
throw new Exception(message: 'Token inválido ou expirado.', code: 401);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decodifica o token para verificar sua validade
|
||||||
|
$decodedToken = JWTService::decode(token: $dbTokenData['output']['data']['token']);
|
||||||
|
|
||||||
|
// Verifica se o token expirou
|
||||||
|
$currentTimestamp = (new DateTime())->getTimestamp();
|
||||||
|
switch (true) {
|
||||||
|
case $currentTimestamp > $decodedToken['exp']:
|
||||||
|
// Revoga o token expirado
|
||||||
|
$revokeToken = (new UsuarioModel())->revokeToken(token: $dbTokenData['output']['data']['token']);
|
||||||
|
if($revokeToken['status'] !== 'success') {
|
||||||
|
DB::rollBack();
|
||||||
|
throw new Exception(message: 'Erro ao revogar token expirado.', code: 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
DB::commit();
|
||||||
|
return [
|
||||||
|
'response_code' => 401,
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => 'Token expirado.',
|
||||||
|
'output' => []
|
||||||
|
];
|
||||||
|
|
||||||
|
case $dbTokenData['output']['data']['revoked'] === 1:
|
||||||
|
DB::commit();
|
||||||
|
return [
|
||||||
|
'response_code' => 401,
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => 'Token inválido ou expirado.',
|
||||||
|
'output' => []
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
DB::commit();
|
||||||
|
return [
|
||||||
|
'response_code' => 200,
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Token is valid.',
|
||||||
|
'output' => ['token_data' => $decodedToken]
|
||||||
|
];
|
||||||
|
} catch(Exception $e) {
|
||||||
|
return [
|
||||||
|
'response_code' => $e->getCode() ?: 500,
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => $e->getMessage() ?: 'An unexpected error occurred during token validation.'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getUserData(string $token): array {
|
||||||
|
try {
|
||||||
|
// Validar se o token existe e não está revogado
|
||||||
|
$validateToken = $this->validateToken(token: $token);
|
||||||
|
if($validateToken['status'] !== 'success') {
|
||||||
|
throw new Exception(message: $validateToken['message'], code: $validateToken['response_code']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'response_code' => 200,
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'User data retrieved successfully.',
|
||||||
|
'output' => [
|
||||||
|
'data' => $validateToken['output']['token_data']
|
||||||
|
]
|
||||||
|
];
|
||||||
|
} catch(Exception $e) {
|
||||||
|
return [
|
||||||
|
'response_code' => $e->getCode() ?: 500,
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => $e->getMessage() ?: 'An unexpected error occurred while retrieving user data.'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
66
app/Module/Auth/v0/bootstrap.php
Executable file
66
app/Module/Auth/v0/bootstrap.php
Executable file
@@ -0,0 +1,66 @@
|
|||||||
|
<?php
|
||||||
|
namespace Zampet\Module\Auth;
|
||||||
|
|
||||||
|
use DateTime;
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
// Caminho do manifest.json
|
||||||
|
$manifestPath = realpath(path: __DIR__ . "/manifest.json");
|
||||||
|
|
||||||
|
if (!$manifestPath || !file_exists(filename: $manifestPath)) {
|
||||||
|
throw new Exception(message: "Arquivo 'manifest.json' não encontrado.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lê e decodifica
|
||||||
|
$manifest = json_decode(json: file_get_contents(filename: $manifestPath), associative: true);
|
||||||
|
|
||||||
|
// Verifica estrutura
|
||||||
|
if (!isset($manifest['apis']) || !is_array(value: $manifest['apis'])) {
|
||||||
|
throw new Exception(message: "Estrutura de 'apis' inválida no manifest.");
|
||||||
|
}
|
||||||
|
|
||||||
|
$now = (new DateTime())->format(format: 'Y-m-d');
|
||||||
|
$newApis = [];
|
||||||
|
$updated = false;
|
||||||
|
|
||||||
|
// Checa se tem versão esperando que começa hoje
|
||||||
|
foreach ($manifest['apis'] as $api) {
|
||||||
|
if ($api['status'] === 'planned' && $api['start_date'] === $now) {
|
||||||
|
// Promote a nova
|
||||||
|
$api['status'] = 'active';
|
||||||
|
$newApis[] = $api;
|
||||||
|
$updated = true;
|
||||||
|
} elseif ($api['status'] === 'active' && $api['start_date'] !== $now) {
|
||||||
|
// Ignora versão antiga ativa
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
$newApis[] = $api;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Atualiza manifest se tiver mudança
|
||||||
|
if ($updated) {
|
||||||
|
$manifest['apis'] = $newApis;
|
||||||
|
file_put_contents(filename: $manifestPath, data: json_encode(value: $manifest, flags: JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Carrega a versão ativa
|
||||||
|
$loaded = false;
|
||||||
|
foreach ($manifest['apis'] as $api) {
|
||||||
|
$startDate = new DateTime(datetime: $api['start_date']);
|
||||||
|
if ($api['status'] === 'active' && $startDate <= new DateTime()) {
|
||||||
|
$routeFile = MODULE_PATH . "/{$manifest['dirName']}/{$api['version']}/Routes/Routes.php";
|
||||||
|
if (file_exists(filename: $routeFile)) {
|
||||||
|
require_once realpath(path: $routeFile);
|
||||||
|
$loaded = true;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
throw new Exception(message: "Arquivo de rotas não encontrado para a versão '{$api['version']}'.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verifica se carregou
|
||||||
|
if (!$loaded) {
|
||||||
|
throw new Exception(message: "Nenhuma versão ativa da API foi carregada.");
|
||||||
|
}
|
||||||
16
app/Module/Auth/v0/manifest.json
Executable file
16
app/Module/Auth/v0/manifest.json
Executable file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"name": "Autenticação",
|
||||||
|
"dirName": "Auth",
|
||||||
|
"slug": "auth",
|
||||||
|
"description": "Gerencia login, logout, sessão e permissões de acesso.",
|
||||||
|
"uuid": "0199abc8-026a-75b0-9b25-0f9456a44c70",
|
||||||
|
"version": "1.0",
|
||||||
|
"dependencies": [],
|
||||||
|
"apis": [
|
||||||
|
{
|
||||||
|
"version": "v0",
|
||||||
|
"status": "active",
|
||||||
|
"start_date": "2025-08-01"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
0
app/Module/Tutor/blank_file
Executable file
0
app/Module/Tutor/blank_file
Executable file
322
app/Module/Tutor/v0/Controllers/PetController.php
Executable file
322
app/Module/Tutor/v0/Controllers/PetController.php
Executable file
@@ -0,0 +1,322 @@
|
|||||||
|
<?php
|
||||||
|
namespace Zampet\Module\Tutor\v0\Controllers;
|
||||||
|
|
||||||
|
use Zampet\Helpers\Sanitizer;
|
||||||
|
use Zampet\Services\JWTService;
|
||||||
|
use AxiumPHP\Helpers\RequestHelper;
|
||||||
|
use Zampet\Module\Tutor\v0\DTO\PetDTO;
|
||||||
|
use Zampet\Module\Tutor\v0\DTO\PetRacaDTO;
|
||||||
|
use Zampet\Module\Tutor\v0\Services\PetService;
|
||||||
|
|
||||||
|
class PetController {
|
||||||
|
/**
|
||||||
|
* Lida com a requisição para listar todas as espécies de pets disponíveis.
|
||||||
|
*
|
||||||
|
* Este método atua como um **controlador** para um endpoint de API. Ele delega a busca da lista completa de espécies para o `PetService` e, em seguida, formata e envia a resposta ao cliente em JSON.
|
||||||
|
*
|
||||||
|
* #### Fluxo de Operação:
|
||||||
|
* 1. **Delegação ao Serviço:** Uma instância de `PetService` é criada, e o método `getAllEspecies` é chamado para obter a lista de espécies. O serviço é responsável pela lógica de busca no modelo.
|
||||||
|
* 2. **Envio da Resposta JSON:** O resultado retornado pelo serviço (`especiesPets`) é formatado para uma resposta JSON padronizada usando **`RequestHelper::sendJsonResponse`**. Os dados da resposta (código HTTP, status, mensagem e lista de espécies) são comunicados ao cliente.
|
||||||
|
*
|
||||||
|
* @return void Este método não retorna um valor, pois seu objetivo é enviar uma resposta JSON e encerrar a execução do script.
|
||||||
|
*/
|
||||||
|
public function getAllEspecies(): void {
|
||||||
|
// Lista as espécies de pets
|
||||||
|
$especiesPets = (new PetService())->getAllEspecies();
|
||||||
|
|
||||||
|
// Retorna a resposta em JSON
|
||||||
|
RequestHelper::sendJsonResponse(
|
||||||
|
response_code: $especiesPets['response_code'],
|
||||||
|
status: $especiesPets['status'],
|
||||||
|
message: $especiesPets['message'],
|
||||||
|
output: $especiesPets['output'] ?? []
|
||||||
|
);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lista todos os pets associados ao usuário atualmente autenticado.
|
||||||
|
*
|
||||||
|
* Este método atua como um **controlador** para um endpoint de API, responsável por identificar o usuário autenticado, buscar a lista de seus pets e retornar os dados em formato JSON padronizado.
|
||||||
|
*
|
||||||
|
* #### Fluxo de Operação:
|
||||||
|
* 1. **Obtenção da Identidade do Usuário:** O método primeiro extrai e decodifica o token JWT presente no cabeçalho `Authorization` usando `JWTService::getBearerToken()` e `JWTService::decode()`. O UUID do usuário (tutor) é obtido diretamente do payload decodificado.
|
||||||
|
* 2. **Delegação ao Serviço:** O método `listPetsByTutorUuid` do **`PetService`** é chamado, passando o UUID do usuário para buscar todos os pets associados.
|
||||||
|
* 3. **Envio da Resposta JSON:** O resultado retornado pelo serviço (`petsTutor`) é formatado e enviado de volta ao cliente usando **`RequestHelper::sendJsonResponse`**.
|
||||||
|
* * O código HTTP, status e mensagem são extraídos do array de resposta do serviço, garantindo que a resposta reflita o sucesso ou falha da operação de busca.
|
||||||
|
*
|
||||||
|
* @return void Este método não retorna um valor, pois seu objetivo é enviar uma resposta JSON e encerrar a execução do script.
|
||||||
|
*/
|
||||||
|
public function listMyPets(): void {
|
||||||
|
// Recebe o token JWT do cabeçalho Authorization
|
||||||
|
$decodedToken = JWTService::decode(token: JWTService::getBearerToken());
|
||||||
|
|
||||||
|
// Inicia o serviço de Pet
|
||||||
|
$petsTutor = (new PetService())->listPetsByTutorUuid(uuid: $decodedToken['user_uuid']);
|
||||||
|
|
||||||
|
// Retorna a resposta em JSON
|
||||||
|
RequestHelper::sendJsonResponse(
|
||||||
|
response_code: $petsTutor['response_code'],
|
||||||
|
status: $petsTutor['status'],
|
||||||
|
message: $petsTutor['message'],
|
||||||
|
output: $petsTutor['output'] ?? []
|
||||||
|
);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lida com a requisição de criação de uma nova raça de pet.
|
||||||
|
*
|
||||||
|
* Este método atua como um **controlador** de API, responsável por receber os dados da nova raça (descrição) e o UUID da espécie, validar as entradas e orquestrar a criação do novo registro na camada de serviço.
|
||||||
|
*
|
||||||
|
* #### Fluxo de Operação:
|
||||||
|
* 1. **Validação de Presença:** Verifica se os campos obrigatórios (`especie_uuid` e `descricao`) estão presentes. Se não estiverem, retorna uma resposta JSON com o código **400 Bad Request** e encerra a execução.
|
||||||
|
* 2. **Busca e Validação da Espécie:** Chama `PetService::getEspecieByIdentifier` para confirmar a existência da espécie pelo UUID e obter seu ID interno. Se a espécie não for encontrada, retorna um erro `400`.
|
||||||
|
* 3. **Preparação do DTO:** Cria e popula um **`PetRacaDTO`** com o ID interno da espécie e a descrição sanitizada.
|
||||||
|
* 4. **Delegação ao Serviço:** O método `storeRaca` do **`PetService`** é chamado para executar a lógica de persistência e validação de negócio.
|
||||||
|
* 5. **Envio da Resposta JSON:** O resultado do serviço (`serviceResult`) é formatado e enviado de volta ao cliente, comunicando o status (`201 Created` em caso de sucesso) e a mensagem da operação.
|
||||||
|
*
|
||||||
|
* @return void Este método não retorna um valor, pois seu objetivo é enviar uma resposta JSON e encerrar a execução do script.
|
||||||
|
*/
|
||||||
|
public function storeRaca(): void {
|
||||||
|
// Receber dados do formulário de registro
|
||||||
|
$form = RequestHelper::getFilteredInput(form_type: INPUT_POST);
|
||||||
|
|
||||||
|
// Campos Obrigatórios
|
||||||
|
$required_fields = [
|
||||||
|
'especie_uuid',
|
||||||
|
'descricao'
|
||||||
|
];
|
||||||
|
|
||||||
|
// Verificar se todos os campos obrigatórios estão presentes
|
||||||
|
foreach ($required_fields as $field) {
|
||||||
|
if (empty($form[$field])) {
|
||||||
|
RequestHelper::sendJsonResponse(
|
||||||
|
response_code: 400,
|
||||||
|
status: 'error',
|
||||||
|
message: "Field '{$field}' is required"
|
||||||
|
);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Consulta informações da espécie com base no UUID fornecido
|
||||||
|
$getEspecie = (new PetService())->getEspecieByIdentifier(
|
||||||
|
identifier: 'uuid',
|
||||||
|
value: $form['especie_uuid']
|
||||||
|
);
|
||||||
|
if($getEspecie['status'] !== 'success') {
|
||||||
|
RequestHelper::sendJsonResponse(
|
||||||
|
response_code: 400,
|
||||||
|
status: 'error',
|
||||||
|
message: "Espécie não encontrada",
|
||||||
|
output: ['data' => $form]
|
||||||
|
);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
$especieData = $getEspecie['output']['data'];
|
||||||
|
|
||||||
|
// Chama o serviço de criar raça
|
||||||
|
$serviceResult = (new PetService())->storeRaca(
|
||||||
|
petRacaDTO: new PetRacaDTO(data: [
|
||||||
|
'especie_id' => $especieData['id'],
|
||||||
|
'descricao' => Sanitizer::string(value: $form['descricao'] ?? '')
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
// Enviar a resposta JSON
|
||||||
|
RequestHelper::sendJsonResponse(
|
||||||
|
response_code: $serviceResult['response_code'],
|
||||||
|
status: $serviceResult['status'],
|
||||||
|
message: $serviceResult['message'],
|
||||||
|
output: $serviceResult['output'] ?? []
|
||||||
|
);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lida com a requisição de criação de um novo pet (animal de estimação).
|
||||||
|
*
|
||||||
|
* Este método atua como um **controlador** de API, responsável por receber os dados do formulário, realizar validações de presença e delegar a lógica de negócio (criação do pet e associação ao tutor) para o `PetService`.
|
||||||
|
*
|
||||||
|
* #### Fluxo de Operação:
|
||||||
|
* 1. **Validação de Presença:** Verifica se os campos obrigatórios (`name`, `raca_uuid`, `birthDate`, `sexo`) estão presentes no formulário. Se algum estiver faltando, retorna um erro **400 Bad Request** e encerra a execução.
|
||||||
|
* 2. **Busca e Validação da Raça/Espécie:** Chama `PetService::getEspecieByIdentifier` (apesar do nome, a lógica sugere que este método deve retornar a raça e a espécie) para validar o `raca_uuid` fornecido e obter os IDs internos. Se a raça não for encontrada ou a busca falhar, retorna um erro **400**.
|
||||||
|
* 3. **Preparação do DTO:** Cria e popula um **`PetDTO`** com os dados sanitizados e os IDs internos da raça e espécie.
|
||||||
|
* 4. **Delegação ao Serviço:** O método `storePet` do **`PetService`** é chamado para executar a lógica de persistência e associação do pet ao tutor autenticado.
|
||||||
|
* 5. **Envio da Resposta JSON:** O resultado do serviço (`serviceResult`) é formatado e enviado de volta ao cliente, comunicando o status (código **201 Created** em caso de sucesso) e a mensagem da operação.
|
||||||
|
*
|
||||||
|
* @return void Este método não retorna um valor, pois seu objetivo é enviar uma resposta JSON e encerrar a execução do script.
|
||||||
|
*/
|
||||||
|
public function storePet(): void {
|
||||||
|
// Receber dados do formulário de registro
|
||||||
|
$form = RequestHelper::getFilteredInput(form_type: INPUT_POST);
|
||||||
|
|
||||||
|
// Campos Obrigatórios
|
||||||
|
$required_fields = [
|
||||||
|
'name',
|
||||||
|
'raca_uuid',
|
||||||
|
'birthDate',
|
||||||
|
'sexo',
|
||||||
|
];
|
||||||
|
|
||||||
|
// Verificar se todos os campos obrigatórios estão presentes
|
||||||
|
foreach ($required_fields as $field) {
|
||||||
|
if (empty($form[$field])) {
|
||||||
|
RequestHelper::sendJsonResponse(
|
||||||
|
response_code: 400,
|
||||||
|
status: 'error',
|
||||||
|
message: "Field '{$field}' is required"
|
||||||
|
);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Consulta informações da raça com base no UUID fornecido
|
||||||
|
$getRaca = (new PetService())->getRacaByIdentifier(
|
||||||
|
identifier: 'uuid',
|
||||||
|
value: $form['raca_uuid']
|
||||||
|
);
|
||||||
|
|
||||||
|
if($getRaca['status'] !== 'success') {
|
||||||
|
RequestHelper::sendJsonResponse(
|
||||||
|
response_code: 400,
|
||||||
|
status: 'error',
|
||||||
|
message: "Raça não encontrada"
|
||||||
|
);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Chama o serviço de criar pet
|
||||||
|
$serviceResult = (new PetService())->storePet(
|
||||||
|
petDTO: new PetDTO(data: [
|
||||||
|
"nome" => Sanitizer::string(value: $form['name'] ?? ''),
|
||||||
|
"data_nascimento" => Sanitizer::string(value: $form['birthDate'] ?? ''),
|
||||||
|
"sexo" => Sanitizer::string(value: $form['sexo'] ?? ''),
|
||||||
|
"raca_id" => $getRaca['output']['data']['id'],
|
||||||
|
"especie_id" => $getRaca['output']['data']['especie_id'],
|
||||||
|
"registro_geral_animal" => Sanitizer::string(value: $form['registro_geral_animal'] ?? NULL),
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
// Enviar a resposta JSON
|
||||||
|
RequestHelper::sendJsonResponse(
|
||||||
|
response_code: $serviceResult['response_code'],
|
||||||
|
status: $serviceResult['status'],
|
||||||
|
message: $serviceResult['message'],
|
||||||
|
output: $serviceResult['output'] ?? []
|
||||||
|
);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lida com a requisição para obter detalhes de um pet específico por seu UUID.
|
||||||
|
*
|
||||||
|
* Este método atua como um **controlador** para um endpoint de API. Ele é responsável por delegar a busca dos dados do pet para o serviço e formatar a resposta para o cliente em JSON.
|
||||||
|
*
|
||||||
|
* #### Fluxo de Operação:
|
||||||
|
* 1. **Busca de Detalhes:** O método chama `PetService::getPetByIdentifier()`, usando o UUID fornecido como identificador. O serviço lida com a lógica de consulta no modelo.
|
||||||
|
* 2. **Envio da Resposta JSON:** O resultado retornado pelo serviço (`petDetails`) é formatado e enviado de volta ao cliente usando **`RequestHelper::sendJsonResponse`**. O código HTTP, status e mensagem são extraídos do array de resposta do serviço, comunicando o resultado da busca (sucesso ou falha).
|
||||||
|
*
|
||||||
|
* @param string $uuid O UUID do pet cujos detalhes devem ser buscados.
|
||||||
|
* @return void Este método não retorna um valor, pois seu objetivo é enviar uma resposta JSON e encerrar a execução do script.
|
||||||
|
*/
|
||||||
|
public function getPetDetails(string $uuid): void {
|
||||||
|
// Consulta detalhes do pet com base no UUID fornecido
|
||||||
|
$petDetails = (new PetService())->getPetByIdentifier(
|
||||||
|
identifier: 'uuid',
|
||||||
|
value: $uuid
|
||||||
|
);
|
||||||
|
|
||||||
|
if($petDetails['status'] !== 'success') {
|
||||||
|
RequestHelper::sendJsonResponse(
|
||||||
|
response_code: $petDetails['response_code'],
|
||||||
|
status: $petDetails['status'],
|
||||||
|
message: $petDetails['message'],
|
||||||
|
output: $petDetails['output'] ?? []
|
||||||
|
);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
$pet = $petDetails['output']['data'];
|
||||||
|
$pet['vacinas'] = [
|
||||||
|
[
|
||||||
|
"id" => 1,
|
||||||
|
"nome" => "Antirrábica",
|
||||||
|
"fabricante" => "Zoetis",
|
||||||
|
"lote" => "ZOE-2025-001",
|
||||||
|
"validade" => "2026-02-15",
|
||||||
|
"data_aplicacao" => "2025-02-10",
|
||||||
|
"data_reforco" => "2026-02-10",
|
||||||
|
"veterinario" => "Dr. Carlos Mendes",
|
||||||
|
"created_at" => "2025-02-10 14:30:00",
|
||||||
|
"updated_at" => null,
|
||||||
|
"deleted_at" => null
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"id" => 2,
|
||||||
|
"nome" => "V10",
|
||||||
|
"fabricante" => "Boehringer Ingelheim",
|
||||||
|
"lote" => "BIO-2025-044",
|
||||||
|
"validade" => "2026-06-20",
|
||||||
|
"data_aplicacao" => "2025-03-05",
|
||||||
|
"data_reforco" => "2026-03-05",
|
||||||
|
"veterinario" => "Dra. Fernanda Lopes",
|
||||||
|
"created_at" => "2025-03-05 09:10:00",
|
||||||
|
"updated_at" => null,
|
||||||
|
"deleted_at" => null
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"id" => 3,
|
||||||
|
"nome" => "Giárdia",
|
||||||
|
"fabricante" => "Ceva",
|
||||||
|
"lote" => "CEV-2025-321",
|
||||||
|
"validade" => "2026-08-01",
|
||||||
|
"data_aplicacao" => "2025-04-15",
|
||||||
|
"data_reforco" => "2025-05-15",
|
||||||
|
"veterinario" => "Dr. Rafael Silva",
|
||||||
|
"created_at" => "2025-04-15 11:45:00",
|
||||||
|
"updated_at" => null,
|
||||||
|
"deleted_at" => null
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"id" => 4,
|
||||||
|
"nome" => "Leptospirose",
|
||||||
|
"fabricante" => "MSD",
|
||||||
|
"lote" => "MSD-2025-112",
|
||||||
|
"validade" => "2026-09-30",
|
||||||
|
"data_aplicacao" => "2025-05-20",
|
||||||
|
"data_reforco" => "2026-05-20",
|
||||||
|
"veterinario" => "Dra. Ana Paula",
|
||||||
|
"created_at" => "2025-05-20 10:00:00",
|
||||||
|
"updated_at" => null,
|
||||||
|
"deleted_at" => null
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"id" => 5,
|
||||||
|
"nome" => "Cinomose",
|
||||||
|
"fabricante" => "Vanguard",
|
||||||
|
"lote" => "VAN-2025-210",
|
||||||
|
"validade" => "2026-11-12",
|
||||||
|
"data_aplicacao" => "2025-06-10",
|
||||||
|
"data_reforco" => "2026-06-10",
|
||||||
|
"veterinario" => "Dr. Eduardo Gomes",
|
||||||
|
"created_at" => "2025-06-10 15:20:00",
|
||||||
|
"updated_at" => null,
|
||||||
|
"deleted_at" => null
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
// Retorna a resposta em JSON
|
||||||
|
RequestHelper::sendJsonResponse(
|
||||||
|
response_code: $petDetails['response_code'],
|
||||||
|
status: $petDetails['status'],
|
||||||
|
message: $petDetails['message'],
|
||||||
|
output: ['data' => $pet] ?? []
|
||||||
|
);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updatePet(string $uuid, array $params): void {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
177
app/Module/Tutor/v0/DTO/PetDTO.php
Executable file
177
app/Module/Tutor/v0/DTO/PetDTO.php
Executable file
@@ -0,0 +1,177 @@
|
|||||||
|
<?php
|
||||||
|
namespace Zampet\Module\Tutor\v0\DTO;
|
||||||
|
|
||||||
|
class PetDTO {
|
||||||
|
private ?int $id = null;
|
||||||
|
private ?string $uuid = null;
|
||||||
|
private ?string $nome = null;
|
||||||
|
private ?int $especie_id = null;
|
||||||
|
private ?int $raca_id = null;
|
||||||
|
private ?string $caminho_foto = null;
|
||||||
|
private ?string $registro_geral_animal = null;
|
||||||
|
private ?string $photo_path = null;
|
||||||
|
private ?string $data_nascimento = null;
|
||||||
|
private ?string $data_obito = null;
|
||||||
|
private ?string $sexo = null;
|
||||||
|
private ?string $created_at = null;
|
||||||
|
private ?string $updated_at = null;
|
||||||
|
private ?string $deleted_at = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construtor do DTO.
|
||||||
|
*
|
||||||
|
* Permite inicializar os atributos do objeto a partir de um array associativo.
|
||||||
|
*
|
||||||
|
* @param array<string, mixed> $data Array associativo opcional para popular os atributos.
|
||||||
|
*/
|
||||||
|
public function __construct(array $data = []) {
|
||||||
|
if (!empty($data)) {
|
||||||
|
$this->fromArray($data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Popula o DTO a partir de um array.
|
||||||
|
*
|
||||||
|
* @param array<string, mixed> $data
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function fromArray(array $data): void {
|
||||||
|
$this->id = $data['id'] ?? $this->id;
|
||||||
|
$this->uuid = $data['uuid'] ?? $this->uuid;
|
||||||
|
$this->nome = $data['nome'] ?? $this->nome;
|
||||||
|
$this->especie_id = $data['especie_id'] ?? $this->especie_id;
|
||||||
|
$this->raca_id = $data['raca_id'] ?? $this->raca_id;
|
||||||
|
$this->caminho_foto = $data['caminho_foto'] ?? $this->caminho_foto;
|
||||||
|
$this->registro_geral_animal = $data['registro_geral_animal'] ?? $this->registro_geral_animal;
|
||||||
|
$this->photo_path = $data['photo_path'] ?? $this->photo_path;
|
||||||
|
$this->data_nascimento = $data['data_nascimento'] ?? $this->data_nascimento;
|
||||||
|
$this->data_obito = $data['data_obito'] ?? $this->data_obito;
|
||||||
|
$this->sexo = $data['sexo'] ?? $this->sexo;
|
||||||
|
$this->created_at = $data['created_at'] ?? $this->created_at;
|
||||||
|
$this->updated_at = $data['updated_at'] ?? $this->updated_at;
|
||||||
|
$this->deleted_at = $data['deleted_at'] ?? $this->deleted_at;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converte o DTO para um array associativo.
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function toArray(): array {
|
||||||
|
return [
|
||||||
|
'id' => $this->id,
|
||||||
|
'uuid' => $this->uuid,
|
||||||
|
'nome' => $this->nome,
|
||||||
|
'especie_id' => $this->especie_id,
|
||||||
|
'raca_id' => $this->raca_id,
|
||||||
|
'caminho_foto' => $this->caminho_foto,
|
||||||
|
'registro_geral_animal' => $this->registro_geral_animal,
|
||||||
|
'photo_path' => $this->photo_path,
|
||||||
|
'data_nascimento' => $this->data_nascimento,
|
||||||
|
'data_obito' => $this->data_obito,
|
||||||
|
'sexo' => $this->sexo,
|
||||||
|
'created_at' => $this->created_at,
|
||||||
|
'updated_at' => $this->updated_at,
|
||||||
|
'deleted_at' => $this->deleted_at,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getId(): ?int {
|
||||||
|
return $this->id;
|
||||||
|
}
|
||||||
|
public function setId(?int $id): void {
|
||||||
|
$this->id = $id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getUuid(): ?string {
|
||||||
|
return $this->uuid;
|
||||||
|
}
|
||||||
|
public function setUuid(?string $uuid): void {
|
||||||
|
$this->uuid = $uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getNome(): ?string {
|
||||||
|
return $this->nome;
|
||||||
|
}
|
||||||
|
public function setNome(?string $nome): void {
|
||||||
|
$this->nome = $nome;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getEspecieId(): ?int {
|
||||||
|
return $this->especie_id;
|
||||||
|
}
|
||||||
|
public function setEspecieId(?int $especie_id): void {
|
||||||
|
$this->especie_id = $especie_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRacaId(): ?int {
|
||||||
|
return $this->raca_id;
|
||||||
|
}
|
||||||
|
public function setRacaId(?int $raca_id): void {
|
||||||
|
$this->raca_id = $raca_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCaminhoFoto(): ?string {
|
||||||
|
return $this->caminho_foto;
|
||||||
|
}
|
||||||
|
public function setCaminhoFoto(?string $caminho_foto): void {
|
||||||
|
$this->caminho_foto = $caminho_foto;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRegistroGeralAnimal(): ?string {
|
||||||
|
return $this->registro_geral_animal;
|
||||||
|
}
|
||||||
|
public function setRegistroGeralAnimal(?string $registro_geral_animal): void {
|
||||||
|
$this->registro_geral_animal = $registro_geral_animal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPhotoPath(): ?string {
|
||||||
|
return $this->photo_path;
|
||||||
|
}
|
||||||
|
public function setPhotoPath(?string $photo_path): void {
|
||||||
|
$this->photo_path = $photo_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDataNascimento(): ?string {
|
||||||
|
return $this->data_nascimento;
|
||||||
|
}
|
||||||
|
public function setDataNascimento(?string $data_nascimento): void {
|
||||||
|
$this->data_nascimento = $data_nascimento;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDataObito(): ?string {
|
||||||
|
return $this->data_obito;
|
||||||
|
}
|
||||||
|
public function setDataObito(?string $data_obito): void {
|
||||||
|
$this->data_obito = $data_obito;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSexo(): ?string {
|
||||||
|
return $this->sexo;
|
||||||
|
}
|
||||||
|
public function setSexo(?string $sexo): void {
|
||||||
|
$this->sexo = $sexo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCreatedAt(): ?string {
|
||||||
|
return $this->created_at;
|
||||||
|
}
|
||||||
|
public function setCreatedAt(?string $created_at): void {
|
||||||
|
$this->created_at = $created_at;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getUpdatedAt(): ?string {
|
||||||
|
return $this->updated_at;
|
||||||
|
}
|
||||||
|
public function setUpdatedAt(?string $updated_at): void {
|
||||||
|
$this->updated_at = $updated_at;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDeletedAt(): ?string {
|
||||||
|
return $this->deleted_at;
|
||||||
|
}
|
||||||
|
public function setDeletedAt(?string $deleted_at): void {
|
||||||
|
$this->deleted_at = $deleted_at;
|
||||||
|
}
|
||||||
|
}
|
||||||
77
app/Module/Tutor/v0/DTO/PetRacaDTO.php
Executable file
77
app/Module/Tutor/v0/DTO/PetRacaDTO.php
Executable file
@@ -0,0 +1,77 @@
|
|||||||
|
<?php
|
||||||
|
namespace Zampet\Module\Tutor\v0\DTO;
|
||||||
|
|
||||||
|
class PetRacaDTO {
|
||||||
|
public ?int $id = null;
|
||||||
|
public ?string $uuid = null;
|
||||||
|
public ?int $especie_id = null;
|
||||||
|
public ?string $descricao = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construtor do DTO.
|
||||||
|
*
|
||||||
|
* Permite inicializar os atributos do objeto a partir de um array associativo.
|
||||||
|
*
|
||||||
|
* @param array<string, mixed> $data Array associativo opcional para popular os atributos.
|
||||||
|
*/
|
||||||
|
public function __construct(array $data = []) {
|
||||||
|
if (!empty($data)) {
|
||||||
|
$this->fromArray($data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Popula o DTO a partir de um array.
|
||||||
|
*
|
||||||
|
* @param array<string, mixed> $data
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function fromArray(array $data): void {
|
||||||
|
$this->id = $data['id'] ?? $this->id;
|
||||||
|
$this->uuid = $data['uuid'] ?? $this->uuid;
|
||||||
|
$this->especie_id = $data['especie_id'] ?? $this->especie_id;
|
||||||
|
$this->descricao = $data['descricao'] ?? $this->descricao;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converte o DTO para um array associativo.
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function toArray(): array {
|
||||||
|
return [
|
||||||
|
'id' => $this->id,
|
||||||
|
'uuid' => $this->uuid,
|
||||||
|
'especie_id' => $this->especie_id,
|
||||||
|
'descricao' => $this->descricao,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getId(): ?int {
|
||||||
|
return $this->id;
|
||||||
|
}
|
||||||
|
public function setId(?int $id): void {
|
||||||
|
$this->id = $id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getUuid(): ?string {
|
||||||
|
return $this->uuid;
|
||||||
|
}
|
||||||
|
public function setUuid(?string $uuid): void {
|
||||||
|
$this->uuid = $uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getEspecieId(): ?int {
|
||||||
|
return $this->especie_id;
|
||||||
|
}
|
||||||
|
public function setEspecieId(?int $especie_id): void {
|
||||||
|
$this->especie_id = $especie_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDescricao(): ?string {
|
||||||
|
return $this->descricao;
|
||||||
|
}
|
||||||
|
public function setDescricao(?string $descricao): void {
|
||||||
|
$this->descricao = $descricao;
|
||||||
|
}
|
||||||
|
}
|
||||||
651
app/Module/Tutor/v0/Models/PetModel.php
Executable file
651
app/Module/Tutor/v0/Models/PetModel.php
Executable file
@@ -0,0 +1,651 @@
|
|||||||
|
<?php
|
||||||
|
namespace Zampet\Module\Tutor\v0\Models;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
use Zampet\Services\DB;
|
||||||
|
use Zampet\Module\Tutor\v0\DTO\PetDTO;
|
||||||
|
use Zampet\Module\Tutor\v0\DTO\PetRacaDTO;
|
||||||
|
use Zampet\Module\Auth\v0\Models\UsuarioModel;
|
||||||
|
|
||||||
|
class PetModel {
|
||||||
|
protected string $petTable = 'pet';
|
||||||
|
protected string $usuarioPet = 'usuario_pet';
|
||||||
|
protected string $petAuxRacaTable = 'pet_aux_raca';
|
||||||
|
protected string $petAuxEspecieTable = 'pet_aux_especie';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lista todos os pets associados a um tutor, buscando o tutor por um identificador flexível.
|
||||||
|
*
|
||||||
|
* Este método busca primeiro o registro do tutor usando um identificador (UUID, ID, e-mail, etc.) e, em seguida, usa o ID interno do tutor para consultar todos os pets a ele associados.
|
||||||
|
*
|
||||||
|
* #### Fluxo de Operação:
|
||||||
|
* 1. **Busca do Tutor:** Utiliza `UsuarioModel::getUsuarioByIdentifier` para buscar os dados do tutor. Se o tutor não for encontrado, lança uma `Exception` com o código 404.
|
||||||
|
* 2. **Consulta dos Pets:** Executa uma consulta `SELECT` que une as tabelas de associação (`up`), pets (`p`), raças (`par`) e espécies (`pae`) para obter uma lista detalhada de todos os pets vinculados ao `usuario_id` do tutor.
|
||||||
|
* 3. **Verificação de Pets:**
|
||||||
|
* * Se a consulta não retornar pets (`empty($petsUsuario)`), retorna um array com `status` 'fail' e uma mensagem informativa.
|
||||||
|
* * Se pets forem encontrados, retorna um array com `status` 'success', código `200 OK` e os dados dos pets na chave `output`.
|
||||||
|
*
|
||||||
|
* #### Tratamento de Erros:
|
||||||
|
* - Qualquer `Exception` lançada durante o processo (como tutor não encontrado ou erro de consulta) é capturada.
|
||||||
|
* - Retorna um array com `status` 'error', o código de resposta HTTP da exceção e uma mensagem descritiva.
|
||||||
|
*
|
||||||
|
* @param string $identifier A coluna da tabela de usuário a ser usada para identificar o tutor (ex: 'uuid', 'id').
|
||||||
|
* @param mixed $value O valor correspondente ao identificador do tutor.
|
||||||
|
* @return array Um array associativo contendo o status da operação, o código HTTP e os dados dos pets (`output['data']`), ou uma mensagem de erro.
|
||||||
|
*/
|
||||||
|
public function listPetsByTutorIdentifier(string $identifier, mixed $value): array {
|
||||||
|
try {
|
||||||
|
$usuarioData = (new UsuarioModel())->getUsuarioByIdentifier(identifier: $identifier, value: $value);
|
||||||
|
if(!$usuarioData) {
|
||||||
|
throw new Exception(message: 'Tutor not found', code: 404);
|
||||||
|
}
|
||||||
|
$usuarioData = $usuarioData['output']['data'];
|
||||||
|
|
||||||
|
$sql =
|
||||||
|
"SELECT
|
||||||
|
p.id,
|
||||||
|
p.uuid,
|
||||||
|
p.nome,
|
||||||
|
p.especie_id,
|
||||||
|
pae.descricao AS especie,
|
||||||
|
p.raca_id,
|
||||||
|
par.descricao AS raca,
|
||||||
|
p.caminho_foto,
|
||||||
|
p.registro_geral_animal,
|
||||||
|
p.photo_path,
|
||||||
|
p.data_nascimento,
|
||||||
|
p.data_obito,
|
||||||
|
p.sexo,
|
||||||
|
p.created_at,
|
||||||
|
p.updated_at,
|
||||||
|
p.deleted_at
|
||||||
|
FROM {$this->usuarioPet} up
|
||||||
|
LEFT JOIN {$this->petTable} p ON up.pet_id = p.id
|
||||||
|
LEFT JOIN {$this->petAuxRacaTable} par ON p.raca_id = par.id
|
||||||
|
LEFT JOIN {$this->petAuxEspecieTable} pae ON p.especie_id = pae.id
|
||||||
|
WHERE up.usuario_id = :usuario_id";
|
||||||
|
|
||||||
|
$params = [
|
||||||
|
':usuario_id' => $usuarioData['id'],
|
||||||
|
];
|
||||||
|
|
||||||
|
$petsUsuario = DB::fetchAll(sql: $sql, params: $params);
|
||||||
|
if(empty($petsUsuario)) {
|
||||||
|
return [
|
||||||
|
'response_code' => 200,
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Você não possui pets cadastrados.',
|
||||||
|
'output' => [
|
||||||
|
'data' => []
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'response_code' => 200,
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Pets encontrados com sucesso.',
|
||||||
|
'output' => [
|
||||||
|
'data' => $petsUsuario
|
||||||
|
]
|
||||||
|
];
|
||||||
|
} catch(Exception $e) {
|
||||||
|
return [
|
||||||
|
'response_code' => $e->getCode() ?? 500,
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => $e->getMessage(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lista todas as espécies de pets disponíveis no sistema auxiliar.
|
||||||
|
*
|
||||||
|
* Este método consulta a tabela de espécies (`{$this->petAuxEspecieTable}`) e retorna uma lista de todos os registros (ID e descrição).
|
||||||
|
*
|
||||||
|
* #### Fluxo de Operação:
|
||||||
|
* 1. **Execução da Consulta:** Executa uma consulta `SELECT` para buscar todos os IDs e descrições da tabela auxiliar de espécies.
|
||||||
|
* 2. **Verificação de Resultados:**
|
||||||
|
* - Se a consulta for bem-sucedida, mas **não retornar nenhuma espécie** (`empty($stmt)`), retorna um array com `status` 'fail' e código `200 OK`.
|
||||||
|
* - Se a consulta for bem-sucedida e **retornar espécies**, retorna um array com `status` 'success', código `200 OK` e os dados encontrados na chave `output`.
|
||||||
|
*
|
||||||
|
* #### Tratamento de Erros:
|
||||||
|
* - Qualquer `Exception` capturada durante a consulta retorna um array com `status` 'error', o código de erro da exceção e uma mensagem detalhada de erro.
|
||||||
|
*
|
||||||
|
* @return array Um array associativo contendo o status da operação, o código de resposta HTTP e os dados das espécies (`output['data']`).
|
||||||
|
*/
|
||||||
|
public function getAllEspecies(): array {
|
||||||
|
try {
|
||||||
|
$sql =
|
||||||
|
"SELECT
|
||||||
|
id,
|
||||||
|
uuid,
|
||||||
|
descricao
|
||||||
|
FROM {$this->petAuxEspecieTable}";
|
||||||
|
|
||||||
|
$result = DB::fetchAll(sql: $sql);
|
||||||
|
if(empty($result)) {
|
||||||
|
return [
|
||||||
|
'response_code' => 200,
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Nenhuma espécie encontrada.',
|
||||||
|
'output' => [
|
||||||
|
'data' => []
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$preparedResult = [];
|
||||||
|
foreach($result as $key => $especie) {
|
||||||
|
$sql =
|
||||||
|
"SELECT
|
||||||
|
uuid,
|
||||||
|
descricao
|
||||||
|
FROM {$this->petAuxRacaTable}
|
||||||
|
WHERE especie_id = :especie_id";
|
||||||
|
|
||||||
|
$params = [
|
||||||
|
':especie_id' => $especie['id']
|
||||||
|
];
|
||||||
|
|
||||||
|
$result = DB::fetchAll(sql: $sql, params: $params);
|
||||||
|
|
||||||
|
$preparedResult[] = [
|
||||||
|
'uuid' => $especie['uuid'],
|
||||||
|
'descricao' => $especie['descricao'],
|
||||||
|
'racas' => $result
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'response_code' => 200,
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Espécies encontradas com sucesso.',
|
||||||
|
'output' => [
|
||||||
|
'data' => $preparedResult
|
||||||
|
]
|
||||||
|
];
|
||||||
|
} catch(Exception $e) {
|
||||||
|
return [
|
||||||
|
'response_code' => $e->getCode() ?? 500,
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => $e->getMessage(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Busca uma única espécie de pet no banco de dados por um identificador específico.
|
||||||
|
*
|
||||||
|
* Este método recupera os dados de uma espécie da tabela auxiliar `{$this->petAuxEspecieTable}` de forma flexível, permitindo a busca por qualquer coluna (como 'id' ou 'uuid') e o valor correspondente.
|
||||||
|
*
|
||||||
|
* #### Fluxo de Operação:
|
||||||
|
* 1. **Execução da Consulta:** O método monta e executa uma consulta `SELECT` que usa o `$identifier` de forma dinâmica na cláusula `WHERE` e um parâmetro nomeado seguro (`:value`) para o valor. A consulta utiliza `DB::fetchOne`, indicando que apenas um registro é esperado.
|
||||||
|
* 2. **Verificação de Resultados:**
|
||||||
|
* - Se a consulta for bem-sucedida e **nenhuma espécie for encontrada** (`empty($result)`), retorna um array com **`status` 'fail'** e código **200 OK**.
|
||||||
|
* - Se uma espécie for encontrada, retorna um array com **`status` 'success'**, código **200 OK** e os dados do registro na chave `output`.
|
||||||
|
*
|
||||||
|
* #### Tratamento de Erros:
|
||||||
|
* - Qualquer `Exception` capturada durante a consulta retorna um array com **`status` 'error'**, o código de erro da exceção e uma mensagem de erro detalhada.
|
||||||
|
*
|
||||||
|
* @param string $identifier A coluna da tabela a ser usada como critério de busca (ex: 'id', 'uuid', 'descricao').
|
||||||
|
* @param mixed $value O valor correspondente ao identificador que está sendo buscado.
|
||||||
|
* @return array Um array associativo contendo o status da operação, um código de resposta HTTP e os dados da espécie, se encontrada.
|
||||||
|
*/
|
||||||
|
public function getEspecieByIdentifier(string $identifier, mixed $value): array {
|
||||||
|
try {
|
||||||
|
$sql =
|
||||||
|
"SELECT
|
||||||
|
id,
|
||||||
|
uuid,
|
||||||
|
descricao
|
||||||
|
FROM {$this->petAuxEspecieTable}
|
||||||
|
WHERE {$identifier} = :value
|
||||||
|
ORDER BY descricao ASC";
|
||||||
|
|
||||||
|
$params = [
|
||||||
|
':value' => $value
|
||||||
|
];
|
||||||
|
|
||||||
|
$result = DB::fetchOne(sql: $sql, params: $params);
|
||||||
|
if(empty($result)) {
|
||||||
|
return [
|
||||||
|
'response_code' => 200,
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Espécie não encontrada.',
|
||||||
|
'output' => [
|
||||||
|
'data' => []
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'response_code' => 200,
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Espécie encontrada com sucesso.',
|
||||||
|
'output' => [
|
||||||
|
'data' => $result
|
||||||
|
]
|
||||||
|
];
|
||||||
|
} catch(Exception $e) {
|
||||||
|
return [
|
||||||
|
'response_code' => $e->getCode() ?? 500,
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => $e->getMessage(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Busca uma única raça de pet no banco de dados por um identificador específico.
|
||||||
|
*
|
||||||
|
* Este método recupera os dados de uma raça da tabela auxiliar `{$this->petAuxRacaTable}` de forma flexível, permitindo a busca por qualquer coluna como identificador (`$identifier`) e o valor correspondente. A consulta retorna apenas um registro e é ordenada pela descrição.
|
||||||
|
*
|
||||||
|
* #### Fluxo de Operação:
|
||||||
|
* 1. **Execução da Consulta:** O método monta e executa uma consulta `SELECT` que usa o `$identifier` de forma dinâmica na cláusula `WHERE` e um parâmetro nomeado seguro (`:value`) para o valor.
|
||||||
|
* 2. **Verificação de Resultados:**
|
||||||
|
* - Se uma raça for encontrada, retorna um array com **`status` 'success'**, código **200 OK** e os dados da raça na chave `output`.
|
||||||
|
* - Se a consulta for bem-sucedida, mas **nenhuma raça for encontrada** (`empty($result)`), retorna um array com **`status` 'success'** (indicando que a consulta foi bem-sucedida) e uma mensagem de "Raça não encontrada.".
|
||||||
|
*
|
||||||
|
* #### Tratamento de Erros:
|
||||||
|
* - Qualquer `Exception` capturada durante a consulta retorna um array com **`status` 'error'**, o código de erro da exceção (ou `500` como fallback) e uma mensagem de erro detalhada.
|
||||||
|
*
|
||||||
|
* @param string $identifier A coluna da tabela a ser usada como critério de busca (ex: 'uuid', 'id', 'descricao').
|
||||||
|
* @param mixed $value O valor correspondente ao identificador que está sendo buscado.
|
||||||
|
* @return array Um array associativo contendo o status da operação, um código de resposta HTTP e os dados da raça, se encontrada.
|
||||||
|
*/
|
||||||
|
public function getRacaByIdentifier(string $identifier, mixed $value): array {
|
||||||
|
try {
|
||||||
|
$sql =
|
||||||
|
"SELECT
|
||||||
|
id,
|
||||||
|
uuid,
|
||||||
|
especie_id,
|
||||||
|
descricao
|
||||||
|
FROM {$this->petAuxRacaTable}
|
||||||
|
WHERE {$identifier} = :value
|
||||||
|
ORDER BY descricao ASC";
|
||||||
|
|
||||||
|
$params = [
|
||||||
|
':value' => $value
|
||||||
|
];
|
||||||
|
|
||||||
|
$result = DB::fetchOne(sql: $sql, params: $params);
|
||||||
|
if(empty($result)) {
|
||||||
|
return [
|
||||||
|
'response_code' => 200,
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Raça não encontrada.',
|
||||||
|
'output' => [
|
||||||
|
'data' => []
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'response_code' => 200,
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Raça encontrada com sucesso.',
|
||||||
|
'output' => [
|
||||||
|
'data' => $result
|
||||||
|
]
|
||||||
|
];
|
||||||
|
} catch(Exception $e) {
|
||||||
|
return [
|
||||||
|
'response_code' => $e->getCode() ?? 500,
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => $e->getMessage(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Busca todas as raças de pets associadas a uma espécie específica.
|
||||||
|
*
|
||||||
|
* Este método primeiro consulta a espécie usando um identificador (UUID, ID, etc.) e, em seguida, usa o ID interno da espécie para listar todas as raças vinculadas a ela na tabela auxiliar `{$this->petAuxRacaTable}`.
|
||||||
|
*
|
||||||
|
* #### Fluxo de Operação:
|
||||||
|
* 1. **Busca da Espécie:** O método chama `getEspecieByIdentifier` para encontrar o ID da espécie. Se a busca falhar, o resultado de erro é retornado imediatamente.
|
||||||
|
* 2. **Consulta das Raças:** Executa uma consulta `SELECT` que busca todas as raças onde `especie_id` corresponde ao ID da espécie encontrado. Os resultados são ordenados alfabeticamente pela descrição.
|
||||||
|
* 3. **Verificação de Resultados:**
|
||||||
|
* - Se a consulta for bem-sucedida, mas **nenhuma raça for encontrada**, retorna um array com **`status` 'fail'** e código **200 OK**.
|
||||||
|
* - Se raças forem encontradas, retorna um array com **`status` 'success'**, código **200 OK** e os dados encontrados na chave `output`.
|
||||||
|
*
|
||||||
|
* #### Tratamento de Erros:
|
||||||
|
* - Qualquer `Exception` capturada durante o processo (além daquelas tratadas na busca inicial) retorna um array com **`status` 'error'**, o código de erro da exceção e uma mensagem de erro detalhada.
|
||||||
|
*
|
||||||
|
* @param string $identifier A coluna da tabela auxiliar de espécies a ser usada para identificar a espécie (ex: 'uuid', 'id').
|
||||||
|
* @param mixed $value O valor correspondente ao identificador da espécie.
|
||||||
|
* @return array Um array associativo contendo o status da operação, o código de resposta HTTP e os dados das raças (`output['data']`).
|
||||||
|
*/
|
||||||
|
public function getRacasByEspecieIdentifier(string $identifier, mixed $value): array {
|
||||||
|
try {
|
||||||
|
$especieData = $this->getEspecieByIdentifier(identifier: $identifier, value: $value);
|
||||||
|
if($especieData['status'] !== 'success') {
|
||||||
|
return $especieData;
|
||||||
|
}
|
||||||
|
$especieData = $especieData['output']['data'];
|
||||||
|
|
||||||
|
$sql =
|
||||||
|
"SELECT
|
||||||
|
id,
|
||||||
|
uuid,
|
||||||
|
descricao
|
||||||
|
FROM {$this->petAuxRacaTable}
|
||||||
|
WHERE especie_id = :especie_id
|
||||||
|
ORDER BY descricao ASC";
|
||||||
|
|
||||||
|
$params = [
|
||||||
|
':especie_id' => $especieData['id']
|
||||||
|
];
|
||||||
|
|
||||||
|
$racas = DB::fetchAll(sql: $sql, params: $params);
|
||||||
|
if(empty($racas)) {
|
||||||
|
return [
|
||||||
|
'response_code' => 200,
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Nenhuma raça encontrada para a espécie informada.',
|
||||||
|
'output' => [
|
||||||
|
'data' => []
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'response_code' => 200,
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Raças encontradas com sucesso.',
|
||||||
|
'output' => [
|
||||||
|
'data' => $racas
|
||||||
|
]
|
||||||
|
];
|
||||||
|
} catch(Exception $e) {
|
||||||
|
return [
|
||||||
|
'response_code' => $e->getCode() ?? 500,
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => $e->getMessage(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Armazena uma nova raça de pet no banco de dados.
|
||||||
|
*
|
||||||
|
* Este método insere os dados de uma nova raça na tabela auxiliar `{$this->petAuxRacaTable}`. A raça é associada a uma espécie existente. A operação utiliza um bloco `try-catch` para garantir o tratamento de erros.
|
||||||
|
*
|
||||||
|
* #### Fluxo de Operação:
|
||||||
|
* 1. **Execução da Query:** Prepara e executa a instrução `INSERT` para inserir o novo registro com o UUID, ID da espécie e a descrição.
|
||||||
|
* 2. **Confirmação e Retorno de Sucesso:** Se a inserção for bem-sucedida, retorna um array com **`status` 'success'**, código **201 Created** e uma mensagem de êxito.
|
||||||
|
*
|
||||||
|
* #### Tratamento de Erros:
|
||||||
|
* - Qualquer `Exception` capturada durante o processo retorna um array com **`status` 'error'**, o código de erro da exceção e uma mensagem detalhada de erro.
|
||||||
|
*
|
||||||
|
* @param PetRacaDTO $petRacaDTO O objeto DTO contendo o UUID, o ID da espécie (`especie_id`) e a descrição da raça a ser criada.
|
||||||
|
* @return array Um array associativo contendo o status da operação, o código de resposta HTTP e uma mensagem.
|
||||||
|
*/
|
||||||
|
public function storeRaca(PetRacaDTO $petRacaDTO): array {
|
||||||
|
try {
|
||||||
|
$sql =
|
||||||
|
"INSERT INTO {$this->petAuxRacaTable} (
|
||||||
|
uuid,
|
||||||
|
especie_id,
|
||||||
|
descricao
|
||||||
|
) VALUES (
|
||||||
|
:uuid,
|
||||||
|
:especie_id,
|
||||||
|
:descricao
|
||||||
|
)";
|
||||||
|
|
||||||
|
$params = [
|
||||||
|
':uuid' => $petRacaDTO->getUuid(),
|
||||||
|
':especie_id' => $petRacaDTO->getEspecieId(),
|
||||||
|
':descricao' => $petRacaDTO->getDescricao()
|
||||||
|
];
|
||||||
|
|
||||||
|
DB::execute(sql: $sql, params: $params);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'response_code' => 201,
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Raça armazenada com sucesso.',
|
||||||
|
'output' => [
|
||||||
|
'data' => [
|
||||||
|
'id' => DB::lastInsertId(),
|
||||||
|
'uuid' => $petRacaDTO->getUuid(),
|
||||||
|
'especie_id' => $petRacaDTO->getEspecieId(),
|
||||||
|
'descricao' => $petRacaDTO->getDescricao()
|
||||||
|
]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
} catch(Exception $e) {
|
||||||
|
return [
|
||||||
|
'response_code' => $e->getCode() ?? 500,
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => $e->getMessage(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Armazena um novo pet no banco de dados.
|
||||||
|
*
|
||||||
|
* Este método insere os dados de um novo pet na tabela `{$this->petTable}`, utilizando um objeto `PetDTO` que encapsula todas as informações relevantes. A operação é atômica e utiliza um bloco `try-catch` para garantir o tratamento de erros.
|
||||||
|
*
|
||||||
|
* #### Fluxo de Operação:
|
||||||
|
* 1. **Execução da Query:** Prepara e executa a instrução `INSERT` para inserir o novo registro com todos os detalhes do pet (UUID, nome, espécie, raça, dados de saúde, foto, etc.).
|
||||||
|
* 2. **Confirmação e Retorno de Sucesso:** Se a inserção for bem-sucedida, retorna um array com **`status` 'success'**, código **201 Created** e uma mensagem de êxito. Os dados do pet recém-criado, incluindo o ID gerado pelo banco, são retornados na chave `output`.
|
||||||
|
*
|
||||||
|
* #### Tratamento de Erros:
|
||||||
|
* - Qualquer `Exception` capturada durante o processo retorna um array com **`status` 'error'**, o código de erro da exceção e uma mensagem detalhada de erro.
|
||||||
|
*
|
||||||
|
* @param PetDTO $petDTO O objeto DTO contendo os dados do pet a ser criado.
|
||||||
|
* @return array Um array associativo contendo o status da operação, o código de resposta HTTP e os dados do pet, se a operação for bem-sucedida.
|
||||||
|
*/
|
||||||
|
public function storePet(PetDTO $petDTO): array {
|
||||||
|
try {
|
||||||
|
$sql =
|
||||||
|
"INSERT INTO {$this->petTable} (
|
||||||
|
uuid,
|
||||||
|
nome,
|
||||||
|
especie_id,
|
||||||
|
raca_id,
|
||||||
|
caminho_foto,
|
||||||
|
registro_geral_animal,
|
||||||
|
photo_path,
|
||||||
|
data_nascimento,
|
||||||
|
data_obito,
|
||||||
|
sexo,
|
||||||
|
created_at,
|
||||||
|
updated_at,
|
||||||
|
deleted_at
|
||||||
|
) VALUES (
|
||||||
|
:uuid,
|
||||||
|
:nome,
|
||||||
|
:especie_id,
|
||||||
|
:raca_id,
|
||||||
|
:caminho_foto,
|
||||||
|
:registro_geral_animal,
|
||||||
|
:photo_path,
|
||||||
|
:data_nascimento,
|
||||||
|
:data_obito,
|
||||||
|
:sexo,
|
||||||
|
:created_at,
|
||||||
|
:updated_at,
|
||||||
|
:deleted_at
|
||||||
|
)";
|
||||||
|
|
||||||
|
$params = [
|
||||||
|
':uuid' => $petDTO->getUuid(),
|
||||||
|
':nome' => $petDTO->getNome(),
|
||||||
|
':especie_id' => $petDTO->getEspecieId(),
|
||||||
|
':raca_id' => $petDTO->getRacaId(),
|
||||||
|
':caminho_foto' => $petDTO->getCaminhoFoto(),
|
||||||
|
':registro_geral_animal' => $petDTO->getRegistroGeralAnimal(),
|
||||||
|
':photo_path' => $petDTO->getPhotoPath(),
|
||||||
|
':data_nascimento' => $petDTO->getDataNascimento(),
|
||||||
|
':data_obito' => $petDTO->getDataObito(),
|
||||||
|
':sexo' => $petDTO->getSexo(),
|
||||||
|
':created_at' => $petDTO->getCreatedAt(),
|
||||||
|
':updated_at' => $petDTO->getUpdatedAt(),
|
||||||
|
':deleted_at' => $petDTO->getDeletedAt()
|
||||||
|
];
|
||||||
|
|
||||||
|
DB::execute(sql: $sql, params: $params);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'response_code' => 201,
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Pet armazenado com sucesso.',
|
||||||
|
'output' => [
|
||||||
|
'data' => [
|
||||||
|
'id' => DB::lastInsertId(),
|
||||||
|
'uuid' => $petDTO->getUuid(),
|
||||||
|
'nome' => $petDTO->getNome(),
|
||||||
|
'especie_id' => $petDTO->getEspecieId(),
|
||||||
|
'raca_id' => $petDTO->getRacaId(),
|
||||||
|
'caminho_foto' => $petDTO->getCaminhoFoto(),
|
||||||
|
'registro_geral_animal' => $petDTO->getRegistroGeralAnimal(),
|
||||||
|
'photo_path' => $petDTO->getPhotoPath(),
|
||||||
|
'data_nascimento' => $petDTO->getDataNascimento(),
|
||||||
|
'data_obito' => $petDTO->getDataObito(),
|
||||||
|
'sexo' => $petDTO->getSexo(),
|
||||||
|
'created_at' => $petDTO->getCreatedAt(),
|
||||||
|
'updated_at' => $petDTO->getUpdatedAt(),
|
||||||
|
'deleted_at' => $petDTO->getDeletedAt()
|
||||||
|
]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
} catch(Exception $e) {
|
||||||
|
return [
|
||||||
|
'response_code' => $e->getCode() ?? 500,
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => $e->getMessage(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Associa um pet a um usuário (tutor) no banco de dados.
|
||||||
|
*
|
||||||
|
* Este método insere um novo registro na tabela de associação `{$this->usuarioPet}`, estabelecendo a relação de propriedade entre o usuário e o pet.
|
||||||
|
*
|
||||||
|
* #### Fluxo de Operação:
|
||||||
|
* 1. **Execução da Query:** Prepara e executa a instrução `INSERT` para inserir os IDs do usuário e do pet na tabela de associação.
|
||||||
|
* 2. **Retorno de Sucesso:** Se a inserção for bem-sucedida, retorna um array com **`status` 'success'**, código **201 Created** e uma mensagem de êxito.
|
||||||
|
*
|
||||||
|
* #### Tratamento de Erros:
|
||||||
|
* - Qualquer `Exception` capturada durante o processo retorna um array com **`status` 'error'**, o código de erro da exceção (ou `500` como fallback) e uma mensagem detalhada de erro.
|
||||||
|
*
|
||||||
|
* @param int $usuario_id O ID do usuário (tutor) a ser associado.
|
||||||
|
* @param int $pet_id O ID do pet a ser associado.
|
||||||
|
* @return array Um array associativo contendo o status da operação, um código de resposta HTTP e uma mensagem.
|
||||||
|
*/
|
||||||
|
public function storeUsuarioPet(int $usuario_id, int $pet_id): array {
|
||||||
|
try {
|
||||||
|
$sql =
|
||||||
|
"INSERT INTO {$this->usuarioPet} (
|
||||||
|
usuario_id,
|
||||||
|
pet_id
|
||||||
|
) VALUES (
|
||||||
|
:usuario_id,
|
||||||
|
:pet_id
|
||||||
|
)";
|
||||||
|
|
||||||
|
$params = [
|
||||||
|
':usuario_id' => $usuario_id,
|
||||||
|
':pet_id' => $pet_id
|
||||||
|
];
|
||||||
|
|
||||||
|
DB::execute(sql: $sql, params: $params);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'response_code' => 201,
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Associação entre usuário e pet criada com sucesso.'
|
||||||
|
];
|
||||||
|
} catch(Exception $e) {
|
||||||
|
return [
|
||||||
|
'response_code' => $e->getCode() ?? 500,
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => $e->getMessage(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Busca um único pet no banco de dados por um identificador específico.
|
||||||
|
*
|
||||||
|
* Este método recupera todos os detalhes de um pet da tabela `{$this->petTable}`. A busca é flexível e permite usar qualquer coluna como identificador (`$identifier`) e o valor correspondente.
|
||||||
|
*
|
||||||
|
* #### Fluxo de Operação:
|
||||||
|
* 1. **Execução da Query:** O método monta e executa uma consulta `SELECT` que usa o `$identifier` de forma dinâmica na cláusula `WHERE`. A consulta usa `DB::fetchOne`, indicando que apenas um registro é esperado, e ordena o resultado pelo nome.
|
||||||
|
* 2. **Verificação de Resultados:**
|
||||||
|
* - Se o pet for encontrado, retorna um array com **`status` 'success'**, código **200 OK** e os dados do pet na chave `output`.
|
||||||
|
* - Se o pet **não for encontrado** (`empty($result)`), retorna um array com **`status` 'success'** (indicando que a consulta foi bem-sucedida) e uma mensagem de "Pet não encontrado.".
|
||||||
|
*
|
||||||
|
* #### Tratamento de Erros:
|
||||||
|
* - Qualquer `Exception` capturada durante o processo retorna um array com **`status` 'error'**, o código de erro da exceção (ou `500` como fallback) e uma mensagem de erro detalhada.
|
||||||
|
*
|
||||||
|
* @param string $identifier A coluna da tabela a ser usada como critério de busca (ex: 'uuid', 'id', 'registro_geral_animal').
|
||||||
|
* @param string $value O valor correspondente ao identificador que está sendo buscado.
|
||||||
|
* @return array Um array associativo contendo o status da operação, um código de resposta HTTP e os dados do pet, se encontrado.
|
||||||
|
*/
|
||||||
|
public function getPetByIdentifier(string $identifier, string $value): array {
|
||||||
|
try {
|
||||||
|
$sql =
|
||||||
|
"SELECT
|
||||||
|
p.id,
|
||||||
|
p.uuid,
|
||||||
|
p.nome,
|
||||||
|
p.especie_id,
|
||||||
|
pae.descricao AS especie,
|
||||||
|
p.raca_id,
|
||||||
|
par.descricao AS raca,
|
||||||
|
p.caminho_foto,
|
||||||
|
p.registro_geral_animal,
|
||||||
|
p.photo_path,
|
||||||
|
p.data_nascimento,
|
||||||
|
p.data_obito,
|
||||||
|
p.sexo,
|
||||||
|
p.created_at,
|
||||||
|
p.updated_at,
|
||||||
|
p.deleted_at
|
||||||
|
FROM {$this->petTable} p
|
||||||
|
LEFT JOIN {$this->petAuxRacaTable} par ON p.raca_id = par.id
|
||||||
|
LEFT JOIN {$this->petAuxEspecieTable} pae ON p.especie_id = pae.id
|
||||||
|
WHERE p.{$identifier} = :value
|
||||||
|
ORDER BY p.nome ASC";
|
||||||
|
|
||||||
|
$params = [
|
||||||
|
':value' => $value
|
||||||
|
];
|
||||||
|
|
||||||
|
$result = DB::fetchOne(sql: $sql, params: $params);
|
||||||
|
if(empty($result)) {
|
||||||
|
return [
|
||||||
|
'response_code' => 200,
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Pet não encontrado.',
|
||||||
|
'output' => [
|
||||||
|
'data' => []
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'response_code' => 200,
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Pet encontrado com sucesso.',
|
||||||
|
'output' => [
|
||||||
|
'data' => $result
|
||||||
|
]
|
||||||
|
];
|
||||||
|
} catch(Exception $e) {
|
||||||
|
return [
|
||||||
|
'response_code' => $e->getCode() ?? 500,
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => $e->getMessage(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
41
app/Module/Tutor/v0/Routes/PetRoutes.php
Executable file
41
app/Module/Tutor/v0/Routes/PetRoutes.php
Executable file
@@ -0,0 +1,41 @@
|
|||||||
|
<?php
|
||||||
|
use AxiumPHP\Core\Router;
|
||||||
|
use Zampet\Module\Tutor\v0\Controllers\PetController;
|
||||||
|
|
||||||
|
Router::group(
|
||||||
|
prefix: '/pet',
|
||||||
|
callback: function() {
|
||||||
|
// Rota para listar todos os pets do tutor autenticado
|
||||||
|
Router::GET(
|
||||||
|
uri: '/list-my-pets',
|
||||||
|
handler: [PetController::class, 'listMyPets']
|
||||||
|
);
|
||||||
|
|
||||||
|
// Rota para listar todas as espécies e raças de pets
|
||||||
|
Router::GET(
|
||||||
|
uri: '/especies',
|
||||||
|
handler: [PetController::class, 'getAllEspecies']
|
||||||
|
);
|
||||||
|
|
||||||
|
// Cria Nova Raça
|
||||||
|
Router::POST(
|
||||||
|
uri: '/create-raca',
|
||||||
|
handler: [PetController::class, 'storeRaca']
|
||||||
|
);
|
||||||
|
|
||||||
|
// Cria Novo Pet
|
||||||
|
Router::POST(
|
||||||
|
uri: '/create-pet',
|
||||||
|
handler: [PetController::class, 'storePet']
|
||||||
|
);
|
||||||
|
|
||||||
|
// Consulta Detalhes do Pet
|
||||||
|
Router::GET(
|
||||||
|
uri: '/{pet_uuid}',
|
||||||
|
handler: [PetController::class, 'getPetDetails']
|
||||||
|
);
|
||||||
|
},
|
||||||
|
middlewares: [
|
||||||
|
|
||||||
|
]
|
||||||
|
);
|
||||||
9
app/Module/Tutor/v0/Routes/Routes.php
Executable file
9
app/Module/Tutor/v0/Routes/Routes.php
Executable file
@@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
use AxiumPHP\Core\Router;
|
||||||
|
|
||||||
|
Router::group(
|
||||||
|
prefix: '/tutor/v0',
|
||||||
|
callback: function() {
|
||||||
|
require 'PetRoutes.php';
|
||||||
|
}
|
||||||
|
);
|
||||||
241
app/Module/Tutor/v0/Services/PetService.php
Executable file
241
app/Module/Tutor/v0/Services/PetService.php
Executable file
@@ -0,0 +1,241 @@
|
|||||||
|
<?php
|
||||||
|
namespace Zampet\Module\Tutor\v0\Services;
|
||||||
|
|
||||||
|
use DateTime;
|
||||||
|
use Exception;
|
||||||
|
use Ramsey\Uuid\Uuid;
|
||||||
|
use Zampet\Module\Auth\v0\Models\UsuarioModel;
|
||||||
|
use Zampet\Services\DB;
|
||||||
|
use Zampet\Services\JWTService;
|
||||||
|
use Zampet\Helpers\DataValidator;
|
||||||
|
use Zampet\Module\Tutor\v0\DTO\PetDTO;
|
||||||
|
use Zampet\Module\Tutor\v0\DTO\PetRacaDTO;
|
||||||
|
use Zampet\Module\Tutor\v0\Models\PetModel;
|
||||||
|
|
||||||
|
class PetService {
|
||||||
|
/**
|
||||||
|
* Retorna a lista de todas as espécies de pets disponíveis no sistema.
|
||||||
|
*
|
||||||
|
* Este método atua como um **wrapper** (invólucro) para o método de mesmo nome na classe `PetModel`. Ele delega a responsabilidade de buscar a lista completa das espécies de pets cadastradas para a camada de modelo (DAO).
|
||||||
|
*
|
||||||
|
* @return array Um array associativo contendo o status da operação, o código HTTP e os dados das espécies, conforme retornado pelo `PetModel`.
|
||||||
|
*/
|
||||||
|
public function getAllEspecies(): array {
|
||||||
|
return (new PetModel())->getAllEspecies();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lista todos os pets de um tutor específico, buscando pelo UUID do tutor.
|
||||||
|
*
|
||||||
|
* Este método atua como um **wrapper** para o método `listPetsByTutorIdentifier` da classe `PetModel`. Ele delega a responsabilidade de buscar a lista completa e detalhada de pets associados a um tutor, utilizando o UUID como o identificador principal.
|
||||||
|
*
|
||||||
|
* @param string $uuid O UUID do tutor cujos pets devem ser listados.
|
||||||
|
* @return array Um array associativo contendo o status da operação, o código HTTP e os dados dos pets, conforme retornado pelo `PetModel`.
|
||||||
|
*/
|
||||||
|
public function listPetsByTutorUuid(string $uuid): array {
|
||||||
|
return (new PetModel())->listPetsByTutorIdentifier(identifier: 'uuid', value: $uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Busca uma espécie de pet no sistema por um identificador específico.
|
||||||
|
*
|
||||||
|
* Este método atua como um **wrapper** para o método de busca de espécies na classe `PetModel`. Ele delega a responsabilidade de consultar a espécie para a camada de modelo (DAO), que lida com a lógica de acesso ao banco de dados.
|
||||||
|
*
|
||||||
|
* @param string $identifier A coluna da tabela a ser usada como critério de busca (ex: 'id', 'uuid', 'descricao').
|
||||||
|
* @param string $value O valor correspondente ao identificador que está sendo buscado.
|
||||||
|
* @return array Um array associativo contendo o status da operação, o código HTTP e os dados da espécie, conforme retornado pelo `PetModel`.
|
||||||
|
*/
|
||||||
|
public function getEspecieByIdentifier(string $identifier, string $value): array {
|
||||||
|
return (new PetModel())->getEspecieByIdentifier(identifier: $identifier, value: $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Busca uma raça de pet no sistema por um identificador específico.
|
||||||
|
*
|
||||||
|
* Este método atua como um **wrapper** (invólucro) para o método de busca de raças na classe `PetModel`. Ele delega a responsabilidade de consultar a raça para a camada de modelo (DAO), que lida com a lógica de acesso ao banco de dados e retorna o resultado.
|
||||||
|
*
|
||||||
|
* @param string $identifier A coluna da tabela a ser usada como critério de busca (ex: 'uuid', 'id', 'descricao').
|
||||||
|
* @param string $value O valor correspondente ao identificador que está sendo buscado.
|
||||||
|
* @return array Um array associativo contendo o status da operação, o código HTTP e os dados da raça, conforme retornado pelo `PetModel`.
|
||||||
|
*/
|
||||||
|
public function getRacaByIdentifier(string $identifier, string $value): array {
|
||||||
|
return (new PetModel())->getRacaByIdentifier(identifier: $identifier, value: $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Busca um pet no sistema por um identificador específico.
|
||||||
|
*
|
||||||
|
* Este método atua como um **wrapper** (invólucro) para o método de busca de pets na classe `PetModel`. Ele delega a responsabilidade de consultar o pet para a camada de modelo (DAO), que lida com a lógica de acesso ao banco de dados.
|
||||||
|
*
|
||||||
|
* @param string $identifier A coluna da tabela a ser usada como critério de busca (ex: 'uuid', 'id', 'nome').
|
||||||
|
* @param string $value O valor correspondente ao identificador que está sendo buscado.
|
||||||
|
* @return array Um array associativo contendo o status da operação, o código HTTP e os dados do pet, conforme retornado pelo `PetModel`.
|
||||||
|
*/
|
||||||
|
public function getPetByIdentifier(string $identifier, string $value): array {
|
||||||
|
return (new PetModel())->getPetByIdentifier(identifier: $identifier, value: $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cria uma nova raça de pet no sistema após validação.
|
||||||
|
*
|
||||||
|
* Este método orquestra a criação de uma nova raça de pet. Ele realiza a validação dos dados de entrada, define metadados essenciais (UUID) e persiste o registro no banco de dados, garantindo a atomicidade da operação através de uma transação.
|
||||||
|
*
|
||||||
|
* #### Fluxo de Operação:
|
||||||
|
* 1. **Início da Transação:** Inicia uma transação no banco de dados (`DB::beginTransaction()`).
|
||||||
|
* 2. **Validação de Dados:** Utiliza o `DataValidator` para verificar se os campos **'especie_id'** e **'descricao'** foram preenchidos.
|
||||||
|
* 3. **Encerramento em Caso de Falha na Validação:** Se houver erros de validação, a transação é revertida (`DB::rollBack()`) e um array de erro formatado é retornado com o código `400 Bad Request`.
|
||||||
|
* 4. **Preparação Final dos Dados:** Define um **UUID** único para a nova raça.
|
||||||
|
* 5. **Persistência:** Chama o método `storeRaca` do `PetModel` para inserir o registro no banco de dados. Se a persistência falhar, uma `Exception` é lançada com a mensagem de erro do modelo.
|
||||||
|
* 6. **Confirmação e Retorno:** Se a persistência for bem-sucedida, a transação é **confirmada** (`DB::commit()`) e o resultado de sucesso é retornado com o código `201 Created`.
|
||||||
|
*
|
||||||
|
* #### Tratamento de Erros:
|
||||||
|
* - Qualquer `Exception` lançada durante o processo é capturada, e a transação é **revertida** (`DB::rollBack()`).
|
||||||
|
* - Retorna um array de erro formatado contendo o código HTTP e a mensagem da exceção.
|
||||||
|
*
|
||||||
|
* @param PetRacaDTO $petRacaDTO O objeto DTO contendo o ID da espécie e a descrição da nova raça.
|
||||||
|
* @return array Um array associativo contendo o status da operação, o código de resposta HTTP e os dados de saída, ou erros de validação.
|
||||||
|
*/
|
||||||
|
public function storeRaca(PetRacaDTO $petRacaDTO): array {
|
||||||
|
// Inicia transação no banco de dados
|
||||||
|
DB::beginTransaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Valida os dados do DTO
|
||||||
|
$dataValidador = new DataValidator(inputs: $petRacaDTO->toArray());
|
||||||
|
$dataValidador->validate(field: 'especie_id', rule: 'notEmpty', message: 'Espécie é obrigatória.');
|
||||||
|
$dataValidador->validate(field: 'descricao', rule: 'notEmpty', message: 'Descrição é obrigatória.');
|
||||||
|
|
||||||
|
// Se houver erros de validação, retornar resposta de erro
|
||||||
|
if(!$dataValidador->passes()) {
|
||||||
|
DB::rollBack();
|
||||||
|
return [
|
||||||
|
'response_code' => 400,
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => 'Validation errors',
|
||||||
|
'output' => [
|
||||||
|
'errors' => $dataValidador->getErrors()
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define dados adicionais
|
||||||
|
$petRacaDTO->setUuid(uuid: Uuid::uuid7()->toString());
|
||||||
|
|
||||||
|
// Cria a nova raça no banco de dados
|
||||||
|
$storeRaca = (new PetModel())->storeRaca(petRacaDTO: $petRacaDTO);
|
||||||
|
if($storeRaca['status'] !== 'success') {
|
||||||
|
throw new Exception(message: $storeRaca['message'], code: $storeRaca['response_code']);
|
||||||
|
}
|
||||||
|
|
||||||
|
DB::commit();
|
||||||
|
return [
|
||||||
|
'response_code' => 201,
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Raça criada com sucesso.',
|
||||||
|
'output' => $storeRaca['output']
|
||||||
|
];
|
||||||
|
} catch(Exception $e) {
|
||||||
|
DB::rollBack();
|
||||||
|
return [
|
||||||
|
'response_code' => $e->getCode(),
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => $e->getMessage()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Armazena um novo pet no sistema e o associa ao usuário autenticado.
|
||||||
|
*
|
||||||
|
* Este método é responsável por orquestrar a criação de um novo pet, desde a validação inicial até a persistência do pet e o registro da relação com o tutor. A operação é **atômica** e garante a integridade dos dados através de uma transação de banco de dados.
|
||||||
|
*
|
||||||
|
* #### Fluxo de Operação:
|
||||||
|
* 1. **Início da Transação:** Inicia uma transação no banco de dados (`DB::beginTransaction()`).
|
||||||
|
* 2. **Validação de Dados:** O `DataValidator` verifica a presença e validade dos campos obrigatórios (nome, raça UUID, data de nascimento e sexo). Se falhar, a transação é revertida e um erro `400 Bad Request` é retornado.
|
||||||
|
* 3. **Validação de Duplicidade (RGA):** Se fornecido, o Registro Geral Animal (RGA) é verificado quanto à unicidade. Se um pet com o mesmo RGA for encontrado, uma `Exception` é lançada com o código `409 Conflict`.
|
||||||
|
* 4. **Preparação Final dos Dados:** O UUID do pet e o timestamp de criação são definidos.
|
||||||
|
* 5. **Criação do Pet:** O método `storePet` do `PetModel` é chamado para inserir o registro do pet. Se falhar, uma `Exception` é lançada.
|
||||||
|
* 6. **Associação ao Tutor:** O token JWT é decodificado para obter o UUID do tutor. O ID interno do tutor é buscado, e o pet é associado ao tutor através do método `storeUsuarioPet`. Se o tutor não for encontrado ou a associação falhar, uma `Exception` é lançada.
|
||||||
|
* 7. **Confirmação e Retorno:** Se todas as operações forem bem-sucedidas, a transação é **confirmada** (`DB::commit()`) e o resultado de sucesso é retornado com o código `201 Created`.
|
||||||
|
*
|
||||||
|
* #### Tratamento de Erros:
|
||||||
|
* - Qualquer `Exception` lançada durante o processo é capturada, a transação é **revertida** (`DB::rollBack()`), e um array de erro formatado contendo o código HTTP e a mensagem da exceção é retornado.
|
||||||
|
*
|
||||||
|
* @param PetDTO $petDTO O objeto DTO contendo os dados do novo pet.
|
||||||
|
* @return array Um array associativo com o status da operação, código HTTP e os dados do pet criado.
|
||||||
|
*/
|
||||||
|
public function storePet(PetDTO $petDTO): array {
|
||||||
|
// Inicia transação no banco de dados
|
||||||
|
DB::beginTransaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Valida os dados do DTO
|
||||||
|
$dataValidador = new DataValidator(inputs: $petDTO->toArray());
|
||||||
|
$dataValidador->validate(field: 'nome', rule: 'notEmpty', message: 'Nome do pet é obrigatório.');
|
||||||
|
$dataValidador->validate(field: 'sexo', rule: 'notEmpty', message: 'Sexo do pet é obrigatório.');
|
||||||
|
$dataValidador->validate(field: 'raca_id', rule: 'notEmpty', message: 'Raça do pet é obrigatória.');
|
||||||
|
$dataValidador->validate(field: 'especie_id', rule: 'notEmpty', message: 'Espécie do pet é obrigatória.');
|
||||||
|
$dataValidador->validate(field: 'data_nascimento', rule: 'notEmpty', message: 'Data de nascimento do pet é obrigatória.');
|
||||||
|
|
||||||
|
// Se houver erros de validação, retornar resposta de erro
|
||||||
|
if(!$dataValidador->passes()) {
|
||||||
|
return [
|
||||||
|
'response_code' => 400,
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => 'Validation errors',
|
||||||
|
'output' => [
|
||||||
|
'errors' => $dataValidador->getErrors()
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verifica se o Registro Geral Animal do PET (RGA) é único, se fornecido
|
||||||
|
if(($petDTO->getRegistroGeralAnimal() !== null) && !empty($petDTO->getRegistroGeralAnimal())) {
|
||||||
|
$existingPet = $this->getPetByIdentifier(identifier: 'registro_geral_animal', value: $petDTO->getRegistroGeralAnimal());
|
||||||
|
if($existingPet['status'] === 'success' && !empty($existingPet['output']['data'])) {
|
||||||
|
throw new Exception(message: 'Já existe um pet cadastrado com este Registro Geral Animal (RGA).', code: 409);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define dados adicionais
|
||||||
|
$petDTO->setUuid(uuid: Uuid::uuid7()->toString());
|
||||||
|
$petDTO->setCreatedAt(created_at: (new DateTime())->format(format: 'Y-m-d H:i:s'));
|
||||||
|
|
||||||
|
// Cria o novo pet no banco de dados
|
||||||
|
$storePet = (new PetModel())->storePet(petDTO: $petDTO);
|
||||||
|
if($storePet['status'] !== 'success') {
|
||||||
|
throw new Exception(message: $storePet['message'], code: $storePet['response_code']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Consulta o token JWT
|
||||||
|
$decodedToken = JWTService::decode(token: JWTService::getBearerToken());
|
||||||
|
|
||||||
|
// Consulta o ID do tutor pelo UUID
|
||||||
|
$getTutor = (new UsuarioModel())->getUsuarioByIdentifier(identifier: 'uuid', value: $decodedToken['user_uuid']);
|
||||||
|
if($getTutor['status'] !== 'success' || empty($getTutor['output']['data'])) {
|
||||||
|
throw new Exception(message: 'Tutor não encontrado.', code: 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Relaciona o pet ao tutor autenticado
|
||||||
|
$dataValidador = (new PetModel())->storeUsuarioPet(usuario_id: $getTutor['output']['data']['id'], pet_id: $storePet['output']['data']['id']);
|
||||||
|
if($dataValidador['status'] !== 'success') {
|
||||||
|
throw new Exception(message: $dataValidador['message'], code: $dataValidador['response_code']);
|
||||||
|
}
|
||||||
|
|
||||||
|
DB::commit();
|
||||||
|
return [
|
||||||
|
'response_code' => 201,
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Pet criado com sucesso.',
|
||||||
|
'output' => $storePet['output']
|
||||||
|
];
|
||||||
|
} catch(Exception $e) {
|
||||||
|
DB::rollBack();
|
||||||
|
return [
|
||||||
|
'response_code' => $e->getCode(),
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => $e->getMessage()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
66
app/Module/Tutor/v0/bootstrap.php
Executable file
66
app/Module/Tutor/v0/bootstrap.php
Executable file
@@ -0,0 +1,66 @@
|
|||||||
|
<?php
|
||||||
|
namespace Zampet\Module\Tutor;
|
||||||
|
|
||||||
|
use DateTime;
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
// Caminho do manifest.json
|
||||||
|
$manifestPath = realpath(path: __DIR__ . "/manifest.json");
|
||||||
|
|
||||||
|
if (!$manifestPath || !file_exists(filename: $manifestPath)) {
|
||||||
|
throw new Exception(message: "Arquivo 'manifest.json' não encontrado.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lê e decodifica
|
||||||
|
$manifest = json_decode(json: file_get_contents(filename: $manifestPath), associative: true);
|
||||||
|
|
||||||
|
// Verifica estrutura
|
||||||
|
if (!isset($manifest['apis']) || !is_array(value: $manifest['apis'])) {
|
||||||
|
throw new Exception(message: "Estrutura de 'apis' inválida no manifest.");
|
||||||
|
}
|
||||||
|
|
||||||
|
$now = (new DateTime())->format(format: 'Y-m-d');
|
||||||
|
$newApis = [];
|
||||||
|
$updated = false;
|
||||||
|
|
||||||
|
// Checa se tem versão esperando que começa hoje
|
||||||
|
foreach ($manifest['apis'] as $api) {
|
||||||
|
if ($api['status'] === 'planned' && $api['start_date'] === $now) {
|
||||||
|
// Promote a nova
|
||||||
|
$api['status'] = 'active';
|
||||||
|
$newApis[] = $api;
|
||||||
|
$updated = true;
|
||||||
|
} elseif ($api['status'] === 'active' && $api['start_date'] !== $now) {
|
||||||
|
// Ignora versão antiga ativa
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
$newApis[] = $api;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Atualiza manifest se tiver mudança
|
||||||
|
if ($updated) {
|
||||||
|
$manifest['apis'] = $newApis;
|
||||||
|
file_put_contents(filename: $manifestPath, data: json_encode(value: $manifest, flags: JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Carrega a versão ativa
|
||||||
|
$loaded = false;
|
||||||
|
foreach ($manifest['apis'] as $api) {
|
||||||
|
$startDate = new DateTime(datetime: $api['start_date']);
|
||||||
|
if ($api['status'] === 'active' && $startDate <= new DateTime()) {
|
||||||
|
$routeFile = MODULE_PATH . "/{$manifest['dirName']}/{$api['version']}/Routes/Routes.php";
|
||||||
|
if (file_exists(filename: $routeFile)) {
|
||||||
|
require_once realpath(path: $routeFile);
|
||||||
|
$loaded = true;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
throw new Exception(message: "Arquivo de rotas não encontrado para a versão '{$api['version']}'.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verifica se carregou
|
||||||
|
if (!$loaded) {
|
||||||
|
throw new Exception(message: "Nenhuma versão ativa da API foi carregada.");
|
||||||
|
}
|
||||||
16
app/Module/Tutor/v0/manifest.json
Executable file
16
app/Module/Tutor/v0/manifest.json
Executable file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"name": "Tutor",
|
||||||
|
"dirName": "Tutor",
|
||||||
|
"slug": "tutor",
|
||||||
|
"description": "Gerencia informações dos tutores e suas interações.",
|
||||||
|
"uuid": "0199b5de-f72c-702a-ba26-1c1d6f6d3dc2",
|
||||||
|
"version": "1.0",
|
||||||
|
"dependencies": [],
|
||||||
|
"apis": [
|
||||||
|
{
|
||||||
|
"version": "v0",
|
||||||
|
"status": "active",
|
||||||
|
"start_date": "2025-08-01"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
0
app/Module/blank_file
Executable file
0
app/Module/blank_file
Executable file
129
app/Storage/Logs/php_errors.log
Executable file
129
app/Storage/Logs/php_errors.log
Executable file
@@ -0,0 +1,129 @@
|
|||||||
|
[03-Oct-2025 16:59:04 America/Fortaleza] PHP Warning: require_once(/var/www/zampet/backend): Failed to open stream: No such file or directory in /var/www/zampet/backend/public/index.php on line 71
|
||||||
|
[03-Oct-2025 16:59:04 America/Fortaleza] PHP Stack trace:
|
||||||
|
[03-Oct-2025 16:59:04 America/Fortaleza] PHP 1. {main}() /var/www/zampet/backend/zampet-cli:0
|
||||||
|
[03-Oct-2025 16:59:04 America/Fortaleza] PHP 2. require_once() /var/www/zampet/backend/zampet-cli:3
|
||||||
|
[03-Oct-2025 16:59:04 America/Fortaleza] PHP Fatal error: Uncaught Error: Failed opening required '' (include_path='.:/usr/share/php') in /var/www/zampet/backend/public/index.php:71
|
||||||
|
Stack trace:
|
||||||
|
#0 /var/www/zampet/backend/zampet-cli(3): require_once()
|
||||||
|
#1 {main}
|
||||||
|
thrown in /var/www/zampet/backend/public/index.php on line 71
|
||||||
|
[03-Oct-2025 16:59:36 America/Fortaleza] PHP Warning: require_once(/var/www/zampet/backend): Failed to open stream: No such file or directory in /var/www/zampet/backend/public/index.php on line 71
|
||||||
|
[03-Oct-2025 16:59:36 America/Fortaleza] PHP Stack trace:
|
||||||
|
[03-Oct-2025 16:59:36 America/Fortaleza] PHP 1. {main}() /var/www/zampet/backend/zampet-cli:0
|
||||||
|
[03-Oct-2025 16:59:36 America/Fortaleza] PHP 2. require_once() /var/www/zampet/backend/zampet-cli:3
|
||||||
|
[03-Oct-2025 16:59:36 America/Fortaleza] PHP Fatal error: Uncaught Error: Failed opening required '' (include_path='.:/usr/share/php') in /var/www/zampet/backend/public/index.php:71
|
||||||
|
Stack trace:
|
||||||
|
#0 /var/www/zampet/backend/zampet-cli(3): require_once()
|
||||||
|
#1 {main}
|
||||||
|
thrown in /var/www/zampet/backend/public/index.php on line 71
|
||||||
|
[03-Oct-2025 17:01:25 America/Fortaleza] PHP Warning: require_once(/var/www/zampet/backend): Failed to open stream: No such file or directory in /var/www/zampet/backend/public/index.php on line 71
|
||||||
|
[03-Oct-2025 17:01:25 America/Fortaleza] PHP Stack trace:
|
||||||
|
[03-Oct-2025 17:01:25 America/Fortaleza] PHP 1. {main}() /var/www/zampet/backend/zampet-cli:0
|
||||||
|
[03-Oct-2025 17:01:25 America/Fortaleza] PHP 2. require_once() /var/www/zampet/backend/zampet-cli:3
|
||||||
|
[03-Oct-2025 17:01:25 America/Fortaleza] PHP Fatal error: Uncaught Error: Failed opening required '' (include_path='.:/usr/share/php') in /var/www/zampet/backend/public/index.php:71
|
||||||
|
Stack trace:
|
||||||
|
#0 /var/www/zampet/backend/zampet-cli(3): require_once()
|
||||||
|
#1 {main}
|
||||||
|
thrown in /var/www/zampet/backend/public/index.php on line 71
|
||||||
|
[03-Oct-2025 17:01:57 America/Fortaleza] PHP Warning: require_once(/var/www/zampet/backend): Failed to open stream: No such file or directory in /var/www/zampet/backend/public/index.php on line 71
|
||||||
|
[03-Oct-2025 17:01:57 America/Fortaleza] PHP Stack trace:
|
||||||
|
[03-Oct-2025 17:01:57 America/Fortaleza] PHP 1. {main}() /var/www/zampet/backend/zampet-cli:0
|
||||||
|
[03-Oct-2025 17:01:57 America/Fortaleza] PHP 2. require_once() /var/www/zampet/backend/zampet-cli:3
|
||||||
|
[03-Oct-2025 17:01:57 America/Fortaleza] PHP Fatal error: Uncaught Error: Failed opening required '' (include_path='.:/usr/share/php') in /var/www/zampet/backend/public/index.php:71
|
||||||
|
Stack trace:
|
||||||
|
#0 /var/www/zampet/backend/zampet-cli(3): require_once()
|
||||||
|
#1 {main}
|
||||||
|
thrown in /var/www/zampet/backend/public/index.php on line 71
|
||||||
|
[03-Oct-2025 17:03:27 America/Fortaleza] PHP Warning: require_once(/var/www/zampet/backend): Failed to open stream: No such file or directory in /var/www/zampet/backend/public/index.php on line 71
|
||||||
|
[03-Oct-2025 17:03:27 America/Fortaleza] PHP Stack trace:
|
||||||
|
[03-Oct-2025 17:03:27 America/Fortaleza] PHP 1. {main}() /var/www/zampet/backend/zampet-cli:0
|
||||||
|
[03-Oct-2025 17:03:27 America/Fortaleza] PHP 2. require_once() /var/www/zampet/backend/zampet-cli:3
|
||||||
|
[03-Oct-2025 17:03:27 America/Fortaleza] PHP Fatal error: Uncaught Error: Failed opening required '' (include_path='.:/usr/share/php') in /var/www/zampet/backend/public/index.php:71
|
||||||
|
Stack trace:
|
||||||
|
#0 /var/www/zampet/backend/zampet-cli(3): require_once()
|
||||||
|
#1 {main}
|
||||||
|
thrown in /var/www/zampet/backend/public/index.php on line 71
|
||||||
|
[03-Oct-2025 17:04:20 America/Fortaleza] PHP Warning: require_once(/var/www/zampet/backend): Failed to open stream: No such file or directory in /var/www/zampet/backend/public/index.php on line 71
|
||||||
|
[03-Oct-2025 17:04:20 America/Fortaleza] PHP Stack trace:
|
||||||
|
[03-Oct-2025 17:04:20 America/Fortaleza] PHP 1. {main}() /var/www/zampet/backend/zampet-cli:0
|
||||||
|
[03-Oct-2025 17:04:20 America/Fortaleza] PHP 2. require_once() /var/www/zampet/backend/zampet-cli:3
|
||||||
|
[03-Oct-2025 17:04:20 America/Fortaleza] PHP Fatal error: Uncaught Error: Failed opening required '' (include_path='.:/usr/share/php') in /var/www/zampet/backend/public/index.php:71
|
||||||
|
Stack trace:
|
||||||
|
#0 /var/www/zampet/backend/zampet-cli(3): require_once()
|
||||||
|
#1 {main}
|
||||||
|
thrown in /var/www/zampet/backend/public/index.php on line 71
|
||||||
|
[03-Oct-2025 17:05:37 America/Fortaleza] PHP Warning: require_once(/var/www/zampet/backend): Failed to open stream: No such file or directory in /var/www/zampet/backend/public/index.php on line 71
|
||||||
|
[03-Oct-2025 17:05:37 America/Fortaleza] PHP Stack trace:
|
||||||
|
[03-Oct-2025 17:05:37 America/Fortaleza] PHP 1. {main}() /var/www/zampet/backend/zampet-cli:0
|
||||||
|
[03-Oct-2025 17:05:37 America/Fortaleza] PHP 2. require_once() /var/www/zampet/backend/zampet-cli:3
|
||||||
|
[03-Oct-2025 17:05:37 America/Fortaleza] PHP Fatal error: Uncaught Error: Failed opening required '' (include_path='.:/usr/share/php') in /var/www/zampet/backend/public/index.php:71
|
||||||
|
Stack trace:
|
||||||
|
#0 /var/www/zampet/backend/zampet-cli(3): require_once()
|
||||||
|
#1 {main}
|
||||||
|
thrown in /var/www/zampet/backend/public/index.php on line 71
|
||||||
|
[03-Oct-2025 17:07:45 America/Fortaleza] PHP Warning: require_once(/var/www/zampet/backend): Failed to open stream: No such file or directory in /var/www/zampet/backend/public/index.php on line 71
|
||||||
|
[03-Oct-2025 17:07:45 America/Fortaleza] PHP Stack trace:
|
||||||
|
[03-Oct-2025 17:07:45 America/Fortaleza] PHP 1. {main}() /var/www/zampet/backend/zampet-cli:0
|
||||||
|
[03-Oct-2025 17:07:45 America/Fortaleza] PHP 2. require_once() /var/www/zampet/backend/zampet-cli:3
|
||||||
|
[03-Oct-2025 17:07:45 America/Fortaleza] PHP Fatal error: Uncaught Error: Failed opening required '' (include_path='.:/usr/share/php') in /var/www/zampet/backend/public/index.php:71
|
||||||
|
Stack trace:
|
||||||
|
#0 /var/www/zampet/backend/zampet-cli(3): require_once()
|
||||||
|
#1 {main}
|
||||||
|
thrown in /var/www/zampet/backend/public/index.php on line 71
|
||||||
|
[03-Oct-2025 17:25:56 America/Fortaleza] PHP Warning: require_once(/var/www/zampet/backend): Failed to open stream: No such file or directory in /var/www/zampet/backend/public/index.php on line 71
|
||||||
|
[03-Oct-2025 17:25:56 America/Fortaleza] PHP Stack trace:
|
||||||
|
[03-Oct-2025 17:25:56 America/Fortaleza] PHP 1. {main}() /var/www/zampet/backend/zampet-cli:0
|
||||||
|
[03-Oct-2025 17:25:56 America/Fortaleza] PHP 2. require_once() /var/www/zampet/backend/zampet-cli:3
|
||||||
|
[03-Oct-2025 17:25:56 America/Fortaleza] PHP Fatal error: Uncaught Error: Failed opening required '' (include_path='.:/usr/share/php') in /var/www/zampet/backend/public/index.php:71
|
||||||
|
Stack trace:
|
||||||
|
#0 /var/www/zampet/backend/zampet-cli(3): require_once()
|
||||||
|
#1 {main}
|
||||||
|
thrown in /var/www/zampet/backend/public/index.php on line 71
|
||||||
|
[03-Oct-2025 17:26:15 America/Fortaleza] PHP Warning: require_once(/var/www/zampet/backend): Failed to open stream: No such file or directory in /var/www/zampet/backend/public/index.php on line 71
|
||||||
|
[03-Oct-2025 17:26:15 America/Fortaleza] PHP Stack trace:
|
||||||
|
[03-Oct-2025 17:26:15 America/Fortaleza] PHP 1. {main}() /var/www/zampet/backend/zampet-cli:0
|
||||||
|
[03-Oct-2025 17:26:15 America/Fortaleza] PHP 2. require_once() /var/www/zampet/backend/zampet-cli:3
|
||||||
|
[03-Oct-2025 17:26:15 America/Fortaleza] PHP Fatal error: Uncaught Error: Failed opening required '' (include_path='.:/usr/share/php') in /var/www/zampet/backend/public/index.php:71
|
||||||
|
Stack trace:
|
||||||
|
#0 /var/www/zampet/backend/zampet-cli(3): require_once()
|
||||||
|
#1 {main}
|
||||||
|
thrown in /var/www/zampet/backend/public/index.php on line 71
|
||||||
|
[03-Oct-2025 17:26:25 America/Fortaleza] PHP Warning: require_once(/var/www/zampet/backend): Failed to open stream: No such file or directory in /var/www/zampet/backend/public/index.php on line 71
|
||||||
|
[03-Oct-2025 17:26:25 America/Fortaleza] PHP Stack trace:
|
||||||
|
[03-Oct-2025 17:26:25 America/Fortaleza] PHP 1. {main}() /var/www/zampet/backend/zampet-cli:0
|
||||||
|
[03-Oct-2025 17:26:25 America/Fortaleza] PHP 2. require() /var/www/zampet/backend/zampet-cli:3
|
||||||
|
[03-Oct-2025 17:26:25 America/Fortaleza] PHP Fatal error: Uncaught Error: Failed opening required '' (include_path='.:/usr/share/php') in /var/www/zampet/backend/public/index.php:71
|
||||||
|
Stack trace:
|
||||||
|
#0 /var/www/zampet/backend/zampet-cli(3): require()
|
||||||
|
#1 {main}
|
||||||
|
thrown in /var/www/zampet/backend/public/index.php on line 71
|
||||||
|
[03-Oct-2025 17:28:02 America/Fortaleza] PHP Warning: require_once(/var/www/zampet/backend): Failed to open stream: No such file or directory in /var/www/zampet/backend/public/index.php on line 71
|
||||||
|
[03-Oct-2025 17:28:02 America/Fortaleza] PHP Stack trace:
|
||||||
|
[03-Oct-2025 17:28:02 America/Fortaleza] PHP 1. {main}() /var/www/zampet/backend/zampet-cli:0
|
||||||
|
[03-Oct-2025 17:28:02 America/Fortaleza] PHP 2. require_once() /var/www/zampet/backend/zampet-cli:3
|
||||||
|
[03-Oct-2025 17:28:02 America/Fortaleza] PHP Fatal error: Uncaught Error: Failed opening required '' (include_path='.:/usr/share/php') in /var/www/zampet/backend/public/index.php:71
|
||||||
|
Stack trace:
|
||||||
|
#0 /var/www/zampet/backend/zampet-cli(3): require_once()
|
||||||
|
#1 {main}
|
||||||
|
thrown in /var/www/zampet/backend/public/index.php on line 71
|
||||||
|
[03-Oct-2025 17:30:33 America/Fortaleza] PHP Warning: require_once(/var/www/zampet/backend): Failed to open stream: No such file or directory in /var/www/zampet/backend/public/index.php on line 71
|
||||||
|
[03-Oct-2025 17:30:33 America/Fortaleza] PHP Stack trace:
|
||||||
|
[03-Oct-2025 17:30:33 America/Fortaleza] PHP 1. {main}() /var/www/zampet/backend/zampet-cli:0
|
||||||
|
[03-Oct-2025 17:30:33 America/Fortaleza] PHP 2. require_once() /var/www/zampet/backend/zampet-cli:4
|
||||||
|
[03-Oct-2025 17:30:33 America/Fortaleza] PHP Fatal error: Uncaught Error: Failed opening required '' (include_path='.:/usr/share/php') in /var/www/zampet/backend/public/index.php:71
|
||||||
|
Stack trace:
|
||||||
|
#0 /var/www/zampet/backend/zampet-cli(4): require_once()
|
||||||
|
#1 {main}
|
||||||
|
thrown in /var/www/zampet/backend/public/index.php on line 71
|
||||||
|
[03-Oct-2025 17:30:45 America/Fortaleza] PHP Warning: require_once(/var/www/zampet/backend): Failed to open stream: No such file or directory in /var/www/zampet/backend/public/index.php on line 71
|
||||||
|
[03-Oct-2025 17:30:45 America/Fortaleza] PHP Stack trace:
|
||||||
|
[03-Oct-2025 17:30:45 America/Fortaleza] PHP 1. {main}() /var/www/zampet/backend/zampet-cli:0
|
||||||
|
[03-Oct-2025 17:30:45 America/Fortaleza] PHP 2. require_once() /var/www/zampet/backend/zampet-cli:3
|
||||||
|
[03-Oct-2025 17:30:45 America/Fortaleza] PHP Fatal error: Uncaught Error: Failed opening required '' (include_path='.:/usr/share/php') in /var/www/zampet/backend/public/index.php:71
|
||||||
|
Stack trace:
|
||||||
|
#0 /var/www/zampet/backend/zampet-cli(3): require_once()
|
||||||
|
#1 {main}
|
||||||
|
thrown in /var/www/zampet/backend/public/index.php on line 71
|
||||||
|
[03-Oct-2025 17:31:46 America/Fortaleza] PHP Warning: Undefined array key 1 in /var/www/zampet/backend/zampet-cli on line 13
|
||||||
|
[03-Oct-2025 17:31:46 America/Fortaleza] PHP Stack trace:
|
||||||
|
[03-Oct-2025 17:31:46 America/Fortaleza] PHP 1. {main}() /var/www/zampet/backend/zampet-cli:0
|
||||||
43
composer.json
Executable file
43
composer.json
Executable file
@@ -0,0 +1,43 @@
|
|||||||
|
{
|
||||||
|
"name": "cybercore/zampet",
|
||||||
|
"type": "project",
|
||||||
|
"require": {
|
||||||
|
"php": ">=8.2",
|
||||||
|
"ramsey/uuid": "4.7.4",
|
||||||
|
"firebase/php-jwt": "v6.11.1",
|
||||||
|
"vlucas/phpdotenv": "v5.6.2",
|
||||||
|
"symfony/var-dumper": "^v6.4.26",
|
||||||
|
"claudecio/axiumphp": "dev-dev-router-update",
|
||||||
|
"ext-json": "*"
|
||||||
|
},
|
||||||
|
"repositories": [
|
||||||
|
{
|
||||||
|
"type": "vcs",
|
||||||
|
"url": "https://github.com/claudecio/axiumphp"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Zampet\\": "app/Common",
|
||||||
|
"Zampet\\Module\\": "app/Module"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "^10.5.58",
|
||||||
|
"squizlabs/php_codesniffer": "^3.13.4"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"test": "phpunit",
|
||||||
|
"cs": "phpcs --standard=PSR12 src/"
|
||||||
|
},
|
||||||
|
"minimum-stability": "dev",
|
||||||
|
"prefer-stable": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Claudecio Martins",
|
||||||
|
"email": "contato@claudecio.is-a.dev",
|
||||||
|
"role": "Developer"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
2726
composer.lock
generated
Executable file
2726
composer.lock
generated
Executable file
File diff suppressed because it is too large
Load Diff
0
database/blank_file
Executable file
0
database/blank_file
Executable file
5
database/v0/migrations/000_create_clinic.sql
Executable file
5
database/v0/migrations/000_create_clinic.sql
Executable file
@@ -0,0 +1,5 @@
|
|||||||
|
CREATE TABLE IF NOT EXISTS prestador (
|
||||||
|
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||||
|
uuid UUID NOT NULL UNIQUE,
|
||||||
|
nome VARCHAR(100) NOT NULL
|
||||||
|
);
|
||||||
78
database/v0/migrations/001_create_user.sql
Executable file
78
database/v0/migrations/001_create_user.sql
Executable file
@@ -0,0 +1,78 @@
|
|||||||
|
CREATE TABLE IF NOT EXISTS usuario_aux_status (
|
||||||
|
id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||||
|
descricao VARCHAR(50) NOT NULL UNIQUE
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS usuario_aux_perfil (
|
||||||
|
id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||||
|
descricao VARCHAR(50) NOT NULL UNIQUE
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS usuario (
|
||||||
|
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||||
|
uuid UUID NOT NULL UNIQUE,
|
||||||
|
nome_completo VARCHAR(255) NOT NULL,
|
||||||
|
status_id INT REFERENCES usuario_aux_status(id) ON UPDATE CASCADE ON DELETE CASCADE DEFAULT 1,
|
||||||
|
documentCpf VARCHAR(14) DEFAULT NULL UNIQUE,
|
||||||
|
documentCrmv VARCHAR(20) DEFAULT NULL UNIQUE,
|
||||||
|
email VARCHAR(255) NOT NULL UNIQUE,
|
||||||
|
senha VARCHAR(255) NOT NULL,
|
||||||
|
telefone VARCHAR(20) DEFAULT NULL,
|
||||||
|
data_nascimento DATE DEFAULT NULL,
|
||||||
|
endereco_rua VARCHAR(255) DEFAULT NULL,
|
||||||
|
endereco_numero VARCHAR(20) DEFAULT NULL,
|
||||||
|
endereco_complemento VARCHAR(255) DEFAULT NULL,
|
||||||
|
endereco_bairro VARCHAR(100) DEFAULT NULL,
|
||||||
|
endereco_cidade VARCHAR(100) DEFAULT NULL,
|
||||||
|
endereco_uf VARCHAR(2) DEFAULT NULL,
|
||||||
|
endereco_cep VARCHAR(10) DEFAULT NULL,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP DEFAULT NULL,
|
||||||
|
deleted_at TIMESTAMP DEFAULT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS usuario_token(
|
||||||
|
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||||
|
usuario_id BIGINT REFERENCES usuario(id) ON UPDATE CASCADE ON DELETE CASCADE,
|
||||||
|
token TEXT NOT NULL UNIQUE,
|
||||||
|
revoked SMALLINT DEFAULT 0,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP DEFAULT NULL,
|
||||||
|
UNIQUE (usuario_id, token)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS pet_aux_especie (
|
||||||
|
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||||
|
uuid UUID NOT NULL UNIQUE,
|
||||||
|
descricao VARCHAR(50) NOT NULL UNIQUE
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS pet_aux_raca (
|
||||||
|
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||||
|
uuid UUID NOT NULL UNIQUE,
|
||||||
|
especie_id BIGINT REFERENCES pet_aux_especie(id) ON UPDATE CASCADE ON DELETE CASCADE,
|
||||||
|
descricao VARCHAR(50) NOT NULL,
|
||||||
|
UNIQUE (especie_id, descricao)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS pet (
|
||||||
|
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||||
|
uuid UUID NOT NULL UNIQUE,
|
||||||
|
nome VARCHAR(100) NOT NULL,
|
||||||
|
especie_id BIGINT REFERENCES pet_aux_especie(id) ON UPDATE CASCADE ON DELETE CASCADE,
|
||||||
|
raca_id BIGINT REFERENCES pet_aux_raca(id) ON UPDATE CASCADE ON DELETE CASCADE,
|
||||||
|
caminho_foto TEXT DEFAULT NULL,
|
||||||
|
registro_geral_animal VARCHAR(50) DEFAULT NULL,
|
||||||
|
photo_path TEXT DEFAULT NULL,
|
||||||
|
data_nascimento DATE NOT NULL,
|
||||||
|
data_obito DATE DEFAULT NULL,
|
||||||
|
sexo CHAR(1) NOT NULL,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP DEFAULT NULL,
|
||||||
|
deleted_at TIMESTAMP DEFAULT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS usuario_pet (
|
||||||
|
usuario_id BIGINT REFERENCES usuario(id) ON UPDATE CASCADE ON DELETE CASCADE,
|
||||||
|
pet_id BIGINT REFERENCES pet(id) ON UPDATE CASCADE ON DELETE CASCADE
|
||||||
|
);
|
||||||
0
database/v0/seeds/000_insert_clinic.sql
Executable file
0
database/v0/seeds/000_insert_clinic.sql
Executable file
46
database/v0/seeds/001_insert_user.sql
Executable file
46
database/v0/seeds/001_insert_user.sql
Executable file
@@ -0,0 +1,46 @@
|
|||||||
|
-- Tabela usuario_aux_status
|
||||||
|
INSERT INTO usuario_aux_status (id, descricao) VALUES
|
||||||
|
(1, 'Ativo'),
|
||||||
|
(2, 'Inativo'),
|
||||||
|
(3, 'Aguardando Confirmação'),
|
||||||
|
(4, 'Suspenso');
|
||||||
|
|
||||||
|
SELECT setval(pg_get_serial_sequence('usuario_aux_status','id'), (SELECT MAX(id) FROM usuario_aux_status));
|
||||||
|
|
||||||
|
-- Tabela usuario_aux_perfil
|
||||||
|
INSERT INTO usuario_aux_perfil (id, descricao) VALUES
|
||||||
|
(1, 'Cliente'),
|
||||||
|
(2, 'Veterinário'),
|
||||||
|
(3, 'Funcionário'),
|
||||||
|
(4, 'Gerência do Prestador'),
|
||||||
|
(5, 'Administrador do Sistema');
|
||||||
|
|
||||||
|
SELECT setval(pg_get_serial_sequence('usuario_aux_perfil','id'), (SELECT MAX(id) FROM usuario_aux_perfil));
|
||||||
|
|
||||||
|
-- Tabela pet_aux_especie
|
||||||
|
INSERT INTO pet_aux_especie (id, uuid, descricao) VALUES
|
||||||
|
(1, '0199b633-a22a-7dbd-8425-6511d1c14200', 'Felina'),
|
||||||
|
(2, '0199b633-fed8-70de-9c99-123196f7dde6', 'Canina'),
|
||||||
|
(3, '0199b634-0fb1-70b7-98c6-7ad9f8d18da5', 'Ave'),
|
||||||
|
(4, '0199b634-2a73-7d77-8312-4c1fbc8ec324', 'Roedor'),
|
||||||
|
(5, '0199b634-45ea-7d48-b209-e4238d1de557', 'Réptil'),
|
||||||
|
(6, '0199b634-652d-7cd1-95d1-511cf57ba3ce', 'Peixe'),
|
||||||
|
(7, '0199b634-8b45-7e5f-86e0-dd34a33a4aa5', 'Anfíbio'),
|
||||||
|
(8, '0199bc00-ab91-772d-8309-e8300482d5f1', 'Procionídeo');
|
||||||
|
|
||||||
|
SELECT setval(pg_get_serial_sequence('pet_aux_especie','id'), (SELECT MAX(id) FROM pet_aux_especie));
|
||||||
|
|
||||||
|
-- Tabela pet_aux_raca
|
||||||
|
INSERT INTO pet_aux_raca (id, uuid, especie_id, descricao) VALUES
|
||||||
|
(1, '0199b634-a30b-7fbc-b69f-ba9b8c9e6716', 1, 'Sem Raça Definida (SRD)'),
|
||||||
|
(2, '0199b634-cb61-77fe-90e7-79018c839c7c', 2, 'Sem Raça Definida (SRD)');
|
||||||
|
|
||||||
|
SELECT setval(pg_get_serial_sequence('pet_aux_raca','id'), (SELECT MAX(id) FROM pet_aux_raca));
|
||||||
|
|
||||||
|
-- Tabela usuario
|
||||||
|
INSERT INTO usuario (id, uuid, nome_completo, status_id, documentcpf, documentcrmv, email, senha, telefone, data_nascimento, endereco_rua, endereco_numero, endereco_complemento, endereco_bairro, endereco_cidade, endereco_uf, endereco_cep, created_at, updated_at, deleted_at) VALUES
|
||||||
|
(1,'0199b5db-037e-73cf-9867-51ad35bc47e8','Iranildo Carlos',1,'04983082352',NULL,'iranildocarlosd@gmail.com','$argon2id$v=19$m=65536,t=4,p=1$Ym9oUEZwMTl0d2RxQ2pKdA$oqa3S2DGkHyGrGlo8A+di4Y6e1y1KS3SlyxWsI0+t5E',NULL,'2004-07-23',NULL,NULL,NULL,NULL,NULL,NULL,NULL,'2025-10-05 16:30:53.000',NULL,NULL),
|
||||||
|
(2,'0199b5db-28ab-73f7-b777-230dc096cac6','Claudecio Santos da Costa Martins Júnior',1,'06799082347',NULL,'contato@claudecio.is-a.dev','$argon2id$v=19$m=65536,t=4,p=1$YTdHbXRJbHpVOHlQN0N3Sg$NKm2mtMa1CQwu8cZQFilYN8Rab75WWRf7vjrCTAKWbA',NULL,'2004-09-07',NULL,NULL,NULL,NULL,NULL,NULL,NULL,'2025-10-05 16:31:02.000',NULL,NULL),
|
||||||
|
(3,'0199b5fa-2fb6-7187-91e4-f79d236923f0','Pablo Ruan Feitoza Cahaves',1,'06426789364',NULL,'pablorfchaves@gmail.com','$argon2id$v=19$m=65536,t=4,p=1$RW9icWNKMW1VdHhBSGhyWQ$sS9aW3pwsShei4FiRf7cJo0Kwo5pIXeZbbyTwwyfrrk',NULL,'2005-04-12',NULL,NULL,NULL,NULL,NULL,NULL,NULL,'2025-10-05 17:04:56.000',NULL,NULL);
|
||||||
|
|
||||||
|
SELECT setval(pg_get_serial_sequence('usuario','id'), (SELECT MAX(id) FROM usuario));
|
||||||
10
public/.htaccess
Executable file
10
public/.htaccess
Executable file
@@ -0,0 +1,10 @@
|
|||||||
|
<IfModule mod_rewrite.c>
|
||||||
|
RewriteEngine On
|
||||||
|
RewriteBase /
|
||||||
|
|
||||||
|
# Se não for arquivo nem pasta, manda tudo pro index.php
|
||||||
|
RewriteCond %{REQUEST_FILENAME} !-f
|
||||||
|
RewriteCond %{REQUEST_FILENAME} !-d
|
||||||
|
|
||||||
|
RewriteRule ^ index.php [L]
|
||||||
|
</IfModule>
|
||||||
92
public/index.php
Executable file
92
public/index.php
Executable file
@@ -0,0 +1,92 @@
|
|||||||
|
<?php
|
||||||
|
// ============================
|
||||||
|
// Configurações de erro
|
||||||
|
// ============================
|
||||||
|
ini_set(option: 'display_errors', value: 1);
|
||||||
|
ini_set(option: 'display_startup_errors', value: 1);
|
||||||
|
ini_set(option: 'log_errors', value: 1);
|
||||||
|
ini_set(option: 'error_log', value: __DIR__ . "/../app/Storage/Logs/php_errors.log");
|
||||||
|
error_reporting(error_level: E_ALL);
|
||||||
|
|
||||||
|
// Importa autoload do Composer
|
||||||
|
require_once realpath(path: __DIR__ . '/../vendor/autoload.php');
|
||||||
|
|
||||||
|
// ============================
|
||||||
|
// Namespaces usados
|
||||||
|
// ============================
|
||||||
|
use AxiumPHP\Core\Router;
|
||||||
|
use Zampet\Config\Config;
|
||||||
|
use AxiumPHP\Core\LoggerService;
|
||||||
|
use Zampet\Module\Auth\v0\Middlewares\AuthMiddleware;
|
||||||
|
|
||||||
|
// ============================
|
||||||
|
// Configurações globais
|
||||||
|
// ============================
|
||||||
|
$Config = Config::getInstance();
|
||||||
|
|
||||||
|
// ============================
|
||||||
|
// Define constantes do sistema
|
||||||
|
// ============================
|
||||||
|
const ROUTER_MODE = 'JSON';
|
||||||
|
const APP_SYS_MODE = 'DEV'; // PROD = Production, DEV = Development
|
||||||
|
define(constant_name: 'ROOT_SYSTEM_PATH', value: realpath(path: __DIR__ . "/.."));
|
||||||
|
define(constant_name: 'INI_SYSTEM_PATH', value: realpath(path: __DIR__ . "/../app"));
|
||||||
|
define(constant_name: 'MODULE_PATH', value: realpath(path: __DIR__ . "/../app/Module"));
|
||||||
|
define(constant_name: 'STORAGE_FOLDER_PATH', value: realpath(path: __DIR__ . "/../app/Storage"));
|
||||||
|
|
||||||
|
// ============================
|
||||||
|
// Inicializa LoggerService
|
||||||
|
// ============================
|
||||||
|
LoggerService::init(
|
||||||
|
driver: LoggerService::DRIVER_FILE,
|
||||||
|
logDir: __DIR__ . '/../app/Storage/Logs'
|
||||||
|
);
|
||||||
|
|
||||||
|
// ============================
|
||||||
|
// Define origens permitidas
|
||||||
|
// ============================
|
||||||
|
const ROUTER_ALLOWED_ORIGINS = [
|
||||||
|
'*'
|
||||||
|
];
|
||||||
|
|
||||||
|
// ============================
|
||||||
|
// Define fuso horário
|
||||||
|
// ============================
|
||||||
|
date_default_timezone_set(timezoneId: $Config->get(key: "SYSTEM_TIMEZONE"));
|
||||||
|
|
||||||
|
// ============================
|
||||||
|
// Inicia sessão se necessário
|
||||||
|
// ============================
|
||||||
|
if (session_status() === PHP_SESSION_NONE) {
|
||||||
|
session_start();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================
|
||||||
|
// Inicializa o roteador
|
||||||
|
// ============================
|
||||||
|
new Router();
|
||||||
|
|
||||||
|
// ============================
|
||||||
|
// Carregamento de Módulos Essenciais do sistema
|
||||||
|
// ============================
|
||||||
|
require_once MODULE_PATH . '/Auth/v0/bootstrap.php';
|
||||||
|
|
||||||
|
Router::group(
|
||||||
|
prefix: '/module',
|
||||||
|
callback: function() {
|
||||||
|
// Carrega módulo do Tutor
|
||||||
|
require_once MODULE_PATH . '/Tutor/v0/bootstrap.php';
|
||||||
|
|
||||||
|
},
|
||||||
|
middlewares: [
|
||||||
|
AuthMiddleware::class."::handle"
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
// ============================
|
||||||
|
// Dispara o roteador
|
||||||
|
// ============================
|
||||||
|
// Só dispara Router se for uma requisição web
|
||||||
|
if (php_sapi_name() !== 'cli') {
|
||||||
|
Router::dispatch();
|
||||||
|
}
|
||||||
22
vendor/autoload.php
vendored
Executable file
22
vendor/autoload.php
vendored
Executable file
@@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload.php @generated by Composer
|
||||||
|
|
||||||
|
if (PHP_VERSION_ID < 50600) {
|
||||||
|
if (!headers_sent()) {
|
||||||
|
header('HTTP/1.1 500 Internal Server Error');
|
||||||
|
}
|
||||||
|
$err = 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
|
||||||
|
if (!ini_get('display_errors')) {
|
||||||
|
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
|
||||||
|
fwrite(STDERR, $err);
|
||||||
|
} elseif (!headers_sent()) {
|
||||||
|
echo $err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new RuntimeException($err);
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once __DIR__ . '/composer/autoload_real.php';
|
||||||
|
|
||||||
|
return ComposerAutoloaderInit1020934c13f8fb367bf4afd130c40a82::getLoader();
|
||||||
119
vendor/bin/php-parse
vendored
Executable file
119
vendor/bin/php-parse
vendored
Executable file
@@ -0,0 +1,119 @@
|
|||||||
|
#!/usr/bin/env php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Proxy PHP file generated by Composer
|
||||||
|
*
|
||||||
|
* This file includes the referenced bin path (../nikic/php-parser/bin/php-parse)
|
||||||
|
* using a stream wrapper to prevent the shebang from being output on PHP<8
|
||||||
|
*
|
||||||
|
* @generated
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Composer;
|
||||||
|
|
||||||
|
$GLOBALS['_composer_bin_dir'] = __DIR__;
|
||||||
|
$GLOBALS['_composer_autoload_path'] = __DIR__ . '/..'.'/autoload.php';
|
||||||
|
|
||||||
|
if (PHP_VERSION_ID < 80000) {
|
||||||
|
if (!class_exists('Composer\BinProxyWrapper')) {
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
final class BinProxyWrapper
|
||||||
|
{
|
||||||
|
private $handle;
|
||||||
|
private $position;
|
||||||
|
private $realpath;
|
||||||
|
|
||||||
|
public function stream_open($path, $mode, $options, &$opened_path)
|
||||||
|
{
|
||||||
|
// get rid of phpvfscomposer:// prefix for __FILE__ & __DIR__ resolution
|
||||||
|
$opened_path = substr($path, 17);
|
||||||
|
$this->realpath = realpath($opened_path) ?: $opened_path;
|
||||||
|
$opened_path = $this->realpath;
|
||||||
|
$this->handle = fopen($this->realpath, $mode);
|
||||||
|
$this->position = 0;
|
||||||
|
|
||||||
|
return (bool) $this->handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_read($count)
|
||||||
|
{
|
||||||
|
$data = fread($this->handle, $count);
|
||||||
|
|
||||||
|
if ($this->position === 0) {
|
||||||
|
$data = preg_replace('{^#!.*\r?\n}', '', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->position += strlen($data);
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_cast($castAs)
|
||||||
|
{
|
||||||
|
return $this->handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_close()
|
||||||
|
{
|
||||||
|
fclose($this->handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_lock($operation)
|
||||||
|
{
|
||||||
|
return $operation ? flock($this->handle, $operation) : true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_seek($offset, $whence)
|
||||||
|
{
|
||||||
|
if (0 === fseek($this->handle, $offset, $whence)) {
|
||||||
|
$this->position = ftell($this->handle);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_tell()
|
||||||
|
{
|
||||||
|
return $this->position;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_eof()
|
||||||
|
{
|
||||||
|
return feof($this->handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_stat()
|
||||||
|
{
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_set_option($option, $arg1, $arg2)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function url_stat($path, $flags)
|
||||||
|
{
|
||||||
|
$path = substr($path, 17);
|
||||||
|
if (file_exists($path)) {
|
||||||
|
return stat($path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
(function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true))
|
||||||
|
|| (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper'))
|
||||||
|
) {
|
||||||
|
return include("phpvfscomposer://" . __DIR__ . '/..'.'/nikic/php-parser/bin/php-parse');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return include __DIR__ . '/..'.'/nikic/php-parser/bin/php-parse';
|
||||||
119
vendor/bin/phpcbf
vendored
Executable file
119
vendor/bin/phpcbf
vendored
Executable file
@@ -0,0 +1,119 @@
|
|||||||
|
#!/usr/bin/env php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Proxy PHP file generated by Composer
|
||||||
|
*
|
||||||
|
* This file includes the referenced bin path (../squizlabs/php_codesniffer/bin/phpcbf)
|
||||||
|
* using a stream wrapper to prevent the shebang from being output on PHP<8
|
||||||
|
*
|
||||||
|
* @generated
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Composer;
|
||||||
|
|
||||||
|
$GLOBALS['_composer_bin_dir'] = __DIR__;
|
||||||
|
$GLOBALS['_composer_autoload_path'] = __DIR__ . '/..'.'/autoload.php';
|
||||||
|
|
||||||
|
if (PHP_VERSION_ID < 80000) {
|
||||||
|
if (!class_exists('Composer\BinProxyWrapper')) {
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
final class BinProxyWrapper
|
||||||
|
{
|
||||||
|
private $handle;
|
||||||
|
private $position;
|
||||||
|
private $realpath;
|
||||||
|
|
||||||
|
public function stream_open($path, $mode, $options, &$opened_path)
|
||||||
|
{
|
||||||
|
// get rid of phpvfscomposer:// prefix for __FILE__ & __DIR__ resolution
|
||||||
|
$opened_path = substr($path, 17);
|
||||||
|
$this->realpath = realpath($opened_path) ?: $opened_path;
|
||||||
|
$opened_path = $this->realpath;
|
||||||
|
$this->handle = fopen($this->realpath, $mode);
|
||||||
|
$this->position = 0;
|
||||||
|
|
||||||
|
return (bool) $this->handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_read($count)
|
||||||
|
{
|
||||||
|
$data = fread($this->handle, $count);
|
||||||
|
|
||||||
|
if ($this->position === 0) {
|
||||||
|
$data = preg_replace('{^#!.*\r?\n}', '', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->position += strlen($data);
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_cast($castAs)
|
||||||
|
{
|
||||||
|
return $this->handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_close()
|
||||||
|
{
|
||||||
|
fclose($this->handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_lock($operation)
|
||||||
|
{
|
||||||
|
return $operation ? flock($this->handle, $operation) : true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_seek($offset, $whence)
|
||||||
|
{
|
||||||
|
if (0 === fseek($this->handle, $offset, $whence)) {
|
||||||
|
$this->position = ftell($this->handle);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_tell()
|
||||||
|
{
|
||||||
|
return $this->position;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_eof()
|
||||||
|
{
|
||||||
|
return feof($this->handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_stat()
|
||||||
|
{
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_set_option($option, $arg1, $arg2)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function url_stat($path, $flags)
|
||||||
|
{
|
||||||
|
$path = substr($path, 17);
|
||||||
|
if (file_exists($path)) {
|
||||||
|
return stat($path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
(function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true))
|
||||||
|
|| (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper'))
|
||||||
|
) {
|
||||||
|
return include("phpvfscomposer://" . __DIR__ . '/..'.'/squizlabs/php_codesniffer/bin/phpcbf');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return include __DIR__ . '/..'.'/squizlabs/php_codesniffer/bin/phpcbf';
|
||||||
119
vendor/bin/phpcs
vendored
Executable file
119
vendor/bin/phpcs
vendored
Executable file
@@ -0,0 +1,119 @@
|
|||||||
|
#!/usr/bin/env php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Proxy PHP file generated by Composer
|
||||||
|
*
|
||||||
|
* This file includes the referenced bin path (../squizlabs/php_codesniffer/bin/phpcs)
|
||||||
|
* using a stream wrapper to prevent the shebang from being output on PHP<8
|
||||||
|
*
|
||||||
|
* @generated
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Composer;
|
||||||
|
|
||||||
|
$GLOBALS['_composer_bin_dir'] = __DIR__;
|
||||||
|
$GLOBALS['_composer_autoload_path'] = __DIR__ . '/..'.'/autoload.php';
|
||||||
|
|
||||||
|
if (PHP_VERSION_ID < 80000) {
|
||||||
|
if (!class_exists('Composer\BinProxyWrapper')) {
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
final class BinProxyWrapper
|
||||||
|
{
|
||||||
|
private $handle;
|
||||||
|
private $position;
|
||||||
|
private $realpath;
|
||||||
|
|
||||||
|
public function stream_open($path, $mode, $options, &$opened_path)
|
||||||
|
{
|
||||||
|
// get rid of phpvfscomposer:// prefix for __FILE__ & __DIR__ resolution
|
||||||
|
$opened_path = substr($path, 17);
|
||||||
|
$this->realpath = realpath($opened_path) ?: $opened_path;
|
||||||
|
$opened_path = $this->realpath;
|
||||||
|
$this->handle = fopen($this->realpath, $mode);
|
||||||
|
$this->position = 0;
|
||||||
|
|
||||||
|
return (bool) $this->handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_read($count)
|
||||||
|
{
|
||||||
|
$data = fread($this->handle, $count);
|
||||||
|
|
||||||
|
if ($this->position === 0) {
|
||||||
|
$data = preg_replace('{^#!.*\r?\n}', '', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->position += strlen($data);
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_cast($castAs)
|
||||||
|
{
|
||||||
|
return $this->handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_close()
|
||||||
|
{
|
||||||
|
fclose($this->handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_lock($operation)
|
||||||
|
{
|
||||||
|
return $operation ? flock($this->handle, $operation) : true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_seek($offset, $whence)
|
||||||
|
{
|
||||||
|
if (0 === fseek($this->handle, $offset, $whence)) {
|
||||||
|
$this->position = ftell($this->handle);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_tell()
|
||||||
|
{
|
||||||
|
return $this->position;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_eof()
|
||||||
|
{
|
||||||
|
return feof($this->handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_stat()
|
||||||
|
{
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_set_option($option, $arg1, $arg2)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function url_stat($path, $flags)
|
||||||
|
{
|
||||||
|
$path = substr($path, 17);
|
||||||
|
if (file_exists($path)) {
|
||||||
|
return stat($path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
(function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true))
|
||||||
|
|| (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper'))
|
||||||
|
) {
|
||||||
|
return include("phpvfscomposer://" . __DIR__ . '/..'.'/squizlabs/php_codesniffer/bin/phpcs');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return include __DIR__ . '/..'.'/squizlabs/php_codesniffer/bin/phpcs';
|
||||||
122
vendor/bin/phpunit
vendored
Executable file
122
vendor/bin/phpunit
vendored
Executable file
@@ -0,0 +1,122 @@
|
|||||||
|
#!/usr/bin/env php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Proxy PHP file generated by Composer
|
||||||
|
*
|
||||||
|
* This file includes the referenced bin path (../phpunit/phpunit/phpunit)
|
||||||
|
* using a stream wrapper to prevent the shebang from being output on PHP<8
|
||||||
|
*
|
||||||
|
* @generated
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Composer;
|
||||||
|
|
||||||
|
$GLOBALS['_composer_bin_dir'] = __DIR__;
|
||||||
|
$GLOBALS['_composer_autoload_path'] = __DIR__ . '/..'.'/autoload.php';
|
||||||
|
$GLOBALS['__PHPUNIT_ISOLATION_EXCLUDE_LIST'] = $GLOBALS['__PHPUNIT_ISOLATION_BLACKLIST'] = array(realpath(__DIR__ . '/..'.'/phpunit/phpunit/phpunit'));
|
||||||
|
|
||||||
|
if (PHP_VERSION_ID < 80000) {
|
||||||
|
if (!class_exists('Composer\BinProxyWrapper')) {
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
final class BinProxyWrapper
|
||||||
|
{
|
||||||
|
private $handle;
|
||||||
|
private $position;
|
||||||
|
private $realpath;
|
||||||
|
|
||||||
|
public function stream_open($path, $mode, $options, &$opened_path)
|
||||||
|
{
|
||||||
|
// get rid of phpvfscomposer:// prefix for __FILE__ & __DIR__ resolution
|
||||||
|
$opened_path = substr($path, 17);
|
||||||
|
$this->realpath = realpath($opened_path) ?: $opened_path;
|
||||||
|
$opened_path = 'phpvfscomposer://'.$this->realpath;
|
||||||
|
$this->handle = fopen($this->realpath, $mode);
|
||||||
|
$this->position = 0;
|
||||||
|
|
||||||
|
return (bool) $this->handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_read($count)
|
||||||
|
{
|
||||||
|
$data = fread($this->handle, $count);
|
||||||
|
|
||||||
|
if ($this->position === 0) {
|
||||||
|
$data = preg_replace('{^#!.*\r?\n}', '', $data);
|
||||||
|
}
|
||||||
|
$data = str_replace('__DIR__', var_export(dirname($this->realpath), true), $data);
|
||||||
|
$data = str_replace('__FILE__', var_export($this->realpath, true), $data);
|
||||||
|
|
||||||
|
$this->position += strlen($data);
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_cast($castAs)
|
||||||
|
{
|
||||||
|
return $this->handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_close()
|
||||||
|
{
|
||||||
|
fclose($this->handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_lock($operation)
|
||||||
|
{
|
||||||
|
return $operation ? flock($this->handle, $operation) : true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_seek($offset, $whence)
|
||||||
|
{
|
||||||
|
if (0 === fseek($this->handle, $offset, $whence)) {
|
||||||
|
$this->position = ftell($this->handle);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_tell()
|
||||||
|
{
|
||||||
|
return $this->position;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_eof()
|
||||||
|
{
|
||||||
|
return feof($this->handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_stat()
|
||||||
|
{
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_set_option($option, $arg1, $arg2)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function url_stat($path, $flags)
|
||||||
|
{
|
||||||
|
$path = substr($path, 17);
|
||||||
|
if (file_exists($path)) {
|
||||||
|
return stat($path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
(function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true))
|
||||||
|
|| (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper'))
|
||||||
|
) {
|
||||||
|
return include("phpvfscomposer://" . __DIR__ . '/..'.'/phpunit/phpunit/phpunit');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return include __DIR__ . '/..'.'/phpunit/phpunit/phpunit';
|
||||||
119
vendor/bin/var-dump-server
vendored
Executable file
119
vendor/bin/var-dump-server
vendored
Executable file
@@ -0,0 +1,119 @@
|
|||||||
|
#!/usr/bin/env php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Proxy PHP file generated by Composer
|
||||||
|
*
|
||||||
|
* This file includes the referenced bin path (../symfony/var-dumper/Resources/bin/var-dump-server)
|
||||||
|
* using a stream wrapper to prevent the shebang from being output on PHP<8
|
||||||
|
*
|
||||||
|
* @generated
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Composer;
|
||||||
|
|
||||||
|
$GLOBALS['_composer_bin_dir'] = __DIR__;
|
||||||
|
$GLOBALS['_composer_autoload_path'] = __DIR__ . '/..'.'/autoload.php';
|
||||||
|
|
||||||
|
if (PHP_VERSION_ID < 80000) {
|
||||||
|
if (!class_exists('Composer\BinProxyWrapper')) {
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
final class BinProxyWrapper
|
||||||
|
{
|
||||||
|
private $handle;
|
||||||
|
private $position;
|
||||||
|
private $realpath;
|
||||||
|
|
||||||
|
public function stream_open($path, $mode, $options, &$opened_path)
|
||||||
|
{
|
||||||
|
// get rid of phpvfscomposer:// prefix for __FILE__ & __DIR__ resolution
|
||||||
|
$opened_path = substr($path, 17);
|
||||||
|
$this->realpath = realpath($opened_path) ?: $opened_path;
|
||||||
|
$opened_path = $this->realpath;
|
||||||
|
$this->handle = fopen($this->realpath, $mode);
|
||||||
|
$this->position = 0;
|
||||||
|
|
||||||
|
return (bool) $this->handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_read($count)
|
||||||
|
{
|
||||||
|
$data = fread($this->handle, $count);
|
||||||
|
|
||||||
|
if ($this->position === 0) {
|
||||||
|
$data = preg_replace('{^#!.*\r?\n}', '', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->position += strlen($data);
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_cast($castAs)
|
||||||
|
{
|
||||||
|
return $this->handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_close()
|
||||||
|
{
|
||||||
|
fclose($this->handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_lock($operation)
|
||||||
|
{
|
||||||
|
return $operation ? flock($this->handle, $operation) : true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_seek($offset, $whence)
|
||||||
|
{
|
||||||
|
if (0 === fseek($this->handle, $offset, $whence)) {
|
||||||
|
$this->position = ftell($this->handle);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_tell()
|
||||||
|
{
|
||||||
|
return $this->position;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_eof()
|
||||||
|
{
|
||||||
|
return feof($this->handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_stat()
|
||||||
|
{
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_set_option($option, $arg1, $arg2)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function url_stat($path, $flags)
|
||||||
|
{
|
||||||
|
$path = substr($path, 17);
|
||||||
|
if (file_exists($path)) {
|
||||||
|
return stat($path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
(function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true))
|
||||||
|
|| (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper'))
|
||||||
|
) {
|
||||||
|
return include("phpvfscomposer://" . __DIR__ . '/..'.'/symfony/var-dumper/Resources/bin/var-dump-server');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return include __DIR__ . '/..'.'/symfony/var-dumper/Resources/bin/var-dump-server';
|
||||||
445
vendor/brick/math/CHANGELOG.md
vendored
Executable file
445
vendor/brick/math/CHANGELOG.md
vendored
Executable file
@@ -0,0 +1,445 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
## [0.11.0](https://github.com/brick/math/releases/tag/0.11.0) - 2023-01-16
|
||||||
|
|
||||||
|
💥 **Breaking changes**
|
||||||
|
|
||||||
|
- Minimum PHP version is now 8.0
|
||||||
|
- Methods accepting a union of types are now strongly typed<sup>*</sup>
|
||||||
|
- `MathException` now extends `Exception` instead of `RuntimeException`
|
||||||
|
|
||||||
|
<sup>* You may now run into type errors if you were passing `Stringable` objects to `of()` or any of the methods
|
||||||
|
internally calling `of()`, with `strict_types` enabled. You can fix this by casting `Stringable` objects to `string`
|
||||||
|
first.</sup>
|
||||||
|
|
||||||
|
## [0.10.2](https://github.com/brick/math/releases/tag/0.10.2) - 2022-08-11
|
||||||
|
|
||||||
|
👌 **Improvements**
|
||||||
|
|
||||||
|
- `BigRational::toFloat()` now simplifies the fraction before performing division (#73) thanks to @olsavmic
|
||||||
|
|
||||||
|
## [0.10.1](https://github.com/brick/math/releases/tag/0.10.1) - 2022-08-02
|
||||||
|
|
||||||
|
✨ **New features**
|
||||||
|
|
||||||
|
- `BigInteger::gcdMultiple()` returns the GCD of multiple `BigInteger` numbers
|
||||||
|
|
||||||
|
## [0.10.0](https://github.com/brick/math/releases/tag/0.10.0) - 2022-06-18
|
||||||
|
|
||||||
|
💥 **Breaking changes**
|
||||||
|
|
||||||
|
- Minimum PHP version is now 7.4
|
||||||
|
|
||||||
|
## [0.9.3](https://github.com/brick/math/releases/tag/0.9.3) - 2021-08-15
|
||||||
|
|
||||||
|
🚀 **Compatibility with PHP 8.1**
|
||||||
|
|
||||||
|
- Support for custom object serialization; this removes a warning on PHP 8.1 due to the `Serializable` interface being deprecated (#60) thanks @TRowbotham
|
||||||
|
|
||||||
|
## [0.9.2](https://github.com/brick/math/releases/tag/0.9.2) - 2021-01-20
|
||||||
|
|
||||||
|
🐛 **Bug fix**
|
||||||
|
|
||||||
|
- Incorrect results could be returned when using the BCMath calculator, with a default scale set with `bcscale()`, on PHP >= 7.2 (#55).
|
||||||
|
|
||||||
|
## [0.9.1](https://github.com/brick/math/releases/tag/0.9.1) - 2020-08-19
|
||||||
|
|
||||||
|
✨ **New features**
|
||||||
|
|
||||||
|
- `BigInteger::not()` returns the bitwise `NOT` value
|
||||||
|
|
||||||
|
🐛 **Bug fixes**
|
||||||
|
|
||||||
|
- `BigInteger::toBytes()` could return an incorrect binary representation for some numbers
|
||||||
|
- The bitwise operations `and()`, `or()`, `xor()` on `BigInteger` could return an incorrect result when the GMP extension is not available
|
||||||
|
|
||||||
|
## [0.9.0](https://github.com/brick/math/releases/tag/0.9.0) - 2020-08-18
|
||||||
|
|
||||||
|
👌 **Improvements**
|
||||||
|
|
||||||
|
- `BigNumber::of()` now accepts `.123` and `123.` formats, both of which return a `BigDecimal`
|
||||||
|
|
||||||
|
💥 **Breaking changes**
|
||||||
|
|
||||||
|
- Deprecated method `BigInteger::powerMod()` has been removed - use `modPow()` instead
|
||||||
|
- Deprecated method `BigInteger::parse()` has been removed - use `fromBase()` instead
|
||||||
|
|
||||||
|
## [0.8.17](https://github.com/brick/math/releases/tag/0.8.17) - 2020-08-19
|
||||||
|
|
||||||
|
🐛 **Bug fix**
|
||||||
|
|
||||||
|
- `BigInteger::toBytes()` could return an incorrect binary representation for some numbers
|
||||||
|
- The bitwise operations `and()`, `or()`, `xor()` on `BigInteger` could return an incorrect result when the GMP extension is not available
|
||||||
|
|
||||||
|
## [0.8.16](https://github.com/brick/math/releases/tag/0.8.16) - 2020-08-18
|
||||||
|
|
||||||
|
🚑 **Critical fix**
|
||||||
|
|
||||||
|
- This version reintroduces the deprecated `BigInteger::parse()` method, that has been removed by mistake in version `0.8.9` and should have lasted for the whole `0.8` release cycle.
|
||||||
|
|
||||||
|
✨ **New features**
|
||||||
|
|
||||||
|
- `BigInteger::modInverse()` calculates a modular multiplicative inverse
|
||||||
|
- `BigInteger::fromBytes()` creates a `BigInteger` from a byte string
|
||||||
|
- `BigInteger::toBytes()` converts a `BigInteger` to a byte string
|
||||||
|
- `BigInteger::randomBits()` creates a pseudo-random `BigInteger` of a given bit length
|
||||||
|
- `BigInteger::randomRange()` creates a pseudo-random `BigInteger` between two bounds
|
||||||
|
|
||||||
|
💩 **Deprecations**
|
||||||
|
|
||||||
|
- `BigInteger::powerMod()` is now deprecated in favour of `modPow()`
|
||||||
|
|
||||||
|
## [0.8.15](https://github.com/brick/math/releases/tag/0.8.15) - 2020-04-15
|
||||||
|
|
||||||
|
🐛 **Fixes**
|
||||||
|
|
||||||
|
- added missing `ext-json` requirement, due to `BigNumber` implementing `JsonSerializable`
|
||||||
|
|
||||||
|
⚡️ **Optimizations**
|
||||||
|
|
||||||
|
- additional optimization in `BigInteger::remainder()`
|
||||||
|
|
||||||
|
## [0.8.14](https://github.com/brick/math/releases/tag/0.8.14) - 2020-02-18
|
||||||
|
|
||||||
|
✨ **New features**
|
||||||
|
|
||||||
|
- `BigInteger::getLowestSetBit()` returns the index of the rightmost one bit
|
||||||
|
|
||||||
|
## [0.8.13](https://github.com/brick/math/releases/tag/0.8.13) - 2020-02-16
|
||||||
|
|
||||||
|
✨ **New features**
|
||||||
|
|
||||||
|
- `BigInteger::isEven()` tests whether the number is even
|
||||||
|
- `BigInteger::isOdd()` tests whether the number is odd
|
||||||
|
- `BigInteger::testBit()` tests if a bit is set
|
||||||
|
- `BigInteger::getBitLength()` returns the number of bits in the minimal representation of the number
|
||||||
|
|
||||||
|
## [0.8.12](https://github.com/brick/math/releases/tag/0.8.12) - 2020-02-03
|
||||||
|
|
||||||
|
🛠️ **Maintenance release**
|
||||||
|
|
||||||
|
Classes are now annotated for better static analysis with [psalm](https://psalm.dev/).
|
||||||
|
|
||||||
|
This is a maintenance release: no bug fixes, no new features, no breaking changes.
|
||||||
|
|
||||||
|
## [0.8.11](https://github.com/brick/math/releases/tag/0.8.11) - 2020-01-23
|
||||||
|
|
||||||
|
✨ **New feature**
|
||||||
|
|
||||||
|
`BigInteger::powerMod()` performs a power-with-modulo operation. Useful for crypto.
|
||||||
|
|
||||||
|
## [0.8.10](https://github.com/brick/math/releases/tag/0.8.10) - 2020-01-21
|
||||||
|
|
||||||
|
✨ **New feature**
|
||||||
|
|
||||||
|
`BigInteger::mod()` returns the **modulo** of two numbers. The *modulo* differs from the *remainder* when the signs of the operands are different.
|
||||||
|
|
||||||
|
## [0.8.9](https://github.com/brick/math/releases/tag/0.8.9) - 2020-01-08
|
||||||
|
|
||||||
|
⚡️ **Performance improvements**
|
||||||
|
|
||||||
|
A few additional optimizations in `BigInteger` and `BigDecimal` when one of the operands can be returned as is. Thanks to @tomtomsen in #24.
|
||||||
|
|
||||||
|
## [0.8.8](https://github.com/brick/math/releases/tag/0.8.8) - 2019-04-25
|
||||||
|
|
||||||
|
🐛 **Bug fixes**
|
||||||
|
|
||||||
|
- `BigInteger::toBase()` could return an empty string for zero values (BCMath & Native calculators only, GMP calculator unaffected)
|
||||||
|
|
||||||
|
✨ **New features**
|
||||||
|
|
||||||
|
- `BigInteger::toArbitraryBase()` converts a number to an arbitrary base, using a custom alphabet
|
||||||
|
- `BigInteger::fromArbitraryBase()` converts a string in an arbitrary base, using a custom alphabet, back to a number
|
||||||
|
|
||||||
|
These methods can be used as the foundation to convert strings between different bases/alphabets, using BigInteger as an intermediate representation.
|
||||||
|
|
||||||
|
💩 **Deprecations**
|
||||||
|
|
||||||
|
- `BigInteger::parse()` is now deprecated in favour of `fromBase()`
|
||||||
|
|
||||||
|
`BigInteger::fromBase()` works the same way as `parse()`, with 2 minor differences:
|
||||||
|
|
||||||
|
- the `$base` parameter is required, it does not default to `10`
|
||||||
|
- it throws a `NumberFormatException` instead of an `InvalidArgumentException` when the number is malformed
|
||||||
|
|
||||||
|
## [0.8.7](https://github.com/brick/math/releases/tag/0.8.7) - 2019-04-20
|
||||||
|
|
||||||
|
**Improvements**
|
||||||
|
|
||||||
|
- Safer conversion from `float` when using custom locales
|
||||||
|
- **Much faster** `NativeCalculator` implementation 🚀
|
||||||
|
|
||||||
|
You can expect **at least a 3x performance improvement** for common arithmetic operations when using the library on systems without GMP or BCMath; it gets exponentially faster on multiplications with a high number of digits. This is due to calculations now being performed on whole blocks of digits (the block size depending on the platform, 32-bit or 64-bit) instead of digit-by-digit as before.
|
||||||
|
|
||||||
|
## [0.8.6](https://github.com/brick/math/releases/tag/0.8.6) - 2019-04-11
|
||||||
|
|
||||||
|
**New method**
|
||||||
|
|
||||||
|
`BigNumber::sum()` returns the sum of one or more numbers.
|
||||||
|
|
||||||
|
## [0.8.5](https://github.com/brick/math/releases/tag/0.8.5) - 2019-02-12
|
||||||
|
|
||||||
|
**Bug fix**: `of()` factory methods could fail when passing a `float` in environments using a `LC_NUMERIC` locale with a decimal separator other than `'.'` (#20).
|
||||||
|
|
||||||
|
Thanks @manowark 👍
|
||||||
|
|
||||||
|
## [0.8.4](https://github.com/brick/math/releases/tag/0.8.4) - 2018-12-07
|
||||||
|
|
||||||
|
**New method**
|
||||||
|
|
||||||
|
`BigDecimal::sqrt()` calculates the square root of a decimal number, to a given scale.
|
||||||
|
|
||||||
|
## [0.8.3](https://github.com/brick/math/releases/tag/0.8.3) - 2018-12-06
|
||||||
|
|
||||||
|
**New method**
|
||||||
|
|
||||||
|
`BigInteger::sqrt()` calculates the square root of a number (thanks @peter279k).
|
||||||
|
|
||||||
|
**New exception**
|
||||||
|
|
||||||
|
`NegativeNumberException` is thrown when calling `sqrt()` on a negative number.
|
||||||
|
|
||||||
|
## [0.8.2](https://github.com/brick/math/releases/tag/0.8.2) - 2018-11-08
|
||||||
|
|
||||||
|
**Performance update**
|
||||||
|
|
||||||
|
- Further improvement of `toInt()` performance
|
||||||
|
- `NativeCalculator` can now perform some multiplications more efficiently
|
||||||
|
|
||||||
|
## [0.8.1](https://github.com/brick/math/releases/tag/0.8.1) - 2018-11-07
|
||||||
|
|
||||||
|
Performance optimization of `toInt()` methods.
|
||||||
|
|
||||||
|
## [0.8.0](https://github.com/brick/math/releases/tag/0.8.0) - 2018-10-13
|
||||||
|
|
||||||
|
**Breaking changes**
|
||||||
|
|
||||||
|
The following deprecated methods have been removed. Use the new method name instead:
|
||||||
|
|
||||||
|
| Method removed | Replacement method |
|
||||||
|
| --- | --- |
|
||||||
|
| `BigDecimal::getIntegral()` | `BigDecimal::getIntegralPart()` |
|
||||||
|
| `BigDecimal::getFraction()` | `BigDecimal::getFractionalPart()` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**New features**
|
||||||
|
|
||||||
|
`BigInteger` has been augmented with 5 new methods for bitwise operations:
|
||||||
|
|
||||||
|
| New method | Description |
|
||||||
|
| --- | --- |
|
||||||
|
| `and()` | performs a bitwise `AND` operation on two numbers |
|
||||||
|
| `or()` | performs a bitwise `OR` operation on two numbers |
|
||||||
|
| `xor()` | performs a bitwise `XOR` operation on two numbers |
|
||||||
|
| `shiftedLeft()` | returns the number shifted left by a number of bits |
|
||||||
|
| `shiftedRight()` | returns the number shifted right by a number of bits |
|
||||||
|
|
||||||
|
Thanks to @DASPRiD 👍
|
||||||
|
|
||||||
|
## [0.7.3](https://github.com/brick/math/releases/tag/0.7.3) - 2018-08-20
|
||||||
|
|
||||||
|
**New method:** `BigDecimal::hasNonZeroFractionalPart()`
|
||||||
|
|
||||||
|
**Renamed/deprecated methods:**
|
||||||
|
|
||||||
|
- `BigDecimal::getIntegral()` has been renamed to `getIntegralPart()` and is now deprecated
|
||||||
|
- `BigDecimal::getFraction()` has been renamed to `getFractionalPart()` and is now deprecated
|
||||||
|
|
||||||
|
## [0.7.2](https://github.com/brick/math/releases/tag/0.7.2) - 2018-07-21
|
||||||
|
|
||||||
|
**Performance update**
|
||||||
|
|
||||||
|
`BigInteger::parse()` and `toBase()` now use GMP's built-in base conversion features when available.
|
||||||
|
|
||||||
|
## [0.7.1](https://github.com/brick/math/releases/tag/0.7.1) - 2018-03-01
|
||||||
|
|
||||||
|
This is a maintenance release, no code has been changed.
|
||||||
|
|
||||||
|
- When installed with `--no-dev`, the autoloader does not autoload tests anymore
|
||||||
|
- Tests and other files unnecessary for production are excluded from the dist package
|
||||||
|
|
||||||
|
This will help make installations more compact.
|
||||||
|
|
||||||
|
## [0.7.0](https://github.com/brick/math/releases/tag/0.7.0) - 2017-10-02
|
||||||
|
|
||||||
|
Methods renamed:
|
||||||
|
|
||||||
|
- `BigNumber:sign()` has been renamed to `getSign()`
|
||||||
|
- `BigDecimal::unscaledValue()` has been renamed to `getUnscaledValue()`
|
||||||
|
- `BigDecimal::scale()` has been renamed to `getScale()`
|
||||||
|
- `BigDecimal::integral()` has been renamed to `getIntegral()`
|
||||||
|
- `BigDecimal::fraction()` has been renamed to `getFraction()`
|
||||||
|
- `BigRational::numerator()` has been renamed to `getNumerator()`
|
||||||
|
- `BigRational::denominator()` has been renamed to `getDenominator()`
|
||||||
|
|
||||||
|
Classes renamed:
|
||||||
|
|
||||||
|
- `ArithmeticException` has been renamed to `MathException`
|
||||||
|
|
||||||
|
## [0.6.2](https://github.com/brick/math/releases/tag/0.6.2) - 2017-10-02
|
||||||
|
|
||||||
|
The base class for all exceptions is now `MathException`.
|
||||||
|
`ArithmeticException` has been deprecated, and will be removed in 0.7.0.
|
||||||
|
|
||||||
|
## [0.6.1](https://github.com/brick/math/releases/tag/0.6.1) - 2017-10-02
|
||||||
|
|
||||||
|
A number of methods have been renamed:
|
||||||
|
|
||||||
|
- `BigNumber:sign()` is deprecated; use `getSign()` instead
|
||||||
|
- `BigDecimal::unscaledValue()` is deprecated; use `getUnscaledValue()` instead
|
||||||
|
- `BigDecimal::scale()` is deprecated; use `getScale()` instead
|
||||||
|
- `BigDecimal::integral()` is deprecated; use `getIntegral()` instead
|
||||||
|
- `BigDecimal::fraction()` is deprecated; use `getFraction()` instead
|
||||||
|
- `BigRational::numerator()` is deprecated; use `getNumerator()` instead
|
||||||
|
- `BigRational::denominator()` is deprecated; use `getDenominator()` instead
|
||||||
|
|
||||||
|
The old methods will be removed in version 0.7.0.
|
||||||
|
|
||||||
|
## [0.6.0](https://github.com/brick/math/releases/tag/0.6.0) - 2017-08-25
|
||||||
|
|
||||||
|
- Minimum PHP version is now [7.1](https://gophp71.org/); for PHP 5.6 and PHP 7.0 support, use version `0.5`
|
||||||
|
- Deprecated method `BigDecimal::withScale()` has been removed; use `toScale()` instead
|
||||||
|
- Method `BigNumber::toInteger()` has been renamed to `toInt()`
|
||||||
|
|
||||||
|
## [0.5.4](https://github.com/brick/math/releases/tag/0.5.4) - 2016-10-17
|
||||||
|
|
||||||
|
`BigNumber` classes now implement [JsonSerializable](http://php.net/manual/en/class.jsonserializable.php).
|
||||||
|
The JSON output is always a string.
|
||||||
|
|
||||||
|
## [0.5.3](https://github.com/brick/math/releases/tag/0.5.3) - 2016-03-31
|
||||||
|
|
||||||
|
This is a bugfix release. Dividing by a negative power of 1 with the same scale as the dividend could trigger an incorrect optimization which resulted in a wrong result. See #6.
|
||||||
|
|
||||||
|
## [0.5.2](https://github.com/brick/math/releases/tag/0.5.2) - 2015-08-06
|
||||||
|
|
||||||
|
The `$scale` parameter of `BigDecimal::dividedBy()` is now optional again.
|
||||||
|
|
||||||
|
## [0.5.1](https://github.com/brick/math/releases/tag/0.5.1) - 2015-07-05
|
||||||
|
|
||||||
|
**New method: `BigNumber::toScale()`**
|
||||||
|
|
||||||
|
This allows to convert any `BigNumber` to a `BigDecimal` with a given scale, using rounding if necessary.
|
||||||
|
|
||||||
|
## [0.5.0](https://github.com/brick/math/releases/tag/0.5.0) - 2015-07-04
|
||||||
|
|
||||||
|
**New features**
|
||||||
|
- Common `BigNumber` interface for all classes, with the following methods:
|
||||||
|
- `sign()` and derived methods (`isZero()`, `isPositive()`, ...)
|
||||||
|
- `compareTo()` and derived methods (`isEqualTo()`, `isGreaterThan()`, ...) that work across different `BigNumber` types
|
||||||
|
- `toBigInteger()`, `toBigDecimal()`, `toBigRational`() conversion methods
|
||||||
|
- `toInteger()` and `toFloat()` conversion methods to native types
|
||||||
|
- Unified `of()` behaviour: every class now accepts any type of number, provided that it can be safely converted to the current type
|
||||||
|
- New method: `BigDecimal::exactlyDividedBy()`; this method automatically computes the scale of the result, provided that the division yields a finite number of digits
|
||||||
|
- New methods: `BigRational::quotient()` and `remainder()`
|
||||||
|
- Fine-grained exceptions: `DivisionByZeroException`, `RoundingNecessaryException`, `NumberFormatException`
|
||||||
|
- Factory methods `zero()`, `one()` and `ten()` available in all classes
|
||||||
|
- Rounding mode reintroduced in `BigInteger::dividedBy()`
|
||||||
|
|
||||||
|
This release also comes with many performance improvements.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Breaking changes**
|
||||||
|
- `BigInteger`:
|
||||||
|
- `getSign()` is renamed to `sign()`
|
||||||
|
- `toString()` is renamed to `toBase()`
|
||||||
|
- `BigInteger::dividedBy()` now throws an exception by default if the remainder is not zero; use `quotient()` to get the previous behaviour
|
||||||
|
- `BigDecimal`:
|
||||||
|
- `getSign()` is renamed to `sign()`
|
||||||
|
- `getUnscaledValue()` is renamed to `unscaledValue()`
|
||||||
|
- `getScale()` is renamed to `scale()`
|
||||||
|
- `getIntegral()` is renamed to `integral()`
|
||||||
|
- `getFraction()` is renamed to `fraction()`
|
||||||
|
- `divideAndRemainder()` is renamed to `quotientAndRemainder()`
|
||||||
|
- `dividedBy()` now takes a **mandatory** `$scale` parameter **before** the rounding mode
|
||||||
|
- `toBigInteger()` does not accept a `$roundingMode` parameter anymore
|
||||||
|
- `toBigRational()` does not simplify the fraction anymore; explicitly add `->simplified()` to get the previous behaviour
|
||||||
|
- `BigRational`:
|
||||||
|
- `getSign()` is renamed to `sign()`
|
||||||
|
- `getNumerator()` is renamed to `numerator()`
|
||||||
|
- `getDenominator()` is renamed to `denominator()`
|
||||||
|
- `of()` is renamed to `nd()`, while `parse()` is renamed to `of()`
|
||||||
|
- Miscellaneous:
|
||||||
|
- `ArithmeticException` is moved to an `Exception\` sub-namespace
|
||||||
|
- `of()` factory methods now throw `NumberFormatException` instead of `InvalidArgumentException`
|
||||||
|
|
||||||
|
## [0.4.3](https://github.com/brick/math/releases/tag/0.4.3) - 2016-03-31
|
||||||
|
|
||||||
|
Backport of two bug fixes from the 0.5 branch:
|
||||||
|
- `BigInteger::parse()` did not always throw `InvalidArgumentException` as expected
|
||||||
|
- Dividing by a negative power of 1 with the same scale as the dividend could trigger an incorrect optimization which resulted in a wrong result. See #6.
|
||||||
|
|
||||||
|
## [0.4.2](https://github.com/brick/math/releases/tag/0.4.2) - 2015-06-16
|
||||||
|
|
||||||
|
New method: `BigDecimal::stripTrailingZeros()`
|
||||||
|
|
||||||
|
## [0.4.1](https://github.com/brick/math/releases/tag/0.4.1) - 2015-06-12
|
||||||
|
|
||||||
|
Introducing a `BigRational` class, to perform calculations on fractions of any size.
|
||||||
|
|
||||||
|
## [0.4.0](https://github.com/brick/math/releases/tag/0.4.0) - 2015-06-12
|
||||||
|
|
||||||
|
Rounding modes have been removed from `BigInteger`, and are now a concept specific to `BigDecimal`.
|
||||||
|
|
||||||
|
`BigInteger::dividedBy()` now always returns the quotient of the division.
|
||||||
|
|
||||||
|
## [0.3.5](https://github.com/brick/math/releases/tag/0.3.5) - 2016-03-31
|
||||||
|
|
||||||
|
Backport of two bug fixes from the 0.5 branch:
|
||||||
|
|
||||||
|
- `BigInteger::parse()` did not always throw `InvalidArgumentException` as expected
|
||||||
|
- Dividing by a negative power of 1 with the same scale as the dividend could trigger an incorrect optimization which resulted in a wrong result. See #6.
|
||||||
|
|
||||||
|
## [0.3.4](https://github.com/brick/math/releases/tag/0.3.4) - 2015-06-11
|
||||||
|
|
||||||
|
New methods:
|
||||||
|
- `BigInteger::remainder()` returns the remainder of a division only
|
||||||
|
- `BigInteger::gcd()` returns the greatest common divisor of two numbers
|
||||||
|
|
||||||
|
## [0.3.3](https://github.com/brick/math/releases/tag/0.3.3) - 2015-06-07
|
||||||
|
|
||||||
|
Fix `toString()` not handling negative numbers.
|
||||||
|
|
||||||
|
## [0.3.2](https://github.com/brick/math/releases/tag/0.3.2) - 2015-06-07
|
||||||
|
|
||||||
|
`BigInteger` and `BigDecimal` now have a `getSign()` method that returns:
|
||||||
|
- `-1` if the number is negative
|
||||||
|
- `0` if the number is zero
|
||||||
|
- `1` if the number is positive
|
||||||
|
|
||||||
|
## [0.3.1](https://github.com/brick/math/releases/tag/0.3.1) - 2015-06-05
|
||||||
|
|
||||||
|
Minor performance improvements
|
||||||
|
|
||||||
|
## [0.3.0](https://github.com/brick/math/releases/tag/0.3.0) - 2015-06-04
|
||||||
|
|
||||||
|
The `$roundingMode` and `$scale` parameters have been swapped in `BigDecimal::dividedBy()`.
|
||||||
|
|
||||||
|
## [0.2.2](https://github.com/brick/math/releases/tag/0.2.2) - 2015-06-04
|
||||||
|
|
||||||
|
Stronger immutability guarantee for `BigInteger` and `BigDecimal`.
|
||||||
|
|
||||||
|
So far, it would have been possible to break immutability of these classes by calling the `unserialize()` internal function. This release fixes that.
|
||||||
|
|
||||||
|
## [0.2.1](https://github.com/brick/math/releases/tag/0.2.1) - 2015-06-02
|
||||||
|
|
||||||
|
Added `BigDecimal::divideAndRemainder()`
|
||||||
|
|
||||||
|
## [0.2.0](https://github.com/brick/math/releases/tag/0.2.0) - 2015-05-22
|
||||||
|
|
||||||
|
- `min()` and `max()` do not accept an `array` anymore, but a variable number of parameters
|
||||||
|
- **minimum PHP version is now 5.6**
|
||||||
|
- continuous integration with PHP 7
|
||||||
|
|
||||||
|
## [0.1.1](https://github.com/brick/math/releases/tag/0.1.1) - 2014-09-01
|
||||||
|
|
||||||
|
- Added `BigInteger::power()`
|
||||||
|
- Added HHVM support
|
||||||
|
|
||||||
|
## [0.1.0](https://github.com/brick/math/releases/tag/0.1.0) - 2014-08-31
|
||||||
|
|
||||||
|
First beta release.
|
||||||
|
|
||||||
20
vendor/brick/math/LICENSE
vendored
Executable file
20
vendor/brick/math/LICENSE
vendored
Executable file
@@ -0,0 +1,20 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013-present Benjamin Morel
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
34
vendor/brick/math/composer.json
vendored
Executable file
34
vendor/brick/math/composer.json
vendored
Executable file
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"name": "brick/math",
|
||||||
|
"description": "Arbitrary-precision arithmetic library",
|
||||||
|
"type": "library",
|
||||||
|
"keywords": [
|
||||||
|
"Brick",
|
||||||
|
"Math",
|
||||||
|
"Arbitrary-precision",
|
||||||
|
"Arithmetic",
|
||||||
|
"BigInteger",
|
||||||
|
"BigDecimal",
|
||||||
|
"BigRational",
|
||||||
|
"Bignum"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"require": {
|
||||||
|
"php": "^8.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "^9.0",
|
||||||
|
"php-coveralls/php-coveralls": "^2.2",
|
||||||
|
"vimeo/psalm": "5.0.0"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Brick\\Math\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload-dev": {
|
||||||
|
"psr-4": {
|
||||||
|
"Brick\\Math\\Tests\\": "tests/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
786
vendor/brick/math/src/BigDecimal.php
vendored
Executable file
786
vendor/brick/math/src/BigDecimal.php
vendored
Executable file
@@ -0,0 +1,786 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Brick\Math;
|
||||||
|
|
||||||
|
use Brick\Math\Exception\DivisionByZeroException;
|
||||||
|
use Brick\Math\Exception\MathException;
|
||||||
|
use Brick\Math\Exception\NegativeNumberException;
|
||||||
|
use Brick\Math\Internal\Calculator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Immutable, arbitrary-precision signed decimal numbers.
|
||||||
|
*
|
||||||
|
* @psalm-immutable
|
||||||
|
*/
|
||||||
|
final class BigDecimal extends BigNumber
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The unscaled value of this decimal number.
|
||||||
|
*
|
||||||
|
* This is a string of digits with an optional leading minus sign.
|
||||||
|
* No leading zero must be present.
|
||||||
|
* No leading minus sign must be present if the value is 0.
|
||||||
|
*/
|
||||||
|
private string $value;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The scale (number of digits after the decimal point) of this decimal number.
|
||||||
|
*
|
||||||
|
* This must be zero or more.
|
||||||
|
*/
|
||||||
|
private int $scale;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Protected constructor. Use a factory method to obtain an instance.
|
||||||
|
*
|
||||||
|
* @param string $value The unscaled value, validated.
|
||||||
|
* @param int $scale The scale, validated.
|
||||||
|
*/
|
||||||
|
protected function __construct(string $value, int $scale = 0)
|
||||||
|
{
|
||||||
|
$this->value = $value;
|
||||||
|
$this->scale = $scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a BigDecimal of the given value.
|
||||||
|
*
|
||||||
|
* @throws MathException If the value cannot be converted to a BigDecimal.
|
||||||
|
*
|
||||||
|
* @psalm-pure
|
||||||
|
*/
|
||||||
|
public static function of(BigNumber|int|float|string $value) : BigDecimal
|
||||||
|
{
|
||||||
|
return parent::of($value)->toBigDecimal();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a BigDecimal from an unscaled value and a scale.
|
||||||
|
*
|
||||||
|
* Example: `(12345, 3)` will result in the BigDecimal `12.345`.
|
||||||
|
*
|
||||||
|
* @param BigNumber|int|float|string $value The unscaled value. Must be convertible to a BigInteger.
|
||||||
|
* @param int $scale The scale of the number, positive or zero.
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException If the scale is negative.
|
||||||
|
*
|
||||||
|
* @psalm-pure
|
||||||
|
*/
|
||||||
|
public static function ofUnscaledValue(BigNumber|int|float|string $value, int $scale = 0) : BigDecimal
|
||||||
|
{
|
||||||
|
if ($scale < 0) {
|
||||||
|
throw new \InvalidArgumentException('The scale cannot be negative.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return new BigDecimal((string) BigInteger::of($value), $scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a BigDecimal representing zero, with a scale of zero.
|
||||||
|
*
|
||||||
|
* @psalm-pure
|
||||||
|
*/
|
||||||
|
public static function zero() : BigDecimal
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @psalm-suppress ImpureStaticVariable
|
||||||
|
* @var BigDecimal|null $zero
|
||||||
|
*/
|
||||||
|
static $zero;
|
||||||
|
|
||||||
|
if ($zero === null) {
|
||||||
|
$zero = new BigDecimal('0');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a BigDecimal representing one, with a scale of zero.
|
||||||
|
*
|
||||||
|
* @psalm-pure
|
||||||
|
*/
|
||||||
|
public static function one() : BigDecimal
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @psalm-suppress ImpureStaticVariable
|
||||||
|
* @var BigDecimal|null $one
|
||||||
|
*/
|
||||||
|
static $one;
|
||||||
|
|
||||||
|
if ($one === null) {
|
||||||
|
$one = new BigDecimal('1');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $one;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a BigDecimal representing ten, with a scale of zero.
|
||||||
|
*
|
||||||
|
* @psalm-pure
|
||||||
|
*/
|
||||||
|
public static function ten() : BigDecimal
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @psalm-suppress ImpureStaticVariable
|
||||||
|
* @var BigDecimal|null $ten
|
||||||
|
*/
|
||||||
|
static $ten;
|
||||||
|
|
||||||
|
if ($ten === null) {
|
||||||
|
$ten = new BigDecimal('10');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $ten;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the sum of this number and the given one.
|
||||||
|
*
|
||||||
|
* The result has a scale of `max($this->scale, $that->scale)`.
|
||||||
|
*
|
||||||
|
* @param BigNumber|int|float|string $that The number to add. Must be convertible to a BigDecimal.
|
||||||
|
*
|
||||||
|
* @throws MathException If the number is not valid, or is not convertible to a BigDecimal.
|
||||||
|
*/
|
||||||
|
public function plus(BigNumber|int|float|string $that) : BigDecimal
|
||||||
|
{
|
||||||
|
$that = BigDecimal::of($that);
|
||||||
|
|
||||||
|
if ($that->value === '0' && $that->scale <= $this->scale) {
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->value === '0' && $this->scale <= $that->scale) {
|
||||||
|
return $that;
|
||||||
|
}
|
||||||
|
|
||||||
|
[$a, $b] = $this->scaleValues($this, $that);
|
||||||
|
|
||||||
|
$value = Calculator::get()->add($a, $b);
|
||||||
|
$scale = $this->scale > $that->scale ? $this->scale : $that->scale;
|
||||||
|
|
||||||
|
return new BigDecimal($value, $scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the difference of this number and the given one.
|
||||||
|
*
|
||||||
|
* The result has a scale of `max($this->scale, $that->scale)`.
|
||||||
|
*
|
||||||
|
* @param BigNumber|int|float|string $that The number to subtract. Must be convertible to a BigDecimal.
|
||||||
|
*
|
||||||
|
* @throws MathException If the number is not valid, or is not convertible to a BigDecimal.
|
||||||
|
*/
|
||||||
|
public function minus(BigNumber|int|float|string $that) : BigDecimal
|
||||||
|
{
|
||||||
|
$that = BigDecimal::of($that);
|
||||||
|
|
||||||
|
if ($that->value === '0' && $that->scale <= $this->scale) {
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
[$a, $b] = $this->scaleValues($this, $that);
|
||||||
|
|
||||||
|
$value = Calculator::get()->sub($a, $b);
|
||||||
|
$scale = $this->scale > $that->scale ? $this->scale : $that->scale;
|
||||||
|
|
||||||
|
return new BigDecimal($value, $scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the product of this number and the given one.
|
||||||
|
*
|
||||||
|
* The result has a scale of `$this->scale + $that->scale`.
|
||||||
|
*
|
||||||
|
* @param BigNumber|int|float|string $that The multiplier. Must be convertible to a BigDecimal.
|
||||||
|
*
|
||||||
|
* @throws MathException If the multiplier is not a valid number, or is not convertible to a BigDecimal.
|
||||||
|
*/
|
||||||
|
public function multipliedBy(BigNumber|int|float|string $that) : BigDecimal
|
||||||
|
{
|
||||||
|
$that = BigDecimal::of($that);
|
||||||
|
|
||||||
|
if ($that->value === '1' && $that->scale === 0) {
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->value === '1' && $this->scale === 0) {
|
||||||
|
return $that;
|
||||||
|
}
|
||||||
|
|
||||||
|
$value = Calculator::get()->mul($this->value, $that->value);
|
||||||
|
$scale = $this->scale + $that->scale;
|
||||||
|
|
||||||
|
return new BigDecimal($value, $scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the result of the division of this number by the given one, at the given scale.
|
||||||
|
*
|
||||||
|
* @param BigNumber|int|float|string $that The divisor.
|
||||||
|
* @param int|null $scale The desired scale, or null to use the scale of this number.
|
||||||
|
* @param int $roundingMode An optional rounding mode.
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException If the scale or rounding mode is invalid.
|
||||||
|
* @throws MathException If the number is invalid, is zero, or rounding was necessary.
|
||||||
|
*/
|
||||||
|
public function dividedBy(BigNumber|int|float|string $that, ?int $scale = null, int $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal
|
||||||
|
{
|
||||||
|
$that = BigDecimal::of($that);
|
||||||
|
|
||||||
|
if ($that->isZero()) {
|
||||||
|
throw DivisionByZeroException::divisionByZero();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($scale === null) {
|
||||||
|
$scale = $this->scale;
|
||||||
|
} elseif ($scale < 0) {
|
||||||
|
throw new \InvalidArgumentException('Scale cannot be negative.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($that->value === '1' && $that->scale === 0 && $scale === $this->scale) {
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
$p = $this->valueWithMinScale($that->scale + $scale);
|
||||||
|
$q = $that->valueWithMinScale($this->scale - $scale);
|
||||||
|
|
||||||
|
$result = Calculator::get()->divRound($p, $q, $roundingMode);
|
||||||
|
|
||||||
|
return new BigDecimal($result, $scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the exact result of the division of this number by the given one.
|
||||||
|
*
|
||||||
|
* The scale of the result is automatically calculated to fit all the fraction digits.
|
||||||
|
*
|
||||||
|
* @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigDecimal.
|
||||||
|
*
|
||||||
|
* @throws MathException If the divisor is not a valid number, is not convertible to a BigDecimal, is zero,
|
||||||
|
* or the result yields an infinite number of digits.
|
||||||
|
*/
|
||||||
|
public function exactlyDividedBy(BigNumber|int|float|string $that) : BigDecimal
|
||||||
|
{
|
||||||
|
$that = BigDecimal::of($that);
|
||||||
|
|
||||||
|
if ($that->value === '0') {
|
||||||
|
throw DivisionByZeroException::divisionByZero();
|
||||||
|
}
|
||||||
|
|
||||||
|
[, $b] = $this->scaleValues($this, $that);
|
||||||
|
|
||||||
|
$d = \rtrim($b, '0');
|
||||||
|
$scale = \strlen($b) - \strlen($d);
|
||||||
|
|
||||||
|
$calculator = Calculator::get();
|
||||||
|
|
||||||
|
foreach ([5, 2] as $prime) {
|
||||||
|
for (;;) {
|
||||||
|
$lastDigit = (int) $d[-1];
|
||||||
|
|
||||||
|
if ($lastDigit % $prime !== 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$d = $calculator->divQ($d, (string) $prime);
|
||||||
|
$scale++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->dividedBy($that, $scale)->stripTrailingZeros();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns this number exponentiated to the given value.
|
||||||
|
*
|
||||||
|
* The result has a scale of `$this->scale * $exponent`.
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException If the exponent is not in the range 0 to 1,000,000.
|
||||||
|
*/
|
||||||
|
public function power(int $exponent) : BigDecimal
|
||||||
|
{
|
||||||
|
if ($exponent === 0) {
|
||||||
|
return BigDecimal::one();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($exponent === 1) {
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($exponent < 0 || $exponent > Calculator::MAX_POWER) {
|
||||||
|
throw new \InvalidArgumentException(\sprintf(
|
||||||
|
'The exponent %d is not in the range 0 to %d.',
|
||||||
|
$exponent,
|
||||||
|
Calculator::MAX_POWER
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new BigDecimal(Calculator::get()->pow($this->value, $exponent), $this->scale * $exponent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the quotient of the division of this number by this given one.
|
||||||
|
*
|
||||||
|
* The quotient has a scale of `0`.
|
||||||
|
*
|
||||||
|
* @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigDecimal.
|
||||||
|
*
|
||||||
|
* @throws MathException If the divisor is not a valid decimal number, or is zero.
|
||||||
|
*/
|
||||||
|
public function quotient(BigNumber|int|float|string $that) : BigDecimal
|
||||||
|
{
|
||||||
|
$that = BigDecimal::of($that);
|
||||||
|
|
||||||
|
if ($that->isZero()) {
|
||||||
|
throw DivisionByZeroException::divisionByZero();
|
||||||
|
}
|
||||||
|
|
||||||
|
$p = $this->valueWithMinScale($that->scale);
|
||||||
|
$q = $that->valueWithMinScale($this->scale);
|
||||||
|
|
||||||
|
$quotient = Calculator::get()->divQ($p, $q);
|
||||||
|
|
||||||
|
return new BigDecimal($quotient, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the remainder of the division of this number by this given one.
|
||||||
|
*
|
||||||
|
* The remainder has a scale of `max($this->scale, $that->scale)`.
|
||||||
|
*
|
||||||
|
* @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigDecimal.
|
||||||
|
*
|
||||||
|
* @throws MathException If the divisor is not a valid decimal number, or is zero.
|
||||||
|
*/
|
||||||
|
public function remainder(BigNumber|int|float|string $that) : BigDecimal
|
||||||
|
{
|
||||||
|
$that = BigDecimal::of($that);
|
||||||
|
|
||||||
|
if ($that->isZero()) {
|
||||||
|
throw DivisionByZeroException::divisionByZero();
|
||||||
|
}
|
||||||
|
|
||||||
|
$p = $this->valueWithMinScale($that->scale);
|
||||||
|
$q = $that->valueWithMinScale($this->scale);
|
||||||
|
|
||||||
|
$remainder = Calculator::get()->divR($p, $q);
|
||||||
|
|
||||||
|
$scale = $this->scale > $that->scale ? $this->scale : $that->scale;
|
||||||
|
|
||||||
|
return new BigDecimal($remainder, $scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the quotient and remainder of the division of this number by the given one.
|
||||||
|
*
|
||||||
|
* The quotient has a scale of `0`, and the remainder has a scale of `max($this->scale, $that->scale)`.
|
||||||
|
*
|
||||||
|
* @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigDecimal.
|
||||||
|
*
|
||||||
|
* @return BigDecimal[] An array containing the quotient and the remainder.
|
||||||
|
*
|
||||||
|
* @throws MathException If the divisor is not a valid decimal number, or is zero.
|
||||||
|
*/
|
||||||
|
public function quotientAndRemainder(BigNumber|int|float|string $that) : array
|
||||||
|
{
|
||||||
|
$that = BigDecimal::of($that);
|
||||||
|
|
||||||
|
if ($that->isZero()) {
|
||||||
|
throw DivisionByZeroException::divisionByZero();
|
||||||
|
}
|
||||||
|
|
||||||
|
$p = $this->valueWithMinScale($that->scale);
|
||||||
|
$q = $that->valueWithMinScale($this->scale);
|
||||||
|
|
||||||
|
[$quotient, $remainder] = Calculator::get()->divQR($p, $q);
|
||||||
|
|
||||||
|
$scale = $this->scale > $that->scale ? $this->scale : $that->scale;
|
||||||
|
|
||||||
|
$quotient = new BigDecimal($quotient, 0);
|
||||||
|
$remainder = new BigDecimal($remainder, $scale);
|
||||||
|
|
||||||
|
return [$quotient, $remainder];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the square root of this number, rounded down to the given number of decimals.
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException If the scale is negative.
|
||||||
|
* @throws NegativeNumberException If this number is negative.
|
||||||
|
*/
|
||||||
|
public function sqrt(int $scale) : BigDecimal
|
||||||
|
{
|
||||||
|
if ($scale < 0) {
|
||||||
|
throw new \InvalidArgumentException('Scale cannot be negative.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->value === '0') {
|
||||||
|
return new BigDecimal('0', $scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->value[0] === '-') {
|
||||||
|
throw new NegativeNumberException('Cannot calculate the square root of a negative number.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$value = $this->value;
|
||||||
|
$addDigits = 2 * $scale - $this->scale;
|
||||||
|
|
||||||
|
if ($addDigits > 0) {
|
||||||
|
// add zeros
|
||||||
|
$value .= \str_repeat('0', $addDigits);
|
||||||
|
} elseif ($addDigits < 0) {
|
||||||
|
// trim digits
|
||||||
|
if (-$addDigits >= \strlen($this->value)) {
|
||||||
|
// requesting a scale too low, will always yield a zero result
|
||||||
|
return new BigDecimal('0', $scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
$value = \substr($value, 0, $addDigits);
|
||||||
|
}
|
||||||
|
|
||||||
|
$value = Calculator::get()->sqrt($value);
|
||||||
|
|
||||||
|
return new BigDecimal($value, $scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a copy of this BigDecimal with the decimal point moved $n places to the left.
|
||||||
|
*/
|
||||||
|
public function withPointMovedLeft(int $n) : BigDecimal
|
||||||
|
{
|
||||||
|
if ($n === 0) {
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($n < 0) {
|
||||||
|
return $this->withPointMovedRight(-$n);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new BigDecimal($this->value, $this->scale + $n);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a copy of this BigDecimal with the decimal point moved $n places to the right.
|
||||||
|
*/
|
||||||
|
public function withPointMovedRight(int $n) : BigDecimal
|
||||||
|
{
|
||||||
|
if ($n === 0) {
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($n < 0) {
|
||||||
|
return $this->withPointMovedLeft(-$n);
|
||||||
|
}
|
||||||
|
|
||||||
|
$value = $this->value;
|
||||||
|
$scale = $this->scale - $n;
|
||||||
|
|
||||||
|
if ($scale < 0) {
|
||||||
|
if ($value !== '0') {
|
||||||
|
$value .= \str_repeat('0', -$scale);
|
||||||
|
}
|
||||||
|
$scale = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new BigDecimal($value, $scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a copy of this BigDecimal with any trailing zeros removed from the fractional part.
|
||||||
|
*/
|
||||||
|
public function stripTrailingZeros() : BigDecimal
|
||||||
|
{
|
||||||
|
if ($this->scale === 0) {
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
$trimmedValue = \rtrim($this->value, '0');
|
||||||
|
|
||||||
|
if ($trimmedValue === '') {
|
||||||
|
return BigDecimal::zero();
|
||||||
|
}
|
||||||
|
|
||||||
|
$trimmableZeros = \strlen($this->value) - \strlen($trimmedValue);
|
||||||
|
|
||||||
|
if ($trimmableZeros === 0) {
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($trimmableZeros > $this->scale) {
|
||||||
|
$trimmableZeros = $this->scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
$value = \substr($this->value, 0, -$trimmableZeros);
|
||||||
|
$scale = $this->scale - $trimmableZeros;
|
||||||
|
|
||||||
|
return new BigDecimal($value, $scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the absolute value of this number.
|
||||||
|
*/
|
||||||
|
public function abs() : BigDecimal
|
||||||
|
{
|
||||||
|
return $this->isNegative() ? $this->negated() : $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the negated value of this number.
|
||||||
|
*/
|
||||||
|
public function negated() : BigDecimal
|
||||||
|
{
|
||||||
|
return new BigDecimal(Calculator::get()->neg($this->value), $this->scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function compareTo(BigNumber|int|float|string $that) : int
|
||||||
|
{
|
||||||
|
$that = BigNumber::of($that);
|
||||||
|
|
||||||
|
if ($that instanceof BigInteger) {
|
||||||
|
$that = $that->toBigDecimal();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($that instanceof BigDecimal) {
|
||||||
|
[$a, $b] = $this->scaleValues($this, $that);
|
||||||
|
|
||||||
|
return Calculator::get()->cmp($a, $b);
|
||||||
|
}
|
||||||
|
|
||||||
|
return - $that->compareTo($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSign() : int
|
||||||
|
{
|
||||||
|
return ($this->value === '0') ? 0 : (($this->value[0] === '-') ? -1 : 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getUnscaledValue() : BigInteger
|
||||||
|
{
|
||||||
|
return self::newBigInteger($this->value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getScale() : int
|
||||||
|
{
|
||||||
|
return $this->scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string representing the integral part of this decimal number.
|
||||||
|
*
|
||||||
|
* Example: `-123.456` => `-123`.
|
||||||
|
*/
|
||||||
|
public function getIntegralPart() : string
|
||||||
|
{
|
||||||
|
if ($this->scale === 0) {
|
||||||
|
return $this->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
$value = $this->getUnscaledValueWithLeadingZeros();
|
||||||
|
|
||||||
|
return \substr($value, 0, -$this->scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string representing the fractional part of this decimal number.
|
||||||
|
*
|
||||||
|
* If the scale is zero, an empty string is returned.
|
||||||
|
*
|
||||||
|
* Examples: `-123.456` => '456', `123` => ''.
|
||||||
|
*/
|
||||||
|
public function getFractionalPart() : string
|
||||||
|
{
|
||||||
|
if ($this->scale === 0) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$value = $this->getUnscaledValueWithLeadingZeros();
|
||||||
|
|
||||||
|
return \substr($value, -$this->scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether this decimal number has a non-zero fractional part.
|
||||||
|
*/
|
||||||
|
public function hasNonZeroFractionalPart() : bool
|
||||||
|
{
|
||||||
|
return $this->getFractionalPart() !== \str_repeat('0', $this->scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toBigInteger() : BigInteger
|
||||||
|
{
|
||||||
|
$zeroScaleDecimal = $this->scale === 0 ? $this : $this->dividedBy(1, 0);
|
||||||
|
|
||||||
|
return self::newBigInteger($zeroScaleDecimal->value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toBigDecimal() : BigDecimal
|
||||||
|
{
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toBigRational() : BigRational
|
||||||
|
{
|
||||||
|
$numerator = self::newBigInteger($this->value);
|
||||||
|
$denominator = self::newBigInteger('1' . \str_repeat('0', $this->scale));
|
||||||
|
|
||||||
|
return self::newBigRational($numerator, $denominator, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toScale(int $scale, int $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal
|
||||||
|
{
|
||||||
|
if ($scale === $this->scale) {
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->dividedBy(BigDecimal::one(), $scale, $roundingMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toInt() : int
|
||||||
|
{
|
||||||
|
return $this->toBigInteger()->toInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toFloat() : float
|
||||||
|
{
|
||||||
|
return (float) (string) $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __toString() : string
|
||||||
|
{
|
||||||
|
if ($this->scale === 0) {
|
||||||
|
return $this->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
$value = $this->getUnscaledValueWithLeadingZeros();
|
||||||
|
|
||||||
|
return \substr($value, 0, -$this->scale) . '.' . \substr($value, -$this->scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is required for serializing the object and SHOULD NOT be accessed directly.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*
|
||||||
|
* @return array{value: string, scale: int}
|
||||||
|
*/
|
||||||
|
public function __serialize(): array
|
||||||
|
{
|
||||||
|
return ['value' => $this->value, 'scale' => $this->scale];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is only here to allow unserializing the object and cannot be accessed directly.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
* @psalm-suppress RedundantPropertyInitializationCheck
|
||||||
|
*
|
||||||
|
* @param array{value: string, scale: int} $data
|
||||||
|
*
|
||||||
|
* @throws \LogicException
|
||||||
|
*/
|
||||||
|
public function __unserialize(array $data): void
|
||||||
|
{
|
||||||
|
if (isset($this->value)) {
|
||||||
|
throw new \LogicException('__unserialize() is an internal function, it must not be called directly.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->value = $data['value'];
|
||||||
|
$this->scale = $data['scale'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is required by interface Serializable and SHOULD NOT be accessed directly.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
public function serialize() : string
|
||||||
|
{
|
||||||
|
return $this->value . ':' . $this->scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is only here to implement interface Serializable and cannot be accessed directly.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
* @psalm-suppress RedundantPropertyInitializationCheck
|
||||||
|
*
|
||||||
|
* @throws \LogicException
|
||||||
|
*/
|
||||||
|
public function unserialize($value) : void
|
||||||
|
{
|
||||||
|
if (isset($this->value)) {
|
||||||
|
throw new \LogicException('unserialize() is an internal function, it must not be called directly.');
|
||||||
|
}
|
||||||
|
|
||||||
|
[$value, $scale] = \explode(':', $value);
|
||||||
|
|
||||||
|
$this->value = $value;
|
||||||
|
$this->scale = (int) $scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Puts the internal values of the given decimal numbers on the same scale.
|
||||||
|
*
|
||||||
|
* @return array{string, string} The scaled integer values of $x and $y.
|
||||||
|
*/
|
||||||
|
private function scaleValues(BigDecimal $x, BigDecimal $y) : array
|
||||||
|
{
|
||||||
|
$a = $x->value;
|
||||||
|
$b = $y->value;
|
||||||
|
|
||||||
|
if ($b !== '0' && $x->scale > $y->scale) {
|
||||||
|
$b .= \str_repeat('0', $x->scale - $y->scale);
|
||||||
|
} elseif ($a !== '0' && $x->scale < $y->scale) {
|
||||||
|
$a .= \str_repeat('0', $y->scale - $x->scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [$a, $b];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function valueWithMinScale(int $scale) : string
|
||||||
|
{
|
||||||
|
$value = $this->value;
|
||||||
|
|
||||||
|
if ($this->value !== '0' && $scale > $this->scale) {
|
||||||
|
$value .= \str_repeat('0', $scale - $this->scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds leading zeros if necessary to the unscaled value to represent the full decimal number.
|
||||||
|
*/
|
||||||
|
private function getUnscaledValueWithLeadingZeros() : string
|
||||||
|
{
|
||||||
|
$value = $this->value;
|
||||||
|
$targetLength = $this->scale + 1;
|
||||||
|
$negative = ($value[0] === '-');
|
||||||
|
$length = \strlen($value);
|
||||||
|
|
||||||
|
if ($negative) {
|
||||||
|
$length--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($length >= $targetLength) {
|
||||||
|
return $this->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($negative) {
|
||||||
|
$value = \substr($value, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
$value = \str_pad($value, $targetLength, '0', STR_PAD_LEFT);
|
||||||
|
|
||||||
|
if ($negative) {
|
||||||
|
$value = '-' . $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
1079
vendor/brick/math/src/BigInteger.php
vendored
Executable file
1079
vendor/brick/math/src/BigInteger.php
vendored
Executable file
File diff suppressed because it is too large
Load Diff
512
vendor/brick/math/src/BigNumber.php
vendored
Executable file
512
vendor/brick/math/src/BigNumber.php
vendored
Executable file
@@ -0,0 +1,512 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Brick\Math;
|
||||||
|
|
||||||
|
use Brick\Math\Exception\DivisionByZeroException;
|
||||||
|
use Brick\Math\Exception\MathException;
|
||||||
|
use Brick\Math\Exception\NumberFormatException;
|
||||||
|
use Brick\Math\Exception\RoundingNecessaryException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Common interface for arbitrary-precision rational numbers.
|
||||||
|
*
|
||||||
|
* @psalm-immutable
|
||||||
|
*/
|
||||||
|
abstract class BigNumber implements \Serializable, \JsonSerializable
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The regular expression used to parse integer, decimal and rational numbers.
|
||||||
|
*/
|
||||||
|
private const PARSE_REGEXP =
|
||||||
|
'/^' .
|
||||||
|
'(?<sign>[\-\+])?' .
|
||||||
|
'(?:' .
|
||||||
|
'(?:' .
|
||||||
|
'(?<integral>[0-9]+)?' .
|
||||||
|
'(?<point>\.)?' .
|
||||||
|
'(?<fractional>[0-9]+)?' .
|
||||||
|
'(?:[eE](?<exponent>[\-\+]?[0-9]+))?' .
|
||||||
|
')|(?:' .
|
||||||
|
'(?<numerator>[0-9]+)' .
|
||||||
|
'\/?' .
|
||||||
|
'(?<denominator>[0-9]+)' .
|
||||||
|
')' .
|
||||||
|
')' .
|
||||||
|
'$/';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a BigNumber of the given value.
|
||||||
|
*
|
||||||
|
* The concrete return type is dependent on the given value, with the following rules:
|
||||||
|
*
|
||||||
|
* - BigNumber instances are returned as is
|
||||||
|
* - integer numbers are returned as BigInteger
|
||||||
|
* - floating point numbers are converted to a string then parsed as such
|
||||||
|
* - strings containing a `/` character are returned as BigRational
|
||||||
|
* - strings containing a `.` character or using an exponential notation are returned as BigDecimal
|
||||||
|
* - strings containing only digits with an optional leading `+` or `-` sign are returned as BigInteger
|
||||||
|
*
|
||||||
|
* @throws NumberFormatException If the format of the number is not valid.
|
||||||
|
* @throws DivisionByZeroException If the value represents a rational number with a denominator of zero.
|
||||||
|
*
|
||||||
|
* @psalm-pure
|
||||||
|
*/
|
||||||
|
public static function of(BigNumber|int|float|string $value) : BigNumber
|
||||||
|
{
|
||||||
|
if ($value instanceof BigNumber) {
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (\is_int($value)) {
|
||||||
|
return new BigInteger((string) $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
$value = \is_float($value) ? self::floatToString($value) : $value;
|
||||||
|
|
||||||
|
$throw = static function() use ($value) : void {
|
||||||
|
throw new NumberFormatException(\sprintf(
|
||||||
|
'The given value "%s" does not represent a valid number.',
|
||||||
|
$value
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
|
if (\preg_match(self::PARSE_REGEXP, $value, $matches) !== 1) {
|
||||||
|
$throw();
|
||||||
|
}
|
||||||
|
|
||||||
|
$getMatch = static fn(string $value): ?string => (($matches[$value] ?? '') !== '') ? $matches[$value] : null;
|
||||||
|
|
||||||
|
$sign = $getMatch('sign');
|
||||||
|
$numerator = $getMatch('numerator');
|
||||||
|
$denominator = $getMatch('denominator');
|
||||||
|
|
||||||
|
if ($numerator !== null) {
|
||||||
|
assert($denominator !== null);
|
||||||
|
|
||||||
|
if ($sign !== null) {
|
||||||
|
$numerator = $sign . $numerator;
|
||||||
|
}
|
||||||
|
|
||||||
|
$numerator = self::cleanUp($numerator);
|
||||||
|
$denominator = self::cleanUp($denominator);
|
||||||
|
|
||||||
|
if ($denominator === '0') {
|
||||||
|
throw DivisionByZeroException::denominatorMustNotBeZero();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new BigRational(
|
||||||
|
new BigInteger($numerator),
|
||||||
|
new BigInteger($denominator),
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$point = $getMatch('point');
|
||||||
|
$integral = $getMatch('integral');
|
||||||
|
$fractional = $getMatch('fractional');
|
||||||
|
$exponent = $getMatch('exponent');
|
||||||
|
|
||||||
|
if ($integral === null && $fractional === null) {
|
||||||
|
$throw();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($integral === null) {
|
||||||
|
$integral = '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($point !== null || $exponent !== null) {
|
||||||
|
$fractional = ($fractional ?? '');
|
||||||
|
$exponent = ($exponent !== null) ? (int) $exponent : 0;
|
||||||
|
|
||||||
|
if ($exponent === PHP_INT_MIN || $exponent === PHP_INT_MAX) {
|
||||||
|
throw new NumberFormatException('Exponent too large.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$unscaledValue = self::cleanUp(($sign ?? ''). $integral . $fractional);
|
||||||
|
|
||||||
|
$scale = \strlen($fractional) - $exponent;
|
||||||
|
|
||||||
|
if ($scale < 0) {
|
||||||
|
if ($unscaledValue !== '0') {
|
||||||
|
$unscaledValue .= \str_repeat('0', - $scale);
|
||||||
|
}
|
||||||
|
$scale = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new BigDecimal($unscaledValue, $scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
$integral = self::cleanUp(($sign ?? '') . $integral);
|
||||||
|
|
||||||
|
return new BigInteger($integral);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Safely converts float to string, avoiding locale-dependent issues.
|
||||||
|
*
|
||||||
|
* @see https://github.com/brick/math/pull/20
|
||||||
|
*
|
||||||
|
* @psalm-pure
|
||||||
|
* @psalm-suppress ImpureFunctionCall
|
||||||
|
*/
|
||||||
|
private static function floatToString(float $float) : string
|
||||||
|
{
|
||||||
|
$currentLocale = \setlocale(LC_NUMERIC, '0');
|
||||||
|
\setlocale(LC_NUMERIC, 'C');
|
||||||
|
|
||||||
|
$result = (string) $float;
|
||||||
|
|
||||||
|
\setlocale(LC_NUMERIC, $currentLocale);
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Proxy method to access BigInteger's protected constructor from sibling classes.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
* @psalm-pure
|
||||||
|
*/
|
||||||
|
protected function newBigInteger(string $value) : BigInteger
|
||||||
|
{
|
||||||
|
return new BigInteger($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Proxy method to access BigDecimal's protected constructor from sibling classes.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
* @psalm-pure
|
||||||
|
*/
|
||||||
|
protected function newBigDecimal(string $value, int $scale = 0) : BigDecimal
|
||||||
|
{
|
||||||
|
return new BigDecimal($value, $scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Proxy method to access BigRational's protected constructor from sibling classes.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
* @psalm-pure
|
||||||
|
*/
|
||||||
|
protected function newBigRational(BigInteger $numerator, BigInteger $denominator, bool $checkDenominator) : BigRational
|
||||||
|
{
|
||||||
|
return new BigRational($numerator, $denominator, $checkDenominator);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the minimum of the given values.
|
||||||
|
*
|
||||||
|
* @param BigNumber|int|float|string ...$values The numbers to compare. All the numbers need to be convertible
|
||||||
|
* to an instance of the class this method is called on.
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException If no values are given.
|
||||||
|
* @throws MathException If an argument is not valid.
|
||||||
|
*
|
||||||
|
* @psalm-suppress LessSpecificReturnStatement
|
||||||
|
* @psalm-suppress MoreSpecificReturnType
|
||||||
|
* @psalm-pure
|
||||||
|
*/
|
||||||
|
public static function min(BigNumber|int|float|string ...$values) : static
|
||||||
|
{
|
||||||
|
$min = null;
|
||||||
|
|
||||||
|
foreach ($values as $value) {
|
||||||
|
$value = static::of($value);
|
||||||
|
|
||||||
|
if ($min === null || $value->isLessThan($min)) {
|
||||||
|
$min = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($min === null) {
|
||||||
|
throw new \InvalidArgumentException(__METHOD__ . '() expects at least one value.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $min;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the maximum of the given values.
|
||||||
|
*
|
||||||
|
* @param BigNumber|int|float|string ...$values The numbers to compare. All the numbers need to be convertible
|
||||||
|
* to an instance of the class this method is called on.
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException If no values are given.
|
||||||
|
* @throws MathException If an argument is not valid.
|
||||||
|
*
|
||||||
|
* @psalm-suppress LessSpecificReturnStatement
|
||||||
|
* @psalm-suppress MoreSpecificReturnType
|
||||||
|
* @psalm-pure
|
||||||
|
*/
|
||||||
|
public static function max(BigNumber|int|float|string ...$values) : static
|
||||||
|
{
|
||||||
|
$max = null;
|
||||||
|
|
||||||
|
foreach ($values as $value) {
|
||||||
|
$value = static::of($value);
|
||||||
|
|
||||||
|
if ($max === null || $value->isGreaterThan($max)) {
|
||||||
|
$max = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($max === null) {
|
||||||
|
throw new \InvalidArgumentException(__METHOD__ . '() expects at least one value.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $max;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the sum of the given values.
|
||||||
|
*
|
||||||
|
* @param BigNumber|int|float|string ...$values The numbers to add. All the numbers need to be convertible
|
||||||
|
* to an instance of the class this method is called on.
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException If no values are given.
|
||||||
|
* @throws MathException If an argument is not valid.
|
||||||
|
*
|
||||||
|
* @psalm-pure
|
||||||
|
*/
|
||||||
|
public static function sum(BigNumber|int|float|string ...$values) : static
|
||||||
|
{
|
||||||
|
/** @var static|null $sum */
|
||||||
|
$sum = null;
|
||||||
|
|
||||||
|
foreach ($values as $value) {
|
||||||
|
$value = static::of($value);
|
||||||
|
|
||||||
|
$sum = $sum === null ? $value : self::add($sum, $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($sum === null) {
|
||||||
|
throw new \InvalidArgumentException(__METHOD__ . '() expects at least one value.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds two BigNumber instances in the correct order to avoid a RoundingNecessaryException.
|
||||||
|
*
|
||||||
|
* @todo This could be better resolved by creating an abstract protected method in BigNumber, and leaving to
|
||||||
|
* concrete classes the responsibility to perform the addition themselves or delegate it to the given number,
|
||||||
|
* depending on their ability to perform the operation. This will also require a version bump because we're
|
||||||
|
* potentially breaking custom BigNumber implementations (if any...)
|
||||||
|
*
|
||||||
|
* @psalm-pure
|
||||||
|
*/
|
||||||
|
private static function add(BigNumber $a, BigNumber $b) : BigNumber
|
||||||
|
{
|
||||||
|
if ($a instanceof BigRational) {
|
||||||
|
return $a->plus($b);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($b instanceof BigRational) {
|
||||||
|
return $b->plus($a);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($a instanceof BigDecimal) {
|
||||||
|
return $a->plus($b);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($b instanceof BigDecimal) {
|
||||||
|
return $b->plus($a);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var BigInteger $a */
|
||||||
|
|
||||||
|
return $a->plus($b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes optional leading zeros and + sign from the given number.
|
||||||
|
*
|
||||||
|
* @param string $number The number, validated as a non-empty string of digits with optional leading sign.
|
||||||
|
*
|
||||||
|
* @psalm-pure
|
||||||
|
*/
|
||||||
|
private static function cleanUp(string $number) : string
|
||||||
|
{
|
||||||
|
$firstChar = $number[0];
|
||||||
|
|
||||||
|
if ($firstChar === '+' || $firstChar === '-') {
|
||||||
|
$number = \substr($number, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
$number = \ltrim($number, '0');
|
||||||
|
|
||||||
|
if ($number === '') {
|
||||||
|
return '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($firstChar === '-') {
|
||||||
|
return '-' . $number;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if this number is equal to the given one.
|
||||||
|
*/
|
||||||
|
public function isEqualTo(BigNumber|int|float|string $that) : bool
|
||||||
|
{
|
||||||
|
return $this->compareTo($that) === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if this number is strictly lower than the given one.
|
||||||
|
*/
|
||||||
|
public function isLessThan(BigNumber|int|float|string $that) : bool
|
||||||
|
{
|
||||||
|
return $this->compareTo($that) < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if this number is lower than or equal to the given one.
|
||||||
|
*/
|
||||||
|
public function isLessThanOrEqualTo(BigNumber|int|float|string $that) : bool
|
||||||
|
{
|
||||||
|
return $this->compareTo($that) <= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if this number is strictly greater than the given one.
|
||||||
|
*/
|
||||||
|
public function isGreaterThan(BigNumber|int|float|string $that) : bool
|
||||||
|
{
|
||||||
|
return $this->compareTo($that) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if this number is greater than or equal to the given one.
|
||||||
|
*/
|
||||||
|
public function isGreaterThanOrEqualTo(BigNumber|int|float|string $that) : bool
|
||||||
|
{
|
||||||
|
return $this->compareTo($that) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if this number equals zero.
|
||||||
|
*/
|
||||||
|
public function isZero() : bool
|
||||||
|
{
|
||||||
|
return $this->getSign() === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if this number is strictly negative.
|
||||||
|
*/
|
||||||
|
public function isNegative() : bool
|
||||||
|
{
|
||||||
|
return $this->getSign() < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if this number is negative or zero.
|
||||||
|
*/
|
||||||
|
public function isNegativeOrZero() : bool
|
||||||
|
{
|
||||||
|
return $this->getSign() <= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if this number is strictly positive.
|
||||||
|
*/
|
||||||
|
public function isPositive() : bool
|
||||||
|
{
|
||||||
|
return $this->getSign() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if this number is positive or zero.
|
||||||
|
*/
|
||||||
|
public function isPositiveOrZero() : bool
|
||||||
|
{
|
||||||
|
return $this->getSign() >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the sign of this number.
|
||||||
|
*
|
||||||
|
* @return int -1 if the number is negative, 0 if zero, 1 if positive.
|
||||||
|
*/
|
||||||
|
abstract public function getSign() : int;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compares this number to the given one.
|
||||||
|
*
|
||||||
|
* @return int [-1,0,1] If `$this` is lower than, equal to, or greater than `$that`.
|
||||||
|
*
|
||||||
|
* @throws MathException If the number is not valid.
|
||||||
|
*/
|
||||||
|
abstract public function compareTo(BigNumber|int|float|string $that) : int;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts this number to a BigInteger.
|
||||||
|
*
|
||||||
|
* @throws RoundingNecessaryException If this number cannot be converted to a BigInteger without rounding.
|
||||||
|
*/
|
||||||
|
abstract public function toBigInteger() : BigInteger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts this number to a BigDecimal.
|
||||||
|
*
|
||||||
|
* @throws RoundingNecessaryException If this number cannot be converted to a BigDecimal without rounding.
|
||||||
|
*/
|
||||||
|
abstract public function toBigDecimal() : BigDecimal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts this number to a BigRational.
|
||||||
|
*/
|
||||||
|
abstract public function toBigRational() : BigRational;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts this number to a BigDecimal with the given scale, using rounding if necessary.
|
||||||
|
*
|
||||||
|
* @param int $scale The scale of the resulting `BigDecimal`.
|
||||||
|
* @param int $roundingMode A `RoundingMode` constant.
|
||||||
|
*
|
||||||
|
* @throws RoundingNecessaryException If this number cannot be converted to the given scale without rounding.
|
||||||
|
* This only applies when RoundingMode::UNNECESSARY is used.
|
||||||
|
*/
|
||||||
|
abstract public function toScale(int $scale, int $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the exact value of this number as a native integer.
|
||||||
|
*
|
||||||
|
* If this number cannot be converted to a native integer without losing precision, an exception is thrown.
|
||||||
|
* Note that the acceptable range for an integer depends on the platform and differs for 32-bit and 64-bit.
|
||||||
|
*
|
||||||
|
* @throws MathException If this number cannot be exactly converted to a native integer.
|
||||||
|
*/
|
||||||
|
abstract public function toInt() : int;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an approximation of this number as a floating-point value.
|
||||||
|
*
|
||||||
|
* Note that this method can discard information as the precision of a floating-point value
|
||||||
|
* is inherently limited.
|
||||||
|
*
|
||||||
|
* If the number is greater than the largest representable floating point number, positive infinity is returned.
|
||||||
|
* If the number is less than the smallest representable floating point number, negative infinity is returned.
|
||||||
|
*/
|
||||||
|
abstract public function toFloat() : float;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string representation of this number.
|
||||||
|
*
|
||||||
|
* The output of this method can be parsed by the `of()` factory method;
|
||||||
|
* this will yield an object equal to this one, without any information loss.
|
||||||
|
*/
|
||||||
|
abstract public function __toString() : string;
|
||||||
|
|
||||||
|
public function jsonSerialize() : string
|
||||||
|
{
|
||||||
|
return $this->__toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
445
vendor/brick/math/src/BigRational.php
vendored
Executable file
445
vendor/brick/math/src/BigRational.php
vendored
Executable file
@@ -0,0 +1,445 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Brick\Math;
|
||||||
|
|
||||||
|
use Brick\Math\Exception\DivisionByZeroException;
|
||||||
|
use Brick\Math\Exception\MathException;
|
||||||
|
use Brick\Math\Exception\NumberFormatException;
|
||||||
|
use Brick\Math\Exception\RoundingNecessaryException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An arbitrarily large rational number.
|
||||||
|
*
|
||||||
|
* This class is immutable.
|
||||||
|
*
|
||||||
|
* @psalm-immutable
|
||||||
|
*/
|
||||||
|
final class BigRational extends BigNumber
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The numerator.
|
||||||
|
*/
|
||||||
|
private BigInteger $numerator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The denominator. Always strictly positive.
|
||||||
|
*/
|
||||||
|
private BigInteger $denominator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Protected constructor. Use a factory method to obtain an instance.
|
||||||
|
*
|
||||||
|
* @param BigInteger $numerator The numerator.
|
||||||
|
* @param BigInteger $denominator The denominator.
|
||||||
|
* @param bool $checkDenominator Whether to check the denominator for negative and zero.
|
||||||
|
*
|
||||||
|
* @throws DivisionByZeroException If the denominator is zero.
|
||||||
|
*/
|
||||||
|
protected function __construct(BigInteger $numerator, BigInteger $denominator, bool $checkDenominator)
|
||||||
|
{
|
||||||
|
if ($checkDenominator) {
|
||||||
|
if ($denominator->isZero()) {
|
||||||
|
throw DivisionByZeroException::denominatorMustNotBeZero();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($denominator->isNegative()) {
|
||||||
|
$numerator = $numerator->negated();
|
||||||
|
$denominator = $denominator->negated();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->numerator = $numerator;
|
||||||
|
$this->denominator = $denominator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a BigRational of the given value.
|
||||||
|
*
|
||||||
|
* @throws MathException If the value cannot be converted to a BigRational.
|
||||||
|
*
|
||||||
|
* @psalm-pure
|
||||||
|
*/
|
||||||
|
public static function of(BigNumber|int|float|string $value) : BigRational
|
||||||
|
{
|
||||||
|
return parent::of($value)->toBigRational();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a BigRational out of a numerator and a denominator.
|
||||||
|
*
|
||||||
|
* If the denominator is negative, the signs of both the numerator and the denominator
|
||||||
|
* will be inverted to ensure that the denominator is always positive.
|
||||||
|
*
|
||||||
|
* @param BigNumber|int|float|string $numerator The numerator. Must be convertible to a BigInteger.
|
||||||
|
* @param BigNumber|int|float|string $denominator The denominator. Must be convertible to a BigInteger.
|
||||||
|
*
|
||||||
|
* @throws NumberFormatException If an argument does not represent a valid number.
|
||||||
|
* @throws RoundingNecessaryException If an argument represents a non-integer number.
|
||||||
|
* @throws DivisionByZeroException If the denominator is zero.
|
||||||
|
*
|
||||||
|
* @psalm-pure
|
||||||
|
*/
|
||||||
|
public static function nd(
|
||||||
|
BigNumber|int|float|string $numerator,
|
||||||
|
BigNumber|int|float|string $denominator,
|
||||||
|
) : BigRational {
|
||||||
|
$numerator = BigInteger::of($numerator);
|
||||||
|
$denominator = BigInteger::of($denominator);
|
||||||
|
|
||||||
|
return new BigRational($numerator, $denominator, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a BigRational representing zero.
|
||||||
|
*
|
||||||
|
* @psalm-pure
|
||||||
|
*/
|
||||||
|
public static function zero() : BigRational
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @psalm-suppress ImpureStaticVariable
|
||||||
|
* @var BigRational|null $zero
|
||||||
|
*/
|
||||||
|
static $zero;
|
||||||
|
|
||||||
|
if ($zero === null) {
|
||||||
|
$zero = new BigRational(BigInteger::zero(), BigInteger::one(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a BigRational representing one.
|
||||||
|
*
|
||||||
|
* @psalm-pure
|
||||||
|
*/
|
||||||
|
public static function one() : BigRational
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @psalm-suppress ImpureStaticVariable
|
||||||
|
* @var BigRational|null $one
|
||||||
|
*/
|
||||||
|
static $one;
|
||||||
|
|
||||||
|
if ($one === null) {
|
||||||
|
$one = new BigRational(BigInteger::one(), BigInteger::one(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $one;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a BigRational representing ten.
|
||||||
|
*
|
||||||
|
* @psalm-pure
|
||||||
|
*/
|
||||||
|
public static function ten() : BigRational
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @psalm-suppress ImpureStaticVariable
|
||||||
|
* @var BigRational|null $ten
|
||||||
|
*/
|
||||||
|
static $ten;
|
||||||
|
|
||||||
|
if ($ten === null) {
|
||||||
|
$ten = new BigRational(BigInteger::ten(), BigInteger::one(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $ten;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getNumerator() : BigInteger
|
||||||
|
{
|
||||||
|
return $this->numerator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDenominator() : BigInteger
|
||||||
|
{
|
||||||
|
return $this->denominator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the quotient of the division of the numerator by the denominator.
|
||||||
|
*/
|
||||||
|
public function quotient() : BigInteger
|
||||||
|
{
|
||||||
|
return $this->numerator->quotient($this->denominator);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the remainder of the division of the numerator by the denominator.
|
||||||
|
*/
|
||||||
|
public function remainder() : BigInteger
|
||||||
|
{
|
||||||
|
return $this->numerator->remainder($this->denominator);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the quotient and remainder of the division of the numerator by the denominator.
|
||||||
|
*
|
||||||
|
* @return BigInteger[]
|
||||||
|
*/
|
||||||
|
public function quotientAndRemainder() : array
|
||||||
|
{
|
||||||
|
return $this->numerator->quotientAndRemainder($this->denominator);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the sum of this number and the given one.
|
||||||
|
*
|
||||||
|
* @param BigNumber|int|float|string $that The number to add.
|
||||||
|
*
|
||||||
|
* @throws MathException If the number is not valid.
|
||||||
|
*/
|
||||||
|
public function plus(BigNumber|int|float|string $that) : BigRational
|
||||||
|
{
|
||||||
|
$that = BigRational::of($that);
|
||||||
|
|
||||||
|
$numerator = $this->numerator->multipliedBy($that->denominator);
|
||||||
|
$numerator = $numerator->plus($that->numerator->multipliedBy($this->denominator));
|
||||||
|
$denominator = $this->denominator->multipliedBy($that->denominator);
|
||||||
|
|
||||||
|
return new BigRational($numerator, $denominator, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the difference of this number and the given one.
|
||||||
|
*
|
||||||
|
* @param BigNumber|int|float|string $that The number to subtract.
|
||||||
|
*
|
||||||
|
* @throws MathException If the number is not valid.
|
||||||
|
*/
|
||||||
|
public function minus(BigNumber|int|float|string $that) : BigRational
|
||||||
|
{
|
||||||
|
$that = BigRational::of($that);
|
||||||
|
|
||||||
|
$numerator = $this->numerator->multipliedBy($that->denominator);
|
||||||
|
$numerator = $numerator->minus($that->numerator->multipliedBy($this->denominator));
|
||||||
|
$denominator = $this->denominator->multipliedBy($that->denominator);
|
||||||
|
|
||||||
|
return new BigRational($numerator, $denominator, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the product of this number and the given one.
|
||||||
|
*
|
||||||
|
* @param BigNumber|int|float|string $that The multiplier.
|
||||||
|
*
|
||||||
|
* @throws MathException If the multiplier is not a valid number.
|
||||||
|
*/
|
||||||
|
public function multipliedBy(BigNumber|int|float|string $that) : BigRational
|
||||||
|
{
|
||||||
|
$that = BigRational::of($that);
|
||||||
|
|
||||||
|
$numerator = $this->numerator->multipliedBy($that->numerator);
|
||||||
|
$denominator = $this->denominator->multipliedBy($that->denominator);
|
||||||
|
|
||||||
|
return new BigRational($numerator, $denominator, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the result of the division of this number by the given one.
|
||||||
|
*
|
||||||
|
* @param BigNumber|int|float|string $that The divisor.
|
||||||
|
*
|
||||||
|
* @throws MathException If the divisor is not a valid number, or is zero.
|
||||||
|
*/
|
||||||
|
public function dividedBy(BigNumber|int|float|string $that) : BigRational
|
||||||
|
{
|
||||||
|
$that = BigRational::of($that);
|
||||||
|
|
||||||
|
$numerator = $this->numerator->multipliedBy($that->denominator);
|
||||||
|
$denominator = $this->denominator->multipliedBy($that->numerator);
|
||||||
|
|
||||||
|
return new BigRational($numerator, $denominator, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns this number exponentiated to the given value.
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException If the exponent is not in the range 0 to 1,000,000.
|
||||||
|
*/
|
||||||
|
public function power(int $exponent) : BigRational
|
||||||
|
{
|
||||||
|
if ($exponent === 0) {
|
||||||
|
$one = BigInteger::one();
|
||||||
|
|
||||||
|
return new BigRational($one, $one, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($exponent === 1) {
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new BigRational(
|
||||||
|
$this->numerator->power($exponent),
|
||||||
|
$this->denominator->power($exponent),
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the reciprocal of this BigRational.
|
||||||
|
*
|
||||||
|
* The reciprocal has the numerator and denominator swapped.
|
||||||
|
*
|
||||||
|
* @throws DivisionByZeroException If the numerator is zero.
|
||||||
|
*/
|
||||||
|
public function reciprocal() : BigRational
|
||||||
|
{
|
||||||
|
return new BigRational($this->denominator, $this->numerator, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the absolute value of this BigRational.
|
||||||
|
*/
|
||||||
|
public function abs() : BigRational
|
||||||
|
{
|
||||||
|
return new BigRational($this->numerator->abs(), $this->denominator, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the negated value of this BigRational.
|
||||||
|
*/
|
||||||
|
public function negated() : BigRational
|
||||||
|
{
|
||||||
|
return new BigRational($this->numerator->negated(), $this->denominator, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the simplified value of this BigRational.
|
||||||
|
*/
|
||||||
|
public function simplified() : BigRational
|
||||||
|
{
|
||||||
|
$gcd = $this->numerator->gcd($this->denominator);
|
||||||
|
|
||||||
|
$numerator = $this->numerator->quotient($gcd);
|
||||||
|
$denominator = $this->denominator->quotient($gcd);
|
||||||
|
|
||||||
|
return new BigRational($numerator, $denominator, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function compareTo(BigNumber|int|float|string $that) : int
|
||||||
|
{
|
||||||
|
return $this->minus($that)->getSign();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSign() : int
|
||||||
|
{
|
||||||
|
return $this->numerator->getSign();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toBigInteger() : BigInteger
|
||||||
|
{
|
||||||
|
$simplified = $this->simplified();
|
||||||
|
|
||||||
|
if (! $simplified->denominator->isEqualTo(1)) {
|
||||||
|
throw new RoundingNecessaryException('This rational number cannot be represented as an integer value without rounding.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $simplified->numerator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toBigDecimal() : BigDecimal
|
||||||
|
{
|
||||||
|
return $this->numerator->toBigDecimal()->exactlyDividedBy($this->denominator);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toBigRational() : BigRational
|
||||||
|
{
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toScale(int $scale, int $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal
|
||||||
|
{
|
||||||
|
return $this->numerator->toBigDecimal()->dividedBy($this->denominator, $scale, $roundingMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toInt() : int
|
||||||
|
{
|
||||||
|
return $this->toBigInteger()->toInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toFloat() : float
|
||||||
|
{
|
||||||
|
$simplified = $this->simplified();
|
||||||
|
return $simplified->numerator->toFloat() / $simplified->denominator->toFloat();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __toString() : string
|
||||||
|
{
|
||||||
|
$numerator = (string) $this->numerator;
|
||||||
|
$denominator = (string) $this->denominator;
|
||||||
|
|
||||||
|
if ($denominator === '1') {
|
||||||
|
return $numerator;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->numerator . '/' . $this->denominator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is required for serializing the object and SHOULD NOT be accessed directly.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*
|
||||||
|
* @return array{numerator: BigInteger, denominator: BigInteger}
|
||||||
|
*/
|
||||||
|
public function __serialize(): array
|
||||||
|
{
|
||||||
|
return ['numerator' => $this->numerator, 'denominator' => $this->denominator];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is only here to allow unserializing the object and cannot be accessed directly.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
* @psalm-suppress RedundantPropertyInitializationCheck
|
||||||
|
*
|
||||||
|
* @param array{numerator: BigInteger, denominator: BigInteger} $data
|
||||||
|
*
|
||||||
|
* @throws \LogicException
|
||||||
|
*/
|
||||||
|
public function __unserialize(array $data): void
|
||||||
|
{
|
||||||
|
if (isset($this->numerator)) {
|
||||||
|
throw new \LogicException('__unserialize() is an internal function, it must not be called directly.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->numerator = $data['numerator'];
|
||||||
|
$this->denominator = $data['denominator'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is required by interface Serializable and SHOULD NOT be accessed directly.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
public function serialize() : string
|
||||||
|
{
|
||||||
|
return $this->numerator . '/' . $this->denominator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is only here to implement interface Serializable and cannot be accessed directly.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
* @psalm-suppress RedundantPropertyInitializationCheck
|
||||||
|
*
|
||||||
|
* @throws \LogicException
|
||||||
|
*/
|
||||||
|
public function unserialize($value) : void
|
||||||
|
{
|
||||||
|
if (isset($this->numerator)) {
|
||||||
|
throw new \LogicException('unserialize() is an internal function, it must not be called directly.');
|
||||||
|
}
|
||||||
|
|
||||||
|
[$numerator, $denominator] = \explode('/', $value);
|
||||||
|
|
||||||
|
$this->numerator = BigInteger::of($numerator);
|
||||||
|
$this->denominator = BigInteger::of($denominator);
|
||||||
|
}
|
||||||
|
}
|
||||||
35
vendor/brick/math/src/Exception/DivisionByZeroException.php
vendored
Executable file
35
vendor/brick/math/src/Exception/DivisionByZeroException.php
vendored
Executable file
@@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Brick\Math\Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception thrown when a division by zero occurs.
|
||||||
|
*/
|
||||||
|
class DivisionByZeroException extends MathException
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @psalm-pure
|
||||||
|
*/
|
||||||
|
public static function divisionByZero() : DivisionByZeroException
|
||||||
|
{
|
||||||
|
return new self('Division by zero.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @psalm-pure
|
||||||
|
*/
|
||||||
|
public static function modulusMustNotBeZero() : DivisionByZeroException
|
||||||
|
{
|
||||||
|
return new self('The modulus must not be zero.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @psalm-pure
|
||||||
|
*/
|
||||||
|
public static function denominatorMustNotBeZero() : DivisionByZeroException
|
||||||
|
{
|
||||||
|
return new self('The denominator of a rational number cannot be zero.');
|
||||||
|
}
|
||||||
|
}
|
||||||
23
vendor/brick/math/src/Exception/IntegerOverflowException.php
vendored
Executable file
23
vendor/brick/math/src/Exception/IntegerOverflowException.php
vendored
Executable file
@@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Brick\Math\Exception;
|
||||||
|
|
||||||
|
use Brick\Math\BigInteger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception thrown when an integer overflow occurs.
|
||||||
|
*/
|
||||||
|
class IntegerOverflowException extends MathException
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @psalm-pure
|
||||||
|
*/
|
||||||
|
public static function toIntOverflow(BigInteger $value) : IntegerOverflowException
|
||||||
|
{
|
||||||
|
$message = '%s is out of range %d to %d and cannot be represented as an integer.';
|
||||||
|
|
||||||
|
return new self(\sprintf($message, (string) $value, PHP_INT_MIN, PHP_INT_MAX));
|
||||||
|
}
|
||||||
|
}
|
||||||
12
vendor/brick/math/src/Exception/MathException.php
vendored
Executable file
12
vendor/brick/math/src/Exception/MathException.php
vendored
Executable file
@@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Brick\Math\Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for all math exceptions.
|
||||||
|
*/
|
||||||
|
class MathException extends \Exception
|
||||||
|
{
|
||||||
|
}
|
||||||
12
vendor/brick/math/src/Exception/NegativeNumberException.php
vendored
Executable file
12
vendor/brick/math/src/Exception/NegativeNumberException.php
vendored
Executable file
@@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Brick\Math\Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception thrown when attempting to perform an unsupported operation, such as a square root, on a negative number.
|
||||||
|
*/
|
||||||
|
class NegativeNumberException extends MathException
|
||||||
|
{
|
||||||
|
}
|
||||||
33
vendor/brick/math/src/Exception/NumberFormatException.php
vendored
Executable file
33
vendor/brick/math/src/Exception/NumberFormatException.php
vendored
Executable file
@@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Brick\Math\Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception thrown when attempting to create a number from a string with an invalid format.
|
||||||
|
*/
|
||||||
|
class NumberFormatException extends MathException
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param string $char The failing character.
|
||||||
|
*
|
||||||
|
* @psalm-pure
|
||||||
|
*/
|
||||||
|
public static function charNotInAlphabet(string $char) : self
|
||||||
|
{
|
||||||
|
$ord = \ord($char);
|
||||||
|
|
||||||
|
if ($ord < 32 || $ord > 126) {
|
||||||
|
$char = \strtoupper(\dechex($ord));
|
||||||
|
|
||||||
|
if ($ord < 10) {
|
||||||
|
$char = '0' . $char;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$char = '"' . $char . '"';
|
||||||
|
}
|
||||||
|
|
||||||
|
return new self(sprintf('Char %s is not a valid character in the given alphabet.', $char));
|
||||||
|
}
|
||||||
|
}
|
||||||
19
vendor/brick/math/src/Exception/RoundingNecessaryException.php
vendored
Executable file
19
vendor/brick/math/src/Exception/RoundingNecessaryException.php
vendored
Executable file
@@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Brick\Math\Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception thrown when a number cannot be represented at the requested scale without rounding.
|
||||||
|
*/
|
||||||
|
class RoundingNecessaryException extends MathException
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @psalm-pure
|
||||||
|
*/
|
||||||
|
public static function roundingNecessary() : RoundingNecessaryException
|
||||||
|
{
|
||||||
|
return new self('Rounding is necessary to represent the result of the operation at this scale.');
|
||||||
|
}
|
||||||
|
}
|
||||||
676
vendor/brick/math/src/Internal/Calculator.php
vendored
Executable file
676
vendor/brick/math/src/Internal/Calculator.php
vendored
Executable file
@@ -0,0 +1,676 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Brick\Math\Internal;
|
||||||
|
|
||||||
|
use Brick\Math\Exception\RoundingNecessaryException;
|
||||||
|
use Brick\Math\RoundingMode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs basic operations on arbitrary size integers.
|
||||||
|
*
|
||||||
|
* Unless otherwise specified, all parameters must be validated as non-empty strings of digits,
|
||||||
|
* without leading zero, and with an optional leading minus sign if the number is not zero.
|
||||||
|
*
|
||||||
|
* Any other parameter format will lead to undefined behaviour.
|
||||||
|
* All methods must return strings respecting this format, unless specified otherwise.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*
|
||||||
|
* @psalm-immutable
|
||||||
|
*/
|
||||||
|
abstract class Calculator
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The maximum exponent value allowed for the pow() method.
|
||||||
|
*/
|
||||||
|
public const MAX_POWER = 1000000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The alphabet for converting from and to base 2 to 36, lowercase.
|
||||||
|
*/
|
||||||
|
public const ALPHABET = '0123456789abcdefghijklmnopqrstuvwxyz';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Calculator instance in use.
|
||||||
|
*/
|
||||||
|
private static ?Calculator $instance = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the Calculator instance to use.
|
||||||
|
*
|
||||||
|
* An instance is typically set only in unit tests: the autodetect is usually the best option.
|
||||||
|
*
|
||||||
|
* @param Calculator|null $calculator The calculator instance, or NULL to revert to autodetect.
|
||||||
|
*/
|
||||||
|
final public static function set(?Calculator $calculator) : void
|
||||||
|
{
|
||||||
|
self::$instance = $calculator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Calculator instance to use.
|
||||||
|
*
|
||||||
|
* If none has been explicitly set, the fastest available implementation will be returned.
|
||||||
|
*
|
||||||
|
* @psalm-pure
|
||||||
|
* @psalm-suppress ImpureStaticProperty
|
||||||
|
*/
|
||||||
|
final public static function get() : Calculator
|
||||||
|
{
|
||||||
|
if (self::$instance === null) {
|
||||||
|
/** @psalm-suppress ImpureMethodCall */
|
||||||
|
self::$instance = self::detect();
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the fastest available Calculator implementation.
|
||||||
|
*
|
||||||
|
* @codeCoverageIgnore
|
||||||
|
*/
|
||||||
|
private static function detect() : Calculator
|
||||||
|
{
|
||||||
|
if (\extension_loaded('gmp')) {
|
||||||
|
return new Calculator\GmpCalculator();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (\extension_loaded('bcmath')) {
|
||||||
|
return new Calculator\BcMathCalculator();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Calculator\NativeCalculator();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts the sign & digits of the operands.
|
||||||
|
*
|
||||||
|
* @return array{bool, bool, string, string} Whether $a and $b are negative, followed by their digits.
|
||||||
|
*/
|
||||||
|
final protected function init(string $a, string $b) : array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
$aNeg = ($a[0] === '-'),
|
||||||
|
$bNeg = ($b[0] === '-'),
|
||||||
|
|
||||||
|
$aNeg ? \substr($a, 1) : $a,
|
||||||
|
$bNeg ? \substr($b, 1) : $b,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the absolute value of a number.
|
||||||
|
*/
|
||||||
|
final public function abs(string $n) : string
|
||||||
|
{
|
||||||
|
return ($n[0] === '-') ? \substr($n, 1) : $n;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Negates a number.
|
||||||
|
*/
|
||||||
|
final public function neg(string $n) : string
|
||||||
|
{
|
||||||
|
if ($n === '0') {
|
||||||
|
return '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($n[0] === '-') {
|
||||||
|
return \substr($n, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return '-' . $n;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compares two numbers.
|
||||||
|
*
|
||||||
|
* @return int [-1, 0, 1] If the first number is less than, equal to, or greater than the second number.
|
||||||
|
*/
|
||||||
|
final public function cmp(string $a, string $b) : int
|
||||||
|
{
|
||||||
|
[$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b);
|
||||||
|
|
||||||
|
if ($aNeg && ! $bNeg) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($bNeg && ! $aNeg) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
$aLen = \strlen($aDig);
|
||||||
|
$bLen = \strlen($bDig);
|
||||||
|
|
||||||
|
if ($aLen < $bLen) {
|
||||||
|
$result = -1;
|
||||||
|
} elseif ($aLen > $bLen) {
|
||||||
|
$result = 1;
|
||||||
|
} else {
|
||||||
|
$result = $aDig <=> $bDig;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $aNeg ? -$result : $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds two numbers.
|
||||||
|
*/
|
||||||
|
abstract public function add(string $a, string $b) : string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subtracts two numbers.
|
||||||
|
*/
|
||||||
|
abstract public function sub(string $a, string $b) : string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Multiplies two numbers.
|
||||||
|
*/
|
||||||
|
abstract public function mul(string $a, string $b) : string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the quotient of the division of two numbers.
|
||||||
|
*
|
||||||
|
* @param string $a The dividend.
|
||||||
|
* @param string $b The divisor, must not be zero.
|
||||||
|
*
|
||||||
|
* @return string The quotient.
|
||||||
|
*/
|
||||||
|
abstract public function divQ(string $a, string $b) : string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the remainder of the division of two numbers.
|
||||||
|
*
|
||||||
|
* @param string $a The dividend.
|
||||||
|
* @param string $b The divisor, must not be zero.
|
||||||
|
*
|
||||||
|
* @return string The remainder.
|
||||||
|
*/
|
||||||
|
abstract public function divR(string $a, string $b) : string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the quotient and remainder of the division of two numbers.
|
||||||
|
*
|
||||||
|
* @param string $a The dividend.
|
||||||
|
* @param string $b The divisor, must not be zero.
|
||||||
|
*
|
||||||
|
* @return array{string, string} An array containing the quotient and remainder.
|
||||||
|
*/
|
||||||
|
abstract public function divQR(string $a, string $b) : array;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exponentiates a number.
|
||||||
|
*
|
||||||
|
* @param string $a The base number.
|
||||||
|
* @param int $e The exponent, validated as an integer between 0 and MAX_POWER.
|
||||||
|
*
|
||||||
|
* @return string The power.
|
||||||
|
*/
|
||||||
|
abstract public function pow(string $a, int $e) : string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $b The modulus; must not be zero.
|
||||||
|
*/
|
||||||
|
public function mod(string $a, string $b) : string
|
||||||
|
{
|
||||||
|
return $this->divR($this->add($this->divR($a, $b), $b), $b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the modular multiplicative inverse of $x modulo $m.
|
||||||
|
*
|
||||||
|
* If $x has no multiplicative inverse mod m, this method must return null.
|
||||||
|
*
|
||||||
|
* This method can be overridden by the concrete implementation if the underlying library has built-in support.
|
||||||
|
*
|
||||||
|
* @param string $m The modulus; must not be negative or zero.
|
||||||
|
*/
|
||||||
|
public function modInverse(string $x, string $m) : ?string
|
||||||
|
{
|
||||||
|
if ($m === '1') {
|
||||||
|
return '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
$modVal = $x;
|
||||||
|
|
||||||
|
if ($x[0] === '-' || ($this->cmp($this->abs($x), $m) >= 0)) {
|
||||||
|
$modVal = $this->mod($x, $m);
|
||||||
|
}
|
||||||
|
|
||||||
|
[$g, $x] = $this->gcdExtended($modVal, $m);
|
||||||
|
|
||||||
|
if ($g !== '1') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->mod($this->add($this->mod($x, $m), $m), $m);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Raises a number into power with modulo.
|
||||||
|
*
|
||||||
|
* @param string $base The base number; must be positive or zero.
|
||||||
|
* @param string $exp The exponent; must be positive or zero.
|
||||||
|
* @param string $mod The modulus; must be strictly positive.
|
||||||
|
*/
|
||||||
|
abstract public function modPow(string $base, string $exp, string $mod) : string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the greatest common divisor of the two numbers.
|
||||||
|
*
|
||||||
|
* This method can be overridden by the concrete implementation if the underlying library
|
||||||
|
* has built-in support for GCD calculations.
|
||||||
|
*
|
||||||
|
* @return string The GCD, always positive, or zero if both arguments are zero.
|
||||||
|
*/
|
||||||
|
public function gcd(string $a, string $b) : string
|
||||||
|
{
|
||||||
|
if ($a === '0') {
|
||||||
|
return $this->abs($b);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($b === '0') {
|
||||||
|
return $this->abs($a);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->gcd($b, $this->divR($a, $b));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array{string, string, string} GCD, X, Y
|
||||||
|
*/
|
||||||
|
private function gcdExtended(string $a, string $b) : array
|
||||||
|
{
|
||||||
|
if ($a === '0') {
|
||||||
|
return [$b, '0', '1'];
|
||||||
|
}
|
||||||
|
|
||||||
|
[$gcd, $x1, $y1] = $this->gcdExtended($this->mod($b, $a), $a);
|
||||||
|
|
||||||
|
$x = $this->sub($y1, $this->mul($this->divQ($b, $a), $x1));
|
||||||
|
$y = $x1;
|
||||||
|
|
||||||
|
return [$gcd, $x, $y];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the square root of the given number, rounded down.
|
||||||
|
*
|
||||||
|
* The result is the largest x such that x² ≤ n.
|
||||||
|
* The input MUST NOT be negative.
|
||||||
|
*/
|
||||||
|
abstract public function sqrt(string $n) : string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a number from an arbitrary base.
|
||||||
|
*
|
||||||
|
* This method can be overridden by the concrete implementation if the underlying library
|
||||||
|
* has built-in support for base conversion.
|
||||||
|
*
|
||||||
|
* @param string $number The number, positive or zero, non-empty, case-insensitively validated for the given base.
|
||||||
|
* @param int $base The base of the number, validated from 2 to 36.
|
||||||
|
*
|
||||||
|
* @return string The converted number, following the Calculator conventions.
|
||||||
|
*/
|
||||||
|
public function fromBase(string $number, int $base) : string
|
||||||
|
{
|
||||||
|
return $this->fromArbitraryBase(\strtolower($number), self::ALPHABET, $base);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a number to an arbitrary base.
|
||||||
|
*
|
||||||
|
* This method can be overridden by the concrete implementation if the underlying library
|
||||||
|
* has built-in support for base conversion.
|
||||||
|
*
|
||||||
|
* @param string $number The number to convert, following the Calculator conventions.
|
||||||
|
* @param int $base The base to convert to, validated from 2 to 36.
|
||||||
|
*
|
||||||
|
* @return string The converted number, lowercase.
|
||||||
|
*/
|
||||||
|
public function toBase(string $number, int $base) : string
|
||||||
|
{
|
||||||
|
$negative = ($number[0] === '-');
|
||||||
|
|
||||||
|
if ($negative) {
|
||||||
|
$number = \substr($number, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
$number = $this->toArbitraryBase($number, self::ALPHABET, $base);
|
||||||
|
|
||||||
|
if ($negative) {
|
||||||
|
return '-' . $number;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a non-negative number in an arbitrary base using a custom alphabet, to base 10.
|
||||||
|
*
|
||||||
|
* @param string $number The number to convert, validated as a non-empty string,
|
||||||
|
* containing only chars in the given alphabet/base.
|
||||||
|
* @param string $alphabet The alphabet that contains every digit, validated as 2 chars minimum.
|
||||||
|
* @param int $base The base of the number, validated from 2 to alphabet length.
|
||||||
|
*
|
||||||
|
* @return string The number in base 10, following the Calculator conventions.
|
||||||
|
*/
|
||||||
|
final public function fromArbitraryBase(string $number, string $alphabet, int $base) : string
|
||||||
|
{
|
||||||
|
// remove leading "zeros"
|
||||||
|
$number = \ltrim($number, $alphabet[0]);
|
||||||
|
|
||||||
|
if ($number === '') {
|
||||||
|
return '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
// optimize for "one"
|
||||||
|
if ($number === $alphabet[1]) {
|
||||||
|
return '1';
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = '0';
|
||||||
|
$power = '1';
|
||||||
|
|
||||||
|
$base = (string) $base;
|
||||||
|
|
||||||
|
for ($i = \strlen($number) - 1; $i >= 0; $i--) {
|
||||||
|
$index = \strpos($alphabet, $number[$i]);
|
||||||
|
|
||||||
|
if ($index !== 0) {
|
||||||
|
$result = $this->add($result, ($index === 1)
|
||||||
|
? $power
|
||||||
|
: $this->mul($power, (string) $index)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($i !== 0) {
|
||||||
|
$power = $this->mul($power, $base);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a non-negative number to an arbitrary base using a custom alphabet.
|
||||||
|
*
|
||||||
|
* @param string $number The number to convert, positive or zero, following the Calculator conventions.
|
||||||
|
* @param string $alphabet The alphabet that contains every digit, validated as 2 chars minimum.
|
||||||
|
* @param int $base The base to convert to, validated from 2 to alphabet length.
|
||||||
|
*
|
||||||
|
* @return string The converted number in the given alphabet.
|
||||||
|
*/
|
||||||
|
final public function toArbitraryBase(string $number, string $alphabet, int $base) : string
|
||||||
|
{
|
||||||
|
if ($number === '0') {
|
||||||
|
return $alphabet[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
$base = (string) $base;
|
||||||
|
$result = '';
|
||||||
|
|
||||||
|
while ($number !== '0') {
|
||||||
|
[$number, $remainder] = $this->divQR($number, $base);
|
||||||
|
$remainder = (int) $remainder;
|
||||||
|
|
||||||
|
$result .= $alphabet[$remainder];
|
||||||
|
}
|
||||||
|
|
||||||
|
return \strrev($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a rounded division.
|
||||||
|
*
|
||||||
|
* Rounding is performed when the remainder of the division is not zero.
|
||||||
|
*
|
||||||
|
* @param string $a The dividend.
|
||||||
|
* @param string $b The divisor, must not be zero.
|
||||||
|
* @param int $roundingMode The rounding mode.
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException If the rounding mode is invalid.
|
||||||
|
* @throws RoundingNecessaryException If RoundingMode::UNNECESSARY is provided but rounding is necessary.
|
||||||
|
*
|
||||||
|
* @psalm-suppress ImpureFunctionCall
|
||||||
|
*/
|
||||||
|
final public function divRound(string $a, string $b, int $roundingMode) : string
|
||||||
|
{
|
||||||
|
[$quotient, $remainder] = $this->divQR($a, $b);
|
||||||
|
|
||||||
|
$hasDiscardedFraction = ($remainder !== '0');
|
||||||
|
$isPositiveOrZero = ($a[0] === '-') === ($b[0] === '-');
|
||||||
|
|
||||||
|
$discardedFractionSign = function() use ($remainder, $b) : int {
|
||||||
|
$r = $this->abs($this->mul($remainder, '2'));
|
||||||
|
$b = $this->abs($b);
|
||||||
|
|
||||||
|
return $this->cmp($r, $b);
|
||||||
|
};
|
||||||
|
|
||||||
|
$increment = false;
|
||||||
|
|
||||||
|
switch ($roundingMode) {
|
||||||
|
case RoundingMode::UNNECESSARY:
|
||||||
|
if ($hasDiscardedFraction) {
|
||||||
|
throw RoundingNecessaryException::roundingNecessary();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RoundingMode::UP:
|
||||||
|
$increment = $hasDiscardedFraction;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RoundingMode::DOWN:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RoundingMode::CEILING:
|
||||||
|
$increment = $hasDiscardedFraction && $isPositiveOrZero;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RoundingMode::FLOOR:
|
||||||
|
$increment = $hasDiscardedFraction && ! $isPositiveOrZero;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RoundingMode::HALF_UP:
|
||||||
|
$increment = $discardedFractionSign() >= 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RoundingMode::HALF_DOWN:
|
||||||
|
$increment = $discardedFractionSign() > 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RoundingMode::HALF_CEILING:
|
||||||
|
$increment = $isPositiveOrZero ? $discardedFractionSign() >= 0 : $discardedFractionSign() > 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RoundingMode::HALF_FLOOR:
|
||||||
|
$increment = $isPositiveOrZero ? $discardedFractionSign() > 0 : $discardedFractionSign() >= 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RoundingMode::HALF_EVEN:
|
||||||
|
$lastDigit = (int) $quotient[-1];
|
||||||
|
$lastDigitIsEven = ($lastDigit % 2 === 0);
|
||||||
|
$increment = $lastDigitIsEven ? $discardedFractionSign() > 0 : $discardedFractionSign() >= 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new \InvalidArgumentException('Invalid rounding mode.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($increment) {
|
||||||
|
return $this->add($quotient, $isPositiveOrZero ? '1' : '-1');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $quotient;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates bitwise AND of two numbers.
|
||||||
|
*
|
||||||
|
* This method can be overridden by the concrete implementation if the underlying library
|
||||||
|
* has built-in support for bitwise operations.
|
||||||
|
*/
|
||||||
|
public function and(string $a, string $b) : string
|
||||||
|
{
|
||||||
|
return $this->bitwise('and', $a, $b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates bitwise OR of two numbers.
|
||||||
|
*
|
||||||
|
* This method can be overridden by the concrete implementation if the underlying library
|
||||||
|
* has built-in support for bitwise operations.
|
||||||
|
*/
|
||||||
|
public function or(string $a, string $b) : string
|
||||||
|
{
|
||||||
|
return $this->bitwise('or', $a, $b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates bitwise XOR of two numbers.
|
||||||
|
*
|
||||||
|
* This method can be overridden by the concrete implementation if the underlying library
|
||||||
|
* has built-in support for bitwise operations.
|
||||||
|
*/
|
||||||
|
public function xor(string $a, string $b) : string
|
||||||
|
{
|
||||||
|
return $this->bitwise('xor', $a, $b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a bitwise operation on a decimal number.
|
||||||
|
*
|
||||||
|
* @param 'and'|'or'|'xor' $operator The operator to use.
|
||||||
|
* @param string $a The left operand.
|
||||||
|
* @param string $b The right operand.
|
||||||
|
*/
|
||||||
|
private function bitwise(string $operator, string $a, string $b) : string
|
||||||
|
{
|
||||||
|
[$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b);
|
||||||
|
|
||||||
|
$aBin = $this->toBinary($aDig);
|
||||||
|
$bBin = $this->toBinary($bDig);
|
||||||
|
|
||||||
|
$aLen = \strlen($aBin);
|
||||||
|
$bLen = \strlen($bBin);
|
||||||
|
|
||||||
|
if ($aLen > $bLen) {
|
||||||
|
$bBin = \str_repeat("\x00", $aLen - $bLen) . $bBin;
|
||||||
|
} elseif ($bLen > $aLen) {
|
||||||
|
$aBin = \str_repeat("\x00", $bLen - $aLen) . $aBin;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($aNeg) {
|
||||||
|
$aBin = $this->twosComplement($aBin);
|
||||||
|
}
|
||||||
|
if ($bNeg) {
|
||||||
|
$bBin = $this->twosComplement($bBin);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ($operator) {
|
||||||
|
case 'and':
|
||||||
|
$value = $aBin & $bBin;
|
||||||
|
$negative = ($aNeg and $bNeg);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'or':
|
||||||
|
$value = $aBin | $bBin;
|
||||||
|
$negative = ($aNeg or $bNeg);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'xor':
|
||||||
|
$value = $aBin ^ $bBin;
|
||||||
|
$negative = ($aNeg xor $bNeg);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// @codeCoverageIgnoreStart
|
||||||
|
default:
|
||||||
|
throw new \InvalidArgumentException('Invalid bitwise operator.');
|
||||||
|
// @codeCoverageIgnoreEnd
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($negative) {
|
||||||
|
$value = $this->twosComplement($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = $this->toDecimal($value);
|
||||||
|
|
||||||
|
return $negative ? $this->neg($result) : $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $number A positive, binary number.
|
||||||
|
*/
|
||||||
|
private function twosComplement(string $number) : string
|
||||||
|
{
|
||||||
|
$xor = \str_repeat("\xff", \strlen($number));
|
||||||
|
|
||||||
|
$number ^= $xor;
|
||||||
|
|
||||||
|
for ($i = \strlen($number) - 1; $i >= 0; $i--) {
|
||||||
|
$byte = \ord($number[$i]);
|
||||||
|
|
||||||
|
if (++$byte !== 256) {
|
||||||
|
$number[$i] = \chr($byte);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$number[$i] = "\x00";
|
||||||
|
|
||||||
|
if ($i === 0) {
|
||||||
|
$number = "\x01" . $number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a decimal number to a binary string.
|
||||||
|
*
|
||||||
|
* @param string $number The number to convert, positive or zero, only digits.
|
||||||
|
*/
|
||||||
|
private function toBinary(string $number) : string
|
||||||
|
{
|
||||||
|
$result = '';
|
||||||
|
|
||||||
|
while ($number !== '0') {
|
||||||
|
[$number, $remainder] = $this->divQR($number, '256');
|
||||||
|
$result .= \chr((int) $remainder);
|
||||||
|
}
|
||||||
|
|
||||||
|
return \strrev($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the positive decimal representation of a binary number.
|
||||||
|
*
|
||||||
|
* @param string $bytes The bytes representing the number.
|
||||||
|
*/
|
||||||
|
private function toDecimal(string $bytes) : string
|
||||||
|
{
|
||||||
|
$result = '0';
|
||||||
|
$power = '1';
|
||||||
|
|
||||||
|
for ($i = \strlen($bytes) - 1; $i >= 0; $i--) {
|
||||||
|
$index = \ord($bytes[$i]);
|
||||||
|
|
||||||
|
if ($index !== 0) {
|
||||||
|
$result = $this->add($result, ($index === 1)
|
||||||
|
? $power
|
||||||
|
: $this->mul($power, (string) $index)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($i !== 0) {
|
||||||
|
$power = $this->mul($power, '256');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
}
|
||||||
75
vendor/brick/math/src/Internal/Calculator/BcMathCalculator.php
vendored
Executable file
75
vendor/brick/math/src/Internal/Calculator/BcMathCalculator.php
vendored
Executable file
@@ -0,0 +1,75 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Brick\Math\Internal\Calculator;
|
||||||
|
|
||||||
|
use Brick\Math\Internal\Calculator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculator implementation built around the bcmath library.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*
|
||||||
|
* @psalm-immutable
|
||||||
|
*/
|
||||||
|
class BcMathCalculator extends Calculator
|
||||||
|
{
|
||||||
|
public function add(string $a, string $b) : string
|
||||||
|
{
|
||||||
|
return \bcadd($a, $b, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function sub(string $a, string $b) : string
|
||||||
|
{
|
||||||
|
return \bcsub($a, $b, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function mul(string $a, string $b) : string
|
||||||
|
{
|
||||||
|
return \bcmul($a, $b, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function divQ(string $a, string $b) : string
|
||||||
|
{
|
||||||
|
return \bcdiv($a, $b, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @psalm-suppress InvalidNullableReturnType
|
||||||
|
* @psalm-suppress NullableReturnStatement
|
||||||
|
*/
|
||||||
|
public function divR(string $a, string $b) : string
|
||||||
|
{
|
||||||
|
return \bcmod($a, $b, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function divQR(string $a, string $b) : array
|
||||||
|
{
|
||||||
|
$q = \bcdiv($a, $b, 0);
|
||||||
|
$r = \bcmod($a, $b, 0);
|
||||||
|
|
||||||
|
assert($r !== null);
|
||||||
|
|
||||||
|
return [$q, $r];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function pow(string $a, int $e) : string
|
||||||
|
{
|
||||||
|
return \bcpow($a, (string) $e, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function modPow(string $base, string $exp, string $mod) : string
|
||||||
|
{
|
||||||
|
return \bcpowmod($base, $exp, $mod, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @psalm-suppress InvalidNullableReturnType
|
||||||
|
* @psalm-suppress NullableReturnStatement
|
||||||
|
*/
|
||||||
|
public function sqrt(string $n) : string
|
||||||
|
{
|
||||||
|
return \bcsqrt($n, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
108
vendor/brick/math/src/Internal/Calculator/GmpCalculator.php
vendored
Executable file
108
vendor/brick/math/src/Internal/Calculator/GmpCalculator.php
vendored
Executable file
@@ -0,0 +1,108 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Brick\Math\Internal\Calculator;
|
||||||
|
|
||||||
|
use Brick\Math\Internal\Calculator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculator implementation built around the GMP library.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*
|
||||||
|
* @psalm-immutable
|
||||||
|
*/
|
||||||
|
class GmpCalculator extends Calculator
|
||||||
|
{
|
||||||
|
public function add(string $a, string $b) : string
|
||||||
|
{
|
||||||
|
return \gmp_strval(\gmp_add($a, $b));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function sub(string $a, string $b) : string
|
||||||
|
{
|
||||||
|
return \gmp_strval(\gmp_sub($a, $b));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function mul(string $a, string $b) : string
|
||||||
|
{
|
||||||
|
return \gmp_strval(\gmp_mul($a, $b));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function divQ(string $a, string $b) : string
|
||||||
|
{
|
||||||
|
return \gmp_strval(\gmp_div_q($a, $b));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function divR(string $a, string $b) : string
|
||||||
|
{
|
||||||
|
return \gmp_strval(\gmp_div_r($a, $b));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function divQR(string $a, string $b) : array
|
||||||
|
{
|
||||||
|
[$q, $r] = \gmp_div_qr($a, $b);
|
||||||
|
|
||||||
|
return [
|
||||||
|
\gmp_strval($q),
|
||||||
|
\gmp_strval($r)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function pow(string $a, int $e) : string
|
||||||
|
{
|
||||||
|
return \gmp_strval(\gmp_pow($a, $e));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function modInverse(string $x, string $m) : ?string
|
||||||
|
{
|
||||||
|
$result = \gmp_invert($x, $m);
|
||||||
|
|
||||||
|
if ($result === false) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return \gmp_strval($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function modPow(string $base, string $exp, string $mod) : string
|
||||||
|
{
|
||||||
|
return \gmp_strval(\gmp_powm($base, $exp, $mod));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function gcd(string $a, string $b) : string
|
||||||
|
{
|
||||||
|
return \gmp_strval(\gmp_gcd($a, $b));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function fromBase(string $number, int $base) : string
|
||||||
|
{
|
||||||
|
return \gmp_strval(\gmp_init($number, $base));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toBase(string $number, int $base) : string
|
||||||
|
{
|
||||||
|
return \gmp_strval($number, $base);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function and(string $a, string $b) : string
|
||||||
|
{
|
||||||
|
return \gmp_strval(\gmp_and($a, $b));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function or(string $a, string $b) : string
|
||||||
|
{
|
||||||
|
return \gmp_strval(\gmp_or($a, $b));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function xor(string $a, string $b) : string
|
||||||
|
{
|
||||||
|
return \gmp_strval(\gmp_xor($a, $b));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function sqrt(string $n) : string
|
||||||
|
{
|
||||||
|
return \gmp_strval(\gmp_sqrt($n));
|
||||||
|
}
|
||||||
|
}
|
||||||
581
vendor/brick/math/src/Internal/Calculator/NativeCalculator.php
vendored
Executable file
581
vendor/brick/math/src/Internal/Calculator/NativeCalculator.php
vendored
Executable file
@@ -0,0 +1,581 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Brick\Math\Internal\Calculator;
|
||||||
|
|
||||||
|
use Brick\Math\Internal\Calculator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculator implementation using only native PHP code.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*
|
||||||
|
* @psalm-immutable
|
||||||
|
*/
|
||||||
|
class NativeCalculator extends Calculator
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The max number of digits the platform can natively add, subtract, multiply or divide without overflow.
|
||||||
|
* For multiplication, this represents the max sum of the lengths of both operands.
|
||||||
|
*
|
||||||
|
* In addition, it is assumed that an extra digit can hold a carry (1) without overflowing.
|
||||||
|
* Example: 32-bit: max number 1,999,999,999 (9 digits + carry)
|
||||||
|
* 64-bit: max number 1,999,999,999,999,999,999 (18 digits + carry)
|
||||||
|
*/
|
||||||
|
private int $maxDigits;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @codeCoverageIgnore
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
switch (PHP_INT_SIZE) {
|
||||||
|
case 4:
|
||||||
|
$this->maxDigits = 9;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 8:
|
||||||
|
$this->maxDigits = 18;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new \RuntimeException('The platform is not 32-bit or 64-bit as expected.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function add(string $a, string $b) : string
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @psalm-var numeric-string $a
|
||||||
|
* @psalm-var numeric-string $b
|
||||||
|
*/
|
||||||
|
$result = $a + $b;
|
||||||
|
|
||||||
|
if (is_int($result)) {
|
||||||
|
return (string) $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($a === '0') {
|
||||||
|
return $b;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($b === '0') {
|
||||||
|
return $a;
|
||||||
|
}
|
||||||
|
|
||||||
|
[$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b);
|
||||||
|
|
||||||
|
$result = $aNeg === $bNeg ? $this->doAdd($aDig, $bDig) : $this->doSub($aDig, $bDig);
|
||||||
|
|
||||||
|
if ($aNeg) {
|
||||||
|
$result = $this->neg($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function sub(string $a, string $b) : string
|
||||||
|
{
|
||||||
|
return $this->add($a, $this->neg($b));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function mul(string $a, string $b) : string
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @psalm-var numeric-string $a
|
||||||
|
* @psalm-var numeric-string $b
|
||||||
|
*/
|
||||||
|
$result = $a * $b;
|
||||||
|
|
||||||
|
if (is_int($result)) {
|
||||||
|
return (string) $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($a === '0' || $b === '0') {
|
||||||
|
return '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($a === '1') {
|
||||||
|
return $b;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($b === '1') {
|
||||||
|
return $a;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($a === '-1') {
|
||||||
|
return $this->neg($b);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($b === '-1') {
|
||||||
|
return $this->neg($a);
|
||||||
|
}
|
||||||
|
|
||||||
|
[$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b);
|
||||||
|
|
||||||
|
$result = $this->doMul($aDig, $bDig);
|
||||||
|
|
||||||
|
if ($aNeg !== $bNeg) {
|
||||||
|
$result = $this->neg($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function divQ(string $a, string $b) : string
|
||||||
|
{
|
||||||
|
return $this->divQR($a, $b)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function divR(string $a, string $b): string
|
||||||
|
{
|
||||||
|
return $this->divQR($a, $b)[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function divQR(string $a, string $b) : array
|
||||||
|
{
|
||||||
|
if ($a === '0') {
|
||||||
|
return ['0', '0'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($a === $b) {
|
||||||
|
return ['1', '0'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($b === '1') {
|
||||||
|
return [$a, '0'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($b === '-1') {
|
||||||
|
return [$this->neg($a), '0'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @psalm-var numeric-string $a */
|
||||||
|
$na = $a * 1; // cast to number
|
||||||
|
|
||||||
|
if (is_int($na)) {
|
||||||
|
/** @psalm-var numeric-string $b */
|
||||||
|
$nb = $b * 1;
|
||||||
|
|
||||||
|
if (is_int($nb)) {
|
||||||
|
// the only division that may overflow is PHP_INT_MIN / -1,
|
||||||
|
// which cannot happen here as we've already handled a divisor of -1 above.
|
||||||
|
$r = $na % $nb;
|
||||||
|
$q = ($na - $r) / $nb;
|
||||||
|
|
||||||
|
assert(is_int($q));
|
||||||
|
|
||||||
|
return [
|
||||||
|
(string) $q,
|
||||||
|
(string) $r
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b);
|
||||||
|
|
||||||
|
[$q, $r] = $this->doDiv($aDig, $bDig);
|
||||||
|
|
||||||
|
if ($aNeg !== $bNeg) {
|
||||||
|
$q = $this->neg($q);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($aNeg) {
|
||||||
|
$r = $this->neg($r);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [$q, $r];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function pow(string $a, int $e) : string
|
||||||
|
{
|
||||||
|
if ($e === 0) {
|
||||||
|
return '1';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($e === 1) {
|
||||||
|
return $a;
|
||||||
|
}
|
||||||
|
|
||||||
|
$odd = $e % 2;
|
||||||
|
$e -= $odd;
|
||||||
|
|
||||||
|
$aa = $this->mul($a, $a);
|
||||||
|
|
||||||
|
/** @psalm-suppress PossiblyInvalidArgument We're sure that $e / 2 is an int now */
|
||||||
|
$result = $this->pow($aa, $e / 2);
|
||||||
|
|
||||||
|
if ($odd === 1) {
|
||||||
|
$result = $this->mul($result, $a);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Algorithm from: https://www.geeksforgeeks.org/modular-exponentiation-power-in-modular-arithmetic/
|
||||||
|
*/
|
||||||
|
public function modPow(string $base, string $exp, string $mod) : string
|
||||||
|
{
|
||||||
|
// special case: the algorithm below fails with 0 power 0 mod 1 (returns 1 instead of 0)
|
||||||
|
if ($base === '0' && $exp === '0' && $mod === '1') {
|
||||||
|
return '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
// special case: the algorithm below fails with power 0 mod 1 (returns 1 instead of 0)
|
||||||
|
if ($exp === '0' && $mod === '1') {
|
||||||
|
return '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
$x = $base;
|
||||||
|
|
||||||
|
$res = '1';
|
||||||
|
|
||||||
|
// numbers are positive, so we can use remainder instead of modulo
|
||||||
|
$x = $this->divR($x, $mod);
|
||||||
|
|
||||||
|
while ($exp !== '0') {
|
||||||
|
if (in_array($exp[-1], ['1', '3', '5', '7', '9'])) { // odd
|
||||||
|
$res = $this->divR($this->mul($res, $x), $mod);
|
||||||
|
}
|
||||||
|
|
||||||
|
$exp = $this->divQ($exp, '2');
|
||||||
|
$x = $this->divR($this->mul($x, $x), $mod);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adapted from https://cp-algorithms.com/num_methods/roots_newton.html
|
||||||
|
*/
|
||||||
|
public function sqrt(string $n) : string
|
||||||
|
{
|
||||||
|
if ($n === '0') {
|
||||||
|
return '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
// initial approximation
|
||||||
|
$x = \str_repeat('9', \intdiv(\strlen($n), 2) ?: 1);
|
||||||
|
|
||||||
|
$decreased = false;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
$nx = $this->divQ($this->add($x, $this->divQ($n, $x)), '2');
|
||||||
|
|
||||||
|
if ($x === $nx || $this->cmp($nx, $x) > 0 && $decreased) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$decreased = $this->cmp($nx, $x) < 0;
|
||||||
|
$x = $nx;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $x;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs the addition of two non-signed large integers.
|
||||||
|
*/
|
||||||
|
private function doAdd(string $a, string $b) : string
|
||||||
|
{
|
||||||
|
[$a, $b, $length] = $this->pad($a, $b);
|
||||||
|
|
||||||
|
$carry = 0;
|
||||||
|
$result = '';
|
||||||
|
|
||||||
|
for ($i = $length - $this->maxDigits;; $i -= $this->maxDigits) {
|
||||||
|
$blockLength = $this->maxDigits;
|
||||||
|
|
||||||
|
if ($i < 0) {
|
||||||
|
$blockLength += $i;
|
||||||
|
/** @psalm-suppress LoopInvalidation */
|
||||||
|
$i = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @psalm-var numeric-string $blockA */
|
||||||
|
$blockA = \substr($a, $i, $blockLength);
|
||||||
|
|
||||||
|
/** @psalm-var numeric-string $blockB */
|
||||||
|
$blockB = \substr($b, $i, $blockLength);
|
||||||
|
|
||||||
|
$sum = (string) ($blockA + $blockB + $carry);
|
||||||
|
$sumLength = \strlen($sum);
|
||||||
|
|
||||||
|
if ($sumLength > $blockLength) {
|
||||||
|
$sum = \substr($sum, 1);
|
||||||
|
$carry = 1;
|
||||||
|
} else {
|
||||||
|
if ($sumLength < $blockLength) {
|
||||||
|
$sum = \str_repeat('0', $blockLength - $sumLength) . $sum;
|
||||||
|
}
|
||||||
|
$carry = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = $sum . $result;
|
||||||
|
|
||||||
|
if ($i === 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($carry === 1) {
|
||||||
|
$result = '1' . $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs the subtraction of two non-signed large integers.
|
||||||
|
*/
|
||||||
|
private function doSub(string $a, string $b) : string
|
||||||
|
{
|
||||||
|
if ($a === $b) {
|
||||||
|
return '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that we always subtract to a positive result: biggest minus smallest.
|
||||||
|
$cmp = $this->doCmp($a, $b);
|
||||||
|
|
||||||
|
$invert = ($cmp === -1);
|
||||||
|
|
||||||
|
if ($invert) {
|
||||||
|
$c = $a;
|
||||||
|
$a = $b;
|
||||||
|
$b = $c;
|
||||||
|
}
|
||||||
|
|
||||||
|
[$a, $b, $length] = $this->pad($a, $b);
|
||||||
|
|
||||||
|
$carry = 0;
|
||||||
|
$result = '';
|
||||||
|
|
||||||
|
$complement = 10 ** $this->maxDigits;
|
||||||
|
|
||||||
|
for ($i = $length - $this->maxDigits;; $i -= $this->maxDigits) {
|
||||||
|
$blockLength = $this->maxDigits;
|
||||||
|
|
||||||
|
if ($i < 0) {
|
||||||
|
$blockLength += $i;
|
||||||
|
/** @psalm-suppress LoopInvalidation */
|
||||||
|
$i = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @psalm-var numeric-string $blockA */
|
||||||
|
$blockA = \substr($a, $i, $blockLength);
|
||||||
|
|
||||||
|
/** @psalm-var numeric-string $blockB */
|
||||||
|
$blockB = \substr($b, $i, $blockLength);
|
||||||
|
|
||||||
|
$sum = $blockA - $blockB - $carry;
|
||||||
|
|
||||||
|
if ($sum < 0) {
|
||||||
|
$sum += $complement;
|
||||||
|
$carry = 1;
|
||||||
|
} else {
|
||||||
|
$carry = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sum = (string) $sum;
|
||||||
|
$sumLength = \strlen($sum);
|
||||||
|
|
||||||
|
if ($sumLength < $blockLength) {
|
||||||
|
$sum = \str_repeat('0', $blockLength - $sumLength) . $sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = $sum . $result;
|
||||||
|
|
||||||
|
if ($i === 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Carry cannot be 1 when the loop ends, as a > b
|
||||||
|
assert($carry === 0);
|
||||||
|
|
||||||
|
$result = \ltrim($result, '0');
|
||||||
|
|
||||||
|
if ($invert) {
|
||||||
|
$result = $this->neg($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs the multiplication of two non-signed large integers.
|
||||||
|
*/
|
||||||
|
private function doMul(string $a, string $b) : string
|
||||||
|
{
|
||||||
|
$x = \strlen($a);
|
||||||
|
$y = \strlen($b);
|
||||||
|
|
||||||
|
$maxDigits = \intdiv($this->maxDigits, 2);
|
||||||
|
$complement = 10 ** $maxDigits;
|
||||||
|
|
||||||
|
$result = '0';
|
||||||
|
|
||||||
|
for ($i = $x - $maxDigits;; $i -= $maxDigits) {
|
||||||
|
$blockALength = $maxDigits;
|
||||||
|
|
||||||
|
if ($i < 0) {
|
||||||
|
$blockALength += $i;
|
||||||
|
/** @psalm-suppress LoopInvalidation */
|
||||||
|
$i = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$blockA = (int) \substr($a, $i, $blockALength);
|
||||||
|
|
||||||
|
$line = '';
|
||||||
|
$carry = 0;
|
||||||
|
|
||||||
|
for ($j = $y - $maxDigits;; $j -= $maxDigits) {
|
||||||
|
$blockBLength = $maxDigits;
|
||||||
|
|
||||||
|
if ($j < 0) {
|
||||||
|
$blockBLength += $j;
|
||||||
|
/** @psalm-suppress LoopInvalidation */
|
||||||
|
$j = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$blockB = (int) \substr($b, $j, $blockBLength);
|
||||||
|
|
||||||
|
$mul = $blockA * $blockB + $carry;
|
||||||
|
$value = $mul % $complement;
|
||||||
|
$carry = ($mul - $value) / $complement;
|
||||||
|
|
||||||
|
$value = (string) $value;
|
||||||
|
$value = \str_pad($value, $maxDigits, '0', STR_PAD_LEFT);
|
||||||
|
|
||||||
|
$line = $value . $line;
|
||||||
|
|
||||||
|
if ($j === 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($carry !== 0) {
|
||||||
|
$line = $carry . $line;
|
||||||
|
}
|
||||||
|
|
||||||
|
$line = \ltrim($line, '0');
|
||||||
|
|
||||||
|
if ($line !== '') {
|
||||||
|
$line .= \str_repeat('0', $x - $blockALength - $i);
|
||||||
|
$result = $this->add($result, $line);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($i === 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs the division of two non-signed large integers.
|
||||||
|
*
|
||||||
|
* @return string[] The quotient and remainder.
|
||||||
|
*/
|
||||||
|
private function doDiv(string $a, string $b) : array
|
||||||
|
{
|
||||||
|
$cmp = $this->doCmp($a, $b);
|
||||||
|
|
||||||
|
if ($cmp === -1) {
|
||||||
|
return ['0', $a];
|
||||||
|
}
|
||||||
|
|
||||||
|
$x = \strlen($a);
|
||||||
|
$y = \strlen($b);
|
||||||
|
|
||||||
|
// we now know that a >= b && x >= y
|
||||||
|
|
||||||
|
$q = '0'; // quotient
|
||||||
|
$r = $a; // remainder
|
||||||
|
$z = $y; // focus length, always $y or $y+1
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
$focus = \substr($a, 0, $z);
|
||||||
|
|
||||||
|
$cmp = $this->doCmp($focus, $b);
|
||||||
|
|
||||||
|
if ($cmp === -1) {
|
||||||
|
if ($z === $x) { // remainder < dividend
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$z++;
|
||||||
|
}
|
||||||
|
|
||||||
|
$zeros = \str_repeat('0', $x - $z);
|
||||||
|
|
||||||
|
$q = $this->add($q, '1' . $zeros);
|
||||||
|
$a = $this->sub($a, $b . $zeros);
|
||||||
|
|
||||||
|
$r = $a;
|
||||||
|
|
||||||
|
if ($r === '0') { // remainder == 0
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$x = \strlen($a);
|
||||||
|
|
||||||
|
if ($x < $y) { // remainder < dividend
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$z = $y;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [$q, $r];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compares two non-signed large numbers.
|
||||||
|
*
|
||||||
|
* @return int [-1, 0, 1]
|
||||||
|
*/
|
||||||
|
private function doCmp(string $a, string $b) : int
|
||||||
|
{
|
||||||
|
$x = \strlen($a);
|
||||||
|
$y = \strlen($b);
|
||||||
|
|
||||||
|
$cmp = $x <=> $y;
|
||||||
|
|
||||||
|
if ($cmp !== 0) {
|
||||||
|
return $cmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
return \strcmp($a, $b) <=> 0; // enforce [-1, 0, 1]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pads the left of one of the given numbers with zeros if necessary to make both numbers the same length.
|
||||||
|
*
|
||||||
|
* The numbers must only consist of digits, without leading minus sign.
|
||||||
|
*
|
||||||
|
* @return array{string, string, int}
|
||||||
|
*/
|
||||||
|
private function pad(string $a, string $b) : array
|
||||||
|
{
|
||||||
|
$x = \strlen($a);
|
||||||
|
$y = \strlen($b);
|
||||||
|
|
||||||
|
if ($x > $y) {
|
||||||
|
$b = \str_repeat('0', $x - $y) . $b;
|
||||||
|
|
||||||
|
return [$a, $b, $x];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($x < $y) {
|
||||||
|
$a = \str_repeat('0', $y - $x) . $a;
|
||||||
|
|
||||||
|
return [$a, $b, $y];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [$a, $b, $x];
|
||||||
|
}
|
||||||
|
}
|
||||||
107
vendor/brick/math/src/RoundingMode.php
vendored
Executable file
107
vendor/brick/math/src/RoundingMode.php
vendored
Executable file
@@ -0,0 +1,107 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Brick\Math;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies a rounding behavior for numerical operations capable of discarding precision.
|
||||||
|
*
|
||||||
|
* Each rounding mode indicates how the least significant returned digit of a rounded result
|
||||||
|
* is to be calculated. If fewer digits are returned than the digits needed to represent the
|
||||||
|
* exact numerical result, the discarded digits will be referred to as the discarded fraction
|
||||||
|
* regardless the digits' contribution to the value of the number. In other words, considered
|
||||||
|
* as a numerical value, the discarded fraction could have an absolute value greater than one.
|
||||||
|
*/
|
||||||
|
final class RoundingMode
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Private constructor. This class is not instantiable.
|
||||||
|
*
|
||||||
|
* @codeCoverageIgnore
|
||||||
|
*/
|
||||||
|
private function __construct()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that the requested operation has an exact result, hence no rounding is necessary.
|
||||||
|
*
|
||||||
|
* If this rounding mode is specified on an operation that yields a result that
|
||||||
|
* cannot be represented at the requested scale, a RoundingNecessaryException is thrown.
|
||||||
|
*/
|
||||||
|
public const UNNECESSARY = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rounds away from zero.
|
||||||
|
*
|
||||||
|
* Always increments the digit prior to a nonzero discarded fraction.
|
||||||
|
* Note that this rounding mode never decreases the magnitude of the calculated value.
|
||||||
|
*/
|
||||||
|
public const UP = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rounds towards zero.
|
||||||
|
*
|
||||||
|
* Never increments the digit prior to a discarded fraction (i.e., truncates).
|
||||||
|
* Note that this rounding mode never increases the magnitude of the calculated value.
|
||||||
|
*/
|
||||||
|
public const DOWN = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rounds towards positive infinity.
|
||||||
|
*
|
||||||
|
* If the result is positive, behaves as for UP; if negative, behaves as for DOWN.
|
||||||
|
* Note that this rounding mode never decreases the calculated value.
|
||||||
|
*/
|
||||||
|
public const CEILING = 3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rounds towards negative infinity.
|
||||||
|
*
|
||||||
|
* If the result is positive, behave as for DOWN; if negative, behave as for UP.
|
||||||
|
* Note that this rounding mode never increases the calculated value.
|
||||||
|
*/
|
||||||
|
public const FLOOR = 4;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rounds towards "nearest neighbor" unless both neighbors are equidistant, in which case round up.
|
||||||
|
*
|
||||||
|
* Behaves as for UP if the discarded fraction is >= 0.5; otherwise, behaves as for DOWN.
|
||||||
|
* Note that this is the rounding mode commonly taught at school.
|
||||||
|
*/
|
||||||
|
public const HALF_UP = 5;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rounds towards "nearest neighbor" unless both neighbors are equidistant, in which case round down.
|
||||||
|
*
|
||||||
|
* Behaves as for UP if the discarded fraction is > 0.5; otherwise, behaves as for DOWN.
|
||||||
|
*/
|
||||||
|
public const HALF_DOWN = 6;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rounds towards "nearest neighbor" unless both neighbors are equidistant, in which case round towards positive infinity.
|
||||||
|
*
|
||||||
|
* If the result is positive, behaves as for HALF_UP; if negative, behaves as for HALF_DOWN.
|
||||||
|
*/
|
||||||
|
public const HALF_CEILING = 7;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rounds towards "nearest neighbor" unless both neighbors are equidistant, in which case round towards negative infinity.
|
||||||
|
*
|
||||||
|
* If the result is positive, behaves as for HALF_DOWN; if negative, behaves as for HALF_UP.
|
||||||
|
*/
|
||||||
|
public const HALF_FLOOR = 8;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rounds towards the "nearest neighbor" unless both neighbors are equidistant, in which case rounds towards the even neighbor.
|
||||||
|
*
|
||||||
|
* Behaves as for HALF_UP if the digit to the left of the discarded fraction is odd;
|
||||||
|
* behaves as for HALF_DOWN if it's even.
|
||||||
|
*
|
||||||
|
* Note that this is the rounding mode that statistically minimizes
|
||||||
|
* cumulative error when applied repeatedly over a sequence of calculations.
|
||||||
|
* It is sometimes known as "Banker's rounding", and is chiefly used in the USA.
|
||||||
|
*/
|
||||||
|
public const HALF_EVEN = 9;
|
||||||
|
}
|
||||||
14
vendor/claudecio/axiumphp/.env.example
vendored
Executable file
14
vendor/claudecio/axiumphp/.env.example
vendored
Executable file
@@ -0,0 +1,14 @@
|
|||||||
|
SYSTEM_VERSION = 1.0
|
||||||
|
SYSTEM_TIMEZONE = 'America/Fortaleza'
|
||||||
|
SYSTEM_URL = 'https://system.localhost'
|
||||||
|
SYSTEM_FRONTEND_URL = 'https://app.system.localhost'
|
||||||
|
# 1 = Production, 2 = Development
|
||||||
|
SYSTEM_ENVIRONMENT_ID = 2
|
||||||
|
|
||||||
|
DEFAULT_DATABASE_HOST = 127.0.0.1
|
||||||
|
DEFAULT_DATABASE_PORT = 3306
|
||||||
|
DEFAULT_DATABASE_DRIVER = mysql
|
||||||
|
DEFAULT_DATABASE_SCHEMA = schema
|
||||||
|
DEFAULT_DATABASE_CHARSET = utf8mb4
|
||||||
|
DEFAULT_DATABASE_USERNAME = username
|
||||||
|
DEFAULT_DATABASE_PASSWORD = password
|
||||||
15
vendor/claudecio/axiumphp/composer.json
vendored
Executable file
15
vendor/claudecio/axiumphp/composer.json
vendored
Executable file
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"name": "claudecio/axiumphp",
|
||||||
|
"description": "Meu framework PHP para sistemas MVC modulares.",
|
||||||
|
"type": "library",
|
||||||
|
"license": "MIT",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"AxiumPHP\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=8.1",
|
||||||
|
"vlucas/phpdotenv": "v5.6.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
482
vendor/claudecio/axiumphp/composer.lock
generated
vendored
Executable file
482
vendor/claudecio/axiumphp/composer.lock
generated
vendored
Executable file
@@ -0,0 +1,482 @@
|
|||||||
|
{
|
||||||
|
"_readme": [
|
||||||
|
"This file locks the dependencies of your project to a known state",
|
||||||
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
|
"This file is @generated automatically"
|
||||||
|
],
|
||||||
|
"content-hash": "6d450325e9b77ca38da0bcec5503c990",
|
||||||
|
"packages": [
|
||||||
|
{
|
||||||
|
"name": "graham-campbell/result-type",
|
||||||
|
"version": "v1.1.3",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/GrahamCampbell/Result-Type.git",
|
||||||
|
"reference": "3ba905c11371512af9d9bdd27d99b782216b6945"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/3ba905c11371512af9d9bdd27d99b782216b6945",
|
||||||
|
"reference": "3ba905c11371512af9d9bdd27d99b782216b6945",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7.2.5 || ^8.0",
|
||||||
|
"phpoption/phpoption": "^1.9.3"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"GrahamCampbell\\ResultType\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Graham Campbell",
|
||||||
|
"email": "hello@gjcampbell.co.uk",
|
||||||
|
"homepage": "https://github.com/GrahamCampbell"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "An Implementation Of The Result Type",
|
||||||
|
"keywords": [
|
||||||
|
"Graham Campbell",
|
||||||
|
"GrahamCampbell",
|
||||||
|
"Result Type",
|
||||||
|
"Result-Type",
|
||||||
|
"result"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/GrahamCampbell/Result-Type/issues",
|
||||||
|
"source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.3"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/GrahamCampbell",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type",
|
||||||
|
"type": "tidelift"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2024-07-20T21:45:45+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "phpoption/phpoption",
|
||||||
|
"version": "1.9.3",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/schmittjoh/php-option.git",
|
||||||
|
"reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/schmittjoh/php-option/zipball/e3fac8b24f56113f7cb96af14958c0dd16330f54",
|
||||||
|
"reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7.2.5 || ^8.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"bamarni/composer-bin-plugin": "^1.8.2",
|
||||||
|
"phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"bamarni-bin": {
|
||||||
|
"bin-links": true,
|
||||||
|
"forward-command": false
|
||||||
|
},
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "1.9-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"PhpOption\\": "src/PhpOption/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"Apache-2.0"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Johannes M. Schmitt",
|
||||||
|
"email": "schmittjoh@gmail.com",
|
||||||
|
"homepage": "https://github.com/schmittjoh"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Graham Campbell",
|
||||||
|
"email": "hello@gjcampbell.co.uk",
|
||||||
|
"homepage": "https://github.com/GrahamCampbell"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Option Type for PHP",
|
||||||
|
"keywords": [
|
||||||
|
"language",
|
||||||
|
"option",
|
||||||
|
"php",
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/schmittjoh/php-option/issues",
|
||||||
|
"source": "https://github.com/schmittjoh/php-option/tree/1.9.3"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/GrahamCampbell",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption",
|
||||||
|
"type": "tidelift"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2024-07-20T21:41:07+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "symfony/polyfill-ctype",
|
||||||
|
"version": "v1.32.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/symfony/polyfill-ctype.git",
|
||||||
|
"reference": "a3cc8b044a6ea513310cbd48ef7333b384945638"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638",
|
||||||
|
"reference": "a3cc8b044a6ea513310cbd48ef7333b384945638",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=7.2"
|
||||||
|
},
|
||||||
|
"provide": {
|
||||||
|
"ext-ctype": "*"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"ext-ctype": "For best performance"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"thanks": {
|
||||||
|
"url": "https://github.com/symfony/polyfill",
|
||||||
|
"name": "symfony/polyfill"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"files": [
|
||||||
|
"bootstrap.php"
|
||||||
|
],
|
||||||
|
"psr-4": {
|
||||||
|
"Symfony\\Polyfill\\Ctype\\": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Gert de Pagter",
|
||||||
|
"email": "BackEndTea@gmail.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Symfony Community",
|
||||||
|
"homepage": "https://symfony.com/contributors"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Symfony polyfill for ctype functions",
|
||||||
|
"homepage": "https://symfony.com",
|
||||||
|
"keywords": [
|
||||||
|
"compatibility",
|
||||||
|
"ctype",
|
||||||
|
"polyfill",
|
||||||
|
"portable"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.32.0"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://symfony.com/sponsor",
|
||||||
|
"type": "custom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/fabpot",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||||
|
"type": "tidelift"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2024-09-09T11:45:10+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "symfony/polyfill-mbstring",
|
||||||
|
"version": "v1.32.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/symfony/polyfill-mbstring.git",
|
||||||
|
"reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493",
|
||||||
|
"reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-iconv": "*",
|
||||||
|
"php": ">=7.2"
|
||||||
|
},
|
||||||
|
"provide": {
|
||||||
|
"ext-mbstring": "*"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"ext-mbstring": "For best performance"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"thanks": {
|
||||||
|
"url": "https://github.com/symfony/polyfill",
|
||||||
|
"name": "symfony/polyfill"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"files": [
|
||||||
|
"bootstrap.php"
|
||||||
|
],
|
||||||
|
"psr-4": {
|
||||||
|
"Symfony\\Polyfill\\Mbstring\\": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Nicolas Grekas",
|
||||||
|
"email": "p@tchwork.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Symfony Community",
|
||||||
|
"homepage": "https://symfony.com/contributors"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Symfony polyfill for the Mbstring extension",
|
||||||
|
"homepage": "https://symfony.com",
|
||||||
|
"keywords": [
|
||||||
|
"compatibility",
|
||||||
|
"mbstring",
|
||||||
|
"polyfill",
|
||||||
|
"portable",
|
||||||
|
"shim"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.32.0"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://symfony.com/sponsor",
|
||||||
|
"type": "custom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/fabpot",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||||
|
"type": "tidelift"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2024-12-23T08:48:59+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "symfony/polyfill-php80",
|
||||||
|
"version": "v1.32.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/symfony/polyfill-php80.git",
|
||||||
|
"reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/0cc9dd0f17f61d8131e7df6b84bd344899fe2608",
|
||||||
|
"reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=7.2"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"thanks": {
|
||||||
|
"url": "https://github.com/symfony/polyfill",
|
||||||
|
"name": "symfony/polyfill"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"files": [
|
||||||
|
"bootstrap.php"
|
||||||
|
],
|
||||||
|
"psr-4": {
|
||||||
|
"Symfony\\Polyfill\\Php80\\": ""
|
||||||
|
},
|
||||||
|
"classmap": [
|
||||||
|
"Resources/stubs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Ion Bazan",
|
||||||
|
"email": "ion.bazan@gmail.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Nicolas Grekas",
|
||||||
|
"email": "p@tchwork.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Symfony Community",
|
||||||
|
"homepage": "https://symfony.com/contributors"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
|
||||||
|
"homepage": "https://symfony.com",
|
||||||
|
"keywords": [
|
||||||
|
"compatibility",
|
||||||
|
"polyfill",
|
||||||
|
"portable",
|
||||||
|
"shim"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"source": "https://github.com/symfony/polyfill-php80/tree/v1.32.0"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://symfony.com/sponsor",
|
||||||
|
"type": "custom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/fabpot",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||||
|
"type": "tidelift"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2025-01-02T08:10:11+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "vlucas/phpdotenv",
|
||||||
|
"version": "v5.6.2",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/vlucas/phpdotenv.git",
|
||||||
|
"reference": "24ac4c74f91ee2c193fa1aaa5c249cb0822809af"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/24ac4c74f91ee2c193fa1aaa5c249cb0822809af",
|
||||||
|
"reference": "24ac4c74f91ee2c193fa1aaa5c249cb0822809af",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-pcre": "*",
|
||||||
|
"graham-campbell/result-type": "^1.1.3",
|
||||||
|
"php": "^7.2.5 || ^8.0",
|
||||||
|
"phpoption/phpoption": "^1.9.3",
|
||||||
|
"symfony/polyfill-ctype": "^1.24",
|
||||||
|
"symfony/polyfill-mbstring": "^1.24",
|
||||||
|
"symfony/polyfill-php80": "^1.24"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"bamarni/composer-bin-plugin": "^1.8.2",
|
||||||
|
"ext-filter": "*",
|
||||||
|
"phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"ext-filter": "Required to use the boolean validator."
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"bamarni-bin": {
|
||||||
|
"bin-links": true,
|
||||||
|
"forward-command": false
|
||||||
|
},
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "5.6-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Dotenv\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"BSD-3-Clause"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Graham Campbell",
|
||||||
|
"email": "hello@gjcampbell.co.uk",
|
||||||
|
"homepage": "https://github.com/GrahamCampbell"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Vance Lucas",
|
||||||
|
"email": "vance@vancelucas.com",
|
||||||
|
"homepage": "https://github.com/vlucas"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.",
|
||||||
|
"keywords": [
|
||||||
|
"dotenv",
|
||||||
|
"env",
|
||||||
|
"environment"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/vlucas/phpdotenv/issues",
|
||||||
|
"source": "https://github.com/vlucas/phpdotenv/tree/v5.6.2"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/GrahamCampbell",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv",
|
||||||
|
"type": "tidelift"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2025-04-30T23:37:27+00:00"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"packages-dev": [],
|
||||||
|
"aliases": [],
|
||||||
|
"minimum-stability": "stable",
|
||||||
|
"stability-flags": {},
|
||||||
|
"prefer-stable": false,
|
||||||
|
"prefer-lowest": false,
|
||||||
|
"platform": {
|
||||||
|
"php": ">=8.1"
|
||||||
|
},
|
||||||
|
"platform-dev": {},
|
||||||
|
"plugin-api-version": "2.6.0"
|
||||||
|
}
|
||||||
152
vendor/claudecio/axiumphp/guide.md
vendored
Executable file
152
vendor/claudecio/axiumphp/guide.md
vendored
Executable file
@@ -0,0 +1,152 @@
|
|||||||
|
# AxiumPHP - Guia de Primeiro Uso
|
||||||
|
|
||||||
|
O **AxiumPHP** é um micro-framework PHP modular no padrão MVC, ideal para criar sistemas organizados e escaláveis sem depender de frameworks pesados.
|
||||||
|
Ele já vem pronto para trabalhar com módulos independentes, carregamento automático de rotas, tratamento global de erros e suporte a APIs (JSON) ou páginas HTML.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 Instalação e Configuração
|
||||||
|
|
||||||
|
### 1. Clonar o repositório
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/claudecio/AxiumPHP.git
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Instalar as dependências via Composer
|
||||||
|
```bash
|
||||||
|
composer install
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Configurar o ambiente
|
||||||
|
Copie o arquivo `.env.example` para `.env`:
|
||||||
|
```bash
|
||||||
|
cp .env.example .env
|
||||||
|
```
|
||||||
|
Depois, ajuste as variáveis conforme seu ambiente (conexão com banco, fuso horário, URL do frontend, etc.).
|
||||||
|
|
||||||
|
### 4. Configurar o servidor
|
||||||
|
**Usando PHP embutido:**
|
||||||
|
```bash
|
||||||
|
php -S localhost:8000 -t public
|
||||||
|
```
|
||||||
|
|
||||||
|
**Usando Apache:**
|
||||||
|
Aponte o `DocumentRoot` para a pasta `public/`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📂 Estrutura do Projeto
|
||||||
|
```
|
||||||
|
app/
|
||||||
|
├── Common/ # Serviços e controladores compartilhados
|
||||||
|
├── Module/ # Módulos independentes (Controllers, Models, Views, Routes, bootstrap.php)
|
||||||
|
└── Core/ # Núcleo do framework
|
||||||
|
public/ # Arquivos públicos (index.php, assets)
|
||||||
|
vendor/ # Dependências do Composer
|
||||||
|
.env # Configurações do ambiente
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📌 Entendendo o index.php
|
||||||
|
O arquivo `public/index.php` é o ponto de entrada da aplicação.
|
||||||
|
Nele, além do autoload do Composer, algumas constantes precisam ser definidas:
|
||||||
|
|
||||||
|
- **ROUTER_MODE** → define o modo de resposta do roteador (`JSON` para APIs, `VIEW` para views).
|
||||||
|
- **APP_SYS_MODE** → define o modo de sistema, PROD para produção ou DEV para desenvolvimento.
|
||||||
|
- **ROUTER_ALLOWED_ORIGINS** → array onde é definido os dominios permitidos para usar o CORS.
|
||||||
|
- **INI_SYSTEM_PATH** → caminho absoluto para a pasta `app` do projeto.
|
||||||
|
- **MODULE_PATH** → caminho absoluto para a pasta `Module`, onde ficam os módulos do sistema.
|
||||||
|
- **STORAGE_FOLDER_PATH** → caminho absoluto para a pasta `Storage`, utilizada para arquivos e logs.
|
||||||
|
|
||||||
|
**Exemplo:**
|
||||||
|
```php
|
||||||
|
const ROUTER_MODE = 'JSON';
|
||||||
|
const APP_SYS_MODE = 'DEV' # PROD = Production // DEV = Development
|
||||||
|
// Case Router Mode in JSON
|
||||||
|
const ROUTER_ALLOWED_ORIGINS = [
|
||||||
|
'*',
|
||||||
|
'http://app.localhost:8000',
|
||||||
|
];
|
||||||
|
define('INI_SYSTEM_PATH', realpath(__DIR__ . "/../app"));
|
||||||
|
define('MODULE_PATH', realpath(__DIR__ . "/../app/Module"));
|
||||||
|
define('STORAGE_FOLDER_PATH', realpath(__DIR__ . "/../app/Storage"));
|
||||||
|
```
|
||||||
|
|
||||||
|
Além disso, o `index.php`:
|
||||||
|
- Carrega configurações globais (`Config`);
|
||||||
|
- Inicializa o `LoggerService`;
|
||||||
|
- Ativa o `ErrorHandler`;
|
||||||
|
- Define o fuso horário (`SYSTEM_TIMEZONE` do `.env`);
|
||||||
|
- Inicia sessão, se necessário;
|
||||||
|
- Configura CORS;
|
||||||
|
- Carrega os módulos iniciais.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🛠 Criando e Registrando um Módulo
|
||||||
|
|
||||||
|
### 1. Criar a pasta do módulo
|
||||||
|
```bash
|
||||||
|
mkdir -p app/Module/Hello
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Criar o Controller
|
||||||
|
Arquivo: `app/Module/Hello/Controllers/HelloController.php`
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
namespace App\Module\Hello\Controllers;
|
||||||
|
|
||||||
|
class HelloController {
|
||||||
|
public function index() {
|
||||||
|
echo "Hello, AxiumPHP!";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Criar a rota
|
||||||
|
Arquivo: `app/Module/Hello/Routes/web.php`
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
use AxiumPHP\Core\Router;
|
||||||
|
use App\Module\Hello\Controllers\HelloController;
|
||||||
|
|
||||||
|
Router::GET('/hello', [HelloController::class, 'index']);
|
||||||
|
Router::PUT('/hello/{id}', [HelloController::class, 'update']);
|
||||||
|
Router::POST('/createHello', [HelloController::class, 'create']);
|
||||||
|
Router::DELETE('/hello/{id}', [HelloController::class, 'delete']);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Criar o bootstrap do módulo
|
||||||
|
Arquivo: `app/Module/Hello/bootstrap.php`
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
require_once __DIR__ . '/Routes/web.php';
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Registrar o módulo no index.php
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
require_once realpath(__DIR__ . "/../app/Module/Hello/bootstrap.php");
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 💡 Dicas Importantes
|
||||||
|
|
||||||
|
**LoggerService** → configure antes do `ErrorHandler`:
|
||||||
|
```php
|
||||||
|
LoggerService::init(
|
||||||
|
driver: LoggerService::DRIVER_FILE,
|
||||||
|
logDir: 'logs'
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
**CORS** → já configurado no `index.php` usando `SYSTEM_FRONTEND_URL` do `.env`.
|
||||||
|
**ROUTER_MODE** → altere para `VIEW` se quiser trabalhar com renderização de views.
|
||||||
|
**ErrorHandler** → use o `SYSTEM_ENVIRONMENT_ID` do `.env` para decidir se exibe ou oculta erros em produção.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📜 Licença
|
||||||
|
Este projeto está licenciado sob a **MIT License**.
|
||||||
10
vendor/claudecio/axiumphp/readme.md
vendored
Executable file
10
vendor/claudecio/axiumphp/readme.md
vendored
Executable file
@@ -0,0 +1,10 @@
|
|||||||
|
# AxiumPHP
|
||||||
|
|
||||||
|
AxiumPHP é um framework simples e modular para aplicações PHP.
|
||||||
|
|
||||||
|
## Instalação
|
||||||
|
|
||||||
|
Com o Composer:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
composer require claudecio/axiumphp
|
||||||
35
vendor/claudecio/axiumphp/src/AxiumPHP.php
vendored
Executable file
35
vendor/claudecio/axiumphp/src/AxiumPHP.php
vendored
Executable file
@@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
namespace AxiumPHP;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
class AxiumPHP {
|
||||||
|
|
||||||
|
private array $requiredConstants = [
|
||||||
|
'INI_SYSTEM_PATH',
|
||||||
|
'STORAGE_FOLDER_PATH'
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construtor que vai garantir que as constantes necessárias estejam definidas antes de
|
||||||
|
* instanciar o AxiumPHP.
|
||||||
|
*/
|
||||||
|
public function __construct() {
|
||||||
|
// Verificar as constantes no momento da criação da instância
|
||||||
|
$this->checkRequiredConstants();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifica se todas as constantes necessárias estão definidas.
|
||||||
|
*
|
||||||
|
* @throws Exception Se alguma constante necessária não estiver definida.
|
||||||
|
*/
|
||||||
|
private function checkRequiredConstants(): void {
|
||||||
|
foreach ($this->requiredConstants as $constant) {
|
||||||
|
if (!defined(constant_name: $constant)) {
|
||||||
|
throw new Exception(message: "Constante '{$constant}' não definida.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
120
vendor/claudecio/axiumphp/src/Core/Database.php
vendored
Executable file
120
vendor/claudecio/axiumphp/src/Core/Database.php
vendored
Executable file
@@ -0,0 +1,120 @@
|
|||||||
|
<?php
|
||||||
|
namespace AxiumPHP\Core;
|
||||||
|
|
||||||
|
use PDO;
|
||||||
|
use Dotenv\Dotenv;
|
||||||
|
use RuntimeException;
|
||||||
|
use AxiumPHP\Core\Database\Drivers\MySQL;
|
||||||
|
use AxiumPHP\Core\Database\Drivers\Postgres;
|
||||||
|
|
||||||
|
class Database {
|
||||||
|
private static array $connections = [];
|
||||||
|
private static bool $envLoaded = false;
|
||||||
|
|
||||||
|
private static function loadEnv(): void {
|
||||||
|
if (!self::$envLoaded) {
|
||||||
|
if (file_exists(filename: ROOT_SYSTEM_PATH . '/.env')) {
|
||||||
|
$dotenv = Dotenv::createImmutable(paths: ROOT_SYSTEM_PATH);
|
||||||
|
$dotenv->load();
|
||||||
|
}
|
||||||
|
self::$envLoaded = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retorna conexão por nome + schema
|
||||||
|
*/
|
||||||
|
public static function getConnection(string $connectionName = 'DEFAULT', ?string $schema = null): mixed {
|
||||||
|
self::loadEnv();
|
||||||
|
$connectionName = strtoupper(string: $connectionName);
|
||||||
|
$key = $connectionName . ($schema ? "_$schema" : '');
|
||||||
|
|
||||||
|
if (!isset(self::$connections[$key])) {
|
||||||
|
$driver = $_ENV["{$connectionName}_DATABASE_DRIVER"];
|
||||||
|
|
||||||
|
switch (strtolower(string: $driver)) {
|
||||||
|
case 'mysql':
|
||||||
|
$conn = new MySQL(envName: $connectionName);
|
||||||
|
break;
|
||||||
|
case 'pgsql':
|
||||||
|
$conn = new Postgres(envName: $connectionName, schema: $schema);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new RuntimeException(message: "Driver desconhecido: {$driver}");
|
||||||
|
}
|
||||||
|
|
||||||
|
self::$connections[$key] = $conn;
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$connections[$key];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function disconnect(string $connectionName = 'DEFAULT', ?string $schema = null): void {
|
||||||
|
$connectionName = strtoupper(string: $connectionName);
|
||||||
|
$key = $connectionName . ($schema ? "_$schema" : '');
|
||||||
|
self::$connections[$key] = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Métodos auxiliares (execute, fetchAll, fetchOne, etc.) */
|
||||||
|
public static function execute(string $sql, array $params = [], string $connectionName = 'DEFAULT', ?string $schema = null): bool {
|
||||||
|
$conn = self::getConnection(connectionName: $connectionName, schema: $schema);
|
||||||
|
if (method_exists(object_or_class: $conn, method: 'execute')) {
|
||||||
|
return $conn->execute($sql, $params);
|
||||||
|
}
|
||||||
|
throw new RuntimeException(message: "O driver '{$connectionName}' não suporta execute()");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function fetchAll(string $sql, array $params = [], string $connectionName = 'DEFAULT', ?string $schema = null): array {
|
||||||
|
$conn = self::getConnection(connectionName: $connectionName, schema: $schema);
|
||||||
|
if (method_exists(object_or_class: $conn, method: 'fetchAll')) {
|
||||||
|
return $conn->fetchAll($sql, $params);
|
||||||
|
}
|
||||||
|
throw new RuntimeException("O driver '{$connectionName}' não suporta fetchAll()");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function fetchOne(string $sql, array $params = [], string $connectionName = 'DEFAULT', ?string $schema = null): ?array {
|
||||||
|
$conn = self::getConnection(connectionName: $connectionName, schema: $schema);
|
||||||
|
if (method_exists(object_or_class: $conn, method: 'fetchOne')) {
|
||||||
|
return $conn->fetchOne($sql, $params);
|
||||||
|
}
|
||||||
|
throw new RuntimeException(message: "O driver '{$connectionName}' não suporta fetchOne()");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transações
|
||||||
|
public static function beginTransaction(string $connectionName = 'DEFAULT', ?string $schema = null): void {
|
||||||
|
$conn = self::getConnection(connectionName: $connectionName, schema: $schema);
|
||||||
|
if (method_exists(object_or_class: $conn, method: 'beginTransaction')) $conn->beginTransaction();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function commit(string $connectionName = 'DEFAULT', ?string $schema = null): void {
|
||||||
|
$conn = self::getConnection(connectionName: $connectionName, schema: $schema);
|
||||||
|
|
||||||
|
if ($conn instanceof PDO && $conn->inTransaction()) {
|
||||||
|
$conn->commit();
|
||||||
|
} elseif (method_exists(object_or_class: $conn, method: 'getPDO')) {
|
||||||
|
$pdo = $conn->getPDO();
|
||||||
|
if ($pdo instanceof PDO && $pdo->inTransaction()) {
|
||||||
|
$pdo->commit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function rollback(string $connectionName = 'DEFAULT', ?string $schema = null): void {
|
||||||
|
$conn = self::getConnection(connectionName: $connectionName, schema: $schema);
|
||||||
|
|
||||||
|
if ($conn instanceof PDO && $conn->inTransaction()) {
|
||||||
|
$conn->rollback();
|
||||||
|
} elseif (method_exists(object_or_class: $conn, method: 'getPDO')) {
|
||||||
|
$pdo = $conn->getPDO();
|
||||||
|
if ($pdo instanceof PDO && $pdo->inTransaction()) {
|
||||||
|
$pdo->rollback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function lastInsertId(string $connectionName = 'DEFAULT', ?string $schema = null): string {
|
||||||
|
$conn = self::getConnection(connectionName: $connectionName, schema: $schema);
|
||||||
|
if (method_exists(object_or_class: $conn, method: 'getPDO')) return $conn->getPDO()->lastInsertId();
|
||||||
|
throw new RuntimeException(message: "O driver '{$connectionName}' não suporta lastInsertId()");
|
||||||
|
}
|
||||||
|
}
|
||||||
32
vendor/claudecio/axiumphp/src/Core/Database/Drivers/MySQL.php
vendored
Executable file
32
vendor/claudecio/axiumphp/src/Core/Database/Drivers/MySQL.php
vendored
Executable file
@@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
namespace AxiumPHP\Core\Database\Drivers;
|
||||||
|
|
||||||
|
use PDO;
|
||||||
|
use PDOException;
|
||||||
|
use RuntimeException;
|
||||||
|
|
||||||
|
class MySQL extends PDOAbstract {
|
||||||
|
protected function connect(string $envName): void {
|
||||||
|
$host = $_ENV["{$envName}_DATABASE_HOST"];
|
||||||
|
$port = $_ENV["{$envName}_DATABASE_PORT"] ?? 3306;
|
||||||
|
$dbname = $_ENV["{$envName}_DATABASE_NAME"];
|
||||||
|
$user = $_ENV["{$envName}_DATABASE_USERNAME"];
|
||||||
|
$password = $_ENV["{$envName}_DATABASE_PASSWORD"];
|
||||||
|
$charset = $_ENV["{$envName}_DATABASE_CHARSET"] ?? 'utf8mb4';
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->connection = new PDO(
|
||||||
|
dsn: "mysql:host={$host};port={$port};dbname={$dbname};charset={$charset}",
|
||||||
|
username: $user,
|
||||||
|
password: $password,
|
||||||
|
options: [
|
||||||
|
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||||
|
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||||
|
PDO::ATTR_PERSISTENT => true,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new RuntimeException(message: "Erro ao conectar no MySQL: {$e->getMessage()}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
55
vendor/claudecio/axiumphp/src/Core/Database/Drivers/PDOAbstract.php
vendored
Executable file
55
vendor/claudecio/axiumphp/src/Core/Database/Drivers/PDOAbstract.php
vendored
Executable file
@@ -0,0 +1,55 @@
|
|||||||
|
<?php
|
||||||
|
namespace AxiumPHP\Core\Database\Drivers;
|
||||||
|
|
||||||
|
use PDO;
|
||||||
|
use RuntimeException;
|
||||||
|
|
||||||
|
abstract class PDOAbstract {
|
||||||
|
protected PDO $connection;
|
||||||
|
|
||||||
|
public function __construct(string $envName) {
|
||||||
|
$this->connect(envName: $envName);
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract protected function connect(string $envName): void;
|
||||||
|
|
||||||
|
public function execute(string $sql, array $params = []): bool {
|
||||||
|
$stmt = $this->connection->prepare(query: $sql);
|
||||||
|
return $stmt->execute(params: $params);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function fetchAll(string $sql, array $params = []): array {
|
||||||
|
$stmt = $this->connection->prepare(query: $sql);
|
||||||
|
$stmt->execute(params: $params);
|
||||||
|
return $stmt->fetchAll(mode: PDO::FETCH_ASSOC);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function fetchOne(string $sql, array $params = []): ?array {
|
||||||
|
$stmt = $this->connection->prepare(query: $sql);
|
||||||
|
$stmt->execute(params: $params);
|
||||||
|
$result = $stmt->fetch(mode: PDO::FETCH_ASSOC);
|
||||||
|
return $result ?: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function beginTransaction(): void {
|
||||||
|
if (!$this->connection->inTransaction()) {
|
||||||
|
$this->connection->beginTransaction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function commit(): void {
|
||||||
|
if ($this->connection->inTransaction()) {
|
||||||
|
$this->connection->commit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function rollback(): void {
|
||||||
|
if ($this->connection->inTransaction()) {
|
||||||
|
$this->connection->rollBack();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPDO(): PDO {
|
||||||
|
return $this->connection;
|
||||||
|
}
|
||||||
|
}
|
||||||
55
vendor/claudecio/axiumphp/src/Core/Database/Drivers/Postgres.php
vendored
Executable file
55
vendor/claudecio/axiumphp/src/Core/Database/Drivers/Postgres.php
vendored
Executable file
@@ -0,0 +1,55 @@
|
|||||||
|
<?php
|
||||||
|
namespace AxiumPHP\Core\Database\Drivers;
|
||||||
|
|
||||||
|
use PDO;
|
||||||
|
use PDOException;
|
||||||
|
use RuntimeException;
|
||||||
|
|
||||||
|
class Postgres extends PDOAbstract {
|
||||||
|
|
||||||
|
public function __construct(string $envName, ?string $schema = null) {
|
||||||
|
$this->connect(envName: $envName, schema: $schema);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function connect(string $envName, ?string $schema = null): void {
|
||||||
|
$host = $_ENV["{$envName}_DATABASE_HOST"];
|
||||||
|
$port = $_ENV["{$envName}_DATABASE_PORT"] ?? 5432;
|
||||||
|
$dbname = $_ENV["{$envName}_DATABASE_NAME"];
|
||||||
|
$user = $_ENV["{$envName}_DATABASE_USERNAME"];
|
||||||
|
$pass = $_ENV["{$envName}_DATABASE_PASSWORD"];
|
||||||
|
$schema = $schema ?? ($_ENV["{$envName}_DATABASE_SCHEMA"] ?? 'public');
|
||||||
|
$systemTimeZone = $_ENV["SYSTEM_TIMEZONE"] ?? 'UTC';
|
||||||
|
$sslMode = $_ENV["{$envName}_DATABASE_SSLMODE"] ?? 'disable'; // disable, require, verify-ca, verify-full
|
||||||
|
|
||||||
|
$dsn = "pgsql:host={$host};port={$port};dbname={$dbname};sslmode={$sslMode}";
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->connection = new PDO(
|
||||||
|
dsn: $dsn,
|
||||||
|
username: $user,
|
||||||
|
password: $pass,
|
||||||
|
options: [
|
||||||
|
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||||
|
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||||
|
PDO::ATTR_PERSISTENT => false
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Define o fuso horário da sessão
|
||||||
|
$this->connection->exec(statement: "SET TIME ZONE '{$systemTimeZone}'");
|
||||||
|
|
||||||
|
// Define o schema padrão
|
||||||
|
$this->connection->exec(statement: "SET search_path TO {$schema}, public");
|
||||||
|
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new RuntimeException(
|
||||||
|
message: "Erro ao conectar no PostgreSQL: {$e->getMessage()}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Método auxiliar para alterar schema dinamicamente
|
||||||
|
public function setSchema(string $schema): void {
|
||||||
|
$this->connection->exec(statement: "SET search_path TO {$schema}, public");
|
||||||
|
}
|
||||||
|
}
|
||||||
118
vendor/claudecio/axiumphp/src/Core/LoggerService.php
vendored
Executable file
118
vendor/claudecio/axiumphp/src/Core/LoggerService.php
vendored
Executable file
@@ -0,0 +1,118 @@
|
|||||||
|
<?php
|
||||||
|
namespace AxiumPHP\Core;
|
||||||
|
|
||||||
|
use DateTime;
|
||||||
|
use Exception;
|
||||||
|
use AxiumPHP\Core\Database;
|
||||||
|
|
||||||
|
class LoggerService {
|
||||||
|
public const DRIVER_FILE = 'FILE';
|
||||||
|
public const DRIVER_DATABASE = 'DATABASE';
|
||||||
|
|
||||||
|
private static string $logDir;
|
||||||
|
private static bool $initialized = false;
|
||||||
|
private static string $driver = self::DRIVER_FILE;
|
||||||
|
private static string $connectionName = 'DEFAULT'; // conexão do Database
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inicializa o Logger
|
||||||
|
*/
|
||||||
|
public static function init(string $driver = self::DRIVER_FILE,?string $logDir = null,string $dbConnectionName = 'DEFAULT'): void {
|
||||||
|
self::$driver = strtoupper(string: $driver);
|
||||||
|
self::$connectionName = $dbConnectionName;
|
||||||
|
|
||||||
|
if (!defined(constant_name: 'STORAGE_FOLDER_PATH')) {
|
||||||
|
throw new Exception(message: "Constante 'STORAGE_FOLDER_PATH' não foi definida.");
|
||||||
|
}
|
||||||
|
|
||||||
|
self::$logDir = $logDir ?? STORAGE_FOLDER_PATH . '/logs';
|
||||||
|
|
||||||
|
if (self::$driver === self::DRIVER_DATABASE) {
|
||||||
|
// Garante que a conexão exista
|
||||||
|
Database::getConnection(connectionName: self::$connectionName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self::$driver === self::DRIVER_FILE && !is_dir(filename: self::$logDir)) {
|
||||||
|
if (!mkdir(directory: self::$logDir, permissions: 0775, recursive: true) && !is_dir(filename: self::$logDir)) {
|
||||||
|
throw new Exception(message: "Não foi possível criar o diretório de logs: " . self::$logDir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self::$initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log genérico
|
||||||
|
*/
|
||||||
|
public static function log(string $message, string $level = 'INFO', array $context = []): void {
|
||||||
|
if (!self::$initialized) {
|
||||||
|
throw new Exception(message: "LoggerService não foi inicializado. Chame LoggerService::init() antes.");
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (self::$driver) {
|
||||||
|
case self::DRIVER_FILE:
|
||||||
|
self::logToFile(message: $message, level: $level);
|
||||||
|
break;
|
||||||
|
case self::DRIVER_DATABASE:
|
||||||
|
self::logToDatabase(message: $message, level: $level, context: $context);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Métodos auxiliares por nível
|
||||||
|
public static function info(string $message, array $context = []): void {
|
||||||
|
self::log(message: $message, level: 'INFO', context: $context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function warning(string $message, array $context = []): void {
|
||||||
|
self::log(message: $message, level: 'WARNING', context: $context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function error(string $message, array $context = []): void {
|
||||||
|
self::log(message: $message, level: 'ERROR', context: $context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function debug(string $message, array $context = []): void {
|
||||||
|
self::log(message: $message, level: 'DEBUG', context: $context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log para arquivo
|
||||||
|
*/
|
||||||
|
private static function logToFile(string $message, string $level): void {
|
||||||
|
$date = (new DateTime())->format(format: 'Y-m-d');
|
||||||
|
$now = (new DateTime())->format(format: 'Y-m-d H:i:s');
|
||||||
|
$filename = self::$logDir . "/app-{$date}.log";
|
||||||
|
$logMessage = "[$now][$level] $message" . PHP_EOL;
|
||||||
|
file_put_contents(filename: $filename, data: $logMessage, flags: FILE_APPEND);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log para database usando o Database multi-driver
|
||||||
|
*/
|
||||||
|
private static function logToDatabase(string $message, string $level, array $context = []): void {
|
||||||
|
$sql =
|
||||||
|
"INSERT INTO logs (
|
||||||
|
level,
|
||||||
|
message,
|
||||||
|
context,
|
||||||
|
created_at
|
||||||
|
) VALUES (
|
||||||
|
:level,
|
||||||
|
:message,
|
||||||
|
:context,
|
||||||
|
:created_at
|
||||||
|
)";
|
||||||
|
|
||||||
|
Database::execute(
|
||||||
|
sql: $sql,
|
||||||
|
params: [
|
||||||
|
'level' => $level,
|
||||||
|
'message' => $message,
|
||||||
|
'context' => json_encode(value: $context),
|
||||||
|
'created_at' => (new DateTime())->format(format: 'Y-m-d H:i:s')
|
||||||
|
],
|
||||||
|
connectionName: self::$connectionName
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
267
vendor/claudecio/axiumphp/src/Core/Module/Loader.php
vendored
Executable file
267
vendor/claudecio/axiumphp/src/Core/Module/Loader.php
vendored
Executable file
@@ -0,0 +1,267 @@
|
|||||||
|
<?php
|
||||||
|
namespace AxiumPHP\Core\Module;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
class Loader {
|
||||||
|
private $configFilePath;
|
||||||
|
private $configData;
|
||||||
|
private $startedModules = [];
|
||||||
|
private static array $loadedShortcuts = [];
|
||||||
|
private static array $loadedModules = [];
|
||||||
|
private array $requiredConstants = [
|
||||||
|
'MODULE_PATH',
|
||||||
|
];
|
||||||
|
private $modulePath = MODULE_PATH;
|
||||||
|
private $iniSystemPath = INI_SYSTEM_PATH;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construtor da classe.
|
||||||
|
*
|
||||||
|
* Inicializa o objeto com o caminho do arquivo de configuração e carrega as configurações.
|
||||||
|
*
|
||||||
|
* @param string $configFileName O caminho do arquivo de configuração (opcional).
|
||||||
|
*/
|
||||||
|
public function __construct(?string $configFileName = "system-ini.json") {
|
||||||
|
$this->configFilePath = "{$this->iniSystemPath}/{$configFileName}";
|
||||||
|
$this->loadConfig();
|
||||||
|
// Verificar as constantes no momento da criação da instância
|
||||||
|
$this->checkRequiredConstants();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifica se todas as constantes necessárias estão definidas.
|
||||||
|
*
|
||||||
|
* @throws Exception Se alguma constante necessária não estiver definida.
|
||||||
|
*/
|
||||||
|
private function checkRequiredConstants(): void {
|
||||||
|
foreach ($this->requiredConstants as $constant) {
|
||||||
|
if (!defined(constant_name: $constant)) {
|
||||||
|
throw new Exception(message: "Constante '{$constant}' não definida.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Carrega as configurações do arquivo JSON.
|
||||||
|
*
|
||||||
|
* Este método lê o conteúdo do arquivo de configuração especificado em
|
||||||
|
* `$this->configFilePath`, decodifica-o de JSON e armazena os dados
|
||||||
|
* no atributo `$this->configData`.
|
||||||
|
*
|
||||||
|
* @throws Exception Se o arquivo de configuração não for encontrado.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function loadConfig():void {
|
||||||
|
$configPath = $this->configFilePath;
|
||||||
|
|
||||||
|
// Verifica se o arquivo de configuração existe
|
||||||
|
if (file_exists(filename: $configPath)) {
|
||||||
|
$jsonContent = file_get_contents(filename: $configPath);
|
||||||
|
$this->configData = json_decode(json: $jsonContent, associative: true);
|
||||||
|
} else {
|
||||||
|
throw new Exception(message: "Arquivo de inicialização não encontrado: {$configPath}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Carrega os módulos do núcleo do sistema definidos na configuração.
|
||||||
|
*
|
||||||
|
* Este método carrega os módulos listados na seção "Core" do arquivo
|
||||||
|
* de configuração, utilizando o método `startModule`.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function loadCoreModules():void {
|
||||||
|
$this->startModule(modules: $this->configData["Modules"]["Core"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Carrega e inicializa todos os módulos ativos da aplicação, conforme
|
||||||
|
* definido na configuração.
|
||||||
|
*
|
||||||
|
* Este método acessa a propriedade `$configData`, especificamente a seção
|
||||||
|
* ["Modules"]["active"], que se espera ser um array contendo os
|
||||||
|
* identificadores (nomes ou objetos) dos módulos que devem ser carregados
|
||||||
|
* e inicializados. Em seguida, chama o método `startModule`, passando este
|
||||||
|
* array de módulos ativos para iniciar o processo de carregamento e
|
||||||
|
* inicialização de cada um deles.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*
|
||||||
|
* @see startModule()
|
||||||
|
* @property array $configData Propriedade da classe que contém os dados de
|
||||||
|
* configuração da aplicação. Espera-se que possua uma chave "Modules" com
|
||||||
|
* uma sub-chave "active" contendo um array de módulos.
|
||||||
|
*/
|
||||||
|
public function loadActiveModules():void {
|
||||||
|
$this->startModule(modules: $this->configData["Modules"]["Active"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Carrega e inicializa um único módulo da aplicação.
|
||||||
|
*
|
||||||
|
* Este método recebe um identificador de módulo (que pode ser uma string
|
||||||
|
* com o nome do módulo ou um objeto representando o módulo) e o passa para
|
||||||
|
* o método `startModule` para iniciar o processo de carregamento e
|
||||||
|
* inicialização desse módulo específico.
|
||||||
|
*
|
||||||
|
* @param mixed $module O identificador do módulo a ser carregado. Pode ser
|
||||||
|
* uma string contendo o nome do módulo ou um objeto de módulo já instanciado.
|
||||||
|
* O tipo exato depende da implementação do sistema de módulos.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*
|
||||||
|
* @see startModule()
|
||||||
|
*/
|
||||||
|
public function loadModule(mixed $module) {
|
||||||
|
$this->startModule(modules: [$module]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retorna os atalhos carregados para um slug de módulo específico.
|
||||||
|
*
|
||||||
|
* Este método estático permite acessar atalhos que foram previamente carregados
|
||||||
|
* e armazenados na propriedade estática `self::$loadedShortcuts`. Ele espera
|
||||||
|
* o slug (identificador amigável) de um módulo como entrada e retorna o array
|
||||||
|
* de atalhos associado a ele.
|
||||||
|
*
|
||||||
|
* @param string $moduleSlug O slug do módulo para o qual os atalhos devem ser retornados.
|
||||||
|
* @return array|null Um array contendo os atalhos para o módulo especificado, ou `null`
|
||||||
|
* se nenhum atalho tiver sido carregado para aquele slug.
|
||||||
|
*/
|
||||||
|
public static function getShortcuts(string $moduleSlug): ?array {
|
||||||
|
if(isset(self::$loadedShortcuts[$moduleSlug]['shortcuts'])) {
|
||||||
|
return self::$loadedShortcuts[$moduleSlug]['shortcuts'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retorna os slugs dos módulos que foram carregados.
|
||||||
|
*
|
||||||
|
* Este método estático fornece acesso à lista de identificadores (slugs)
|
||||||
|
* de todos os módulos que foram previamente carregados e armazenados na
|
||||||
|
* propriedade estática `self::$loadedModules`. É útil para verificar
|
||||||
|
* quais módulos estão ativos ou disponíveis no contexto atual da aplicação.
|
||||||
|
*
|
||||||
|
* @return array|null Um array contendo os slugs dos módulos carregados, ou `null`
|
||||||
|
* se nenhum módulo tiver sido carregado ou se a propriedade estiver vazia.
|
||||||
|
*/
|
||||||
|
public static function getSlugLoadedModules(): ?array {
|
||||||
|
return self::$loadedModules;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Busca o nome real de uma subpasta dentro de um diretório base,
|
||||||
|
* ignorando a diferença entre maiúsculas e minúsculas.
|
||||||
|
*
|
||||||
|
* Este método privado recebe um `$basePath` (o diretório onde procurar)
|
||||||
|
* e um `$targetName` (o nome da pasta desejada). Primeiro, verifica se o
|
||||||
|
* `$basePath` é um diretório válido. Se não for, retorna null. Em seguida,
|
||||||
|
* lê todos os arquivos e pastas dentro do `$basePath`. Para cada entrada,
|
||||||
|
* compara o nome da entrada com o `$targetName` de forma case-insensitive.
|
||||||
|
* Se encontrar uma entrada que corresponda ao `$targetName` (ignorando o case)
|
||||||
|
* e que seja um diretório, retorna o nome da entrada com o seu case real.
|
||||||
|
* Se após verificar todas as entradas nenhuma pasta correspondente for
|
||||||
|
* encontrada, retorna null.
|
||||||
|
*
|
||||||
|
* @param string $basePath O caminho para o diretório base onde a subpasta será procurada.
|
||||||
|
* @param string $targetName O nome da subpasta a ser encontrada (a comparação é case-insensitive).
|
||||||
|
* @return string|null O nome real da pasta (com o case correto) se encontrada, ou null caso contrário.
|
||||||
|
*/
|
||||||
|
private function getRealFolderName(string $basePath, string $targetName): ?string {
|
||||||
|
if (!is_dir(filename: $basePath)) return null;
|
||||||
|
|
||||||
|
$entries = scandir(directory: $basePath);
|
||||||
|
foreach ($entries as $entry) {
|
||||||
|
if (strcasecmp(string1: $entry, string2: $targetName) === 0 && is_dir(filename: "{$basePath}/{$entry}")) {
|
||||||
|
return $entry; // Nome com o case real
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inicia os módulos especificados.
|
||||||
|
*
|
||||||
|
* Este método itera sobre a lista de módulos fornecida, carrega seus manifestos,
|
||||||
|
* verifica a compatibilidade da versão, carrega as dependências e inclui o
|
||||||
|
* arquivo de bootstrap do módulo.
|
||||||
|
*
|
||||||
|
* @param array $modules Um array de strings representando os módulos a serem iniciados.
|
||||||
|
* Cada string deve estar no formato "nome_do_modulo@versao".
|
||||||
|
*
|
||||||
|
* @throws Exception Se o manifesto do módulo não for encontrado, se houver um erro ao decodificar
|
||||||
|
* o manifesto, se a versão do módulo for incompatível ou se o bootstrap
|
||||||
|
* do módulo não for encontrado.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function startModule(array $modules): void {
|
||||||
|
foreach ($modules as $module) {
|
||||||
|
// Identifica o módulo requisitado e sua versão
|
||||||
|
[$moduleName, $version] = explode(separator: '@', string: $module);
|
||||||
|
|
||||||
|
// Pega o nome real da pasta do módulo
|
||||||
|
$realModuleFolder = $this->getRealFolderName(basePath: $this->modulePath, targetName: $moduleName);
|
||||||
|
if (!$realModuleFolder) {
|
||||||
|
throw new Exception(message: "Pasta do módulo '{$moduleName}' não encontrada.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Caminho do manifesto usando o nome real
|
||||||
|
$manifestPath = "{$this->modulePath}/{$realModuleFolder}/manifest.json";
|
||||||
|
if (!file_exists(filename: $manifestPath)) {
|
||||||
|
throw new Exception(message: "Manifesto do módulo '{$moduleName}' não encontrado.");
|
||||||
|
}
|
||||||
|
|
||||||
|
$moduleManifest = json_decode(json: file_get_contents(filename: $manifestPath), associative: true);
|
||||||
|
if (!$moduleManifest) {
|
||||||
|
throw new Exception(message: "Erro ao decodificar o manifesto do módulo '{$moduleName}'.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Evita carregar o mesmo módulo mais de uma vez
|
||||||
|
if (in_array(needle: $moduleManifest["uuid"], haystack: $this->startedModules)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verifica se a versão é compatível
|
||||||
|
if ($moduleManifest['version'] !== $version) {
|
||||||
|
throw new Exception(message: "Versão do módulo '{$moduleName}' é incompatível. Versão requerida: {$version}. Versão instalada: {$moduleManifest['version']}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inicia Bootstrap do Módulo
|
||||||
|
$bootstrapModuleFile = "{$this->modulePath}/{$realModuleFolder}/bootstrap.php";
|
||||||
|
if (file_exists(filename: $bootstrapModuleFile)) {
|
||||||
|
require_once $bootstrapModuleFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Procura a pasta Routes com o case correto
|
||||||
|
$realRoutesFolder = $this->getRealFolderName(basePath: "{$this->modulePath}/{$realModuleFolder}", targetName: 'Routes');
|
||||||
|
if ($realRoutesFolder) {
|
||||||
|
// Procura arquivo com atalhos de rotas
|
||||||
|
$shortcutsFile = "{$this->modulePath}/{$realModuleFolder}/{$realRoutesFolder}/shortcuts.json";
|
||||||
|
if (file_exists(filename: $shortcutsFile)) {
|
||||||
|
$shortcuts = json_decode(
|
||||||
|
json: file_get_contents(filename: $shortcutsFile),
|
||||||
|
associative: true
|
||||||
|
);
|
||||||
|
|
||||||
|
self::$loadedShortcuts[$moduleManifest["slug"]] = $shortcuts;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marca como carregado
|
||||||
|
$this->startedModules[] = $moduleManifest["uuid"];
|
||||||
|
self::$loadedModules[] = $moduleManifest["slug"];
|
||||||
|
|
||||||
|
// Carrega dependências, se existirem
|
||||||
|
if (!empty($moduleManifest['dependencies'])) {
|
||||||
|
$this->startModule(modules: $moduleManifest['dependencies']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
635
vendor/claudecio/axiumphp/src/Core/Router.php
vendored
Executable file
635
vendor/claudecio/axiumphp/src/Core/Router.php
vendored
Executable file
@@ -0,0 +1,635 @@
|
|||||||
|
<?php
|
||||||
|
namespace AxiumPHP\Core;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
class Router {
|
||||||
|
private static $routes = [];
|
||||||
|
private static $params = [];
|
||||||
|
private static $ROUTER_MODE = null;
|
||||||
|
private static $APP_SYS_MODE = null;
|
||||||
|
private static $ROUTER_ALLOWED_ORIGINS = ['*'];
|
||||||
|
private static $currentGroupPrefix = '';
|
||||||
|
private static $currentGroupMiddlewares = [];
|
||||||
|
private static array $requiredConstants = [
|
||||||
|
'ROUTER_MODE',
|
||||||
|
'APP_SYS_MODE'
|
||||||
|
];
|
||||||
|
private static array $allowedHttpRequests = [
|
||||||
|
'GET',
|
||||||
|
'POST',
|
||||||
|
'PUT',
|
||||||
|
'PATCH',
|
||||||
|
'DELETE',
|
||||||
|
'OPTIONS'
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construtor que vai garantir que as constantes necessárias estejam definidas antes de
|
||||||
|
* instanciar a view.
|
||||||
|
*/
|
||||||
|
public function __construct() {
|
||||||
|
// Verificar as constantes no momento da criação da instância
|
||||||
|
$this->checkRequiredConstant();
|
||||||
|
|
||||||
|
// Define constante
|
||||||
|
self::$ROUTER_MODE = strtoupper(string: ROUTER_MODE);
|
||||||
|
self::$APP_SYS_MODE = strtoupper(string: APP_SYS_MODE);
|
||||||
|
|
||||||
|
// Se o modo de roteamento for JSON, define as origens permitidas
|
||||||
|
if(self::$ROUTER_MODE === 'JSON') {
|
||||||
|
self::$ROUTER_ALLOWED_ORIGINS = ROUTER_ALLOWED_ORIGINS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retorna o modo de roteamento atual.
|
||||||
|
*
|
||||||
|
* Este método estático simplesmente retorna o valor da propriedade estática `self::$ROUTER_MODE`.
|
||||||
|
* Ele é usado para obter o modo de operação do roteador, que pode indicar se está
|
||||||
|
* em modo de desenvolvimento, produção ou outro modo configurado.
|
||||||
|
*
|
||||||
|
* @return string O modo de roteamento como uma string.
|
||||||
|
*/
|
||||||
|
public static function getMode(): string {
|
||||||
|
return (string) self::$ROUTER_MODE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifica se todas as constantes necessárias estão definidas.
|
||||||
|
*
|
||||||
|
* @throws Exception Se alguma constante necessária não estiver definida.
|
||||||
|
*/
|
||||||
|
private function checkRequiredConstant(): void {
|
||||||
|
foreach (self::$requiredConstants as $constant) {
|
||||||
|
if (!defined(constant_name: $constant)) {
|
||||||
|
http_response_code(response_code: 500);
|
||||||
|
header(header: "Content-Type: application/json; charset=utf-8");
|
||||||
|
echo json_encode(value: [
|
||||||
|
"status" => 'error',
|
||||||
|
"message" => "Constante '{$constant}' não definida.",
|
||||||
|
]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adiciona uma rota com método GET à lista de rotas da aplicação.
|
||||||
|
*
|
||||||
|
* Este método é um atalho para adicionar rotas com o método HTTP GET. Ele
|
||||||
|
* chama o método `addRoute` internamente, passando os parâmetros
|
||||||
|
* fornecidos e o método 'GET'.
|
||||||
|
*
|
||||||
|
* @param string $uri O caminho da rota (ex: '/usuarios', '/produtos').
|
||||||
|
* @param array $handler Um array contendo o nome do controlador e o nome da ação
|
||||||
|
* que devem ser executados quando a rota for
|
||||||
|
* corresponder (ex: ['UsuarioController', 'index']).
|
||||||
|
* @param array $middlewares Um array opcional contendo os nomes dos middlewares que
|
||||||
|
* devem ser executados antes do handler da rota.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function GET(string $uri, array $handler, array $middlewares = []): void {
|
||||||
|
self::addRoute(method: "GET", uri: $uri, handler: $handler, middlewares: $middlewares);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adiciona uma rota com método POST à lista de rotas da aplicação.
|
||||||
|
*
|
||||||
|
* Este método é um atalho para adicionar rotas com o método HTTP POST. Ele
|
||||||
|
* chama o método `addRoute` internamente, passando os parâmetros
|
||||||
|
* fornecidos e o método 'POST'.
|
||||||
|
*
|
||||||
|
* @param string $uri O caminho da rota (ex: '/usuarios', '/produtos').
|
||||||
|
* @param array $handler Um array contendo o nome do controlador e o nome da ação
|
||||||
|
* que devem ser executados quando a rota for
|
||||||
|
* corresponder (ex: ['UsuarioController', 'salvar']).
|
||||||
|
* @param array $middlewares Um array opcional contendo os nomes dos middlewares que
|
||||||
|
* devem ser executados antes do handler da rota.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function POST(string $uri, array $handler, array $middlewares = []): void {
|
||||||
|
self::addRoute(method: "POST", uri: $uri, handler: $handler, middlewares: $middlewares);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adiciona uma rota com método PUT à lista de rotas da aplicação.
|
||||||
|
*
|
||||||
|
* Este método é um atalho para adicionar rotas com o método HTTP PUT. Ele
|
||||||
|
* chama o método `addRoute` internamente, passando os parâmetros
|
||||||
|
* fornecidos e o método 'PUT'.
|
||||||
|
*
|
||||||
|
* @param string $uri O caminho da rota (ex: '/usuarios', '/produtos').
|
||||||
|
* @param array $handler Um array contendo o nome do controlador e o nome da ação
|
||||||
|
* que devem ser executados quando a rota for
|
||||||
|
* corresponder (ex: ['UsuarioController', 'salvar']).
|
||||||
|
* @param array $middlewares Um array opcional contendo os nomes dos middlewares que
|
||||||
|
* devem ser executados antes do handler da rota.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function PUT(string $uri, array $handler, array $middlewares = []): void {
|
||||||
|
self::addRoute(method: "PUT", uri: $uri, handler: $handler, middlewares: $middlewares);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adiciona uma nova rota que responde a requisições PATCH.
|
||||||
|
*
|
||||||
|
* Este é um método estático de conveniência que simplifica o registro de rotas
|
||||||
|
* para o método HTTP PATCH. Ele delega a tarefa principal para o método
|
||||||
|
* privado `addRoute`, passando o método HTTP, a URI, a função de manipulação
|
||||||
|
* (`handler`) e quaisquer middlewares associados.
|
||||||
|
*
|
||||||
|
* @param string $uri A URI da rota (ex: '/api/resource/{id}').
|
||||||
|
* @param array $handler Um array de dois elementos que define o manipulador da rota: o nome da classe do controlador e o nome do método a ser executado.
|
||||||
|
* @param array $middlewares Um array opcional de middlewares a serem executados antes do manipulador da rota.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function PATCH(string $uri, array $handler, array $middlewares = []): void {
|
||||||
|
self::addRoute(method: "PATCH", uri: $uri, handler: $handler, middlewares: $middlewares);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adiciona uma rota com método DELETE à lista de rotas da aplicação.
|
||||||
|
*
|
||||||
|
* Este método é um atalho para adicionar rotas com o método HTTP DELETE. Ele
|
||||||
|
* chama o método `addRoute` internamente, passando os parâmetros
|
||||||
|
* fornecidos e o método 'DELETE'.
|
||||||
|
*
|
||||||
|
* @param string $uri O caminho da rota (ex: '/usuarios', '/produtos').
|
||||||
|
* @param array $handler Um array contendo o nome do controlador e o nome da ação
|
||||||
|
* que devem ser executados quando a rota for
|
||||||
|
* corresponder (ex: ['UsuarioController', 'salvar']).
|
||||||
|
* @param array $middlewares Um array opcional contendo os nomes dos middlewares que
|
||||||
|
* devem ser executados antes do handler da rota.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function DELETE(string $uri, array $handler, array $middlewares = []): void {
|
||||||
|
self::addRoute(method: "DELETE", uri: $uri, handler: $handler, middlewares: $middlewares);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adiciona uma rota à lista de rotas da aplicação.
|
||||||
|
*
|
||||||
|
* Este método estático armazena informações sobre uma rota (método HTTP,
|
||||||
|
* caminho, controlador, ação e middlewares) em um array interno `$routes`
|
||||||
|
* para posterior processamento pelo roteador.
|
||||||
|
*
|
||||||
|
* @param string $method O método HTTP da rota (ex: 'GET', 'POST', 'PUT', 'DELETE').
|
||||||
|
* @param string $uri O caminho da rota (ex: '/usuarios', '/produtos/:id').
|
||||||
|
* @param array $handler Um array contendo o nome do controlador e o nome da ação
|
||||||
|
* que devem ser executados quando a rota for
|
||||||
|
* corresponder (ex: ['UsuarioController', 'index']).
|
||||||
|
* @param array $middlewares Um array opcional contendo os nomes dos middlewares que
|
||||||
|
* devem ser executados antes do handler da rota.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private static function addRoute(string $method, string $uri, array $handler, array $middlewares = []): void {
|
||||||
|
self::$routes[] = [
|
||||||
|
'method' => strtoupper(string: $method),
|
||||||
|
'path' => '/' . trim(string: self::$currentGroupPrefix . '/' . trim(string: $uri, characters: '/'), characters: '/'),
|
||||||
|
'controller' => $handler[0],
|
||||||
|
'action' => $handler[1],
|
||||||
|
'middlewares' => array_merge(self::$currentGroupMiddlewares, $middlewares)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifica se um caminho de rota corresponde a um caminho de requisição.
|
||||||
|
*
|
||||||
|
* Este método estático compara um caminho de rota definido (ex: '/usuarios/:id')
|
||||||
|
* com um caminho de requisição (ex: '/usuarios/123'). Ele suporta parâmetros
|
||||||
|
* de rota definidos entre chaves (ex: ':id', ':nome'). Os parâmetros
|
||||||
|
* correspondentes do caminho de requisição são armazenados no array estático
|
||||||
|
* `$params` da classe.
|
||||||
|
*
|
||||||
|
* @param string $routePath O caminho da rota a ser comparado.
|
||||||
|
* @param string $requestPath O caminho da requisição a ser comparado.
|
||||||
|
*
|
||||||
|
* @return bool True se o caminho da requisição corresponder ao caminho da
|
||||||
|
* rota, false caso contrário.
|
||||||
|
*/
|
||||||
|
private static function matchPath($routePath, $requestPath): bool {
|
||||||
|
// Limpa os parâmetros antes de capturar novos
|
||||||
|
self::$params = []; // Certifica que a cada nova tentativa, a lista de parâmetros começa vazia
|
||||||
|
|
||||||
|
$routeParts = explode(separator: '/', string: trim(string: $routePath, characters: '/'));
|
||||||
|
$requestParts = explode(separator: '/', string: trim(string: $requestPath, characters: '/'));
|
||||||
|
|
||||||
|
if (count(value: $routeParts) !== count(value: $requestParts)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($routeParts as $i => $part) {
|
||||||
|
if (preg_match(pattern: '/^{\w+}$/', subject: $part)) {
|
||||||
|
self::$params[] = $requestParts[$i];
|
||||||
|
} elseif ($part !== $requestParts[$i]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Agrupa rotas sob um prefixo e middlewares.
|
||||||
|
*
|
||||||
|
* Este método estático permite agrupar rotas que compartilham um prefixo de
|
||||||
|
* caminho e/ou middlewares. O prefixo e os middlewares definidos dentro do
|
||||||
|
* grupo serão aplicados a todas as rotas definidas dentro da função de
|
||||||
|
* callback.
|
||||||
|
*
|
||||||
|
* @param string $prefix O prefixo a ser adicionado aos caminhos das rotas
|
||||||
|
* dentro do grupo (ex: '/admin', '/api/v1').
|
||||||
|
* @param callable $callback Uma função anônima (callback) que define as rotas
|
||||||
|
* que pertencem a este grupo.
|
||||||
|
* @param array $middlewares Um array opcional contendo os middlewares que devem
|
||||||
|
* ser aplicados a todas as rotas dentro do
|
||||||
|
* grupo.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function group(string $prefix, callable $callback, array $middlewares = []): void {
|
||||||
|
$previousPrefix = self::$currentGroupPrefix ?? '';
|
||||||
|
$previousMiddlewares = self::$currentGroupMiddlewares ?? [];
|
||||||
|
|
||||||
|
self::$currentGroupPrefix = $previousPrefix . $prefix;
|
||||||
|
self::$currentGroupMiddlewares = array_merge($previousMiddlewares, $middlewares);
|
||||||
|
|
||||||
|
call_user_func(callback: $callback);
|
||||||
|
|
||||||
|
self::$currentGroupPrefix = $previousPrefix;
|
||||||
|
self::$currentGroupMiddlewares = $previousMiddlewares;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extrai os dados do corpo da requisição HTTP.
|
||||||
|
*
|
||||||
|
* Este método estático é responsável por analisar e retornar os dados enviados
|
||||||
|
* no corpo de uma requisição HTTP, especialmente para métodos como `PUT` e `DELETE`,
|
||||||
|
* onde os dados não são automaticamente populados em superglobais como `$_POST` ou `$_GET`.
|
||||||
|
*
|
||||||
|
* O fluxo de extração é o seguinte:
|
||||||
|
* 1. **Lê o Corpo da Requisição:** `file_get_contents('php://input')` lê o conteúdo bruto do corpo da requisição HTTP.
|
||||||
|
* 2. **Processamento Condicional:**
|
||||||
|
* * **Para métodos `PUT` ou `DELETE`:**
|
||||||
|
* * Verifica se o corpo da requisição (`$inputData`) não está vazio.
|
||||||
|
* * **Verifica o `Content-Type`:**
|
||||||
|
* * Se o `Content-Type` for `application/json`, tenta decodificar o corpo da requisição como JSON.
|
||||||
|
* * Se houver um erro na decodificação JSON, ele define o código de status HTTP para 500,
|
||||||
|
* envia uma resposta JSON com uma mensagem de erro e encerra a execução.
|
||||||
|
* * Se o `Content-Type` não for `application/json` (ou não estiver definido), tenta analisar o corpo da requisição
|
||||||
|
* como uma string de query URL (`parse_str`), o que é comum para `application/x-www-form-urlencoded`
|
||||||
|
* em requisições `PUT`/`DELETE`.
|
||||||
|
* * **Para outros métodos (e.g., `POST`, `GET`):** O método não tenta extrair dados do `php://input`.
|
||||||
|
* Nesses casos, presume-se que os dados já estariam disponíveis em `$_POST`, `$_GET`, etc.
|
||||||
|
* 3. **Limpeza Final:** Remove a chave `_method` dos dados, se presente. Essa chave é frequentemente
|
||||||
|
* usada em formulários HTML para simular métodos `PUT` ou `DELETE` através de um campo oculto.
|
||||||
|
*
|
||||||
|
* @param string $method O método HTTP da requisição (e.g., 'GET', 'POST', 'PUT', 'DELETE').
|
||||||
|
* @return array Um array associativo contendo os dados extraídos do corpo da requisição.
|
||||||
|
* Em caso de erro de decodificação JSON, a execução é encerrada com uma resposta de erro HTTP.
|
||||||
|
*/
|
||||||
|
private static function extractRequestData(string $method): array {
|
||||||
|
$inputData = file_get_contents(filename: 'php://input');
|
||||||
|
$data = [];
|
||||||
|
|
||||||
|
// Só processa se for PUT ou DELETE e tiver dados no corpo
|
||||||
|
if (in_array(needle: $method, haystack: ['PUT', 'DELETE', 'PATCH']) && !empty($inputData)) {
|
||||||
|
$contentType = $_SERVER['CONTENT_TYPE'] ?? '';
|
||||||
|
|
||||||
|
// Se for JSON
|
||||||
|
if (str_contains(haystack: $contentType, needle: 'application/json')) {
|
||||||
|
$data = json_decode(json: $inputData, associative: true);
|
||||||
|
|
||||||
|
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||||
|
http_response_code(response_code: 500);
|
||||||
|
header(header: "Content-Type: application/json; charset=utf-8");
|
||||||
|
$json_error = json_last_error_msg();
|
||||||
|
|
||||||
|
echo json_encode(value: [
|
||||||
|
"status" => 'error',
|
||||||
|
"message" => "Erro ao decodificar JSON: {$json_error}"
|
||||||
|
]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Se for outro tipo (ex: x-www-form-urlencoded)
|
||||||
|
else {
|
||||||
|
parse_str(string: $inputData, result: $data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove o _method se tiver
|
||||||
|
if(isset($data['_method'])) {
|
||||||
|
unset($data['_method']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executa os middlewares.
|
||||||
|
*
|
||||||
|
* Este método estático itera sobre um array de middlewares e executa cada um
|
||||||
|
* deles. Os middlewares podem ser especificados como 'Classe::metodo' ou
|
||||||
|
* 'Classe::metodo:argumento1:argumento2' para passar argumentos para o
|
||||||
|
* método do middleware. Se um middleware retornar `false`, a execução é
|
||||||
|
* interrompida e a função retorna `false`.
|
||||||
|
*
|
||||||
|
* @param array $middlewares Um array contendo os middlewares a serem executados.
|
||||||
|
*
|
||||||
|
* @return bool True se todos os middlewares forem executados com sucesso,
|
||||||
|
* false se algum middleware falhar.
|
||||||
|
* @throws Exception Se o formato do middleware for inválido ou se o método
|
||||||
|
* do middleware não existir.
|
||||||
|
*/
|
||||||
|
public static function runMiddlewares(array $middlewares): bool {
|
||||||
|
foreach ($middlewares as $middleware) {
|
||||||
|
if (strpos(haystack: $middleware, needle: '::') !== false) {
|
||||||
|
[$middlewareClass, $methodWithArgs] = explode(separator: '::', string: $middleware);
|
||||||
|
|
||||||
|
// Suporte a argumentos no middleware (ex: Middleware::Permission:ADMIN)
|
||||||
|
$methodParts = explode(separator: ':', string: $methodWithArgs);
|
||||||
|
$method = $methodParts[0];
|
||||||
|
$args = array_slice(array: $methodParts, offset: 1);
|
||||||
|
|
||||||
|
if (method_exists(object_or_class: $middlewareClass, method: $method)) {
|
||||||
|
$result = call_user_func_array(callback: [$middlewareClass, $method], args: $args);
|
||||||
|
|
||||||
|
// Se retornar false -> erro genérico
|
||||||
|
if ($result === false) {
|
||||||
|
http_response_code(response_code: 403);
|
||||||
|
header(header: 'Content-Type: application/json; charset=utf-8');
|
||||||
|
echo json_encode(value: [
|
||||||
|
"status" => 'error',
|
||||||
|
"message" => "Middleware {$middlewareClass}::{$method} bloqueou a requisição."
|
||||||
|
]);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Se retornar array com status 'success' -> mostra a mensagem
|
||||||
|
if (is_array(value: $result) && ($result['status'] ?? '') !== 'success') {
|
||||||
|
http_response_code(response_code: $result['response_code'] ?? 403);
|
||||||
|
header(header: 'Content-Type: application/json; charset=utf-8');
|
||||||
|
|
||||||
|
$response = [
|
||||||
|
"response_code" => $result['response_code'] ?? 403,
|
||||||
|
"status" => $result['status'] ?? 'error',
|
||||||
|
"message" => $result['message'] ?? 'Erro no middleware'
|
||||||
|
];
|
||||||
|
|
||||||
|
if(isset($result['output'])) {
|
||||||
|
$response['output'] = $result['output'];
|
||||||
|
}
|
||||||
|
|
||||||
|
echo json_encode(value: $response);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
http_response_code(response_code: 500);
|
||||||
|
header(header: 'Content-Type: application/json; charset=utf-8');
|
||||||
|
echo json_encode(value: [
|
||||||
|
"status" => 'error',
|
||||||
|
"message" => "Método '{$method}' não existe na classe '{$middlewareClass}'"
|
||||||
|
]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
http_response_code(response_code: 500);
|
||||||
|
header(header: 'Content-Type: application/json; charset=utf-8');
|
||||||
|
echo json_encode(value: [
|
||||||
|
"status" => 'error',
|
||||||
|
"message" => "Formato inválido do middleware: '{$middleware}'"
|
||||||
|
]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true; // todos os middlewares passaram
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifica se uma rota corresponde à requisição.
|
||||||
|
*
|
||||||
|
* Este método verifica se o método HTTP e o caminho da requisição correspondem
|
||||||
|
* aos da rota fornecida.
|
||||||
|
*
|
||||||
|
* @param string $method O método HTTP da requisição.
|
||||||
|
* @param string $uri O caminho da requisição.
|
||||||
|
* @param array $route Um array associativo contendo os dados da rota.
|
||||||
|
*
|
||||||
|
* @return bool Retorna true se a rota corresponder, false caso contrário.
|
||||||
|
*/
|
||||||
|
private static function matchRoute(string $method, string $uri, array $route) {
|
||||||
|
// Verifica se o método HTTP da rota corresponde ao da requisição
|
||||||
|
if ($route['method'] !== $method) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verifica se o caminho da requisição corresponde ao caminho da rota
|
||||||
|
return self::matchPath(routePath: $route['path'], requestPath: $uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepara os parâmetros para um método de requisição.
|
||||||
|
*
|
||||||
|
* Este método combina os parâmetros da rota, os parâmetros GET e, para
|
||||||
|
* requisições PUT ou DELETE, os parâmetros fornecidos, retornando um
|
||||||
|
* array de parâmetros preparados.
|
||||||
|
*
|
||||||
|
* @param string $method O método HTTP da requisição (GET, POST, PUT, DELETE).
|
||||||
|
* @param array|null $params Um array opcional de parâmetros adicionais.
|
||||||
|
*
|
||||||
|
* @return array Um array contendo os parâmetros preparados.
|
||||||
|
*/
|
||||||
|
private static function prepareMethodParameters(string $method, ?array $params = []): array {
|
||||||
|
// Guarda os parâmetros atuais (da rota)
|
||||||
|
$atuallParams = self::$params;
|
||||||
|
|
||||||
|
// Adiciona os dados de PUT/DELETE/PATCH como um array no final
|
||||||
|
if (in_array(needle: $method, haystack: ['PUT', 'DELETE', 'PATCH'])) {
|
||||||
|
self::$params = array_merge($atuallParams, $params);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normaliza os parâmetros para um array sequencial
|
||||||
|
$preparedParams = array_values(array: self::$params);
|
||||||
|
|
||||||
|
return $preparedParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Despacha a requisição para o controlador e ação correspondentes.
|
||||||
|
*
|
||||||
|
* Este método estático analisa a requisição (método HTTP e caminho), encontra
|
||||||
|
* a rota correspondente na lista de rotas definidas, executa os middlewares
|
||||||
|
* da rota (se houver), instancia o controlador e chama a ação (método)
|
||||||
|
* especificada na rota, passando os parâmetros da requisição (parâmetros da
|
||||||
|
* rota, parâmetros GET e dados de PUT/DELETE). Se nenhuma rota
|
||||||
|
* corresponder, um erro 404 é enviado.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function dispatch(): void {
|
||||||
|
$method = $_SERVER['REQUEST_METHOD'];
|
||||||
|
$uri = parse_url(url: $_SERVER['REQUEST_URI'], component: PHP_URL_PATH);
|
||||||
|
$uri = trim(string: rtrim(string: $uri, characters: '/'), characters: '/');
|
||||||
|
|
||||||
|
// Suporte ao _method em POST para PUT/DELETE/PATCH
|
||||||
|
if ($method === 'POST' && isset($_POST['_method'])) {
|
||||||
|
$method = strtoupper(string: $_POST['_method']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verifica se o método HTTP é permitido
|
||||||
|
if(!in_array(needle: $method, haystack: self::$allowedHttpRequests)) {
|
||||||
|
http_response_code(response_code: 405);
|
||||||
|
header(header: 'Content-Type: application/json; charset=utf-8');
|
||||||
|
echo json_encode(value: [
|
||||||
|
"status" => 'error',
|
||||||
|
"message" => "Método HTTP '{$method}' não permitido."
|
||||||
|
]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =============================
|
||||||
|
// HEADERS CORS PARA TODOS OS MÉTODOS
|
||||||
|
// =============================
|
||||||
|
if (self::$ROUTER_MODE === 'JSON') {
|
||||||
|
$origin = $_SERVER['HTTP_ORIGIN'] ?? '*';
|
||||||
|
|
||||||
|
if (in_array(needle: '*', haystack: self::$ROUTER_ALLOWED_ORIGINS)) {
|
||||||
|
header(header: "Access-Control-Allow-Origin: *");
|
||||||
|
} elseif (in_array(needle: $origin, haystack: self::$ROUTER_ALLOWED_ORIGINS)) {
|
||||||
|
header(header: "Access-Control-Allow-Origin: $origin");
|
||||||
|
} else {
|
||||||
|
if (self::$APP_SYS_MODE === 'DEV') {
|
||||||
|
header(header: "Access-Control-Allow-Origin: $origin");
|
||||||
|
} else {
|
||||||
|
http_response_code(response_code: 403);
|
||||||
|
header(header: 'Content-Type: application/json; charset=utf-8');
|
||||||
|
echo json_encode(value: [
|
||||||
|
"status" => 'error',
|
||||||
|
"message" => "Origem '{$origin}' não permitida pelo CORS."
|
||||||
|
]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
header(header: 'Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE, OPTIONS');
|
||||||
|
header(header: 'Access-Control-Allow-Headers: Content-Type, Authorization');
|
||||||
|
}
|
||||||
|
|
||||||
|
// =============================
|
||||||
|
// TRATAMENTO DO PRE-FLIGHT (OPTIONS)
|
||||||
|
// =============================
|
||||||
|
if ($method === 'OPTIONS') {
|
||||||
|
http_response_code(response_code: 204); // No Content
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =============================
|
||||||
|
// EXTRAI DADOS DE PUT, DELETE, PATCH
|
||||||
|
// =============================
|
||||||
|
$requestData = match($method) {
|
||||||
|
'GET' => null,
|
||||||
|
'POST' => null,
|
||||||
|
default => self::extractRequestData(method: $method)
|
||||||
|
};
|
||||||
|
|
||||||
|
// =============================
|
||||||
|
// LOOP PARA PROCESSAR ROTAS
|
||||||
|
// =============================
|
||||||
|
foreach (self::$routes as $route) {
|
||||||
|
if (self::matchRoute(method: $method, uri: $uri, route: $route)) {
|
||||||
|
// Executa middlewares
|
||||||
|
if (!empty($route['middlewares']) && !self::runMiddlewares(middlewares: $route['middlewares'])) {
|
||||||
|
return; // Middleware bloqueou
|
||||||
|
}
|
||||||
|
|
||||||
|
$controller = new $route['controller']();
|
||||||
|
$action = $route['action'];
|
||||||
|
$params = self::prepareMethodParameters(method: $method, params: [$requestData]);
|
||||||
|
|
||||||
|
switch (self::$ROUTER_MODE) {
|
||||||
|
case 'VIEW':
|
||||||
|
if (method_exists(object_or_class: $controller, method: $action)) {
|
||||||
|
http_response_code(response_code: 200);
|
||||||
|
call_user_func_array(callback: [$controller, $action], args: $params);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'JSON':
|
||||||
|
if (method_exists(object_or_class: $controller, method: $action)) {
|
||||||
|
http_response_code(response_code: 200);
|
||||||
|
header(header: 'Content-Type: application/json; charset=utf-8');
|
||||||
|
call_user_func_array(callback: [$controller, $action], args: $params);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// =============================
|
||||||
|
// ROTA NÃO ENCONTRADA
|
||||||
|
// =============================
|
||||||
|
self::pageNotFound();
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exibe a página de erro 404 (Página não encontrada).
|
||||||
|
*
|
||||||
|
* Este método estático define o código de resposta HTTP como 404 e renderiza
|
||||||
|
* a view "/Errors/404" para exibir a página de erro. Após a renderização,
|
||||||
|
* o script é encerrado.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private static function pageNotFound(): void {
|
||||||
|
switch (self::$ROUTER_MODE) {
|
||||||
|
case 'VIEW':
|
||||||
|
// Notifica erro em caso constante não definida
|
||||||
|
if(!defined(constant_name: 'ERROR_404_VIEW_PATH')) {
|
||||||
|
http_response_code(response_code: 500);
|
||||||
|
header(header: 'Content-Type: application/json; charset=utf-8');
|
||||||
|
echo json_encode( value: [
|
||||||
|
"status" => 'error',
|
||||||
|
"message" => "Constante 'ERROR_404_VIEW_PATH' não foi definida.",
|
||||||
|
]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Caso o arquivo da constante não exista, notifica erro
|
||||||
|
if(!file_exists(filename: ERROR_404_VIEW_PATH)) {
|
||||||
|
http_response_code(response_code: 500);
|
||||||
|
header(header: 'Content-Type: application/json; charset=utf-8');
|
||||||
|
echo json_encode( value: [
|
||||||
|
"status" => 'error',
|
||||||
|
"message" => "Arquivo da constante 'ERROR_404_VIEW_PATH' não foi encontrado.",
|
||||||
|
]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
http_response_code(response_code: 404);
|
||||||
|
require_once ERROR_404_VIEW_PATH;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'JSON':
|
||||||
|
http_response_code(response_code: 404);
|
||||||
|
header(header: 'Content-Type: application/json; charset=utf-8');
|
||||||
|
echo json_encode( value: [
|
||||||
|
"status" => 'error',
|
||||||
|
"message" => "Página não encontrada.",
|
||||||
|
]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
140
vendor/claudecio/axiumphp/src/Core/View.php
vendored
Executable file
140
vendor/claudecio/axiumphp/src/Core/View.php
vendored
Executable file
@@ -0,0 +1,140 @@
|
|||||||
|
<?php
|
||||||
|
namespace AxiumPHP\Core;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
class View {
|
||||||
|
private array $requiredConstants = [
|
||||||
|
'VIEW_PATH',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construtor que vai garantir que as constantes necessárias estejam definidas antes de
|
||||||
|
* instanciar a view.
|
||||||
|
*/
|
||||||
|
public function __construct() {
|
||||||
|
// Verificar as constantes no momento da criação da instância
|
||||||
|
$this->checkRequiredConstants();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifica se todas as constantes necessárias estão definidas.
|
||||||
|
*
|
||||||
|
* @throws Exception Se alguma constante necessária não estiver definida.
|
||||||
|
*/
|
||||||
|
private function checkRequiredConstants(): void {
|
||||||
|
foreach ($this->requiredConstants as $constant) {
|
||||||
|
if (!defined(constant_name: $constant)) {
|
||||||
|
throw new Exception(message: "Constante '{$constant}' não definida.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Busca o nome real de uma subpasta dentro de um diretório base,
|
||||||
|
* ignorando a diferença entre maiúsculas e minúsculas.
|
||||||
|
*
|
||||||
|
* Este método privado recebe um `$basePath` (o diretório onde procurar)
|
||||||
|
* e um `$targetName` (o nome da pasta desejada). Primeiro, verifica se o
|
||||||
|
* `$basePath` é um diretório válido. Se não for, retorna null. Em seguida,
|
||||||
|
* lê todos os arquivos e pastas dentro do `$basePath`. Para cada entrada,
|
||||||
|
* compara o nome da entrada com o `$targetName` de forma case-insensitive.
|
||||||
|
* Se encontrar uma entrada que corresponda ao `$targetName` (ignorando o case)
|
||||||
|
* e que seja um diretório, retorna o nome da entrada com o seu case real.
|
||||||
|
* Se após verificar todas as entradas nenhuma pasta correspondente for
|
||||||
|
* encontrada, retorna null.
|
||||||
|
*
|
||||||
|
* @param string $basePath O caminho para o diretório base onde a subpasta será procurada.
|
||||||
|
* @param string $targetName O nome da subpasta a ser encontrada (a comparação é case-insensitive).
|
||||||
|
* @return string|null O nome real da pasta (com o case correto) se encontrada, ou null caso contrário.
|
||||||
|
*/
|
||||||
|
private static function getRealFolderName(string $basePath, string $targetName): ?string {
|
||||||
|
if (!is_dir(filename: $basePath)) return null;
|
||||||
|
|
||||||
|
$entries = scandir(directory: $basePath);
|
||||||
|
foreach ($entries as $entry) {
|
||||||
|
if (strcasecmp(string1: $entry, string2: $targetName) === 0 && is_dir(filename: $basePath . '/' . $entry)) {
|
||||||
|
return $entry; // Nome com o case real
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renderiza uma view dentro de um layout, com suporte a módulos.
|
||||||
|
*
|
||||||
|
* Este método estático inclui o arquivo de view especificado, permitindo a
|
||||||
|
* passagem de dados para a view através do array `$data`. As variáveis
|
||||||
|
* do array `$data` são extraídas para uso dentro da view usando `extract()`.
|
||||||
|
* O método também permite especificar um layout e um módulo para a view.
|
||||||
|
*
|
||||||
|
* @param string $view O caminho para o arquivo de view, relativo ao diretório
|
||||||
|
* `views` ou `modules/{$module}/views` (ex:
|
||||||
|
* 'usuarios/listar', 'index').
|
||||||
|
* @param array $data Um array associativo contendo os dados a serem passados
|
||||||
|
* para a view. As chaves do array se tornarão variáveis
|
||||||
|
* disponíveis dentro da view.
|
||||||
|
* @param string $layout O caminho para o arquivo de layout, relativo ao
|
||||||
|
* diretório `views` (ex: 'layouts/main').
|
||||||
|
* @param string $module O nome do módulo ao qual a view pertence (opcional).
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function render(string $view, array $data = [], ?string $layout = null, ?string $module = null): void {
|
||||||
|
$viewPath = VIEW_PATH . "/{$view}.php";
|
||||||
|
|
||||||
|
// Se for módulo, resolve o nome da pasta do módulo e da pasta Views
|
||||||
|
if ($module) {
|
||||||
|
$realModule = self::getRealFolderName(basePath: MODULE_PATH, targetName: $module);
|
||||||
|
if (!$realModule) {
|
||||||
|
http_response_code(response_code: 404);
|
||||||
|
die("Módulo '{$module}' não encontrado.");
|
||||||
|
}
|
||||||
|
|
||||||
|
$realViews = self::getRealFolderName(basePath: MODULE_PATH . "/{$realModule}", targetName: 'Views');
|
||||||
|
if (!$realViews) {
|
||||||
|
http_response_code(response_code: 404);
|
||||||
|
die("Pasta 'Views' do módulo '{$module}' não encontrada.");
|
||||||
|
}
|
||||||
|
|
||||||
|
$moduleViewPath = MODULE_PATH . "/{$realModule}/{$realViews}/{$view}.php";
|
||||||
|
if (file_exists(filename: $moduleViewPath)) {
|
||||||
|
$viewPath = $moduleViewPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!file_exists(filename: $viewPath)) {
|
||||||
|
http_response_code(response_code: 404);
|
||||||
|
die("View '{$view}' não encontrada.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($data)) {
|
||||||
|
extract($data, EXTR_SKIP);
|
||||||
|
}
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
require_once $viewPath;
|
||||||
|
$content = ob_get_clean();
|
||||||
|
|
||||||
|
if ($layout) {
|
||||||
|
if ($module) {
|
||||||
|
// Mesmo esquema pra layout dentro de módulo
|
||||||
|
$realModule = self::getRealFolderName(basePath: MODULE_PATH, targetName: $module);
|
||||||
|
$realViews = self::getRealFolderName(basePath: MODULE_PATH . "/{$realModule}", targetName: 'Views');
|
||||||
|
$layoutPath = MODULE_PATH . "/{$realModule}/{$realViews}/{$layout}.php";
|
||||||
|
} else {
|
||||||
|
$layoutPath = VIEW_PATH . "/{$layout}.php";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file_exists(filename: $layoutPath)) {
|
||||||
|
require_once $layoutPath;
|
||||||
|
} else {
|
||||||
|
http_response_code(response_code: 404);
|
||||||
|
die("Layout '{$layout}' não encontrado.");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
echo $content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
108
vendor/claudecio/axiumphp/src/Helpers/NavigationHelper.php
vendored
Executable file
108
vendor/claudecio/axiumphp/src/Helpers/NavigationHelper.php
vendored
Executable file
@@ -0,0 +1,108 @@
|
|||||||
|
<?php
|
||||||
|
namespace AxiumPHP\Helpers;
|
||||||
|
|
||||||
|
class NavigationHelper {
|
||||||
|
private static $max_stack;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construtor da classe que define a profundidade máxima da pilha.
|
||||||
|
*
|
||||||
|
* Este método inicializa a classe e configura a propriedade estática `self::$max_stack`
|
||||||
|
* com o valor fornecido por `$max_stack`. Essa propriedade provavelmente
|
||||||
|
* controla o número máximo de itens ou operações que podem ser armazenados em uma pilha.
|
||||||
|
* O valor padrão é 5.
|
||||||
|
*
|
||||||
|
* @param int $max_stack O número máximo de itens permitidos na pilha. O valor padrão é 5.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct(int $max_stack = 5) {
|
||||||
|
self::$max_stack = $max_stack;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rastreia a navegação do usuário, mantendo um histórico das páginas visitadas
|
||||||
|
* na sessão.
|
||||||
|
*
|
||||||
|
* Este método estático obtém a URI atual da requisição. Se a URI corresponder
|
||||||
|
* a um padrão de API ou chamada AJAX, a função retorna imediatamente sem
|
||||||
|
* registrar a navegação.
|
||||||
|
*
|
||||||
|
* Se a variável de sessão 'navigation_stack' não existir, ela é inicializada
|
||||||
|
* como um array vazio.
|
||||||
|
*
|
||||||
|
* Para evitar duplicatas, verifica se a URI atual é diferente da última URI
|
||||||
|
* registrada na pilha de navegação. Se for diferente, e se a pilha atingir
|
||||||
|
* um tamanho máximo definido por `self::$max_stack`, a URI mais antiga é removida
|
||||||
|
* do início da pilha. A URI atual é então adicionada ao final da pilha.
|
||||||
|
*
|
||||||
|
* A URI atual também é armazenada na variável de sessão 'current_page', e a
|
||||||
|
* página anterior (obtida através do método `self::getPreviousPage()`) é
|
||||||
|
* armazenada em 'previous_page'.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*
|
||||||
|
* @see self::getPreviousPage()
|
||||||
|
*/
|
||||||
|
public static function trackNavigation(): void {
|
||||||
|
$currentUri = $_SERVER['REQUEST_URI'] ?? '/';
|
||||||
|
|
||||||
|
// Ignora chamadas para API ou AJAX
|
||||||
|
if (preg_match(pattern: '/\/api\/|\/ajax\//i', subject: $currentUri)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($_SESSION['navigation_stack'])) {
|
||||||
|
$_SESSION['navigation_stack'] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Evita duplicar a última página
|
||||||
|
$last = end($_SESSION['navigation_stack']);
|
||||||
|
if ($last !== $currentUri) {
|
||||||
|
if (count(value: $_SESSION['navigation_stack']) >= self::$max_stack) {
|
||||||
|
array_shift($_SESSION['navigation_stack']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$_SESSION['navigation_stack'][] = $currentUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
$_SESSION['current_page'] = $currentUri;
|
||||||
|
$_SESSION['previous_page'] = self::getPreviousPage();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extrai a string de query da URI da requisição.
|
||||||
|
*
|
||||||
|
* Este método estático obtém a URI completa da requisição do servidor (`$_SERVER['REQUEST_URI']`) e a divide em duas partes usando o caractere `?` como separador. A primeira parte é a URI base, e a segunda é a string de query (os parâmetros da URL).
|
||||||
|
*
|
||||||
|
* @return string|null A string de query completa (por exemplo, "param1=value1¶m2=value2"), ou `null` se a URI não contiver uma string de query.
|
||||||
|
*/
|
||||||
|
public static function extractQueryString(): ?string {
|
||||||
|
$parts = explode(separator: '?', string: $_SERVER['REQUEST_URI'], limit: 2); // limite 2 garante que só divide em duas partes
|
||||||
|
$request = $parts[1] ?? null; // se não existir, define null
|
||||||
|
|
||||||
|
return ($request !== null && $request !== '') ? "?{$request}" : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtém a URI da página anterior visitada pelo usuário, com base na pilha
|
||||||
|
* de navegação armazenada na sessão.
|
||||||
|
*
|
||||||
|
* Este método estático acessa a variável de sessão 'navigation_stack'. Se a
|
||||||
|
* pilha contiver mais de uma URI, retorna a penúltima URI da pilha, que
|
||||||
|
* representa a página visitada imediatamente antes da atual.
|
||||||
|
*
|
||||||
|
* Se a pilha contiver apenas uma ou nenhuma URI, significa que não há uma
|
||||||
|
* página anterior no histórico de navegação da sessão, e o método retorna null.
|
||||||
|
*
|
||||||
|
* @return string|null A URI da página anterior, ou null se não houver uma.
|
||||||
|
*/
|
||||||
|
public static function getPreviousPage(): ?string {
|
||||||
|
$stack = $_SESSION['navigation_stack'] ?? [];
|
||||||
|
|
||||||
|
if (count(value: $stack) > 1) {
|
||||||
|
return $stack[count(value: $stack) - 2];
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
270
vendor/claudecio/axiumphp/src/Helpers/RequestHelper.php
vendored
Executable file
270
vendor/claudecio/axiumphp/src/Helpers/RequestHelper.php
vendored
Executable file
@@ -0,0 +1,270 @@
|
|||||||
|
<?php
|
||||||
|
namespace AxiumPHP\Helpers;
|
||||||
|
|
||||||
|
class RequestHelper {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retorna os dados de entrada (GET, POST, COOKIE ou SERVER) filtrados.
|
||||||
|
*
|
||||||
|
* Permite passar filtros personalizados por campo. Se nenhum filtro for passado,
|
||||||
|
* usa o filtro padrão (`FILTER_DEFAULT`).
|
||||||
|
*
|
||||||
|
* @param int $form_type Tipo de entrada (INPUT_GET, INPUT_POST, INPUT_COOKIE ou INPUT_SERVER).
|
||||||
|
* @param array|null $filters Filtros personalizados no formato aceito por filter_input_array().
|
||||||
|
* @return array Retorna um array associativo com os dados filtrados, ou array vazio se nenhum dado for encontrado.
|
||||||
|
*/
|
||||||
|
public static function getFilteredInput(int $form_type = INPUT_GET, ?array $filters = null): array {
|
||||||
|
switch ($form_type) {
|
||||||
|
case INPUT_GET:
|
||||||
|
$form = $filters !== null ? filter_input_array(type: INPUT_GET, options: $filters) : filter_input_array(type: INPUT_GET);
|
||||||
|
break;
|
||||||
|
case INPUT_POST:
|
||||||
|
$inputData = file_get_contents(filename: 'php://input');
|
||||||
|
$data = [];
|
||||||
|
|
||||||
|
$contentType = $_SERVER['CONTENT_TYPE'] ?? '';
|
||||||
|
|
||||||
|
if (strpos(haystack: $contentType, needle: 'application/json') !== false) {
|
||||||
|
$data = json_decode(json: $inputData, associative: true);
|
||||||
|
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||||
|
http_response_code(response_code: 500);
|
||||||
|
header(header: "Content-Type: application/json; charset=utf-8");
|
||||||
|
echo json_encode(value: [
|
||||||
|
"success" => false,
|
||||||
|
"message" => "Erro ao decodificar JSON: " . json_last_error_msg()
|
||||||
|
]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isset($data['_method'])) {
|
||||||
|
unset($data['_method']);
|
||||||
|
}
|
||||||
|
$form = $data;
|
||||||
|
} else {
|
||||||
|
$form = $filters !== null ? filter_input_array(type: INPUT_POST, options: $filters) : filter_input_array(type: INPUT_POST);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case INPUT_COOKIE:
|
||||||
|
$form = $filters !== null ? filter_input_array(type: INPUT_COOKIE, options: $filters) : filter_input_array(type: INPUT_COOKIE);
|
||||||
|
break;
|
||||||
|
case INPUT_SERVER:
|
||||||
|
$form = $filters !== null ? filter_input_array(type: INPUT_SERVER, options: $filters) : filter_input_array(type: INPUT_SERVER);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$form = $filters !== null ? filter_input_array(type: INPUT_GET, options: $filters) : filter_input_array(type: INPUT_GET);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $form && is_array(value: $form) ? $form : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Envia uma resposta JSON padronizada e encerra a execução do script.
|
||||||
|
*
|
||||||
|
* Este método estático é um utilitário para endpoints de API, garantindo que as respostas
|
||||||
|
* enviadas ao cliente sigam um formato consistente. Ele define o código de resposta HTTP,
|
||||||
|
* o cabeçalho `Content-Type` e um corpo JSON estruturado.
|
||||||
|
*
|
||||||
|
* O corpo da resposta inclui sempre um `status` e uma `message`. Opcionalmente, pode
|
||||||
|
* incluir um array de `data`. A validação interna garante que os parâmetros sejam
|
||||||
|
* consistentes e que a execução seja interrompida após o envio da resposta.
|
||||||
|
*
|
||||||
|
* #### Validações e Comportamento:
|
||||||
|
* - O parâmetro `status` é restrito a 'success', 'error' ou 'fail'. Se um valor inválido for passado,
|
||||||
|
* ele será padronizado para 'error'.
|
||||||
|
* - Se o parâmetro `message` estiver vazio, o código de resposta é alterado para `422` (Entidade
|
||||||
|
* Não Processável), e uma mensagem de erro padrão é definida.
|
||||||
|
* - O código de resposta HTTP é definido com `http_response_code()`.
|
||||||
|
* - O cabeçalho `Content-Type` é configurado para `application/json; charset=utf-8`.
|
||||||
|
* - O array de resposta é codificado para JSON e impresso.
|
||||||
|
* - A execução do script é finalizada com `exit`.
|
||||||
|
*
|
||||||
|
* @param int $response_code O código de status HTTP da resposta (padrão: 200).
|
||||||
|
* @param string $status O status da operação ('success', 'error' ou 'fail'). Padrão: 'success'.
|
||||||
|
* @param string $message Uma mensagem descritiva da resposta. Este campo é obrigatório e será validado.
|
||||||
|
* @param array $data Um array associativo opcional com os dados a serem retornados. Padrão: `[]`.
|
||||||
|
* @return void Este método não tem retorno, pois ele finaliza a execução do script.
|
||||||
|
*/
|
||||||
|
public static function sendJsonResponse(int $response_code = 200, string $status = 'success', string $message = '', array $output = []): void {
|
||||||
|
// Garante que o status seja válido
|
||||||
|
if (!in_array(needle: $status, haystack: ['success', 'error', 'fail'])) {
|
||||||
|
$status = 'error';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Se não tiver mensagem, é erro de uso do método
|
||||||
|
if (empty($message)) {
|
||||||
|
$response_code = 422;
|
||||||
|
$status = 'error';
|
||||||
|
$message = "Mensagem obrigatória não informada.";
|
||||||
|
}
|
||||||
|
|
||||||
|
$responseArray = [
|
||||||
|
'response_code' => $response_code,
|
||||||
|
'status' => $status,
|
||||||
|
'message' => $message
|
||||||
|
];
|
||||||
|
|
||||||
|
if (isset($output['data']) && !empty($output['data'])) {
|
||||||
|
$responseArray['data'] = $output['data'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($output['errors']) && !empty($output['errors'])) {
|
||||||
|
$responseArray['errors'] = $output['errors'];
|
||||||
|
}
|
||||||
|
|
||||||
|
http_response_code(response_code: $response_code);
|
||||||
|
header(header: 'Content-Type: application/json; charset=utf-8');
|
||||||
|
echo json_encode(value: $responseArray);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calcula o valor de OFFSET para consultas de paginação SQL.
|
||||||
|
*
|
||||||
|
* Este método estático recebe o número da página desejada (`$page`) e o
|
||||||
|
* número de itens por página (`$limit`). Ele calcula o valor de OFFSET
|
||||||
|
* que deve ser usado em uma consulta SQL para buscar os registros corretos
|
||||||
|
* para a página especificada. O OFFSET é calculado como `($page - 1) * $limit`.
|
||||||
|
* A função `max(0, ...)` garante que o OFFSET nunca seja negativo, o que
|
||||||
|
* pode acontecer se um valor de página menor que 1 for passado.
|
||||||
|
*
|
||||||
|
* @param int $page O número da página para a qual se deseja calcular o OFFSET (a primeira página é 1).
|
||||||
|
* @param int $limit O número de itens a serem exibidos por página.
|
||||||
|
* @return int O valor de OFFSET a ser utilizado na cláusula LIMIT de uma consulta SQL.
|
||||||
|
*/
|
||||||
|
public static function getOffset(int $page, int $limit): int {
|
||||||
|
return max(0, ($page - 1) * $limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepara e formata os resultados de uma consulta paginada.
|
||||||
|
*
|
||||||
|
* Este método estático organiza os dados de uma resposta de banco de dados,
|
||||||
|
* juntamente com informações de paginação, em um formato padronizado.
|
||||||
|
* Ele é útil para enviar dados para a camada de visualização (frontend)
|
||||||
|
* de forma consistente.
|
||||||
|
*
|
||||||
|
* @param mixed $atuallPage O número da página atual. Pode ser um `int` ou `string`.
|
||||||
|
* @param array $dbResponse Um array contendo os resultados da consulta ao banco de dados para a página atual.
|
||||||
|
* @param string $totalResults O número total de resultados encontrados pela consulta, antes da aplicação do limite de paginação. Deve ser uma `string` (será convertido para `float`).
|
||||||
|
* @param mixed $limit O limite de resultados por página. Pode ser um `int` ou `string` (padrão é "10").
|
||||||
|
* @return array Um array associativo contendo:
|
||||||
|
* - `atuallPage`: A página atual.
|
||||||
|
* - `response`: Os dados da resposta do banco de dados para a página.
|
||||||
|
* - `totalResults`: O total de resultados, convertido para `float`.
|
||||||
|
* - `limit`: O limite de resultados por página.
|
||||||
|
*/
|
||||||
|
public static function preparePaginationResult(mixed $atuallPage, array $dbResponse, string $totalResults, mixed $limit = "10"): array {
|
||||||
|
return [
|
||||||
|
'atuallPage' => $atuallPage,
|
||||||
|
'response' => $dbResponse,
|
||||||
|
'totalResults' => floatval(value: $totalResults),
|
||||||
|
'limit' => $limit
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gera HTML para um componente de paginação.
|
||||||
|
*
|
||||||
|
* Este método estático recebe a página atual, o total de registros e um limite
|
||||||
|
* de registros por página para gerar uma navegação de paginação em HTML,
|
||||||
|
* utilizando classes do Bootstrap para estilização.
|
||||||
|
*
|
||||||
|
* Primeiro, calcula o número total de páginas. Em seguida, constrói a query
|
||||||
|
* string da URL atual, removendo os parâmetros 'page' e 'url' para evitar
|
||||||
|
* duplicações nos links de paginação.
|
||||||
|
*
|
||||||
|
* Limita o número máximo de botões de página exibidos e calcula o início e
|
||||||
|
* o fim da janela de botões a serem mostrados, ajustando essa janela para
|
||||||
|
* garantir que o número máximo de botões seja exibido, dentro dos limites
|
||||||
|
* do total de páginas.
|
||||||
|
*
|
||||||
|
* Se o total de registros for maior que o limite por página, o HTML da
|
||||||
|
* paginação é gerado, incluindo botões "Anterior" (desabilitado na primeira
|
||||||
|
* página), os números das páginas dentro da janela calculada (com a página
|
||||||
|
* atual marcada como ativa) e um botão "Próximo" (desabilitado na última
|
||||||
|
* página). Se o total de registros não for maior que o limite, uma string
|
||||||
|
* vazia é retornada.
|
||||||
|
*
|
||||||
|
* @param int $current_page O número da página atualmente visualizada.
|
||||||
|
* @param int $total_rows O número total de registros disponíveis.
|
||||||
|
* @param int $limit O número máximo de registros a serem exibidos por página (padrão: 20).
|
||||||
|
*
|
||||||
|
* @return string O HTML da navegação de paginação, estilizado com classes do Bootstrap,
|
||||||
|
* ou uma string vazia se não houver necessidade de paginação.
|
||||||
|
*/
|
||||||
|
public static function generatePaginationHtml(int $current_page, int $total_rows, int $limit = 20): string {
|
||||||
|
$current_page ??= 1;
|
||||||
|
$total_rows ??= 0;
|
||||||
|
|
||||||
|
// Calcula o total de páginas
|
||||||
|
$total_paginas = ceil(num: $total_rows / $limit);
|
||||||
|
|
||||||
|
// Construir a query string com os parâmetros atuais, exceto 'page'
|
||||||
|
$query_params = $_GET;
|
||||||
|
unset($query_params['page']); // Remove 'page' para evitar duplicação
|
||||||
|
unset($query_params['url']); // Remove 'url' para evitar duplicação
|
||||||
|
$query_string = http_build_query(data: $query_params);
|
||||||
|
|
||||||
|
// Limitar a quantidade máxima de botões a serem exibidos
|
||||||
|
$max_botoes = 10;
|
||||||
|
$inicio = max(1, $current_page - intval(value: $max_botoes / 2));
|
||||||
|
$fim = min($total_paginas, $inicio + $max_botoes - 1);
|
||||||
|
|
||||||
|
// Ajustar a janela de exibição se atingir o limite inferior ou superior
|
||||||
|
if ($fim - $inicio + 1 < $max_botoes) {
|
||||||
|
$inicio = max(1, $fim - $max_botoes + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validação das paginações
|
||||||
|
if($total_rows > $limit){
|
||||||
|
// Inicia a criação do HTML da paginação
|
||||||
|
$html = "<nav aria-label='Page navigation'>";
|
||||||
|
$html .= "<ul class='pagination justify-content-center'>";
|
||||||
|
|
||||||
|
// Botão Anterior (desabilitado na primeira página)
|
||||||
|
if ($current_page > 1) {
|
||||||
|
$anterior = $current_page - 1;
|
||||||
|
$html .= "<li class='page-item'>";
|
||||||
|
$html .= "<a class='page-link' href='?{$query_string}&page={$anterior}'>Anterior</a>";
|
||||||
|
$html .= "</li>";
|
||||||
|
} else {
|
||||||
|
$html .= "<li class='page-item disabled'>";
|
||||||
|
$html .= "<a class='page-link'>Anterior</a>";
|
||||||
|
$html .= "</li>";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Geração dos links de cada página dentro da janela definida
|
||||||
|
for ($i = $inicio; $i <= $fim; $i++) {
|
||||||
|
if ($i == $current_page) {
|
||||||
|
$html .= "<li class='page-item active'>";
|
||||||
|
$html .= "<a class='page-link' href='?{$query_string}&page={$i}'>{$i}</a>";
|
||||||
|
$html .= "</li>";
|
||||||
|
} else {
|
||||||
|
$html .= '<li class="page-item">';
|
||||||
|
$html .= "<a class='page-link' href='?{$query_string}&page={$i}'>{$i}</a>";
|
||||||
|
$html .= "</li>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Botão Próximo (desabilitado na última página)
|
||||||
|
if ($current_page < $total_paginas) {
|
||||||
|
$proxima = $current_page + 1;
|
||||||
|
$html .= "<li class='page-item'>";
|
||||||
|
$html .= "<a class='page-link' href='?{$query_string}&page={$proxima}'>Próximo</a>";
|
||||||
|
$html .= "</li>";
|
||||||
|
} else {
|
||||||
|
$html .= "<li class='page-item disabled'>";
|
||||||
|
$html .= "<a class='page-link'>Próximo</a>";
|
||||||
|
$html .= "</li>";
|
||||||
|
}
|
||||||
|
|
||||||
|
$html .= "</ul>";
|
||||||
|
$html .= "</nav>";
|
||||||
|
} else {
|
||||||
|
$html = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
}
|
||||||
22
vendor/claudecio/axiumphp/vendor/autoload.php
vendored
Executable file
22
vendor/claudecio/axiumphp/vendor/autoload.php
vendored
Executable file
@@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload.php @generated by Composer
|
||||||
|
|
||||||
|
if (PHP_VERSION_ID < 50600) {
|
||||||
|
if (!headers_sent()) {
|
||||||
|
header('HTTP/1.1 500 Internal Server Error');
|
||||||
|
}
|
||||||
|
$err = 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
|
||||||
|
if (!ini_get('display_errors')) {
|
||||||
|
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
|
||||||
|
fwrite(STDERR, $err);
|
||||||
|
} elseif (!headers_sent()) {
|
||||||
|
echo $err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new RuntimeException($err);
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once __DIR__ . '/composer/autoload_real.php';
|
||||||
|
|
||||||
|
return ComposerAutoloaderInit6c176ae0681796ba87da0d8bfe73b645::getLoader();
|
||||||
579
vendor/claudecio/axiumphp/vendor/composer/ClassLoader.php
vendored
Executable file
579
vendor/claudecio/axiumphp/vendor/composer/ClassLoader.php
vendored
Executable file
@@ -0,0 +1,579 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Composer.
|
||||||
|
*
|
||||||
|
* (c) Nils Adermann <naderman@naderman.de>
|
||||||
|
* Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Composer\Autoload;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
|
||||||
|
*
|
||||||
|
* $loader = new \Composer\Autoload\ClassLoader();
|
||||||
|
*
|
||||||
|
* // register classes with namespaces
|
||||||
|
* $loader->add('Symfony\Component', __DIR__.'/component');
|
||||||
|
* $loader->add('Symfony', __DIR__.'/framework');
|
||||||
|
*
|
||||||
|
* // activate the autoloader
|
||||||
|
* $loader->register();
|
||||||
|
*
|
||||||
|
* // to enable searching the include path (eg. for PEAR packages)
|
||||||
|
* $loader->setUseIncludePath(true);
|
||||||
|
*
|
||||||
|
* In this example, if you try to use a class in the Symfony\Component
|
||||||
|
* namespace or one of its children (Symfony\Component\Console for instance),
|
||||||
|
* the autoloader will first look for the class under the component/
|
||||||
|
* directory, and it will then fallback to the framework/ directory if not
|
||||||
|
* found before giving up.
|
||||||
|
*
|
||||||
|
* This class is loosely based on the Symfony UniversalClassLoader.
|
||||||
|
*
|
||||||
|
* @author Fabien Potencier <fabien@symfony.com>
|
||||||
|
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
* @see https://www.php-fig.org/psr/psr-0/
|
||||||
|
* @see https://www.php-fig.org/psr/psr-4/
|
||||||
|
*/
|
||||||
|
class ClassLoader
|
||||||
|
{
|
||||||
|
/** @var \Closure(string):void */
|
||||||
|
private static $includeFile;
|
||||||
|
|
||||||
|
/** @var string|null */
|
||||||
|
private $vendorDir;
|
||||||
|
|
||||||
|
// PSR-4
|
||||||
|
/**
|
||||||
|
* @var array<string, array<string, int>>
|
||||||
|
*/
|
||||||
|
private $prefixLengthsPsr4 = array();
|
||||||
|
/**
|
||||||
|
* @var array<string, list<string>>
|
||||||
|
*/
|
||||||
|
private $prefixDirsPsr4 = array();
|
||||||
|
/**
|
||||||
|
* @var list<string>
|
||||||
|
*/
|
||||||
|
private $fallbackDirsPsr4 = array();
|
||||||
|
|
||||||
|
// PSR-0
|
||||||
|
/**
|
||||||
|
* List of PSR-0 prefixes
|
||||||
|
*
|
||||||
|
* Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2')))
|
||||||
|
*
|
||||||
|
* @var array<string, array<string, list<string>>>
|
||||||
|
*/
|
||||||
|
private $prefixesPsr0 = array();
|
||||||
|
/**
|
||||||
|
* @var list<string>
|
||||||
|
*/
|
||||||
|
private $fallbackDirsPsr0 = array();
|
||||||
|
|
||||||
|
/** @var bool */
|
||||||
|
private $useIncludePath = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array<string, string>
|
||||||
|
*/
|
||||||
|
private $classMap = array();
|
||||||
|
|
||||||
|
/** @var bool */
|
||||||
|
private $classMapAuthoritative = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array<string, bool>
|
||||||
|
*/
|
||||||
|
private $missingClasses = array();
|
||||||
|
|
||||||
|
/** @var string|null */
|
||||||
|
private $apcuPrefix;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array<string, self>
|
||||||
|
*/
|
||||||
|
private static $registeredLoaders = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string|null $vendorDir
|
||||||
|
*/
|
||||||
|
public function __construct($vendorDir = null)
|
||||||
|
{
|
||||||
|
$this->vendorDir = $vendorDir;
|
||||||
|
self::initializeIncludeClosure();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string, list<string>>
|
||||||
|
*/
|
||||||
|
public function getPrefixes()
|
||||||
|
{
|
||||||
|
if (!empty($this->prefixesPsr0)) {
|
||||||
|
return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
|
||||||
|
}
|
||||||
|
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string, list<string>>
|
||||||
|
*/
|
||||||
|
public function getPrefixesPsr4()
|
||||||
|
{
|
||||||
|
return $this->prefixDirsPsr4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return list<string>
|
||||||
|
*/
|
||||||
|
public function getFallbackDirs()
|
||||||
|
{
|
||||||
|
return $this->fallbackDirsPsr0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return list<string>
|
||||||
|
*/
|
||||||
|
public function getFallbackDirsPsr4()
|
||||||
|
{
|
||||||
|
return $this->fallbackDirsPsr4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string, string> Array of classname => path
|
||||||
|
*/
|
||||||
|
public function getClassMap()
|
||||||
|
{
|
||||||
|
return $this->classMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string, string> $classMap Class to filename map
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function addClassMap(array $classMap)
|
||||||
|
{
|
||||||
|
if ($this->classMap) {
|
||||||
|
$this->classMap = array_merge($this->classMap, $classMap);
|
||||||
|
} else {
|
||||||
|
$this->classMap = $classMap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a set of PSR-0 directories for a given prefix, either
|
||||||
|
* appending or prepending to the ones previously set for this prefix.
|
||||||
|
*
|
||||||
|
* @param string $prefix The prefix
|
||||||
|
* @param list<string>|string $paths The PSR-0 root directories
|
||||||
|
* @param bool $prepend Whether to prepend the directories
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function add($prefix, $paths, $prepend = false)
|
||||||
|
{
|
||||||
|
$paths = (array) $paths;
|
||||||
|
if (!$prefix) {
|
||||||
|
if ($prepend) {
|
||||||
|
$this->fallbackDirsPsr0 = array_merge(
|
||||||
|
$paths,
|
||||||
|
$this->fallbackDirsPsr0
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$this->fallbackDirsPsr0 = array_merge(
|
||||||
|
$this->fallbackDirsPsr0,
|
||||||
|
$paths
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$first = $prefix[0];
|
||||||
|
if (!isset($this->prefixesPsr0[$first][$prefix])) {
|
||||||
|
$this->prefixesPsr0[$first][$prefix] = $paths;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ($prepend) {
|
||||||
|
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||||
|
$paths,
|
||||||
|
$this->prefixesPsr0[$first][$prefix]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||||
|
$this->prefixesPsr0[$first][$prefix],
|
||||||
|
$paths
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a set of PSR-4 directories for a given namespace, either
|
||||||
|
* appending or prepending to the ones previously set for this namespace.
|
||||||
|
*
|
||||||
|
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||||
|
* @param list<string>|string $paths The PSR-4 base directories
|
||||||
|
* @param bool $prepend Whether to prepend the directories
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function addPsr4($prefix, $paths, $prepend = false)
|
||||||
|
{
|
||||||
|
$paths = (array) $paths;
|
||||||
|
if (!$prefix) {
|
||||||
|
// Register directories for the root namespace.
|
||||||
|
if ($prepend) {
|
||||||
|
$this->fallbackDirsPsr4 = array_merge(
|
||||||
|
$paths,
|
||||||
|
$this->fallbackDirsPsr4
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$this->fallbackDirsPsr4 = array_merge(
|
||||||
|
$this->fallbackDirsPsr4,
|
||||||
|
$paths
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
|
||||||
|
// Register directories for a new namespace.
|
||||||
|
$length = strlen($prefix);
|
||||||
|
if ('\\' !== $prefix[$length - 1]) {
|
||||||
|
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||||
|
}
|
||||||
|
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||||
|
$this->prefixDirsPsr4[$prefix] = $paths;
|
||||||
|
} elseif ($prepend) {
|
||||||
|
// Prepend directories for an already registered namespace.
|
||||||
|
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||||
|
$paths,
|
||||||
|
$this->prefixDirsPsr4[$prefix]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Append directories for an already registered namespace.
|
||||||
|
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||||
|
$this->prefixDirsPsr4[$prefix],
|
||||||
|
$paths
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a set of PSR-0 directories for a given prefix,
|
||||||
|
* replacing any others previously set for this prefix.
|
||||||
|
*
|
||||||
|
* @param string $prefix The prefix
|
||||||
|
* @param list<string>|string $paths The PSR-0 base directories
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function set($prefix, $paths)
|
||||||
|
{
|
||||||
|
if (!$prefix) {
|
||||||
|
$this->fallbackDirsPsr0 = (array) $paths;
|
||||||
|
} else {
|
||||||
|
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a set of PSR-4 directories for a given namespace,
|
||||||
|
* replacing any others previously set for this namespace.
|
||||||
|
*
|
||||||
|
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||||
|
* @param list<string>|string $paths The PSR-4 base directories
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setPsr4($prefix, $paths)
|
||||||
|
{
|
||||||
|
if (!$prefix) {
|
||||||
|
$this->fallbackDirsPsr4 = (array) $paths;
|
||||||
|
} else {
|
||||||
|
$length = strlen($prefix);
|
||||||
|
if ('\\' !== $prefix[$length - 1]) {
|
||||||
|
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||||
|
}
|
||||||
|
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||||
|
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turns on searching the include path for class files.
|
||||||
|
*
|
||||||
|
* @param bool $useIncludePath
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setUseIncludePath($useIncludePath)
|
||||||
|
{
|
||||||
|
$this->useIncludePath = $useIncludePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Can be used to check if the autoloader uses the include path to check
|
||||||
|
* for classes.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function getUseIncludePath()
|
||||||
|
{
|
||||||
|
return $this->useIncludePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turns off searching the prefix and fallback directories for classes
|
||||||
|
* that have not been registered with the class map.
|
||||||
|
*
|
||||||
|
* @param bool $classMapAuthoritative
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setClassMapAuthoritative($classMapAuthoritative)
|
||||||
|
{
|
||||||
|
$this->classMapAuthoritative = $classMapAuthoritative;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should class lookup fail if not found in the current class map?
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isClassMapAuthoritative()
|
||||||
|
{
|
||||||
|
return $this->classMapAuthoritative;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
|
||||||
|
*
|
||||||
|
* @param string|null $apcuPrefix
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setApcuPrefix($apcuPrefix)
|
||||||
|
{
|
||||||
|
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The APCu prefix in use, or null if APCu caching is not enabled.
|
||||||
|
*
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function getApcuPrefix()
|
||||||
|
{
|
||||||
|
return $this->apcuPrefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers this instance as an autoloader.
|
||||||
|
*
|
||||||
|
* @param bool $prepend Whether to prepend the autoloader or not
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function register($prepend = false)
|
||||||
|
{
|
||||||
|
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
|
||||||
|
|
||||||
|
if (null === $this->vendorDir) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($prepend) {
|
||||||
|
self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
|
||||||
|
} else {
|
||||||
|
unset(self::$registeredLoaders[$this->vendorDir]);
|
||||||
|
self::$registeredLoaders[$this->vendorDir] = $this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregisters this instance as an autoloader.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function unregister()
|
||||||
|
{
|
||||||
|
spl_autoload_unregister(array($this, 'loadClass'));
|
||||||
|
|
||||||
|
if (null !== $this->vendorDir) {
|
||||||
|
unset(self::$registeredLoaders[$this->vendorDir]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the given class or interface.
|
||||||
|
*
|
||||||
|
* @param string $class The name of the class
|
||||||
|
* @return true|null True if loaded, null otherwise
|
||||||
|
*/
|
||||||
|
public function loadClass($class)
|
||||||
|
{
|
||||||
|
if ($file = $this->findFile($class)) {
|
||||||
|
$includeFile = self::$includeFile;
|
||||||
|
$includeFile($file);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the path to the file where the class is defined.
|
||||||
|
*
|
||||||
|
* @param string $class The name of the class
|
||||||
|
*
|
||||||
|
* @return string|false The path if found, false otherwise
|
||||||
|
*/
|
||||||
|
public function findFile($class)
|
||||||
|
{
|
||||||
|
// class map lookup
|
||||||
|
if (isset($this->classMap[$class])) {
|
||||||
|
return $this->classMap[$class];
|
||||||
|
}
|
||||||
|
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (null !== $this->apcuPrefix) {
|
||||||
|
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
|
||||||
|
if ($hit) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$file = $this->findFileWithExtension($class, '.php');
|
||||||
|
|
||||||
|
// Search for Hack files if we are running on HHVM
|
||||||
|
if (false === $file && defined('HHVM_VERSION')) {
|
||||||
|
$file = $this->findFileWithExtension($class, '.hh');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== $this->apcuPrefix) {
|
||||||
|
apcu_add($this->apcuPrefix.$class, $file);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (false === $file) {
|
||||||
|
// Remember that this class does not exist.
|
||||||
|
$this->missingClasses[$class] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the currently registered loaders keyed by their corresponding vendor directories.
|
||||||
|
*
|
||||||
|
* @return array<string, self>
|
||||||
|
*/
|
||||||
|
public static function getRegisteredLoaders()
|
||||||
|
{
|
||||||
|
return self::$registeredLoaders;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $class
|
||||||
|
* @param string $ext
|
||||||
|
* @return string|false
|
||||||
|
*/
|
||||||
|
private function findFileWithExtension($class, $ext)
|
||||||
|
{
|
||||||
|
// PSR-4 lookup
|
||||||
|
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
|
||||||
|
|
||||||
|
$first = $class[0];
|
||||||
|
if (isset($this->prefixLengthsPsr4[$first])) {
|
||||||
|
$subPath = $class;
|
||||||
|
while (false !== $lastPos = strrpos($subPath, '\\')) {
|
||||||
|
$subPath = substr($subPath, 0, $lastPos);
|
||||||
|
$search = $subPath . '\\';
|
||||||
|
if (isset($this->prefixDirsPsr4[$search])) {
|
||||||
|
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
|
||||||
|
foreach ($this->prefixDirsPsr4[$search] as $dir) {
|
||||||
|
if (file_exists($file = $dir . $pathEnd)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PSR-4 fallback dirs
|
||||||
|
foreach ($this->fallbackDirsPsr4 as $dir) {
|
||||||
|
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PSR-0 lookup
|
||||||
|
if (false !== $pos = strrpos($class, '\\')) {
|
||||||
|
// namespaced class name
|
||||||
|
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
|
||||||
|
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
|
||||||
|
} else {
|
||||||
|
// PEAR-like class name
|
||||||
|
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($this->prefixesPsr0[$first])) {
|
||||||
|
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
|
||||||
|
if (0 === strpos($class, $prefix)) {
|
||||||
|
foreach ($dirs as $dir) {
|
||||||
|
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PSR-0 fallback dirs
|
||||||
|
foreach ($this->fallbackDirsPsr0 as $dir) {
|
||||||
|
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PSR-0 include paths.
|
||||||
|
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private static function initializeIncludeClosure()
|
||||||
|
{
|
||||||
|
if (self::$includeFile !== null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scope isolated include.
|
||||||
|
*
|
||||||
|
* Prevents access to $this/self from included files.
|
||||||
|
*
|
||||||
|
* @param string $file
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
self::$includeFile = \Closure::bind(static function($file) {
|
||||||
|
include $file;
|
||||||
|
}, null, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
396
vendor/claudecio/axiumphp/vendor/composer/InstalledVersions.php
vendored
Executable file
396
vendor/claudecio/axiumphp/vendor/composer/InstalledVersions.php
vendored
Executable file
@@ -0,0 +1,396 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Composer.
|
||||||
|
*
|
||||||
|
* (c) Nils Adermann <naderman@naderman.de>
|
||||||
|
* Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Composer;
|
||||||
|
|
||||||
|
use Composer\Autoload\ClassLoader;
|
||||||
|
use Composer\Semver\VersionParser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is copied in every Composer installed project and available to all
|
||||||
|
*
|
||||||
|
* See also https://getcomposer.org/doc/07-runtime.md#installed-versions
|
||||||
|
*
|
||||||
|
* To require its presence, you can require `composer-runtime-api ^2.0`
|
||||||
|
*
|
||||||
|
* @final
|
||||||
|
*/
|
||||||
|
class InstalledVersions
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string|null if set (by reflection by Composer), this should be set to the path where this class is being copied to
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
private static $selfDir = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var mixed[]|null
|
||||||
|
* @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
|
||||||
|
*/
|
||||||
|
private static $installed;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private static $installedIsLocalDir;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool|null
|
||||||
|
*/
|
||||||
|
private static $canGetVendors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array[]
|
||||||
|
* @psalm-var array<string, array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||||
|
*/
|
||||||
|
private static $installedByVendor = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of all package names which are present, either by being installed, replaced or provided
|
||||||
|
*
|
||||||
|
* @return string[]
|
||||||
|
* @psalm-return list<string>
|
||||||
|
*/
|
||||||
|
public static function getInstalledPackages()
|
||||||
|
{
|
||||||
|
$packages = array();
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
$packages[] = array_keys($installed['versions']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (1 === \count($packages)) {
|
||||||
|
return $packages[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of all package names with a specific type e.g. 'library'
|
||||||
|
*
|
||||||
|
* @param string $type
|
||||||
|
* @return string[]
|
||||||
|
* @psalm-return list<string>
|
||||||
|
*/
|
||||||
|
public static function getInstalledPackagesByType($type)
|
||||||
|
{
|
||||||
|
$packagesByType = array();
|
||||||
|
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
foreach ($installed['versions'] as $name => $package) {
|
||||||
|
if (isset($package['type']) && $package['type'] === $type) {
|
||||||
|
$packagesByType[] = $name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $packagesByType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the given package is installed
|
||||||
|
*
|
||||||
|
* This also returns true if the package name is provided or replaced by another package
|
||||||
|
*
|
||||||
|
* @param string $packageName
|
||||||
|
* @param bool $includeDevRequirements
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function isInstalled($packageName, $includeDevRequirements = true)
|
||||||
|
{
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
if (isset($installed['versions'][$packageName])) {
|
||||||
|
return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the given package satisfies a version constraint
|
||||||
|
*
|
||||||
|
* e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
|
||||||
|
*
|
||||||
|
* Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
|
||||||
|
*
|
||||||
|
* @param VersionParser $parser Install composer/semver to have access to this class and functionality
|
||||||
|
* @param string $packageName
|
||||||
|
* @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function satisfies(VersionParser $parser, $packageName, $constraint)
|
||||||
|
{
|
||||||
|
$constraint = $parser->parseConstraints((string) $constraint);
|
||||||
|
$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
|
||||||
|
|
||||||
|
return $provided->matches($constraint);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a version constraint representing all the range(s) which are installed for a given package
|
||||||
|
*
|
||||||
|
* It is easier to use this via isInstalled() with the $constraint argument if you need to check
|
||||||
|
* whether a given version of a package is installed, and not just whether it exists
|
||||||
|
*
|
||||||
|
* @param string $packageName
|
||||||
|
* @return string Version constraint usable with composer/semver
|
||||||
|
*/
|
||||||
|
public static function getVersionRanges($packageName)
|
||||||
|
{
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
if (!isset($installed['versions'][$packageName])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$ranges = array();
|
||||||
|
if (isset($installed['versions'][$packageName]['pretty_version'])) {
|
||||||
|
$ranges[] = $installed['versions'][$packageName]['pretty_version'];
|
||||||
|
}
|
||||||
|
if (array_key_exists('aliases', $installed['versions'][$packageName])) {
|
||||||
|
$ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
|
||||||
|
}
|
||||||
|
if (array_key_exists('replaced', $installed['versions'][$packageName])) {
|
||||||
|
$ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
|
||||||
|
}
|
||||||
|
if (array_key_exists('provided', $installed['versions'][$packageName])) {
|
||||||
|
$ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return implode(' || ', $ranges);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $packageName
|
||||||
|
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
|
||||||
|
*/
|
||||||
|
public static function getVersion($packageName)
|
||||||
|
{
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
if (!isset($installed['versions'][$packageName])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($installed['versions'][$packageName]['version'])) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $installed['versions'][$packageName]['version'];
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $packageName
|
||||||
|
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
|
||||||
|
*/
|
||||||
|
public static function getPrettyVersion($packageName)
|
||||||
|
{
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
if (!isset($installed['versions'][$packageName])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($installed['versions'][$packageName]['pretty_version'])) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $installed['versions'][$packageName]['pretty_version'];
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $packageName
|
||||||
|
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
|
||||||
|
*/
|
||||||
|
public static function getReference($packageName)
|
||||||
|
{
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
if (!isset($installed['versions'][$packageName])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($installed['versions'][$packageName]['reference'])) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $installed['versions'][$packageName]['reference'];
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $packageName
|
||||||
|
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
|
||||||
|
*/
|
||||||
|
public static function getInstallPath($packageName)
|
||||||
|
{
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
if (!isset($installed['versions'][$packageName])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
* @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}
|
||||||
|
*/
|
||||||
|
public static function getRootPackage()
|
||||||
|
{
|
||||||
|
$installed = self::getInstalled();
|
||||||
|
|
||||||
|
return $installed[0]['root'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the raw installed.php data for custom implementations
|
||||||
|
*
|
||||||
|
* @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
|
||||||
|
* @return array[]
|
||||||
|
* @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}
|
||||||
|
*/
|
||||||
|
public static function getRawData()
|
||||||
|
{
|
||||||
|
@trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
|
||||||
|
|
||||||
|
if (null === self::$installed) {
|
||||||
|
// only require the installed.php file if this file is loaded from its dumped location,
|
||||||
|
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
|
||||||
|
if (substr(__DIR__, -8, 1) !== 'C') {
|
||||||
|
self::$installed = include __DIR__ . '/installed.php';
|
||||||
|
} else {
|
||||||
|
self::$installed = array();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$installed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the raw data of all installed.php which are currently loaded for custom implementations
|
||||||
|
*
|
||||||
|
* @return array[]
|
||||||
|
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||||
|
*/
|
||||||
|
public static function getAllRawData()
|
||||||
|
{
|
||||||
|
return self::getInstalled();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lets you reload the static array from another file
|
||||||
|
*
|
||||||
|
* This is only useful for complex integrations in which a project needs to use
|
||||||
|
* this class but then also needs to execute another project's autoloader in process,
|
||||||
|
* and wants to ensure both projects have access to their version of installed.php.
|
||||||
|
*
|
||||||
|
* A typical case would be PHPUnit, where it would need to make sure it reads all
|
||||||
|
* the data it needs from this class, then call reload() with
|
||||||
|
* `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
|
||||||
|
* the project in which it runs can then also use this class safely, without
|
||||||
|
* interference between PHPUnit's dependencies and the project's dependencies.
|
||||||
|
*
|
||||||
|
* @param array[] $data A vendor/composer/installed.php data set
|
||||||
|
* @return void
|
||||||
|
*
|
||||||
|
* @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $data
|
||||||
|
*/
|
||||||
|
public static function reload($data)
|
||||||
|
{
|
||||||
|
self::$installed = $data;
|
||||||
|
self::$installedByVendor = array();
|
||||||
|
|
||||||
|
// when using reload, we disable the duplicate protection to ensure that self::$installed data is
|
||||||
|
// always returned, but we cannot know whether it comes from the installed.php in __DIR__ or not,
|
||||||
|
// so we have to assume it does not, and that may result in duplicate data being returned when listing
|
||||||
|
// all installed packages for example
|
||||||
|
self::$installedIsLocalDir = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private static function getSelfDir()
|
||||||
|
{
|
||||||
|
if (self::$selfDir === null) {
|
||||||
|
self::$selfDir = strtr(__DIR__, '\\', '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$selfDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array[]
|
||||||
|
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||||
|
*/
|
||||||
|
private static function getInstalled()
|
||||||
|
{
|
||||||
|
if (null === self::$canGetVendors) {
|
||||||
|
self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
|
||||||
|
}
|
||||||
|
|
||||||
|
$installed = array();
|
||||||
|
$copiedLocalDir = false;
|
||||||
|
|
||||||
|
if (self::$canGetVendors) {
|
||||||
|
$selfDir = self::getSelfDir();
|
||||||
|
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
|
||||||
|
$vendorDir = strtr($vendorDir, '\\', '/');
|
||||||
|
if (isset(self::$installedByVendor[$vendorDir])) {
|
||||||
|
$installed[] = self::$installedByVendor[$vendorDir];
|
||||||
|
} elseif (is_file($vendorDir.'/composer/installed.php')) {
|
||||||
|
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
|
||||||
|
$required = require $vendorDir.'/composer/installed.php';
|
||||||
|
self::$installedByVendor[$vendorDir] = $required;
|
||||||
|
$installed[] = $required;
|
||||||
|
if (self::$installed === null && $vendorDir.'/composer' === $selfDir) {
|
||||||
|
self::$installed = $required;
|
||||||
|
self::$installedIsLocalDir = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (self::$installedIsLocalDir && $vendorDir.'/composer' === $selfDir) {
|
||||||
|
$copiedLocalDir = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null === self::$installed) {
|
||||||
|
// only require the installed.php file if this file is loaded from its dumped location,
|
||||||
|
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
|
||||||
|
if (substr(__DIR__, -8, 1) !== 'C') {
|
||||||
|
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
|
||||||
|
$required = require __DIR__ . '/installed.php';
|
||||||
|
self::$installed = $required;
|
||||||
|
} else {
|
||||||
|
self::$installed = array();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self::$installed !== array() && !$copiedLocalDir) {
|
||||||
|
$installed[] = self::$installed;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $installed;
|
||||||
|
}
|
||||||
|
}
|
||||||
21
vendor/claudecio/axiumphp/vendor/composer/LICENSE
vendored
Executable file
21
vendor/claudecio/axiumphp/vendor/composer/LICENSE
vendored
Executable file
@@ -0,0 +1,21 @@
|
|||||||
|
|
||||||
|
Copyright (c) Nils Adermann, Jordi Boggiano
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is furnished
|
||||||
|
to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
|
||||||
15
vendor/claudecio/axiumphp/vendor/composer/autoload_classmap.php
vendored
Executable file
15
vendor/claudecio/axiumphp/vendor/composer/autoload_classmap.php
vendored
Executable file
@@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_classmap.php @generated by Composer
|
||||||
|
|
||||||
|
$vendorDir = dirname(__DIR__);
|
||||||
|
$baseDir = dirname($vendorDir);
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'Attribute' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Attribute.php',
|
||||||
|
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
|
||||||
|
'PhpToken' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php',
|
||||||
|
'Stringable' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Stringable.php',
|
||||||
|
'UnhandledMatchError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php',
|
||||||
|
'ValueError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/ValueError.php',
|
||||||
|
);
|
||||||
12
vendor/claudecio/axiumphp/vendor/composer/autoload_files.php
vendored
Executable file
12
vendor/claudecio/axiumphp/vendor/composer/autoload_files.php
vendored
Executable file
@@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_files.php @generated by Composer
|
||||||
|
|
||||||
|
$vendorDir = dirname(__DIR__);
|
||||||
|
$baseDir = dirname($vendorDir);
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php',
|
||||||
|
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
|
||||||
|
'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php',
|
||||||
|
);
|
||||||
9
vendor/claudecio/axiumphp/vendor/composer/autoload_namespaces.php
vendored
Executable file
9
vendor/claudecio/axiumphp/vendor/composer/autoload_namespaces.php
vendored
Executable file
@@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_namespaces.php @generated by Composer
|
||||||
|
|
||||||
|
$vendorDir = dirname(__DIR__);
|
||||||
|
$baseDir = dirname($vendorDir);
|
||||||
|
|
||||||
|
return array(
|
||||||
|
);
|
||||||
16
vendor/claudecio/axiumphp/vendor/composer/autoload_psr4.php
vendored
Executable file
16
vendor/claudecio/axiumphp/vendor/composer/autoload_psr4.php
vendored
Executable file
@@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_psr4.php @generated by Composer
|
||||||
|
|
||||||
|
$vendorDir = dirname(__DIR__);
|
||||||
|
$baseDir = dirname($vendorDir);
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'Symfony\\Polyfill\\Php80\\' => array($vendorDir . '/symfony/polyfill-php80'),
|
||||||
|
'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'),
|
||||||
|
'Symfony\\Polyfill\\Ctype\\' => array($vendorDir . '/symfony/polyfill-ctype'),
|
||||||
|
'PhpOption\\' => array($vendorDir . '/phpoption/phpoption/src/PhpOption'),
|
||||||
|
'GrahamCampbell\\ResultType\\' => array($vendorDir . '/graham-campbell/result-type/src'),
|
||||||
|
'Dotenv\\' => array($vendorDir . '/vlucas/phpdotenv/src'),
|
||||||
|
'AxiumPHP\\' => array($baseDir . '/src'),
|
||||||
|
);
|
||||||
50
vendor/claudecio/axiumphp/vendor/composer/autoload_real.php
vendored
Executable file
50
vendor/claudecio/axiumphp/vendor/composer/autoload_real.php
vendored
Executable file
@@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_real.php @generated by Composer
|
||||||
|
|
||||||
|
class ComposerAutoloaderInit6c176ae0681796ba87da0d8bfe73b645
|
||||||
|
{
|
||||||
|
private static $loader;
|
||||||
|
|
||||||
|
public static function loadClassLoader($class)
|
||||||
|
{
|
||||||
|
if ('Composer\Autoload\ClassLoader' === $class) {
|
||||||
|
require __DIR__ . '/ClassLoader.php';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \Composer\Autoload\ClassLoader
|
||||||
|
*/
|
||||||
|
public static function getLoader()
|
||||||
|
{
|
||||||
|
if (null !== self::$loader) {
|
||||||
|
return self::$loader;
|
||||||
|
}
|
||||||
|
|
||||||
|
require __DIR__ . '/platform_check.php';
|
||||||
|
|
||||||
|
spl_autoload_register(array('ComposerAutoloaderInit6c176ae0681796ba87da0d8bfe73b645', 'loadClassLoader'), true, true);
|
||||||
|
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
|
||||||
|
spl_autoload_unregister(array('ComposerAutoloaderInit6c176ae0681796ba87da0d8bfe73b645', 'loadClassLoader'));
|
||||||
|
|
||||||
|
require __DIR__ . '/autoload_static.php';
|
||||||
|
call_user_func(\Composer\Autoload\ComposerStaticInit6c176ae0681796ba87da0d8bfe73b645::getInitializer($loader));
|
||||||
|
|
||||||
|
$loader->register(true);
|
||||||
|
|
||||||
|
$filesToLoad = \Composer\Autoload\ComposerStaticInit6c176ae0681796ba87da0d8bfe73b645::$files;
|
||||||
|
$requireFile = \Closure::bind(static function ($fileIdentifier, $file) {
|
||||||
|
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
|
||||||
|
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
|
||||||
|
|
||||||
|
require $file;
|
||||||
|
}
|
||||||
|
}, null, null);
|
||||||
|
foreach ($filesToLoad as $fileIdentifier => $file) {
|
||||||
|
$requireFile($fileIdentifier, $file);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $loader;
|
||||||
|
}
|
||||||
|
}
|
||||||
89
vendor/claudecio/axiumphp/vendor/composer/autoload_static.php
vendored
Executable file
89
vendor/claudecio/axiumphp/vendor/composer/autoload_static.php
vendored
Executable file
@@ -0,0 +1,89 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_static.php @generated by Composer
|
||||||
|
|
||||||
|
namespace Composer\Autoload;
|
||||||
|
|
||||||
|
class ComposerStaticInit6c176ae0681796ba87da0d8bfe73b645
|
||||||
|
{
|
||||||
|
public static $files = array (
|
||||||
|
'320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php',
|
||||||
|
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
|
||||||
|
'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php',
|
||||||
|
);
|
||||||
|
|
||||||
|
public static $prefixLengthsPsr4 = array (
|
||||||
|
'S' =>
|
||||||
|
array (
|
||||||
|
'Symfony\\Polyfill\\Php80\\' => 23,
|
||||||
|
'Symfony\\Polyfill\\Mbstring\\' => 26,
|
||||||
|
'Symfony\\Polyfill\\Ctype\\' => 23,
|
||||||
|
),
|
||||||
|
'P' =>
|
||||||
|
array (
|
||||||
|
'PhpOption\\' => 10,
|
||||||
|
),
|
||||||
|
'G' =>
|
||||||
|
array (
|
||||||
|
'GrahamCampbell\\ResultType\\' => 26,
|
||||||
|
),
|
||||||
|
'D' =>
|
||||||
|
array (
|
||||||
|
'Dotenv\\' => 7,
|
||||||
|
),
|
||||||
|
'A' =>
|
||||||
|
array (
|
||||||
|
'AxiumPHP\\' => 9,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
public static $prefixDirsPsr4 = array (
|
||||||
|
'Symfony\\Polyfill\\Php80\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/symfony/polyfill-php80',
|
||||||
|
),
|
||||||
|
'Symfony\\Polyfill\\Mbstring\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/symfony/polyfill-mbstring',
|
||||||
|
),
|
||||||
|
'Symfony\\Polyfill\\Ctype\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/symfony/polyfill-ctype',
|
||||||
|
),
|
||||||
|
'PhpOption\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/phpoption/phpoption/src/PhpOption',
|
||||||
|
),
|
||||||
|
'GrahamCampbell\\ResultType\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/graham-campbell/result-type/src',
|
||||||
|
),
|
||||||
|
'Dotenv\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/vlucas/phpdotenv/src',
|
||||||
|
),
|
||||||
|
'AxiumPHP\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/../..' . '/src',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
public static $classMap = array (
|
||||||
|
'Attribute' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Attribute.php',
|
||||||
|
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
|
||||||
|
'PhpToken' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php',
|
||||||
|
'Stringable' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Stringable.php',
|
||||||
|
'UnhandledMatchError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php',
|
||||||
|
'ValueError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/ValueError.php',
|
||||||
|
);
|
||||||
|
|
||||||
|
public static function getInitializer(ClassLoader $loader)
|
||||||
|
{
|
||||||
|
return \Closure::bind(function () use ($loader) {
|
||||||
|
$loader->prefixLengthsPsr4 = ComposerStaticInit6c176ae0681796ba87da0d8bfe73b645::$prefixLengthsPsr4;
|
||||||
|
$loader->prefixDirsPsr4 = ComposerStaticInit6c176ae0681796ba87da0d8bfe73b645::$prefixDirsPsr4;
|
||||||
|
$loader->classMap = ComposerStaticInit6c176ae0681796ba87da0d8bfe73b645::$classMap;
|
||||||
|
|
||||||
|
}, null, ClassLoader::class);
|
||||||
|
}
|
||||||
|
}
|
||||||
485
vendor/claudecio/axiumphp/vendor/composer/installed.json
vendored
Executable file
485
vendor/claudecio/axiumphp/vendor/composer/installed.json
vendored
Executable file
@@ -0,0 +1,485 @@
|
|||||||
|
{
|
||||||
|
"packages": [
|
||||||
|
{
|
||||||
|
"name": "graham-campbell/result-type",
|
||||||
|
"version": "v1.1.3",
|
||||||
|
"version_normalized": "1.1.3.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/GrahamCampbell/Result-Type.git",
|
||||||
|
"reference": "3ba905c11371512af9d9bdd27d99b782216b6945"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/3ba905c11371512af9d9bdd27d99b782216b6945",
|
||||||
|
"reference": "3ba905c11371512af9d9bdd27d99b782216b6945",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7.2.5 || ^8.0",
|
||||||
|
"phpoption/phpoption": "^1.9.3"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28"
|
||||||
|
},
|
||||||
|
"time": "2024-07-20T21:45:45+00:00",
|
||||||
|
"type": "library",
|
||||||
|
"installation-source": "dist",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"GrahamCampbell\\ResultType\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Graham Campbell",
|
||||||
|
"email": "hello@gjcampbell.co.uk",
|
||||||
|
"homepage": "https://github.com/GrahamCampbell"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "An Implementation Of The Result Type",
|
||||||
|
"keywords": [
|
||||||
|
"Graham Campbell",
|
||||||
|
"GrahamCampbell",
|
||||||
|
"Result Type",
|
||||||
|
"Result-Type",
|
||||||
|
"result"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/GrahamCampbell/Result-Type/issues",
|
||||||
|
"source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.3"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/GrahamCampbell",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type",
|
||||||
|
"type": "tidelift"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"install-path": "../graham-campbell/result-type"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "phpoption/phpoption",
|
||||||
|
"version": "1.9.3",
|
||||||
|
"version_normalized": "1.9.3.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/schmittjoh/php-option.git",
|
||||||
|
"reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/schmittjoh/php-option/zipball/e3fac8b24f56113f7cb96af14958c0dd16330f54",
|
||||||
|
"reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7.2.5 || ^8.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"bamarni/composer-bin-plugin": "^1.8.2",
|
||||||
|
"phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28"
|
||||||
|
},
|
||||||
|
"time": "2024-07-20T21:41:07+00:00",
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"bamarni-bin": {
|
||||||
|
"bin-links": true,
|
||||||
|
"forward-command": false
|
||||||
|
},
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "1.9-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"installation-source": "dist",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"PhpOption\\": "src/PhpOption/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"Apache-2.0"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Johannes M. Schmitt",
|
||||||
|
"email": "schmittjoh@gmail.com",
|
||||||
|
"homepage": "https://github.com/schmittjoh"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Graham Campbell",
|
||||||
|
"email": "hello@gjcampbell.co.uk",
|
||||||
|
"homepage": "https://github.com/GrahamCampbell"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Option Type for PHP",
|
||||||
|
"keywords": [
|
||||||
|
"language",
|
||||||
|
"option",
|
||||||
|
"php",
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/schmittjoh/php-option/issues",
|
||||||
|
"source": "https://github.com/schmittjoh/php-option/tree/1.9.3"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/GrahamCampbell",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption",
|
||||||
|
"type": "tidelift"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"install-path": "../phpoption/phpoption"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "symfony/polyfill-ctype",
|
||||||
|
"version": "v1.32.0",
|
||||||
|
"version_normalized": "1.32.0.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/symfony/polyfill-ctype.git",
|
||||||
|
"reference": "a3cc8b044a6ea513310cbd48ef7333b384945638"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638",
|
||||||
|
"reference": "a3cc8b044a6ea513310cbd48ef7333b384945638",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=7.2"
|
||||||
|
},
|
||||||
|
"provide": {
|
||||||
|
"ext-ctype": "*"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"ext-ctype": "For best performance"
|
||||||
|
},
|
||||||
|
"time": "2024-09-09T11:45:10+00:00",
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"thanks": {
|
||||||
|
"url": "https://github.com/symfony/polyfill",
|
||||||
|
"name": "symfony/polyfill"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"installation-source": "dist",
|
||||||
|
"autoload": {
|
||||||
|
"files": [
|
||||||
|
"bootstrap.php"
|
||||||
|
],
|
||||||
|
"psr-4": {
|
||||||
|
"Symfony\\Polyfill\\Ctype\\": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Gert de Pagter",
|
||||||
|
"email": "BackEndTea@gmail.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Symfony Community",
|
||||||
|
"homepage": "https://symfony.com/contributors"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Symfony polyfill for ctype functions",
|
||||||
|
"homepage": "https://symfony.com",
|
||||||
|
"keywords": [
|
||||||
|
"compatibility",
|
||||||
|
"ctype",
|
||||||
|
"polyfill",
|
||||||
|
"portable"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.32.0"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://symfony.com/sponsor",
|
||||||
|
"type": "custom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/fabpot",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||||
|
"type": "tidelift"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"install-path": "../symfony/polyfill-ctype"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "symfony/polyfill-mbstring",
|
||||||
|
"version": "v1.32.0",
|
||||||
|
"version_normalized": "1.32.0.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/symfony/polyfill-mbstring.git",
|
||||||
|
"reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493",
|
||||||
|
"reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-iconv": "*",
|
||||||
|
"php": ">=7.2"
|
||||||
|
},
|
||||||
|
"provide": {
|
||||||
|
"ext-mbstring": "*"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"ext-mbstring": "For best performance"
|
||||||
|
},
|
||||||
|
"time": "2024-12-23T08:48:59+00:00",
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"thanks": {
|
||||||
|
"url": "https://github.com/symfony/polyfill",
|
||||||
|
"name": "symfony/polyfill"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"installation-source": "dist",
|
||||||
|
"autoload": {
|
||||||
|
"files": [
|
||||||
|
"bootstrap.php"
|
||||||
|
],
|
||||||
|
"psr-4": {
|
||||||
|
"Symfony\\Polyfill\\Mbstring\\": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Nicolas Grekas",
|
||||||
|
"email": "p@tchwork.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Symfony Community",
|
||||||
|
"homepage": "https://symfony.com/contributors"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Symfony polyfill for the Mbstring extension",
|
||||||
|
"homepage": "https://symfony.com",
|
||||||
|
"keywords": [
|
||||||
|
"compatibility",
|
||||||
|
"mbstring",
|
||||||
|
"polyfill",
|
||||||
|
"portable",
|
||||||
|
"shim"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.32.0"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://symfony.com/sponsor",
|
||||||
|
"type": "custom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/fabpot",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||||
|
"type": "tidelift"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"install-path": "../symfony/polyfill-mbstring"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "symfony/polyfill-php80",
|
||||||
|
"version": "v1.32.0",
|
||||||
|
"version_normalized": "1.32.0.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/symfony/polyfill-php80.git",
|
||||||
|
"reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/0cc9dd0f17f61d8131e7df6b84bd344899fe2608",
|
||||||
|
"reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=7.2"
|
||||||
|
},
|
||||||
|
"time": "2025-01-02T08:10:11+00:00",
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"thanks": {
|
||||||
|
"url": "https://github.com/symfony/polyfill",
|
||||||
|
"name": "symfony/polyfill"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"installation-source": "dist",
|
||||||
|
"autoload": {
|
||||||
|
"files": [
|
||||||
|
"bootstrap.php"
|
||||||
|
],
|
||||||
|
"psr-4": {
|
||||||
|
"Symfony\\Polyfill\\Php80\\": ""
|
||||||
|
},
|
||||||
|
"classmap": [
|
||||||
|
"Resources/stubs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Ion Bazan",
|
||||||
|
"email": "ion.bazan@gmail.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Nicolas Grekas",
|
||||||
|
"email": "p@tchwork.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Symfony Community",
|
||||||
|
"homepage": "https://symfony.com/contributors"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
|
||||||
|
"homepage": "https://symfony.com",
|
||||||
|
"keywords": [
|
||||||
|
"compatibility",
|
||||||
|
"polyfill",
|
||||||
|
"portable",
|
||||||
|
"shim"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"source": "https://github.com/symfony/polyfill-php80/tree/v1.32.0"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://symfony.com/sponsor",
|
||||||
|
"type": "custom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/fabpot",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||||
|
"type": "tidelift"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"install-path": "../symfony/polyfill-php80"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "vlucas/phpdotenv",
|
||||||
|
"version": "v5.6.2",
|
||||||
|
"version_normalized": "5.6.2.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/vlucas/phpdotenv.git",
|
||||||
|
"reference": "24ac4c74f91ee2c193fa1aaa5c249cb0822809af"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/24ac4c74f91ee2c193fa1aaa5c249cb0822809af",
|
||||||
|
"reference": "24ac4c74f91ee2c193fa1aaa5c249cb0822809af",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-pcre": "*",
|
||||||
|
"graham-campbell/result-type": "^1.1.3",
|
||||||
|
"php": "^7.2.5 || ^8.0",
|
||||||
|
"phpoption/phpoption": "^1.9.3",
|
||||||
|
"symfony/polyfill-ctype": "^1.24",
|
||||||
|
"symfony/polyfill-mbstring": "^1.24",
|
||||||
|
"symfony/polyfill-php80": "^1.24"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"bamarni/composer-bin-plugin": "^1.8.2",
|
||||||
|
"ext-filter": "*",
|
||||||
|
"phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"ext-filter": "Required to use the boolean validator."
|
||||||
|
},
|
||||||
|
"time": "2025-04-30T23:37:27+00:00",
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"bamarni-bin": {
|
||||||
|
"bin-links": true,
|
||||||
|
"forward-command": false
|
||||||
|
},
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "5.6-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"installation-source": "dist",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Dotenv\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"BSD-3-Clause"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Graham Campbell",
|
||||||
|
"email": "hello@gjcampbell.co.uk",
|
||||||
|
"homepage": "https://github.com/GrahamCampbell"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Vance Lucas",
|
||||||
|
"email": "vance@vancelucas.com",
|
||||||
|
"homepage": "https://github.com/vlucas"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.",
|
||||||
|
"keywords": [
|
||||||
|
"dotenv",
|
||||||
|
"env",
|
||||||
|
"environment"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/vlucas/phpdotenv/issues",
|
||||||
|
"source": "https://github.com/vlucas/phpdotenv/tree/v5.6.2"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/GrahamCampbell",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv",
|
||||||
|
"type": "tidelift"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"install-path": "../vlucas/phpdotenv"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"dev-package-names": []
|
||||||
|
}
|
||||||
77
vendor/claudecio/axiumphp/vendor/composer/installed.php
vendored
Executable file
77
vendor/claudecio/axiumphp/vendor/composer/installed.php
vendored
Executable file
@@ -0,0 +1,77 @@
|
|||||||
|
<?php return array(
|
||||||
|
'root' => array(
|
||||||
|
'name' => 'claudecio/axiumphp',
|
||||||
|
'pretty_version' => 'dev-main',
|
||||||
|
'version' => 'dev-main',
|
||||||
|
'reference' => 'bff4a2e22b0ca200aee4d6a2c7619bcfdee139dc',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../../',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev' => true,
|
||||||
|
),
|
||||||
|
'versions' => array(
|
||||||
|
'claudecio/axiumphp' => array(
|
||||||
|
'pretty_version' => 'dev-main',
|
||||||
|
'version' => 'dev-main',
|
||||||
|
'reference' => 'bff4a2e22b0ca200aee4d6a2c7619bcfdee139dc',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../../',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => false,
|
||||||
|
),
|
||||||
|
'graham-campbell/result-type' => array(
|
||||||
|
'pretty_version' => 'v1.1.3',
|
||||||
|
'version' => '1.1.3.0',
|
||||||
|
'reference' => '3ba905c11371512af9d9bdd27d99b782216b6945',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../graham-campbell/result-type',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => false,
|
||||||
|
),
|
||||||
|
'phpoption/phpoption' => array(
|
||||||
|
'pretty_version' => '1.9.3',
|
||||||
|
'version' => '1.9.3.0',
|
||||||
|
'reference' => 'e3fac8b24f56113f7cb96af14958c0dd16330f54',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../phpoption/phpoption',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => false,
|
||||||
|
),
|
||||||
|
'symfony/polyfill-ctype' => array(
|
||||||
|
'pretty_version' => 'v1.32.0',
|
||||||
|
'version' => '1.32.0.0',
|
||||||
|
'reference' => 'a3cc8b044a6ea513310cbd48ef7333b384945638',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../symfony/polyfill-ctype',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => false,
|
||||||
|
),
|
||||||
|
'symfony/polyfill-mbstring' => array(
|
||||||
|
'pretty_version' => 'v1.32.0',
|
||||||
|
'version' => '1.32.0.0',
|
||||||
|
'reference' => '6d857f4d76bd4b343eac26d6b539585d2bc56493',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../symfony/polyfill-mbstring',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => false,
|
||||||
|
),
|
||||||
|
'symfony/polyfill-php80' => array(
|
||||||
|
'pretty_version' => 'v1.32.0',
|
||||||
|
'version' => '1.32.0.0',
|
||||||
|
'reference' => '0cc9dd0f17f61d8131e7df6b84bd344899fe2608',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../symfony/polyfill-php80',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => false,
|
||||||
|
),
|
||||||
|
'vlucas/phpdotenv' => array(
|
||||||
|
'pretty_version' => 'v5.6.2',
|
||||||
|
'version' => '5.6.2.0',
|
||||||
|
'reference' => '24ac4c74f91ee2c193fa1aaa5c249cb0822809af',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../vlucas/phpdotenv',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => false,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
26
vendor/claudecio/axiumphp/vendor/composer/platform_check.php
vendored
Executable file
26
vendor/claudecio/axiumphp/vendor/composer/platform_check.php
vendored
Executable file
@@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// platform_check.php @generated by Composer
|
||||||
|
|
||||||
|
$issues = array();
|
||||||
|
|
||||||
|
if (!(PHP_VERSION_ID >= 80100)) {
|
||||||
|
$issues[] = 'Your Composer dependencies require a PHP version ">= 8.1.0". You are running ' . PHP_VERSION . '.';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($issues) {
|
||||||
|
if (!headers_sent()) {
|
||||||
|
header('HTTP/1.1 500 Internal Server Error');
|
||||||
|
}
|
||||||
|
if (!ini_get('display_errors')) {
|
||||||
|
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
|
||||||
|
fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
|
||||||
|
} elseif (!headers_sent()) {
|
||||||
|
echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
trigger_error(
|
||||||
|
'Composer detected issues in your platform: ' . implode(' ', $issues),
|
||||||
|
E_USER_ERROR
|
||||||
|
);
|
||||||
|
}
|
||||||
21
vendor/claudecio/axiumphp/vendor/graham-campbell/result-type/LICENSE
vendored
Executable file
21
vendor/claudecio/axiumphp/vendor/graham-campbell/result-type/LICENSE
vendored
Executable file
@@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2020-2024 Graham Campbell <hello@gjcampbell.co.uk>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
33
vendor/claudecio/axiumphp/vendor/graham-campbell/result-type/composer.json
vendored
Executable file
33
vendor/claudecio/axiumphp/vendor/graham-campbell/result-type/composer.json
vendored
Executable file
@@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"name": "graham-campbell/result-type",
|
||||||
|
"description": "An Implementation Of The Result Type",
|
||||||
|
"keywords": ["result", "result-type", "Result", "Result Type", "Result-Type", "Graham Campbell", "GrahamCampbell"],
|
||||||
|
"license": "MIT",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Graham Campbell",
|
||||||
|
"email": "hello@gjcampbell.co.uk",
|
||||||
|
"homepage": "https://github.com/GrahamCampbell"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"php": "^7.2.5 || ^8.0",
|
||||||
|
"phpoption/phpoption": "^1.9.3"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"GrahamCampbell\\ResultType\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload-dev": {
|
||||||
|
"psr-4": {
|
||||||
|
"GrahamCampbell\\Tests\\ResultType\\": "tests/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"preferred-install": "dist"
|
||||||
|
}
|
||||||
|
}
|
||||||
121
vendor/claudecio/axiumphp/vendor/graham-campbell/result-type/src/Error.php
vendored
Executable file
121
vendor/claudecio/axiumphp/vendor/graham-campbell/result-type/src/Error.php
vendored
Executable file
@@ -0,0 +1,121 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Result Type.
|
||||||
|
*
|
||||||
|
* (c) Graham Campbell <hello@gjcampbell.co.uk>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace GrahamCampbell\ResultType;
|
||||||
|
|
||||||
|
use PhpOption\None;
|
||||||
|
use PhpOption\Some;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template T
|
||||||
|
* @template E
|
||||||
|
*
|
||||||
|
* @extends \GrahamCampbell\ResultType\Result<T,E>
|
||||||
|
*/
|
||||||
|
final class Error extends Result
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var E
|
||||||
|
*/
|
||||||
|
private $value;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal constructor for an error value.
|
||||||
|
*
|
||||||
|
* @param E $value
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function __construct($value)
|
||||||
|
{
|
||||||
|
$this->value = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new error value.
|
||||||
|
*
|
||||||
|
* @template F
|
||||||
|
*
|
||||||
|
* @param F $value
|
||||||
|
*
|
||||||
|
* @return \GrahamCampbell\ResultType\Result<T,F>
|
||||||
|
*/
|
||||||
|
public static function create($value)
|
||||||
|
{
|
||||||
|
return new self($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the success option value.
|
||||||
|
*
|
||||||
|
* @return \PhpOption\Option<T>
|
||||||
|
*/
|
||||||
|
public function success()
|
||||||
|
{
|
||||||
|
return None::create();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map over the success value.
|
||||||
|
*
|
||||||
|
* @template S
|
||||||
|
*
|
||||||
|
* @param callable(T):S $f
|
||||||
|
*
|
||||||
|
* @return \GrahamCampbell\ResultType\Result<S,E>
|
||||||
|
*/
|
||||||
|
public function map(callable $f)
|
||||||
|
{
|
||||||
|
return self::create($this->value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flat map over the success value.
|
||||||
|
*
|
||||||
|
* @template S
|
||||||
|
* @template F
|
||||||
|
*
|
||||||
|
* @param callable(T):\GrahamCampbell\ResultType\Result<S,F> $f
|
||||||
|
*
|
||||||
|
* @return \GrahamCampbell\ResultType\Result<S,F>
|
||||||
|
*/
|
||||||
|
public function flatMap(callable $f)
|
||||||
|
{
|
||||||
|
/** @var \GrahamCampbell\ResultType\Result<S,F> */
|
||||||
|
return self::create($this->value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the error option value.
|
||||||
|
*
|
||||||
|
* @return \PhpOption\Option<E>
|
||||||
|
*/
|
||||||
|
public function error()
|
||||||
|
{
|
||||||
|
return Some::create($this->value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map over the error value.
|
||||||
|
*
|
||||||
|
* @template F
|
||||||
|
*
|
||||||
|
* @param callable(E):F $f
|
||||||
|
*
|
||||||
|
* @return \GrahamCampbell\ResultType\Result<T,F>
|
||||||
|
*/
|
||||||
|
public function mapError(callable $f)
|
||||||
|
{
|
||||||
|
return self::create($f($this->value));
|
||||||
|
}
|
||||||
|
}
|
||||||
69
vendor/claudecio/axiumphp/vendor/graham-campbell/result-type/src/Result.php
vendored
Executable file
69
vendor/claudecio/axiumphp/vendor/graham-campbell/result-type/src/Result.php
vendored
Executable file
@@ -0,0 +1,69 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Result Type.
|
||||||
|
*
|
||||||
|
* (c) Graham Campbell <hello@gjcampbell.co.uk>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace GrahamCampbell\ResultType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template T
|
||||||
|
* @template E
|
||||||
|
*/
|
||||||
|
abstract class Result
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get the success option value.
|
||||||
|
*
|
||||||
|
* @return \PhpOption\Option<T>
|
||||||
|
*/
|
||||||
|
abstract public function success();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map over the success value.
|
||||||
|
*
|
||||||
|
* @template S
|
||||||
|
*
|
||||||
|
* @param callable(T):S $f
|
||||||
|
*
|
||||||
|
* @return \GrahamCampbell\ResultType\Result<S,E>
|
||||||
|
*/
|
||||||
|
abstract public function map(callable $f);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flat map over the success value.
|
||||||
|
*
|
||||||
|
* @template S
|
||||||
|
* @template F
|
||||||
|
*
|
||||||
|
* @param callable(T):\GrahamCampbell\ResultType\Result<S,F> $f
|
||||||
|
*
|
||||||
|
* @return \GrahamCampbell\ResultType\Result<S,F>
|
||||||
|
*/
|
||||||
|
abstract public function flatMap(callable $f);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the error option value.
|
||||||
|
*
|
||||||
|
* @return \PhpOption\Option<E>
|
||||||
|
*/
|
||||||
|
abstract public function error();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map over the error value.
|
||||||
|
*
|
||||||
|
* @template F
|
||||||
|
*
|
||||||
|
* @param callable(E):F $f
|
||||||
|
*
|
||||||
|
* @return \GrahamCampbell\ResultType\Result<T,F>
|
||||||
|
*/
|
||||||
|
abstract public function mapError(callable $f);
|
||||||
|
}
|
||||||
120
vendor/claudecio/axiumphp/vendor/graham-campbell/result-type/src/Success.php
vendored
Executable file
120
vendor/claudecio/axiumphp/vendor/graham-campbell/result-type/src/Success.php
vendored
Executable file
@@ -0,0 +1,120 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Result Type.
|
||||||
|
*
|
||||||
|
* (c) Graham Campbell <hello@gjcampbell.co.uk>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace GrahamCampbell\ResultType;
|
||||||
|
|
||||||
|
use PhpOption\None;
|
||||||
|
use PhpOption\Some;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template T
|
||||||
|
* @template E
|
||||||
|
*
|
||||||
|
* @extends \GrahamCampbell\ResultType\Result<T,E>
|
||||||
|
*/
|
||||||
|
final class Success extends Result
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var T
|
||||||
|
*/
|
||||||
|
private $value;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal constructor for a success value.
|
||||||
|
*
|
||||||
|
* @param T $value
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function __construct($value)
|
||||||
|
{
|
||||||
|
$this->value = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new error value.
|
||||||
|
*
|
||||||
|
* @template S
|
||||||
|
*
|
||||||
|
* @param S $value
|
||||||
|
*
|
||||||
|
* @return \GrahamCampbell\ResultType\Result<S,E>
|
||||||
|
*/
|
||||||
|
public static function create($value)
|
||||||
|
{
|
||||||
|
return new self($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the success option value.
|
||||||
|
*
|
||||||
|
* @return \PhpOption\Option<T>
|
||||||
|
*/
|
||||||
|
public function success()
|
||||||
|
{
|
||||||
|
return Some::create($this->value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map over the success value.
|
||||||
|
*
|
||||||
|
* @template S
|
||||||
|
*
|
||||||
|
* @param callable(T):S $f
|
||||||
|
*
|
||||||
|
* @return \GrahamCampbell\ResultType\Result<S,E>
|
||||||
|
*/
|
||||||
|
public function map(callable $f)
|
||||||
|
{
|
||||||
|
return self::create($f($this->value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flat map over the success value.
|
||||||
|
*
|
||||||
|
* @template S
|
||||||
|
* @template F
|
||||||
|
*
|
||||||
|
* @param callable(T):\GrahamCampbell\ResultType\Result<S,F> $f
|
||||||
|
*
|
||||||
|
* @return \GrahamCampbell\ResultType\Result<S,F>
|
||||||
|
*/
|
||||||
|
public function flatMap(callable $f)
|
||||||
|
{
|
||||||
|
return $f($this->value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the error option value.
|
||||||
|
*
|
||||||
|
* @return \PhpOption\Option<E>
|
||||||
|
*/
|
||||||
|
public function error()
|
||||||
|
{
|
||||||
|
return None::create();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map over the error value.
|
||||||
|
*
|
||||||
|
* @template F
|
||||||
|
*
|
||||||
|
* @param callable(E):F $f
|
||||||
|
*
|
||||||
|
* @return \GrahamCampbell\ResultType\Result<T,F>
|
||||||
|
*/
|
||||||
|
public function mapError(callable $f)
|
||||||
|
{
|
||||||
|
return self::create($this->value);
|
||||||
|
}
|
||||||
|
}
|
||||||
201
vendor/claudecio/axiumphp/vendor/phpoption/phpoption/LICENSE
vendored
Executable file
201
vendor/claudecio/axiumphp/vendor/phpoption/phpoption/LICENSE
vendored
Executable file
@@ -0,0 +1,201 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
50
vendor/claudecio/axiumphp/vendor/phpoption/phpoption/composer.json
vendored
Executable file
50
vendor/claudecio/axiumphp/vendor/phpoption/phpoption/composer.json
vendored
Executable file
@@ -0,0 +1,50 @@
|
|||||||
|
{
|
||||||
|
"name": "phpoption/phpoption",
|
||||||
|
"description": "Option Type for PHP",
|
||||||
|
"keywords": ["php", "option", "language", "type"],
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Johannes M. Schmitt",
|
||||||
|
"email": "schmittjoh@gmail.com",
|
||||||
|
"homepage": "https://github.com/schmittjoh"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Graham Campbell",
|
||||||
|
"email": "hello@gjcampbell.co.uk",
|
||||||
|
"homepage": "https://github.com/GrahamCampbell"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"php": "^7.2.5 || ^8.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"bamarni/composer-bin-plugin": "^1.8.2",
|
||||||
|
"phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"PhpOption\\": "src/PhpOption/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload-dev": {
|
||||||
|
"psr-4": {
|
||||||
|
"PhpOption\\Tests\\": "tests/PhpOption/Tests/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"allow-plugins": {
|
||||||
|
"bamarni/composer-bin-plugin": true
|
||||||
|
},
|
||||||
|
"preferred-install": "dist"
|
||||||
|
},
|
||||||
|
"extra": {
|
||||||
|
"bamarni-bin": {
|
||||||
|
"bin-links": true,
|
||||||
|
"forward-command": false
|
||||||
|
},
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "1.9-dev"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
175
vendor/claudecio/axiumphp/vendor/phpoption/phpoption/src/PhpOption/LazyOption.php
vendored
Executable file
175
vendor/claudecio/axiumphp/vendor/phpoption/phpoption/src/PhpOption/LazyOption.php
vendored
Executable file
@@ -0,0 +1,175 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2012 Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace PhpOption;
|
||||||
|
|
||||||
|
use Traversable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template T
|
||||||
|
*
|
||||||
|
* @extends Option<T>
|
||||||
|
*/
|
||||||
|
final class LazyOption extends Option
|
||||||
|
{
|
||||||
|
/** @var callable(mixed...):(Option<T>) */
|
||||||
|
private $callback;
|
||||||
|
|
||||||
|
/** @var array<int, mixed> */
|
||||||
|
private $arguments;
|
||||||
|
|
||||||
|
/** @var Option<T>|null */
|
||||||
|
private $option;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template S
|
||||||
|
* @param callable(mixed...):(Option<S>) $callback
|
||||||
|
* @param array<int, mixed> $arguments
|
||||||
|
*
|
||||||
|
* @return LazyOption<S>
|
||||||
|
*/
|
||||||
|
public static function create($callback, array $arguments = []): self
|
||||||
|
{
|
||||||
|
return new self($callback, $arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param callable(mixed...):(Option<T>) $callback
|
||||||
|
* @param array<int, mixed> $arguments
|
||||||
|
*/
|
||||||
|
public function __construct($callback, array $arguments = [])
|
||||||
|
{
|
||||||
|
if (!is_callable($callback)) {
|
||||||
|
throw new \InvalidArgumentException('Invalid callback given');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->callback = $callback;
|
||||||
|
$this->arguments = $arguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isDefined(): bool
|
||||||
|
{
|
||||||
|
return $this->option()->isDefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isEmpty(): bool
|
||||||
|
{
|
||||||
|
return $this->option()->isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get()
|
||||||
|
{
|
||||||
|
return $this->option()->get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getOrElse($default)
|
||||||
|
{
|
||||||
|
return $this->option()->getOrElse($default);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getOrCall($callable)
|
||||||
|
{
|
||||||
|
return $this->option()->getOrCall($callable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getOrThrow(\Exception $ex)
|
||||||
|
{
|
||||||
|
return $this->option()->getOrThrow($ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function orElse(Option $else)
|
||||||
|
{
|
||||||
|
return $this->option()->orElse($else);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function ifDefined($callable)
|
||||||
|
{
|
||||||
|
$this->option()->forAll($callable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function forAll($callable)
|
||||||
|
{
|
||||||
|
return $this->option()->forAll($callable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function map($callable)
|
||||||
|
{
|
||||||
|
return $this->option()->map($callable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function flatMap($callable)
|
||||||
|
{
|
||||||
|
return $this->option()->flatMap($callable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function filter($callable)
|
||||||
|
{
|
||||||
|
return $this->option()->filter($callable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function filterNot($callable)
|
||||||
|
{
|
||||||
|
return $this->option()->filterNot($callable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function select($value)
|
||||||
|
{
|
||||||
|
return $this->option()->select($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function reject($value)
|
||||||
|
{
|
||||||
|
return $this->option()->reject($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Traversable<T>
|
||||||
|
*/
|
||||||
|
public function getIterator(): Traversable
|
||||||
|
{
|
||||||
|
return $this->option()->getIterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function foldLeft($initialValue, $callable)
|
||||||
|
{
|
||||||
|
return $this->option()->foldLeft($initialValue, $callable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function foldRight($initialValue, $callable)
|
||||||
|
{
|
||||||
|
return $this->option()->foldRight($initialValue, $callable);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Option<T>
|
||||||
|
*/
|
||||||
|
private function option(): Option
|
||||||
|
{
|
||||||
|
if (null === $this->option) {
|
||||||
|
/** @var mixed */
|
||||||
|
$option = call_user_func_array($this->callback, $this->arguments);
|
||||||
|
if ($option instanceof Option) {
|
||||||
|
$this->option = $option;
|
||||||
|
} else {
|
||||||
|
throw new \RuntimeException(sprintf('Expected instance of %s', Option::class));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->option;
|
||||||
|
}
|
||||||
|
}
|
||||||
136
vendor/claudecio/axiumphp/vendor/phpoption/phpoption/src/PhpOption/None.php
vendored
Executable file
136
vendor/claudecio/axiumphp/vendor/phpoption/phpoption/src/PhpOption/None.php
vendored
Executable file
@@ -0,0 +1,136 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2012 Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace PhpOption;
|
||||||
|
|
||||||
|
use EmptyIterator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @extends Option<mixed>
|
||||||
|
*/
|
||||||
|
final class None extends Option
|
||||||
|
{
|
||||||
|
/** @var None|null */
|
||||||
|
private static $instance;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return None
|
||||||
|
*/
|
||||||
|
public static function create(): self
|
||||||
|
{
|
||||||
|
if (null === self::$instance) {
|
||||||
|
self::$instance = new self();
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get()
|
||||||
|
{
|
||||||
|
throw new \RuntimeException('None has no value.');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getOrCall($callable)
|
||||||
|
{
|
||||||
|
return $callable();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getOrElse($default)
|
||||||
|
{
|
||||||
|
return $default;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getOrThrow(\Exception $ex)
|
||||||
|
{
|
||||||
|
throw $ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isEmpty(): bool
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isDefined(): bool
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function orElse(Option $else)
|
||||||
|
{
|
||||||
|
return $else;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function ifDefined($callable)
|
||||||
|
{
|
||||||
|
// Just do nothing in that case.
|
||||||
|
}
|
||||||
|
|
||||||
|
public function forAll($callable)
|
||||||
|
{
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function map($callable)
|
||||||
|
{
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function flatMap($callable)
|
||||||
|
{
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function filter($callable)
|
||||||
|
{
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function filterNot($callable)
|
||||||
|
{
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function select($value)
|
||||||
|
{
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function reject($value)
|
||||||
|
{
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getIterator(): EmptyIterator
|
||||||
|
{
|
||||||
|
return new EmptyIterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function foldLeft($initialValue, $callable)
|
||||||
|
{
|
||||||
|
return $initialValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function foldRight($initialValue, $callable)
|
||||||
|
{
|
||||||
|
return $initialValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function __construct()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user