first commit
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
namespace WorkbloomERP\Constants;
|
||||
|
||||
class BrasilUfsConst {
|
||||
public const UFS = [
|
||||
'AC' => 'Acre',
|
||||
'AL' => 'Alagoas',
|
||||
'AP' => 'Amapá',
|
||||
'AM' => 'Amazonas',
|
||||
'BA' => 'Bahia',
|
||||
'CE' => 'Ceará',
|
||||
'DF' => 'Distrito Federal',
|
||||
'ES' => 'Espírito Santo',
|
||||
'EX' => 'Estrangeiro',
|
||||
'GO' => 'Goiás',
|
||||
'MA' => 'Maranhão',
|
||||
'MT' => 'Mato Grosso',
|
||||
'MS' => 'Mato Grosso do Sul',
|
||||
'MG' => 'Minas Gerais',
|
||||
'PA' => 'Pará',
|
||||
'PB' => 'Paraíba',
|
||||
'PR' => 'Paraná',
|
||||
'PE' => 'Pernambuco',
|
||||
'PI' => 'Piauí',
|
||||
'RJ' => 'Rio de Janeiro',
|
||||
'RN' => 'Rio Grande do Norte',
|
||||
'RS' => 'Rio Grande do Sul',
|
||||
'RO' => 'Rondônia',
|
||||
'RR' => 'Roraima',
|
||||
'SC' => 'Santa Catarina',
|
||||
'SP' => 'São Paulo',
|
||||
'SE' => 'Sergipe',
|
||||
'TO' => 'Tocantins'
|
||||
];
|
||||
|
||||
public static function getAll(): array {
|
||||
return self::UFS;
|
||||
}
|
||||
|
||||
public static function exists(string $uf): bool {
|
||||
return isset(self::UFS[strtoupper($uf)]);
|
||||
}
|
||||
|
||||
public static function getName(string $uf): ?string {
|
||||
return self::UFS[strtoupper($uf)] ?? null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
namespace WorkbloomERP\Constants;
|
||||
|
||||
class SpedCRTConst {
|
||||
public const CRT = [
|
||||
"1" => "Simples Nacional",
|
||||
"2" => "Simples Nacional - Excesso de Sublimite de Receita Bruta",
|
||||
"3" => "Regime Normal",
|
||||
"4" => "MEI - Microempreendedor Individual"
|
||||
];
|
||||
|
||||
public static function getAll(): array {
|
||||
return self::CRT;
|
||||
}
|
||||
|
||||
public static function getDescription(string|int|null $code): string {
|
||||
return self::CRT[$code] ?? 'Código de regime tributário desconhecido.';
|
||||
}
|
||||
|
||||
public static function exists(string|int|null $code): bool {
|
||||
return isset(self::CRT[$code]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,262 @@
|
||||
<?php
|
||||
namespace WorkbloomERP\Constants;
|
||||
|
||||
class SpedPaisesConst {
|
||||
public const PAISES = [
|
||||
"0132" => "AFEGANISTAO",
|
||||
"7560" => "AFRICA DO SUL",
|
||||
"0175" => "ALBANIA",
|
||||
"0230" => "ALEMANHA",
|
||||
"0370" => "ANDORRA",
|
||||
"0400" => "ANGOLA",
|
||||
"0418" => "ANGUILLA",
|
||||
"0434" => "ANTIGUA E BARBUDA",
|
||||
"0531" => "ARABIA SAUDITA",
|
||||
"0590" => "ARGELIA",
|
||||
"0639" => "ARGENTINA",
|
||||
"0647" => "ARMENIA",
|
||||
"0655" => "ARUBA",
|
||||
"0698" => "AUSTRALIA",
|
||||
"0728" => "AUSTRIA",
|
||||
"0736" => "AZERBAIDJAO",
|
||||
"0779" => "BAHAMAS, ILHAS",
|
||||
"0817" => "BANGLADESH",
|
||||
"0833" => "BARBADOS",
|
||||
"0809" => "BAREIN",
|
||||
"0850" => "BELARUS",
|
||||
"0876" => "BELGICA",
|
||||
"0884" => "BELIZE",
|
||||
"2291" => "BENIN",
|
||||
"0906" => "BERMUDAS",
|
||||
"0973" => "BOLIVIA",
|
||||
"0990" => "BONAIRE, SAINT EUSTATIUS E SABA",
|
||||
"0981" => "BOSNIA-HERZEGOVINA",
|
||||
"1015" => "BOTSUANA",
|
||||
"1098" => "BOUVET, ILHA",
|
||||
"1058" => "BRASIL",
|
||||
"1082" => "BRUNEI",
|
||||
"1112" => "BULGARIA",
|
||||
"0310" => "BURKINA FASO",
|
||||
"1155" => "BURUNDI",
|
||||
"1198" => "BUTAO",
|
||||
"1279" => "CABO VERDE",
|
||||
"1457" => "CAMAROES",
|
||||
"1414" => "CAMBOJA",
|
||||
"1490" => "CANADA",
|
||||
"1546" => "CATAR",
|
||||
"1538" => "CAZAQUISTAO",
|
||||
"7889" => "CHADE",
|
||||
"1589" => "CHILE",
|
||||
"1600" => "CHINA",
|
||||
"1635" => "CHIPRE",
|
||||
"5118" => "CHRISTMAS, ILHA (NAVIDAD)",
|
||||
"7412" => "CINGAPURA",
|
||||
"1651" => "COCOS (KEELINGS)",
|
||||
"1694" => "COLOMBIA",
|
||||
"1775" => "CONGO",
|
||||
"1872" => "COREIA DO NORTE",
|
||||
"1937" => "COSTA DO MARFIM",
|
||||
"1376" => "CAYMAN",
|
||||
"1732" => "COMORES",
|
||||
"1830" => "COOK",
|
||||
"1902" => "COREIA DO SUL",
|
||||
"1961" => "COSTA RICA",
|
||||
"1953" => "CROACIA",
|
||||
"1996" => "CUBA",
|
||||
"2003" => "CURACAO",
|
||||
"2321" => "DINAMARCA",
|
||||
"7838" => "DJIBUTI",
|
||||
"2356" => "DOMINICA",
|
||||
"2402" => "EGITO",
|
||||
"6874" => "EL SALVADOR",
|
||||
"2445" => "EMIRADOS ARABES UNIDOS",
|
||||
"2399" => "EQUADOR",
|
||||
"2437" => "ERITREIA",
|
||||
"2470" => "ESLOVAQUIA",
|
||||
"2461" => "ESLOVENIA",
|
||||
"2453" => "ESPANHA",
|
||||
"2496" => "ESTADOS UNIDOS",
|
||||
"2518" => "ESTONIA",
|
||||
"7544" => "ESWATINI (ANTIGA SUAZILANDIA)",
|
||||
"2534" => "ETIOPIA",
|
||||
"2550" => "FALKLAND (MALVINAS)",
|
||||
"2593" => "FAROE",
|
||||
"8702" => "FIJI",
|
||||
"2674" => "FILIPINAS",
|
||||
"2712" => "FINLANDIA",
|
||||
"2755" => "FRANCA",
|
||||
"2810" => "GABAO",
|
||||
"2852" => "GAMBIA",
|
||||
"2895" => "GANA",
|
||||
"2917" => "GEORGIA",
|
||||
"2933" => "GIBRALTAR",
|
||||
"2976" => "GRANADA",
|
||||
"3018" => "GRECIA",
|
||||
"3050" => "GROENLANDIA",
|
||||
"3093" => "GUADALUPE",
|
||||
"3131" => "GUAM",
|
||||
"3174" => "GUATEMALA",
|
||||
"1504" => "GUERNSEY, ILHA DO CANAL (INCLUI ALDERNEY E SARK)",
|
||||
"3379" => "GUIANA",
|
||||
"3255" => "GUIANA FRANCESA",
|
||||
"3298" => "GUINE",
|
||||
"3344" => "GUINE-BISSAU",
|
||||
"3310" => "GUINE-EQUATORIAL",
|
||||
"3417" => "HAITI",
|
||||
"5738" => "HOLANDA (PAISES BAIXOS)",
|
||||
"3450" => "HONDURAS",
|
||||
"3514" => "HONG KONG",
|
||||
"3557" => "HUNGRIA",
|
||||
"3573" => "IEMEN",
|
||||
"3611" => "INDIA",
|
||||
"3654" => "INDONESIA",
|
||||
"3727" => "IRA",
|
||||
"3697" => "IRAQUE",
|
||||
"3751" => "IRLANDA",
|
||||
"3794" => "ISLANDIA",
|
||||
"3832" => "ISRAEL",
|
||||
"3867" => "ITALIA",
|
||||
"3913" => "JAMAICA",
|
||||
"3999" => "JAPAO",
|
||||
"1508" => "JERSEY, ILHA DO CANAL",
|
||||
"3964" => "JOHNSTON",
|
||||
"4030" => "JORDANIA",
|
||||
"4111" => "KIRIBATI",
|
||||
"1988" => "KUWEIT (ou Coveite)",
|
||||
"4200" => "LAOS",
|
||||
"4260" => "LESOTO",
|
||||
"4278" => "LETONIA",
|
||||
"4316" => "LIBANO",
|
||||
"4340" => "LIBERIA",
|
||||
"4383" => "LIBIA",
|
||||
"4405" => "LIECHTENSTEIN",
|
||||
"4421" => "LITUANIA",
|
||||
"4456" => "LUXEMBURGO",
|
||||
"4472" => "MACAU",
|
||||
"4499" => "MACEDONIA",
|
||||
"4502" => "MADAGASCAR",
|
||||
"4553" => "MALASIA",
|
||||
"4588" => "MALAVI",
|
||||
"4618" => "MALDIVAS",
|
||||
"4642" => "MALI",
|
||||
"4677" => "MALTA",
|
||||
"3595" => "MAN, ILHA DE",
|
||||
"4723" => "MARIANAS DO NORTE",
|
||||
"4740" => "MARROCOS",
|
||||
"4766" => "MARSHALL, ILHAS",
|
||||
"4774" => "MARTINICA",
|
||||
"4855" => "MAURICIO",
|
||||
"4880" => "MAURITANIA",
|
||||
"4936" => "MEXICO",
|
||||
"0930" => "MIANMAR",
|
||||
"4995" => "MICRONESIA",
|
||||
"5053" => "MOCAMBIQUE",
|
||||
"4944" => "MOLDAVIA",
|
||||
"4952" => "MONACO",
|
||||
"4979" => "MONGOLIA",
|
||||
"4985" => "MONTENEGRO",
|
||||
"5010" => "MONTSERRAT",
|
||||
"5070" => "NAMIBIA",
|
||||
"5088" => "NAURU",
|
||||
"5177" => "NEPAL",
|
||||
"5215" => "NICARAGUA",
|
||||
"5258" => "NIGER",
|
||||
"5282" => "NIGERIA",
|
||||
"5312" => "NIUE",
|
||||
"5355" => "NORFOLK, ILHA",
|
||||
"5380" => "NORUEGA",
|
||||
"5428" => "NOVA CALEDONIA",
|
||||
"5487" => "NOVA ZELANDIA",
|
||||
"5568" => "OMA",
|
||||
"5665" => "PACIFICO, ILHAS DO (POSSESSAO DOS EUA)",
|
||||
"5754" => "PALAU",
|
||||
"5780" => "PALESTINA",
|
||||
"5800" => "PANAMA",
|
||||
"5452" => "PAPUA NOVA GUINE",
|
||||
"5762" => "PAQUISTAO",
|
||||
"5860" => "PARAGUAI",
|
||||
"5894" => "PERU",
|
||||
"5932" => "PITCAIRN",
|
||||
"5991" => "POLINESIA FRANCESA",
|
||||
"6033" => "POLONIA",
|
||||
"6114" => "PORTO RICO",
|
||||
"6076" => "PORTUGAL",
|
||||
"6238" => "QUENIA",
|
||||
"6254" => "QUIRGUISTAO",
|
||||
"6289" => "REINO UNIDO",
|
||||
"6408" => "REPUBLICA CENTRO-AFRICANA",
|
||||
"8885" => "REPUBLICA DEMOCRATICA DO CONGO",
|
||||
"6475" => "REPUBLICA DOMINICANA",
|
||||
"7919" => "REPUBLICA TCHECA",
|
||||
"6602" => "REUNIAO",
|
||||
"6700" => "ROMENIA",
|
||||
"6750" => "RUANDA",
|
||||
"6769" => "RUSSIA",
|
||||
"6858" => "SAARA OCIDENTAL",
|
||||
"6777" => "SALOMAO, ILHAS",
|
||||
"6904" => "SAMOA",
|
||||
"6912" => "SAMOA AMERICANA",
|
||||
"6971" => "SAN MARINO",
|
||||
"7102" => "SANTA HELENA",
|
||||
"7153" => "SANTA LUCIA",
|
||||
"6955" => "SAO CRISTOVAO E NEVES",
|
||||
"6980" => "SAO MARTINHO, ILHA DE (PARTE FRANCESA)",
|
||||
"6998" => "SAO MARTINHO, ILHA DE (PARTE HOLANDESA)",
|
||||
"7005" => "SAO PEDRO E MIQUELON",
|
||||
"7200" => "SAO TOME E PRINCIPE",
|
||||
"7056" => "SAO VICENTE E GRANADINAS",
|
||||
"7315" => "SEICHELES",
|
||||
"7285" => "SENEGAL",
|
||||
"7358" => "SERRA LEOA",
|
||||
"7370" => "SERVIA",
|
||||
"7447" => "SIRIA",
|
||||
"7480" => "SOMALIA",
|
||||
"7501" => "SRI LANKA",
|
||||
"7595" => "SUDAO",
|
||||
"7600" => "SUDÃO DO SUL",
|
||||
"7641" => "SUECIA",
|
||||
"7676" => "SUICA",
|
||||
"7706" => "SURINAME",
|
||||
"7552" => "SVALBARD E JAN MAYEN",
|
||||
"7722" => "TADJIQUISTAO",
|
||||
"7765" => "TAILANDIA",
|
||||
"1619" => "TAIWAN",
|
||||
"7803" => "TANZANIA",
|
||||
"7820" => "TERRITORIO BRITANICO OCEANO INDICO",
|
||||
"7951" => "TIMOR LESTE",
|
||||
"8001" => "TOGO",
|
||||
"8109" => "TONGA",
|
||||
"8052" => "TOQUELAU",
|
||||
"8150" => "TRINIDAD E TOBAGO",
|
||||
"8206" => "TUNISIA",
|
||||
"8230" => "TURCAS E CAICOS",
|
||||
"8249" => "TURCOMENISTAO",
|
||||
"8273" => "TURQUIA",
|
||||
"8281" => "TUVALU",
|
||||
"8311" => "UCRANIA",
|
||||
"8338" => "UGANDA",
|
||||
"8451" => "URUGUAI",
|
||||
"8478" => "UZBEQUISTAO",
|
||||
"5517" => "VANUATU",
|
||||
"8486" => "VATICANO",
|
||||
"8508" => "VENEZUELA",
|
||||
"8583" => "VIETNA",
|
||||
"8630" => "VIRGENS, ILHAS (BRITANICAS)",
|
||||
"8664" => "VIRGENS, ILHAS (EUA)",
|
||||
"8753" => "WALLIS E FUTUNA, ILHAS",
|
||||
"8907" => "ZAMBIA",
|
||||
"6653" => "ZIMBABUE"
|
||||
];
|
||||
|
||||
public static function getAll(): array {
|
||||
return self::PAISES;
|
||||
}
|
||||
|
||||
public static function getNomeByCod(string $codigo): ?string {
|
||||
return self::PAISES[$codigo] ?? null;
|
||||
}
|
||||
|
||||
public static function exists(string $codigo): bool {
|
||||
return isset(self::PAISES[$codigo]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
namespace WorkbloomERP\Database;
|
||||
|
||||
use WorkbloomERP\Services\DBService;
|
||||
|
||||
class DBFactory {
|
||||
/**
|
||||
* Método para criação simplificada de instâncias do serviço de banco de dados.
|
||||
*
|
||||
* Este método implementa o padrão de projeto Factory, permitindo que o desenvolvedor obtenha
|
||||
* uma instância pronta do **DBService** sem a necessidade de gerenciar manualmente o operador `new`.
|
||||
* Sua principal vantagem é a flexibilidade para alternar entre diferentes conexões e esquemas
|
||||
* (schemas) de banco de dados através de parâmetros opcionais, facilitando a operação em
|
||||
* ambientes multi-banco ou arquiteturas multi-tenant.
|
||||
*
|
||||
*
|
||||
*
|
||||
* ---
|
||||
* ## Funcionalidades Principais
|
||||
* 1. **Abstração de Instanciação:** Centraliza a criação do objeto, permitindo futuras implementações de Singletons ou Service Locators sem quebrar o código cliente.
|
||||
* 2. **Configuração On-the-fly:** Permite definir a conexão (ex: 'mysql_prod', 'sqlite_test') e o schema de trabalho no momento da criação.
|
||||
* 3. **Fluidez:** Ideal para ser utilizado em chamadas encadeadas (ex: `DBService::make()->select(...)`), tornando o código mais legível e conciso.
|
||||
*
|
||||
* ---
|
||||
* ## Exemplos de Uso
|
||||
* - **Conexão Padrão:** `DBService::make();` (Utiliza as definições default da classe).
|
||||
* - **Esquema Específico:** `DBService::make('', 'vendas');` (Mantém a conexão padrão, mas aponta para o schema de vendas).
|
||||
* - **Conexão Externa:** `DBService::make('external_db', 'public');`
|
||||
*
|
||||
* @param string $connection Identificador da conexão configurada no sistema.
|
||||
* @param string $schema Nome do esquema de banco de dados a ser selecionado.
|
||||
* @return DBService Uma nova instância do serviço de banco de dados configurada.
|
||||
*/
|
||||
public static function make(string $connection = 'DEFAULT', string $schema = 'shared'): DBService {
|
||||
return new DBService(connection: $connection, schema: $schema);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
namespace WorkbloomERP\Exceptions;
|
||||
|
||||
use Exception;
|
||||
use Throwable;
|
||||
|
||||
class AppException extends Exception {
|
||||
protected array $details = [];
|
||||
public function __construct(string $message, int $code = 500, array $details = [], ?Throwable $previous = null) {
|
||||
parent::__construct($message, $code, $previous);
|
||||
$this->details = $details;
|
||||
}
|
||||
|
||||
public function getDetails(): array {
|
||||
return $this->details;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
namespace WorkbloomERP\Services;
|
||||
|
||||
use KrothiumAPI\Database\RedisManager;
|
||||
|
||||
class CacheService extends RedisManager {
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
namespace WorkbloomERP\Services;
|
||||
|
||||
use Throwable;
|
||||
use KrothiumAPI\Database\DBManager;
|
||||
|
||||
class DBService {
|
||||
protected string $connection;
|
||||
protected ?string $schema;
|
||||
|
||||
public function __construct(string $connection = 'DEFAULT', ?string $schema = 'shared') {
|
||||
$this->schema = $schema;
|
||||
$this->connection = strtoupper(string: $connection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executa dentro de transação
|
||||
*/
|
||||
public function transaction(callable $callback): mixed {
|
||||
DBManager::beginTransaction(connectionName: $this->connection, schema: $this->schema);
|
||||
try {
|
||||
$result = $callback($this);
|
||||
DBManager::commit(connectionName: $this->connection, schema: $this->schema);
|
||||
return $result;
|
||||
} catch (Throwable $e) {
|
||||
DBManager::rollback(connectionName: $this->connection, schema: $this->schema);
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executa query sem retorno
|
||||
*/
|
||||
public function execute(string $sql, array $params = []): bool {
|
||||
return DBManager::execute(
|
||||
sql: $sql,
|
||||
params: $params,
|
||||
connectionName: $this->connection,
|
||||
schema: $this->schema
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retorna um único registro
|
||||
*/
|
||||
public function fetchOne(string $sql, array $params = []): ?array {
|
||||
return DBManager::fetchOne(
|
||||
sql: $sql,
|
||||
params: $params,
|
||||
connectionName: $this->connection,
|
||||
schema: $this->schema
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retorna múltiplos registros
|
||||
*/
|
||||
public function fetchAll(string $sql, array $params = []): array {
|
||||
return DBManager::fetchAll(
|
||||
sql: $sql,
|
||||
params: $params,
|
||||
connectionName: $this->connection,
|
||||
schema: $this->schema
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Último ID inserido (via PDO interno do driver)
|
||||
*/
|
||||
public function lastInsertId(): string {
|
||||
return DBManager::lastInsertId(
|
||||
connectionName: $this->connection,
|
||||
schema: $this->schema
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
namespace WorkbloomERP\Utils;
|
||||
|
||||
use WorkbloomERP\Services\CacheService;
|
||||
use WorkbloomERP\Exceptions\AppException;
|
||||
|
||||
class CacheUtil {
|
||||
public static function set(string $key, int $ttl, mixed $value): bool {
|
||||
return CacheService::set(
|
||||
key: $key,
|
||||
value: is_string($value) ? $value : json_encode(
|
||||
value: match (true) {
|
||||
is_array($value), is_object($value) => $value,
|
||||
default => throw new AppException(message: 'Valor para cache deve ser string, array ou objeto.', code: 400)
|
||||
},
|
||||
flags: JSON_UNESCAPED_UNICODE
|
||||
),
|
||||
ttl: max(-1, $ttl)
|
||||
);
|
||||
}
|
||||
|
||||
public static function get(string $key): ?array {
|
||||
$cachedValue = CacheService::get(key: $key);
|
||||
return $cachedValue ? json_decode($cachedValue, true) : null;
|
||||
}
|
||||
|
||||
public static function delete(array $keys): bool {
|
||||
return CacheService::del(keys: $keys);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
<?php
|
||||
namespace WorkbloomERP\Utils;
|
||||
|
||||
use Exception;
|
||||
use JsonException;
|
||||
|
||||
class CryptoUtil {
|
||||
/**
|
||||
* Criptografa uma string.
|
||||
*
|
||||
* @param string $data
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function encrypt(string $data): string {
|
||||
$algorithm = self::getAlgorithm();
|
||||
$key = self::getKey();
|
||||
|
||||
$ivLength = openssl_cipher_iv_length($algorithm);
|
||||
if ($ivLength === false) {
|
||||
throw new Exception('Unable to determine IV length');
|
||||
}
|
||||
|
||||
$iv = random_bytes($ivLength);
|
||||
$cipherText = openssl_encrypt(
|
||||
$data,
|
||||
$algorithm,
|
||||
$key,
|
||||
OPENSSL_RAW_DATA,
|
||||
$iv,
|
||||
$tag
|
||||
);
|
||||
|
||||
if ($cipherText === false) {
|
||||
throw new Exception('Failed to encrypt data');
|
||||
}
|
||||
|
||||
return self::encodePayload([
|
||||
'iv' => base64_encode($iv),
|
||||
'tag' => base64_encode($tag),
|
||||
'value' => base64_encode($cipherText)
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Descriptografa uma string.
|
||||
*
|
||||
* @param string $payload
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function decrypt(string $payload): string {
|
||||
$algorithm = self::getAlgorithm();
|
||||
$key = self::getKey();
|
||||
|
||||
$decodedPayload = self::decodePayload($payload);
|
||||
|
||||
$plainText = openssl_decrypt(
|
||||
base64_decode($decodedPayload['value']),
|
||||
$algorithm,
|
||||
$key,
|
||||
OPENSSL_RAW_DATA,
|
||||
base64_decode($decodedPayload['iv']),
|
||||
base64_decode($decodedPayload['tag'])
|
||||
);
|
||||
if ($plainText === false) {
|
||||
throw new Exception('Failed to decrypt data');
|
||||
}
|
||||
|
||||
return $plainText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retorna o algoritmo configurado.
|
||||
*
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
private static function getAlgorithm(): string {
|
||||
$algorithm = $_ENV['SYSTEM_CRYPTO_ALGO'];
|
||||
if (!in_array($algorithm, openssl_get_cipher_methods(), true)) {
|
||||
throw new Exception('Invalid cipher algorithm');
|
||||
}
|
||||
return $algorithm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retorna a chave de criptografia.
|
||||
*
|
||||
* A chave deve ser uma string hexadecimal de 64 caracteres (32 bytes).
|
||||
*
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
private static function getKey(): string {
|
||||
$hexKey = $_ENV['SYSTEM_CRYPTO_KEY'];
|
||||
if (empty($hexKey)) {
|
||||
throw new Exception('Encryption key not configured');
|
||||
}
|
||||
|
||||
$key = hex2bin($hexKey);
|
||||
if ($key === false || strlen($key) !== 32) {
|
||||
throw new Exception(message: 'Invalid encryption key. Expected 32 bytes (64 hex characters).');
|
||||
}
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Codifica o payload para armazenamento.
|
||||
*
|
||||
* @param array $payload
|
||||
* @return string
|
||||
* @throws JsonException
|
||||
*/
|
||||
private static function encodePayload(array $payload): string {
|
||||
return base64_encode(
|
||||
json_encode(
|
||||
$payload,
|
||||
JSON_THROW_ON_ERROR
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodifica o payload criptografado.
|
||||
*
|
||||
* @param string $payload
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
private static function decodePayload(string $payload): array {
|
||||
try {
|
||||
$decoded = json_decode(
|
||||
base64_decode($payload),
|
||||
true,
|
||||
512,
|
||||
JSON_THROW_ON_ERROR
|
||||
);
|
||||
} catch (JsonException $e) {
|
||||
throw new Exception(message: 'Invalid encrypted payload', previous: $e);
|
||||
}
|
||||
|
||||
foreach (['iv', 'tag', 'value'] as $field) {
|
||||
if (!isset($decoded[$field])) {
|
||||
throw new Exception(sprintf('Missing "%s" field in encrypted payload', $field));
|
||||
}
|
||||
}
|
||||
return $decoded;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
namespace WorkbloomERP\Utils;
|
||||
|
||||
use Exception;
|
||||
|
||||
class CypherUtil {
|
||||
public static function hash(string $data): string {
|
||||
if (!isset($_ENV['SYSTEM_CYPHER_ALGO']) || !isset($_ENV['SYSTEM_CYPHER_PEPPER'])) {
|
||||
throw new Exception('Missing required environment variables for hashing');
|
||||
}
|
||||
return hash_hmac(algo: $_ENV['SYSTEM_CYPHER_ALGO'], data: $data, key: $_ENV['SYSTEM_CYPHER_PEPPER']);
|
||||
}
|
||||
|
||||
public static function verify(string $data, string $hash): bool {
|
||||
return hash_equals(known_string: self::hash($data), user_string: $hash);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
namespace WorkbloomERP\Utils;
|
||||
|
||||
use Exception;
|
||||
use Firebase\JWT\JWT;
|
||||
use Firebase\JWT\Key;
|
||||
use DateTimeImmutable;
|
||||
|
||||
class JwtUtil {
|
||||
private int $ttl;
|
||||
private string $issuer;
|
||||
private string $secretKey;
|
||||
private string $algorithm;
|
||||
|
||||
public function __construct(string $secretKey, string $algorithm = 'HS256', int $ttl = 3600, string $issuer = 'workbloomerp') {
|
||||
if (empty($secretKey)) {
|
||||
throw new Exception(message: 'JWT secret não configurado.');
|
||||
}
|
||||
$this->ttl = $ttl;
|
||||
$this->secretKey = $secretKey;
|
||||
$this->algorithm = $algorithm;
|
||||
$this->issuer = $issuer;
|
||||
}
|
||||
|
||||
public function generate(array $payload): string {
|
||||
$defaultClaims = [];
|
||||
$defaultClaims['iat'] = $payload['iat'] ?? (new DateTimeImmutable())->getTimestamp();
|
||||
$defaultClaims['exp'] = $payload['exp'] ?? ((new DateTimeImmutable())->modify("+{$this->ttl} seconds")->getTimestamp());
|
||||
|
||||
$finalPayload = array_merge($payload, $defaultClaims);
|
||||
return JWT::encode(
|
||||
payload: $finalPayload,
|
||||
key: $this->secretKey,
|
||||
alg: $this->algorithm
|
||||
);
|
||||
}
|
||||
|
||||
public function validate(string $token): object {
|
||||
return JWT::decode(
|
||||
jwt: $token,
|
||||
keyOrKeyArray: new Key(
|
||||
keyMaterial: $this->secretKey,
|
||||
algorithm: $this->algorithm
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
namespace WorkbloomERP\Utils;
|
||||
|
||||
class SanitizeUtil {
|
||||
public static function string(mixed $value): ?string {
|
||||
if ($value === null) {
|
||||
return null;
|
||||
}
|
||||
$value = trim(string: $value);
|
||||
$value = strip_tags(string: $value);
|
||||
return $value;
|
||||
}
|
||||
|
||||
public static function email(mixed $value): ?string {
|
||||
if ($value === null) {
|
||||
return null;
|
||||
}
|
||||
$value = filter_var(value: $value, filter: FILTER_SANITIZE_EMAIL);
|
||||
return $value ?: '';
|
||||
}
|
||||
|
||||
public static function int(mixed $value): ?int {
|
||||
if ($value === null) {
|
||||
return null;
|
||||
}
|
||||
return (int) filter_var(value: $value, filter: FILTER_SANITIZE_NUMBER_INT);
|
||||
}
|
||||
|
||||
public static function float(mixed $value): ?float {
|
||||
if($value === null) {
|
||||
return null;
|
||||
}
|
||||
return (float) filter_var(
|
||||
value: $value,
|
||||
filter: FILTER_SANITIZE_NUMBER_FLOAT,
|
||||
options: FILTER_FLAG_ALLOW_FRACTION | FILTER_FLAG_ALLOW_THOUSAND
|
||||
);
|
||||
}
|
||||
|
||||
public static function document(mixed $value): ?string {
|
||||
if ($value === null) {
|
||||
return null;
|
||||
}
|
||||
// remove espaços
|
||||
$value = trim($value);
|
||||
// converte caracteres especiais (Ç -> C, Á -> A, etc.)
|
||||
$value = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $value);
|
||||
// mantém apenas letras e números
|
||||
$value = preg_replace('/[^a-zA-Z0-9]/', '', $value);
|
||||
return $value;
|
||||
}
|
||||
|
||||
public static function boolean(mixed $value): ?bool {
|
||||
if ($value === null) {
|
||||
return null;
|
||||
}
|
||||
if (is_bool($value)) {
|
||||
return $value;
|
||||
}
|
||||
if (is_string($value)) {
|
||||
$value = strtolower($value);
|
||||
if (in_array($value, ['true', '1', 'yes'], true)) {
|
||||
return true;
|
||||
}
|
||||
if (in_array($value, ['false', '0', 'no'], true)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (is_int($value)) {
|
||||
return $value === 1;
|
||||
}
|
||||
// Se não for possível converter, retorna null
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function phone(mixed $value, bool $withCountryCode = false): ?string {
|
||||
if ($value === null) {
|
||||
return null;
|
||||
}
|
||||
// remove tudo que não for número
|
||||
$value = preg_replace('/\D/', '', $value);
|
||||
if (!$value) {
|
||||
return '';
|
||||
}
|
||||
// adiciona DDI do Brasil se não tiver
|
||||
if ($withCountryCode) {
|
||||
if (strlen($value) === 10 || strlen($value) === 11) {
|
||||
$value = "55{$value}";
|
||||
}
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
namespace WorkbloomERP\Utils;
|
||||
|
||||
class ValidateUtil {
|
||||
public static function cpf(?string $cpf): bool {
|
||||
if ($cpf == null) {
|
||||
return false;
|
||||
}
|
||||
// Remove tudo que não for número
|
||||
$cpf = preg_replace('/\D/', '', $cpf);
|
||||
// Precisa ter 11 dígitos
|
||||
if (strlen($cpf) !== 11) {
|
||||
return false;
|
||||
}
|
||||
// Bloqueia CPFs com todos os dígitos iguais (11111111111, 00000000000, etc)
|
||||
if (preg_match('/^(\d)\1{10}$/', $cpf)) {
|
||||
return false;
|
||||
}
|
||||
// Valida primeiro dígito verificador
|
||||
for ($t = 9; $t < 11; $t++) {
|
||||
$soma = 0;
|
||||
for ($i = 0; $i < $t; $i++) {
|
||||
$soma += $cpf[$i] * (($t + 1) - $i);
|
||||
}
|
||||
$digito = ((10 * $soma) % 11) % 10;
|
||||
if ($cpf[$t] != $digito) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function cnpj(?string $cnpj): bool {
|
||||
if ($cnpj == null) {
|
||||
return false;
|
||||
}
|
||||
// Remove máscara
|
||||
$cnpj = strtoupper(preg_replace('/[^A-Z0-9]/', '', $cnpj));
|
||||
// Tamanho fixo
|
||||
if (strlen($cnpj) !== 14) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Bloqueia CNPJs com todos os dígitos iguais (11111111111111, 00000000000000, etc)
|
||||
if (preg_match('/^(\d)\1{13}$/', $cnpj)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Regex: 12 alfanum + 2 numéricos
|
||||
if (!preg_match('/^[A-Z0-9]{12}[0-9]{2}$/', $cnpj)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Converte para valores numéricos
|
||||
$valores = [];
|
||||
for ($i = 0; $i < 14; $i++) {
|
||||
$char = $cnpj[$i];
|
||||
if (ctype_digit($char)) {
|
||||
$valores[$i] = (int)$char;
|
||||
} else {
|
||||
$valores[$i] = ord($char) - 48;
|
||||
}
|
||||
}
|
||||
|
||||
// === Primeiro DV ===
|
||||
$pesos1 = [5,4,3,2,9,8,7,6,5,4,3,2];
|
||||
$soma = 0;
|
||||
for ($i = 0; $i < 12; $i++) {
|
||||
$soma += $valores[$i] * $pesos1[$i];
|
||||
}
|
||||
|
||||
$resto = $soma % 11;
|
||||
$dv1 = ($resto < 2) ? 0 : 11 - $resto;
|
||||
if ($dv1 !== $valores[12]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// === Segundo DV ===
|
||||
$pesos2 = [6,5,4,3,2,9,8,7,6,5,4,3,2];
|
||||
$soma = 0;
|
||||
for ($i = 0; $i < 13; $i++) {
|
||||
$soma += $valores[$i] * $pesos2[$i];
|
||||
}
|
||||
|
||||
$resto = $soma % 11;
|
||||
$dv2 = ($resto < 2) ? 0 : 11 - $resto;
|
||||
return $dv2 === $valores[13];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user