first commit.

This commit is contained in:
kj
2026-10-05 15:08:39 -03:00
commit b7799f9fff
93 changed files with 8966 additions and 0 deletions
+17
View File
@@ -0,0 +1,17 @@
{
"name": "http",
"version": "0.1.0",
"description": "Utilidades HTTP (CurlRequest, Aria2, Proxy, CompreFace)",
"core": { "requires": ">=0.1", "tested": "0.1" },
"php": ">=8.1",
"components": {
"curlRequest": { "files": ["src/Libs/CurlRequest.php"] },
"aria2": { "files": ["src/Libs/Aria2.php"] },
"proxy": { "files": ["src/Libs/Proxy.php"] },
"compreFace": {
"files": ["src/Libs/CompreFace.php"],
"require": { "http:curlRequest": ">=0.1" }
}
},
"default": ["curlRequest", "aria2", "proxy", "compreFace"]
}
+179
View File
@@ -0,0 +1,179 @@
<?php
namespace Libs;
use CurlHandle;
/**
* Aria2
*
* Library to control aria2c from PHP
* using its jsonrpc API.
*
* @author KJ
* @website https://kj2.me
* @licence MIT
*/
class Aria2
{
protected CurlHandle $ch;
protected ?string $token;
protected bool $batch = false;
protected array $batch_list = [];
/**
* Constructor
*
* @param string $server
* Path to aria2's jsonrpc server.
* @param string|null $token
* (optional) Access token for aria2c API.
*/
public function __construct(string $server = 'http://127.0.0.1:6800/jsonrpc', ?string $token = null)
{
$this->ch = curl_init($server);
$this->token = $token;
curl_setopt_array($this->ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HEADER => false,
]);
}
/**
* Destructor
*
* @return void
*/
public function __destruct()
{
curl_close($this->ch);
}
/**
* Allows starting a batch call.
*
* @return static
*/
public function batch(): static
{
$this->batch = true;
return $this;
}
/**
* Returns whether it is in a batch call or not.
*
* @return bool
*/
public function inBatch(): bool
{
return $this->batch;
}
/**
* Sends a batch request.
*
* @return mixed
*/
public function commit(): mixed
{
$data = json_encode($this->batch_list);
$this->batch = false;
$this->batch_list = [];
$result = $this->send($data);
return json_decode($result);
}
/**
* Sends a request to aria2c via jsonrpc.
*
* @param string $data
* @return string|bool
*/
protected function send(string $data): string|bool
{
curl_setopt($this->ch, CURLOPT_POSTFIELDS, $data);
return curl_exec($this->ch);
}
/**
* Magic to simplify work :).
*
* PHP methods are automatically converted into aria2 methods.
*
* aria2.addUri => Aria2::addUri
* aria2.addTorrent => Aria2::addTorrent
* aria2.getPeers => Aria2::getPeers
* aria2.addMetalink => Aria2::addMetalink
* aria2.remove => Aria2::remove
* aria2.pause => Aria2::pause
* aria2.forcePause => Aria2::forcePause
* aria2.pauseAll => Aria2::pauseAll
* aria2.forcePauseAll => Aria2::forcePauseAll
* aria2.unpause => Aria2::unpause
* aria2.unpauseAll => Aria2::unpauseAll
* aria2.forceRemove => Aria2::forceRemove
* aria2.changePosition => Aria2::changePosition
* aria2.tellStatus => Aria2::tellStatus
* aria2.getUris => Aria2::getUris
* aria2.getFiles => Aria2::getFiles
* aria2.getServers => Aria2::getServers
* aria2.tellActive => Aria2::tellActive
* aria2.tellWaiting => Aria2::tellWaiting
* aria2.tellStopped => Aria2::tellStopped
* aria2.getOption => Aria2::getOption
* aria2.changeUri => Aria2::changeUri
* aria2.changeOption => Aria2::changeOption
* aria2.getGlobalOption => Aria2::getGlobalOption
* aria2.changeGlobalOption => Aria2::changeGlobalOption
* aria2.purgeDownloadResult => Aria2::purgeDownloadResult
* aria2.removeDownloadResult => Aria2::removeDownloadResult
* aria2.getVersion => Aria2::getVersion
* aria2.getSessionInfo => Aria2::getSessionInfo
* aria2.shutdown => Aria2::shutdown
* aria2.forceShutdown => Aria2::forceShutdown
* aria2.getGlobalStat => Aria2::getGlobalStat
* aria2.saveSession => Aria2::saveSession
* system.multicall => Aria2::system_multicall
* system.listMethods => Aria2::system_listMethods
* system.listNotifications => Aria2::system_listNotifications
*
* @param array<int,mixed> $arg
* @return mixed
*/
public function __call(string $name, array $arg): mixed
{
if (!is_null($this->token)) {
array_unshift($arg, $this->token);
}
// Allow system methods (the last 3 on the list)
if (strpos($name, '_') === false) { // if it doesn't contain underscores, it's an aria2 method command.
$name = 'aria2.' . $name;
} else {
$name = str_replace('_', '.', $name);
}
$data = [
'jsonrpc' => '2.0',
'id' => '1',
'method' => $name,
'params' => $arg,
];
// Apply batch call if we are in one
if ($this->batch) {
$this->batch_list[] = $data;
return $this;
}
$data = json_encode($data);
$response = $this->send($data);
if ($response === false) {
trigger_error(curl_error($this->ch));
}
return json_decode($response);
}
}
+211
View File
@@ -0,0 +1,211 @@
<?php
namespace Libs;
/**
* CompreFase - DuckBrain
*
* Library for CompreFace (https://github.com/exadel-inc/CompreFace/).
* Can use the constants COMPREFACE_API and COMPREFACE_KEY.
*
* @author KJ
* @website https://kj2.me
* @license MIT
*/
class CompreFace
{
private string $apiUrl;
private CurlRequest $request;
/**
* __construct
*
* @param string $apiKey
* @param string $apiUrl
*/
public function __construct(string $apiKey = '', string $apiUrl = '')
{
if (!defined('COMPREFACE_KEY') && empty($apiKey)) {
throw new \Exception('CompreFace API Key is not defined.');
}
if (empty($apiKey)) {
$apiKey = COMPREFACE_KEY;
}
if (!empty($apiUrl)) {
$this->apiUrl = $apiUrl;
} elseif (defined('COMPREFACE_API')) {
$this->apiUrl = COMPREFACE_API;
} else {
$this->apiUrl = 'http://localhost:8000/api/v1/';
}
$this->request = new CurlRequest();
$this->request->headers['x-api-key'] = $apiKey;
$this->request->headers['Content-Type'] = 'application/json';
}
/**
* Parses the request's JSON and returns it as Neuron.
*
* @param mixed $default
* Default value to return if the result is not JSON.
*/
private function parseJSON(mixed $default = [])
{
$json = json_decode($this->request->body());
if (json_last_error() === JSON_ERROR_NONE) {
return $json;
}
return $default;
}
/**
* Returns the list of subjects registered in CompreFace.
*/
public function subjects()
{
$this->request->get($this->apiUrl . 'recognition/subjects');
$response = $this->parseJSON(['subjects' => []]);
if (isset($response->subjects)) {
return $response->subjects;
}
return [];
}
/**
* Adds a subject.
*
* @param string $name
*/
public function addSubject(string $name)
{
$this->request->post(
$this->apiUrl . 'recognition/subjects',
json_encode(['subject' => $name])
);
return $this->parseJSON();
}
/**
* Renames a subject.
*
* @param string $name Current name
* @param string $newName New name
*/
public function renameSubject(string $name, string $newName)
{
$this->request->put(
$this->apiUrl . 'recognition/subjects/' . rawurlencode($name),
json_encode(['subject' => $newName])
);
return $this->parseJSON();
}
/**
* Deletes a subject.
*
* @param string $name
*/
public function deleteSubject(string $name)
{
$this->request->delete(
$this->apiUrl . 'recognition/subjects/' . rawurlencode($name)
);
return $this->parseJSON();
}
/**
* Deletes all subjects from CompreFace.
*/
public function deleteAllSubjects()
{
$this->request->delete(
$this->apiUrl . 'recognition/subjects'
);
return $this->parseJSON();
}
/**
* Returns the subject's images.
*
* @param string $subjectName
*/
public function subjectImages(string $subjectName)
{
$this->request->get(
$this->apiUrl . 'recognition/faces',
['size' => 100, 'subject' => $subjectName]
);
return $this->parseJSON();
}
/**
* Adds an image to the subject.
*
* @param string $subjectName
* @param string $base64_image
*/
public function addSubjectImage(string $subjectName, string $base64_image)
{
$this->request->post(
$this->apiUrl . 'recognition/faces?subject=' . rawurlencode($subjectName),
json_encode(['file' => $base64_image])
);
return $this->parseJSON();
}
/**
* Deletes an image from the subject.
*
* @param string $imageId
*/
public function deteleSubjectImage(string $imageId)
{
// CompreFace also allows deleting multiple or deleting all,
// but for now I won't add it to this library, as it is not necessary.
$this->request->delete(
$this->apiUrl . 'recognition/faces/' . rawurlencode($imageId)
);
return $this->parseJSON();
}
/**
* Facial recognition of the sent image.
*
* @param string $base64_image
*/
public function recognize(string $base64_image)
{
$this->request->post(
$this->apiUrl . 'recognition/recognize',
json_encode(['file' => $base64_image])
);
return $this->parseJSON();
}
/**
* Checks if the subject exists in CompreFace.
*
* @param string $name
*/
public function subjectExist(string $name)
{
$subject = $this->subjects();
return in_array($name, $subject);
}
}
+174
View File
@@ -0,0 +1,174 @@
<?php
namespace Libs;
/**
* CurlRequest - DuckBrain
*
* Facade for CURL.
*
* @author KJ
* @website https://kj2.me
* @licence MIT
*/
class CurlRequest
{
public array $headers = [];
public array $options = [
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_SSL_VERIFYHOST => false,
CURLOPT_RETURNTRANSFER => true,
CURLINFO_HEADER_OUT => true,
];
public int $statusCode = 0;
protected string $responseHeaders = '';
protected string $responseBody = '';
/**
* Sends the request.
*
* @param array $options
* @return void
*/
protected function send(array $options): void
{
$handler = curl_init();
$headers = [];
foreach ($this->headers as $index => $value) {
$headers[] = "$index: $value";
}
$options[CURLOPT_HTTPHEADER] = $headers;
foreach ($this->options as $index => $option) {
if (!isset($options[$index])) {
$options[$index] = $option;
}
}
curl_setopt_array($handler, $options);
$this->responseBody = curl_exec($handler);
$this->responseHeaders = curl_getinfo($handler, CURLINFO_HEADER_OUT);
$this->statusCode = curl_getinfo($handler, CURLINFO_HTTP_CODE);
// throw new Exception('Error al realizar la solicitud. '. curl_error($handler));
curl_close($handler);
}
/**
* Sends the request using the GET method.
*
* @param string $url
* @param array $args
*
* @return CurlRequest
*/
public function get(string $url, array $args = []): CurlRequest
{
$this->send([
CURLOPT_URL => $url . '?' . http_build_query($args),
CURLOPT_POST => false,
CURLOPT_PUT => false,
]);
return $this;
}
/**
* Sends the request using the POST method.
*
* @param string $url
* @param string $fields
*
* @return CurlRequest
*/
public function post(string $url, string $fields = ''): CurlRequest
{
$this->send([
CURLOPT_URL => $url,
CURLOPT_POSTFIELDS => $fields,
]);
return $this;
}
/**
* Sends the request using the PUT method.
*
* @param string $url
* @param string $fields
*
* @return CurlRequest
*/
public function put(string $url, string $fields = ''): CurlRequest
{
$this->send([
CURLOPT_URL => $url,
CURLOPT_CUSTOMREQUEST => 'PUT',
CURLOPT_POSTFIELDS => $fields,
]);
return $this;
}
/**
* Sends the request using the PATCH method.
*
* @param string $url
* @param string $fields
*
* @return CurlRequest
*/
public function patch(string $url, string $fields = ''): CurlRequest
{
$this->send([
CURLOPT_URL => $url,
CURLOPT_CUSTOMREQUEST => 'PATCH',
CURLOPT_POSTFIELDS => $fields,
]);
return $this;
}
/**
* Sends the request using the DELETE method.
*
* @param string $url
* @param string $fields
*
* @return CurlRequest
*/
public function delete(string $url, string $fields = ''): CurlRequest
{
$this->send([
CURLOPT_URL => $url,
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_POSTFIELDS => $fields,
]);
return $this;
}
/**
* Returns the response headers.
*
* @return string
*/
public function headers(): string
{
return $this->responseHeaders;
}
/**
* Returns the response body.
*
* @return string
*/
public function body(): string
{
return $this->responseBody;
}
}
+160
View File
@@ -0,0 +1,160 @@
<?php
namespace Libs;
/**
* PHP Proxy - DuckBrain
*
* Proxy is a library to create a PHP proxy.
* It allows controlling the headers that will be returned to the client,
* in addition to placing filters on the resulting HTML code.
*
* @author KJ
* @website https://kj2.me
* @licence MIT
*/
class Proxy
{
public static $proxied_headers = [
'Set-Cookie',
'Content-Type',
'Cookie',
'Location',
];
public static $proxy_base_url_canonical = '/';
public static $destination_host = SOURCE_URL;
protected static $filters = [];
/**
* Adds a callable (AKA filter) that will receive the content
* and should return a string.
*
* @param callable $cb
* Function that will modify the content.
*
* @return void
*/
public static function add_filter(callable $cb): void
{
static::$filters[] = $cb;
}
/**
* Applies the filters configured with add_filter.
*
* @param string $content
* Content on which the filters will be applied.
*
* @return mixed
* It would be correct to return a string, but
* since the filters might not return correctly,
* to avoid a design error, we specify that it returns mixed.
*/
protected static function apply_filters(string $content): mixed
{
foreach (static::$filters as $filter) {
$content = call_user_func($filter, $content);
}
return $content;
}
/**
* Starts the proxy process.
*
* @param string|null $proxy_request_uri
* The address to which the proxy will be applied.
*
* @return void
*/
public static function run(?string $proxy_request_uri = null): void
{
// Get the URI if not defined
if (is_null($proxy_request_uri)) {
$proxy_request_uri = substr($_SERVER['REQUEST_URI'], strlen(static::$proxy_base_url_canonical));
} elseif (substr($proxy_request_uri, 0, 1) == '/') {
$proxy_request_uri = substr($proxy_request_uri, 1);
}
// Generate the final URL
$proxy_request_url = static::$destination_host . $proxy_request_uri;
// cURL initialization
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $proxy_request_url);
curl_setopt($ch, CURLOPT_AUTOREFERER, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:'));
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
// Capture client cookies
$hdrs = [];
if (isset($_SERVER['HTTP_COOKIE'])) {
$hdrs[] = "Cookie: " . $_SERVER['HTTP_COOKIE'];
}
// Capture client browser (User-Agent)
if (isset($_SERVER['HTTP_USER_AGENT'])) {
$hdrs[] = "User-Agent: " . $_SERVER['HTTP_USER_AGENT'];
}
// Add captured headers
curl_setopt($ch, CURLOPT_HTTPHEADER, $hdrs);
// Capture all client headers
$hdrs = [];
foreach (\getallheaders() as $name => $value) {
$hdrs[] = "$name: $value";
}
// Add captured headers to the request
curl_setopt($ch, CURLOPT_HTTPHEADER, $hdrs);
// Add POST parameters
if (sizeof($_POST) > 0) {
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($_POST));
}
// Execute the request
$res = curl_exec($ch);
curl_close($ch);
// Parse the response
list($headers, $body) = explode("\r\n\r\n", $res, 2);
$headers = str_replace(static::$destination_host, '/', $headers);
$headers = explode("\r\n", $headers);
$hs = [];
foreach ($headers as $header) {
if (false !== strpos($header, ': ')) {
list($h, $v) = explode(': ', $header);
$hs[$h][] = $v;
} else {
$header1 = $header;
}
}
// Define headers in the client
list($proto, $code, $text) = explode(' ', $header1);
header($_SERVER['SERVER_PROTOCOL'] . ' ' . $code . ' ' . $text);
foreach (static::$proxied_headers as $hname) {
if (isset($hs[$hname])) {
foreach ($hs[$hname] as $v) {
if ($hname === 'Set-Cookie') {
header($hname . ": " . $v, false);
} else {
header($hname . ": " . $v);
}
}
}
}
die(static::apply_filters($body));
}
}