diff --git a/.gitignore b/.gitignore index da9bbec808da1301c6f5a214a993667863f7c737..1ce60fa1d119752d3ed00908a8dd346922f01629 100644 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,7 @@ bin/workflow/tutorial/ cassandra certmanager log -.* +/.* bin/workflow new/ prometheus/ diff --git a/api/context.php b/api/context.php new file mode 100644 index 0000000000000000000000000000000000000000..95d669e18ae4dd3655e4008aace89052b69cbe92 --- /dev/null +++ b/api/context.php @@ -0,0 +1,73 @@ +<?php + +namespace SmartData; +require_once('../bin/smartdata/SmartAPI.php'); +require_once('../bin/smartdata/Config.php'); + +use SmartData\Exception\CustomException; + +function context($url, $domain, $content) { + + if (substr( $url, 0, 5 ) != "/docs" && substr( $url, 0, 8 ) != "/openapi") { + $url = $url . "/$domain"; + header('Content-Type: application/json'); + } + + if (substr( $url, 0, 4 ) == "/get") { + $url = $url . "/" . $content['smartDataContextId']; + } + + $ch = curl_init(); + + curl_setopt($ch, CURLOPT_URL, Config::config()::SMARTDATACONTEXT_API . "$url"); + + if (substr( $url, 0, 4 ) != "/get" && substr( $url, 0, 5 ) != "/docs" && substr( $url, 0, 8 ) != "/openapi") { + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($content)); + curl_setopt($ch, CURLOPT_HTTPHEADER, array( + 'Content-Type: application/json' + )); + } + + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + + $response = curl_exec($ch); + $http_status = curl_getinfo($ch, CURLINFO_HTTP_CODE); + + if ($http_status == 0) { + # Could not connect to the smartdatacontext API + $http_status = 503; # Service Unavailable + $response = '{"errors": ["SmartDataContext API not available"]}'; + } + + http_response_code($http_status); + return $response; +} + +try { + if (array_key_exists('docs', $_GET)) { + echo context("/docs", null, null); + } else if (array_key_exists('openapi', $_GET)) { + echo context("/openapi", null, null); + } else { + $content = file_get_contents('php://input'); + $json = json_decode($content, false, 512, JSON_BIGINT_AS_STRING); + + // Validate certificate usage and domain usage + $credentials = null; + if(isset($json->credentials)) + $credentials = Credentials::fromJson($json->credentials); + $backend = new Backend($credentials); + + $json = json_decode($content, true, 512, JSON_BIGINT_AS_STRING); + + // Pass the request to the smartdatacontext backend and retrieve result + echo context($json['command'], $backend->domainInUse(), $json['request']); } +} catch (CustomException $e) { + http_response_code($e->getHTTPCodeError()); + header('X-Message: ' . $e->getMessage(), false); + return false; +} catch (\Exception $e) { + http_response_code(HttpStatusCode::BAD_REQUEST); + return false; +} \ No newline at end of file diff --git a/bin/smartdata/Backend.php b/bin/smartdata/Backend.php index 858d640b7e6d027a32b6c240c8e9145ab833d4fa..e339ed7c716f4dec646bad4ff775c74ee8d33f60 100644 --- a/bin/smartdata/Backend.php +++ b/bin/smartdata/Backend.php @@ -40,7 +40,6 @@ class Backend_Common // Please, be careful and use only for development tasks. public function __construct(Credentials $credentials = NULL, $internal=false) { $credentials = $credentials ?? new Credentials(); - $testint = 0x00FF; // Ensure the machine is little-endian if(!($testint===current(unpack('v', pack('S', $testint))))) throw new \Exception("The machine is not little-endian"); $this->_gw_id = -1; //NOTE: That means that the client isn't a known gateway @@ -110,6 +109,7 @@ class Backend_Common case 'finish.php' : case 'describe.php' : case 'list.php' : + case 'context.php' : /** * ====== Summary of rules ====== * HTTPS ^ CERT ^ domain -> OK, domain = $domain @@ -201,6 +201,7 @@ class Backend_Common $parameters = array(':certificate' => $certificate, ':level' => $level); + try { $conn = self::_mysqlConnect(Config::config()::MYSQL_SEVERNAME, Config::config()::MYSQL_PORT, Config::config()::MYSQL_DBNAME, Config::config()::MYSQL_USERNAME, Config::config()::MYSQL_PASSWORD); @@ -355,6 +356,10 @@ class Backend_Common } public function __toString() { return REQUEST_DBG.' crd:{'.$this->_username.'@'.$this->_domain.'}'; } + + public function domainInUse():string { + return $this->_domain; + } } class Tracker diff --git a/bin/smartdata/Config.php.template b/bin/smartdata/Config.php.template index 8b29a74a4801427871f05cc27050247a4671072e..af36fb69920389b70c25268d786fa97f3033ab26 100644 --- a/bin/smartdata/Config.php.template +++ b/bin/smartdata/Config.php.template @@ -8,6 +8,7 @@ namespace SmartData\Config class Config_Common { + const SMARTDATACONTEXT_API = 'http://smartdata-context-api'; const MYSQL_SEVERNAME = 'db'; const MYSQL_PORT = 3306; const MYSQL_USERNAME = 'smartdata'; diff --git a/context/.gitignore b/context/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..49ce3c193f83295e932ffb427c3d515c19b50b18 --- /dev/null +++ b/context/.gitignore @@ -0,0 +1 @@ +/vendor \ No newline at end of file diff --git a/context/.htaccess b/context/.htaccess new file mode 100644 index 0000000000000000000000000000000000000000..11b28decbcfb40030c319b0053da289e4007c0e3 --- /dev/null +++ b/context/.htaccess @@ -0,0 +1,4 @@ +RewriteEngine On +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d +RewriteRule ^ index.php [QSA,L] \ No newline at end of file diff --git a/context/Dockerfile b/context/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..4f2d9892776c325178523641ec986cafb8150c50 --- /dev/null +++ b/context/Dockerfile @@ -0,0 +1,18 @@ +FROM php:8.2-apache-bookworm +RUN apt-get update && apt-get install -y \ + zip unzip \ + libssl-dev \ + pkg-config \ + libcurl4-openssl-dev \ + git \ + && pecl install mongodb \ + && docker-php-ext-enable mongodb +RUN a2enmod rewrite +RUN a2enmod actions +RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer +WORKDIR /var/www/html +ADD composer.* /var/www/html/ +ADD api /var/www/html/api/ +ADD index.php /var/www/html/ +ADD .htaccess /var/www/html/ +RUN composer install \ No newline at end of file diff --git a/context/api/controller/crud.php b/context/api/controller/crud.php new file mode 100644 index 0000000000000000000000000000000000000000..e7a262ab347b6796b020bfbf7728497682a73484 --- /dev/null +++ b/context/api/controller/crud.php @@ -0,0 +1,249 @@ +<?php +namespace SmartDataContext\Controller; + +use SmartDataContext\Persistence\SmartDataContextCrud; +use SmartDataContext\Persistence\DBManager; +use SmartDataContext\Domain\SmartDataContext; + +use Slim\Psr7\Request; +use Slim\Psr7\Response; + +use OpenApi\Attributes as OA; + +class Crud { + + private function _basicValidation($params, $request, $response) { + # Basic validation + $body = $request->getParsedBody(); + $errors = []; + if (! $body) { + $errors[] = "missing body"; + } + $domain = $this->_get_value_or_default($params, 'domain', null); + if (! $domain) { + $errors[] = "missing domain"; + } + return [$domain, $body, $errors]; + } + + function create(Request $request, Response $response, array $params) { + # Basic validation + [$domain, $body, $errors] = $this->_basicValidation($params, $request, $response); + if ($errors) { + return $this->return_error($response, $errors, 400); + } + + # Parameters + $content = $this->_get_value_or_default($body, 'content', []); + $features = $this->_get_value_or_default($body, 'features', []); + $t0 = $this->_get_value_or_default($body, 't0', -1); + $t1 = $this->_get_value_or_default($body, 't1', -1); + + $smartDataSources = $this->_get_value_or_default($body, 'smartDataSources', []); + $smartDataUnits = $this->_get_value_or_default($body, 'smartDataUnits', []); + + # Validate parameters + $errors = array_merge( + SmartDataContext::ValidateContent($content), + SmartDataContext::ValidateFeatures($features), + SmartDataContext::ValidateTimestamp($t0,$t1), + SmartDataContext::ValidateContent($content), + SmartDataContext::ValidateFeatures($features), + SmartDataContext::ValidateTimestamp($t0,$t1), + SmartDataContext::ValidateSmartDataSources($smartDataSources), + SmartDataContext::ValidateSmartDataUnits($smartDataUnits)); + + if ($errors) { + return $this->return_error($response, $errors, 400); + } + + # Create new SmartDataContext + $persistence = new SmartDataContextCrud($this->_get_db()); + $result = $persistence->create(domain: $domain, t0: $t0, t1: $t1, content: $content, features: $features, smartDataSources: $smartDataSources, + smartDataUnits: $smartDataUnits); + + if ($result['success']) { + return $this->return_json($response, $result["contents"]); + } else { + return $this->return_error($response, $result["errors"], 400); + } + } + + function associate(Request $request, Response $response, array $params) { + # Basic validation + [$domain, $body, $errors] = $this->_basicValidation($params, $request, $response); + if ($errors) { + return $this->return_error($response, $errors, 400); + } + + $smartdatacontextids = $this->_get_value_or_default($body, 'smartDataContextIds', []); + if (! is_array($smartdatacontextids)) { + $smartdatacontextids = [$smartdatacontextids]; + } + $smartDataSources = $this->_get_value_or_default($body, 'smartDataSources', []); + $smartDataUnits = $this->_get_value_or_default($body, 'smartDataUnits', []); + + $errors = array_merge( + SmartDataContext::ValidateSmartDataSources($smartDataSources), + SmartDataContext::ValidateSmartDataUnits($smartDataUnits)); + + if ($errors) { + return $this->return_error($response, $errors, 400); + } + + $persistence = new SmartDataContextCrud($this->_get_db()); + $fullResult = []; + foreach ($smartdatacontextids as $id) { + $fullResult[] = $persistence->associate($domain, $id, $smartDataSources, $smartDataUnits); + } + return $this->return_json($response, $fullResult); + } + + function unassociate(Request $request, Response $response, array $params) { + # Basic validation + [$domain, $body, $errors] = $this->_basicValidation($params, $request, $response); + if ($errors) { + return $this->return_error($response, $errors, 400); + } + + $smartdatacontextids = $this->_get_value_or_default($body, 'smartDataContextIds', []); + if (! is_array($smartdatacontextids)) { + $smartdatacontextids = [$smartdatacontextids]; + } + $smartDataSources = $this->_get_value_or_default($body, 'smartDataSources', []); + $smartDataUnits = $this->_get_value_or_default($body, 'smartDataUnits', []); + + $errors = array_merge( + SmartDataContext::ValidateSmartDataSources($smartDataSources), + SmartDataContext::ValidateSmartDataUnits($smartDataUnits)); + + if ($errors) { + return $this->return_error($response, $errors, 400); + } + + $persistence = new SmartDataContextCrud($this->_get_db()); + $fullResult = []; + foreach ($smartdatacontextids as $id) { + $fullResult[] = $persistence->unassociate($domain, $id, $smartDataSources, $smartDataUnits); + } + + return $this->return_json($response, $fullResult); + } + + + + + function get(Request $request, Response $response, array $params) { + $domain = $this->_get_value_or_default($params, 'domain', null); + $id = $this->_get_value_or_default($params, 'id', null); + + if (isset($domain) && isset($id)) { + $persistence = new SmartDataContextCrud($this->_get_db()); + $result = $persistence->get(domain: $domain, id: $id); + + if ($result['success']) { + return $this->return_json($response, $result["contents"]); + } else { + return $this->return_error($response, $result["errors"], 400); + } + } elseif (! $id) { + return $this->return_error($response, ["missing SmartDataContextID"], 400); + } + elseif (! $domain) { + return $this->return_error($response, ["missing domain"], 400); + } + } + + function contexts(Request $request, Response $response, array $params) { + # Basic validation + [$domain, $body, $errors] = $this->_basicValidation($params, $request, $response); + if ($errors) { + return $this->return_error($response, $errors, 400); + } + + $smartDataSources = $this->_get_value_or_default($body, 'smartDataSources', []); + $smartDataUnits = $this->_get_value_or_default($body, 'smartDataUnits', []); + + if (count($smartDataSources) + count($smartDataUnits) == 0) { + return $this->return_error($response, ["at least one smartDataSource or smartDataUnit must be provided"], 400); + } + + $t0 = $this->_get_value_or_default($body, 't0', -1); + $t1 = $this->_get_value_or_default($body, 't1', -1); + + $sourceFilter = []; + foreach ($smartDataUnits as $smartDataUnit) { + $sourceFilter[] = ["smartDataUnits" => $smartDataUnit]; + } + foreach ($smartDataSources as $smartDataSource) { + $sourceFilter[] = ["smartDataSources" => $smartDataSource]; + } + $sourceFilter = ['$or' => $sourceFilter]; + + $timeFilter = []; + if (isset($t0) && isset($t1)) { + $timeFilter = ['$and' => [['t0' => ['$lte' => $t0]], ['$or' => [['t1' => ['$gte' => $t1]], ["t1" => -1]]]]]; + } + + if ($timeFilter) { + $filter = ['$and' => [$sourceFilter, $timeFilter]]; + } else { + $filter = $sourceFilter; + } + + $persistence = new SmartDataContextCrud($this->_get_db()); + $result = $persistence->query(domain: $domain, query: $filter); + + + + if ($result['success']) { + return $this->return_json($response, $result["contents"]); + } else { + return $this->return_error($response, $result["errors"], 400); + } + } + + + function query(Request $request, Response $response, array $params) { + # Basic validation + [$domain, $query, $errors] = $this->_basicValidation($params, $request, $response); + if ($errors) { + return $this->return_error($response, $errors, 400); + } + + $persistence = new SmartDataContextCrud($this->_get_db()); + $result = $persistence->query(domain: $domain, query: $query); + + if ($result['success']) { + return $this->return_json($response, $result["contents"]); + } else { + return $this->return_error($response, $result["errors"], 400); + } + } + + private function _get_db() { + return DBManager::GetMongoDBConnection(); + } + + private function return_json(Response $response, mixed $object, $status = 200) { + $result = array("result" => $object); + $response = $response->withAddedHeader("Content-Type", "application/json")->withStatus($status); + $response->getBody()->write(json_encode($result)); + return $response; + } + + private function return_error(Response $response, mixed $errorList, $status = 200) { + $result = array("errors" => $errorList); + $response = $response->withAddedHeader("Content-Type", "application/json")->withStatus($status); + $response->getBody()->write(json_encode($result)); + return $response; + } + + private function _get_value_or_default($body, $key, $default) { + if (! array_key_exists($key, $body)) { + return $default; + } else { + return $body[$key]; + } + } +} \ No newline at end of file diff --git a/context/api/domain/SmartDataContext.php b/context/api/domain/SmartDataContext.php new file mode 100644 index 0000000000000000000000000000000000000000..00d9b76f35d2c3fb0318af6ebe88a310bcd2af8d --- /dev/null +++ b/context/api/domain/SmartDataContext.php @@ -0,0 +1,69 @@ +<?php + +namespace SmartDataContext\Domain; + +class SmartDataContext { + static function ValidateContent($content) { + if (! $content) { + return ["content property empty"]; + } else { + return []; + } + } + + static function ValidateFeatures($features) { + if ($features && $features['tags']) { + return []; + } else { + return ["features missing tags entry"]; + } + } + + static function ValidateTimestamp($t0, $t1) { + if (! is_int($t0) || ! is_int($t1)) { + return ["t0 and t1 must time integer timestamps"]; + } else if ($t0 > $t1 && $t1 != -1) { + return ["t1 must be equal or after t0"]; + } else { + return []; + } + } + + static function ValidateSmartDataSources(mixed $smartDataSources) { + if (! is_array($smartDataSources)) { + return ["smartDataSources should be an array"]; + } + $result = []; + foreach ($smartDataSources as $smartDataSource) { + if (is_string($smartDataSource)) { + // TODO: Support confidence + if (! SmartDataContext::ValidSignature($smartDataSource)) { + $result[] = "$smartDataSource is an invalid signature"; + } + } else if (is_array($smartDataSource)) { + if (! SmartDataContext::ValidSphere($smartDataSource)) { + $result[] = json_encode($smartDataSource) . " is an invalid sphere"; + } + } + } + return $result; + } + + static function ValidSignature(string $signature) { + return preg_match('/^[a-f0-9]+$/i', $signature); + } + + static function ValidSphere(array $sphere) { + // TODO: Support confidence + return count(array_filter($sphere, 'is_int')) == 4; + } + + static function ValidateSmartDataUnits(mixed $smartDataUnits) { + if (is_array($smartDataUnits) && count(array_filter($smartDataUnits, 'is_int')) == count($smartDataUnits)) { + return []; + } else { + return ["smartDataUnits should be a array of longs"]; + } + } + +} diff --git a/context/api/persistence/db.php b/context/api/persistence/db.php new file mode 100644 index 0000000000000000000000000000000000000000..6dce80cff986b29fff48a856a6a741be912f0130 --- /dev/null +++ b/context/api/persistence/db.php @@ -0,0 +1,21 @@ +<?php + +namespace SmartDataContext\Persistence; + +use MongoDB\Client; + +class DBManager { + + static function GetMongoDBConnection() { + $MONGO_HOST=getenv("MONGO_HOST") ? getenv("MONGO_HOST") : 'localhost'; + $MONGO_DB=getenv("MONGO_DATABASE") ? getenv("MONGO_DATABASE") : 'smartdatacontext'; + $MONGO_PORT=getenv("MONGO_PORT") ? getenv("MONGO_PORT") : 27017; + $MONGO_USER=getenv("MONGO_USER") ? getenv("MONGO_USER") : "smartdatacontext"; + $MONGO_PASSWORD=getenv("MONGO_PASSWORD") ? getenv("MONGO_PASSWORD") : "smartdatacontext"; + return (new Client("mongodb://$MONGO_HOST:$MONGO_PORT/?authSource=$MONGO_DB", [ + "username" => $MONGO_USER, + "password" => $MONGO_PASSWORD + ]))->selectDatabase($MONGO_DB); + } + +} diff --git a/context/api/persistence/smartdatacontext.php b/context/api/persistence/smartdatacontext.php new file mode 100644 index 0000000000000000000000000000000000000000..5b433e189545b70b6f5b6b5530e4d3ff294c108c --- /dev/null +++ b/context/api/persistence/smartdatacontext.php @@ -0,0 +1,199 @@ +<?php + +namespace SmartDataContext\Persistence; + +use Exception; +use MongoDB\Database; +use MongoDB\Collection; +use Ramsey\Uuid\Uuid; +use SmartDataContext\Domain\SmartDataContext; + +class SmartDataContextCrud { + + private Database $connection; + private static array $collections = []; + + private function generateID():string { + return Uuid::uuid4()->toString(); + } + + function __construct(Database $dbconn) { + $this->connection = $dbconn; + SmartDataContextCrud::$collections = []; + } + + function create($domain, $t0, $t1, $content, $features, $smartDataSources, $smartDataUnits):array { + if (! array_key_exists('identifier', $features)) { + $features['identifier'] = $this->build_identifier($content); + } + + $id = $this->generateID(); + + $this->getCollection($domain)->insertOne([ + "id" => $id, + "t0" => $t0, + "t1" => $t1, + "content" => $content, + "features" => $features, + "smartDataSources" => $smartDataSources, + "smartDataUnits" => $smartDataUnits + ] + ); + + return array("success" => true, "contents" => array("smartDataContextId" => $id)); + } + + + private function getCollection($domain): Collection { + if (in_array($domain, SmartDataContextCrud::$collections)) { + return $this->connection->{$domain}; + } else { + $cols = $this->connection->listCollectionNames(["name" => $domain]); + $cols->next(); + if (! $cols->valid()) { + $this->createCollection($domain); + } else { + SmartDataContextCrud::$collections[] = $domain; + } + return $this->connection->{$domain}; + } + } + + private function createCollection($domain) { + $this->connection->createCollection($domain); + $this->connection->selectCollection($domain)->createIndex(["id" => 1]); + $this->connection->selectCollection($domain)->createIndex(["features" => 1]); + $this->connection->selectCollection($domain)->createIndex(["tags" => 1]); + $this->connection->selectCollection($domain)->createIndex(["smartDataSources" => 1]); + $this->connection->selectCollection($domain)->createIndex(["smartDataUnits" => 1]); + $this->connection->selectCollection($domain)->createIndex(["t0" => 1, "t1" => 1, "smartDataUnits" => 1]); + $this->connection->selectCollection($domain)->createIndex(["t0" => 1, "t1" => 1, "smartDataSources" => 1]); + } + + private function build_identifier($content) { + return sha1(json_encode($content)); + } + + public function get($domain, $id):mixed { + $smartdatacontext = $this->getCollection($domain)->findOne(["id" => $id]); + if (! isset($smartdatacontext)) { + return ["success" => false, "errors" => ["not found"]]; + } else { + return ["success" => true, "contents" => json_decode(json_encode($smartdatacontext), true)]; + } + } + + public function associate(mixed $domain, mixed $id, mixed $smartDataSources, mixed $smartDataUnits) + { + $errors = array_merge(SmartDataContext::ValidateSmartDataSources($smartDataSources), + SmartDataContext::ValidateSmartDataUnits($smartDataUnits)); + + if ($errors) { + return ["success" => false, "errors" => $errors]; + } + + $smartdatacontext = json_decode(json_encode($this->getCollection($domain)->findOne(["id" => $id])), true); + + if ($smartdatacontext) { + $smartDataUnitsToAdd = []; + foreach ($smartDataUnits as $smartDataUnit) { + if (! in_array($smartDataUnit, $smartdatacontext['smartDataUnits'])) { + $smartDataUnitsToAdd[] = $smartDataUnit; + } + } + + $smartDataSourcesToAdd = []; + foreach ($smartDataSources as $smartDataSource) { + if (! in_array($smartDataSource, $smartdatacontext['smartDataSources'])) { + $smartDataSourcesToAdd[] = $smartDataSource; + } + } + + $updateData = [ + '$push' => [ + 'smartDataSources' => [ + '$each' => $smartDataSourcesToAdd + ], + 'smartDataUnits' => [ + '$each' => $smartDataUnitsToAdd + ] + ] + ]; + + $this->getCollection($domain)->updateOne(["id" => $id], $updateData); + return ["success" => true, "result" => json_decode(json_encode($this->getCollection($domain)->findOne(["id" => $id])), true)]; + } else { + return ["success" => false, "errors" => ["smartdatacontext id not found"]]; + } + } + + public function unassociate(mixed $domain, mixed $id, mixed $smartDataSources, mixed $smartDataUnits) + { + $errors = array_merge(SmartDataContext::ValidateSmartDataSources($smartDataSources), + SmartDataContext::ValidateSmartDataUnits($smartDataUnits)); + + if ($errors) { + return ["success" => false, "errors" => $errors]; + } + + $smartdatacontext = json_decode(json_encode($this->getCollection($domain)->findOne(["id" => $id])), true); + + if ($smartdatacontext) { + $smartDataUnitsToRemove = []; + foreach ($smartDataUnits as $smartDataUnit) { + if (in_array($smartDataUnit, $smartdatacontext['smartDataUnits'])) { + $smartDataUnitsToRemove[] = $smartDataUnit; + } + } + + $smartDataSourcesToRemove = []; + foreach ($smartDataSources as $smartDataSource) { + if (in_array($smartDataSource, $smartdatacontext['smartDataSources'])) { + $smartDataSourcesToRemove[] = $smartDataSource; + } + } + + if ($smartDataSourcesToRemove && $smartDataUnitsToRemove) { + $updateData = [ + '$pull' => [ + 'smartDataSources' => ['$in' => $smartDataSourcesToRemove], + 'smartDataUnits' => ['$in' => $smartDataUnitsToRemove] + ] + ]; + } else if ($smartDataSourcesToRemove) { + $updateData = [ + '$pull' => [ + 'smartDataSources' => ['$in' => $smartDataSourcesToRemove] + ] + ]; + } else if ($smartDataUnitsToRemove) { + $updateData = [ + '$pull' => [ + 'smartDataUnits' => ['$in' => $smartDataUnitsToRemove] + ] + ]; + } else { + return ["success" => true, "result" => json_decode(json_encode($this->getCollection($domain)->findOne(["id" => $id])), true)]; + } + + $this->getCollection($domain)->updateOne(["id" => $id], $updateData); + return ["success" => true, "result" => json_decode(json_encode($this->getCollection($domain)->findOne(["id" => $id])), true)]; + } else { + return ["success" => false, "errors" => ["smartdatacontext id not found"]]; + } + } + + public function query(string $domain, mixed $query) + { + try { + $contentsMongo = $this->getCollection($domain)->find($query); + $contents = []; + foreach ($contentsMongo as $contentMongo) { + $contents[] = json_decode(json_encode($contentMongo), true); + } + return ["success" => true, "contents" => $contents]; + } catch (Exception $exception) { + return ["success" => false, "errors" => [$exception->getMessage()]]; + } + } +} \ No newline at end of file diff --git a/context/api/routes.php b/context/api/routes.php new file mode 100644 index 0000000000000000000000000000000000000000..06f6dbe25e158c9774f16107f906d6aed14dae27 --- /dev/null +++ b/context/api/routes.php @@ -0,0 +1,27 @@ +<?php + +use Slim\App; +use Slim\Psr7\Response; + +function setupRoutes(App $app) { + # API Routes + $app->post('/create/{domain}', '\SmartDataContext\Controller\Crud:create'); + $app->post('/associate/{domain}', '\SmartDataContext\Controller\Crud:associate'); + $app->post('/unassociate/{domain}', '\SmartDataContext\Controller\Crud:unassociate'); + $app->get('/get/{domain}/{id}', '\SmartDataContext\Controller\Crud:get'); + $app->post('/query/{domain}', '\SmartDataContext\Controller\Crud:query'); + $app->post('/contexts/{domain}', '\SmartDataContext\Controller\Crud:contexts'); + + # Documentation + $app->get('/docs', function ($request, Response $response) { + $response->getBody()->write(file_get_contents(__DIR__ . '/swagger/swagger-ui.html')); + return $response; + }); + + $app->get('/openapi', function ($request, $response, $args) { + $response->getBody()->write(file_get_contents(__DIR__ . '/swagger/openapi.yaml')); + return $response; + }); + + return $app; +} \ No newline at end of file diff --git a/context/api/setup.php b/context/api/setup.php new file mode 100644 index 0000000000000000000000000000000000000000..476f281d7eac598dd878e0627f840f8da81ad8c1 --- /dev/null +++ b/context/api/setup.php @@ -0,0 +1,16 @@ +<?php +require_once __DIR__ . '/../vendor/autoload.php'; + +use SmartDataContext\Persistence\DBManager; +use Psr\Http\Message\ResponseInterface as Response; +use Psr\Http\Message\ServerRequestInterface as Request; +use Slim\Factory\AppFactory; +use Slim\App; + +function setupSlim():App { + # Slim initialization + $app = AppFactory::create(); + $app->addBodyParsingMiddleware(); + return $app; +} + diff --git a/context/api/swagger/openapi.yaml b/context/api/swagger/openapi.yaml new file mode 100644 index 0000000000000000000000000000000000000000..24ca9ee3533fa16eb78a587e4da8c75a566a26af --- /dev/null +++ b/context/api/swagger/openapi.yaml @@ -0,0 +1,489 @@ +openapi: 3.0.3 +info: + title: SmartDataContext API + version: 0.1.0 + description: API to manage SmartDataContext entities. +paths: + /api/v1_1/context.php#create: + post: + summary: Creates a new SmartDataContext + requestBody: + description: Create a new SmartDataContext with the given parameters. + required: true + content: + application/json: + schema: + type: object + properties: + command: + type: string + example: "/create" + description: The command to be executed. + request: + type: object + properties: + smartDataUnits: + type: array + items: + type: integer + description: The values of the SmartDataUnits to associate with this SmartDataContext. + example: [98812] + smartDataSources: + type: array + items: + oneOf: + - type: string + - type: array + items: + type: integer + description: The signatures (mobile) or space sphere (stationary) of the SmartDataSources associated with this context. + example: ["signature", [1, 1, 1, 1]] + t0: + type: integer + description: If not informed, assumed -1 as the value. + example: 1 + t1: + type: integer + description: If not informed, assumed -1 as the value. + example: 1 + features: + type: object + description: At least a single feature "tags" with at least one tag should be provided. + properties: + tags: + type: array + items: + type: string + example: ["sim"] + content: + type: object + description: The JSON of the SmartDataContext to be stored. + example: {"meta": true} + required: + - command + - request + responses: + '200': + description: The id for the created SmartDataContext. + content: + application/json: + schema: + type: object + properties: + smartdatacontextid: + type: string + example: "zzz-eee-ddd" + description: The ID of the created SmartDataContext. + errors: + type: array + example: ["Invalid id"] + description: "Present if errors ocurred during the request." + /api/v1_1/context.php#associate: + post: + summary: Associates SmartDataUnits or SmartDataSources to a SmartDataContext + operationId: associateSmartDataContext + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + command: + type: string + example: /associate + request: + type: object + properties: + smartDataUnits: + type: array + items: + type: integer + description: The values of the SmartDataUnits to associate with this SmartDataContext + smartDataSources: + type: array + items: + oneOf: + - type: string + - type: array + items: + type: integer + description: The signatures (mobile) or space sphere (stationary) of the SmartDataSources to associate with this context + smartDataContextIds: + type: array + items: + type: string + description: A list of SmartDataContext IDs to associate + required: + - smartDataContextIds + oneOf: + - required: + - smartDataUnits + - required: + - smartDataSources + example: + smartDataUnits: [98812] + smartDataSources: ["signature", [1,1,1,1]] + smartDataContextIds: ['u3iu48-34323-d343-rffe', 'u3iu48-34323-d343-rffe'] + responses: + '200': + description: Update SmartDataContext entities or list of errors + content: + application/json: + schema: + oneOf: + - type: object + properties: + result: + type: array + items: + type: object + properties: + success: + type: boolean + result: + type: object + properties: + _id: + type: object + properties: + $oid: + type: string + example: "66c351f6fb8347b8c406db52" + id: + type: string + example: "88690087-a4f4-4e1d-8c8d-8c67d19ddaed" + t0: + type: integer + example: -1 + t1: + type: integer + example: -1 + content: + type: object + properties: + meta: + type: boolean + example: true + features: + type: object + properties: + tags: + type: array + items: + type: string + example: "sim" + identifier: + type: string + example: "0d8c9a721e46a26f31acce9a714620510c1722d1" + smartDataSources: + type: array + items: + oneOf: + - type: string + - type: array + items: + type: integer + smartDataUnits: + type: array + items: + type: integer + example: 111 + - type: object + properties: + result: + type: array + items: + type: object + properties: + errors: + type: array + items: + type: string + example: "Invalid id" + + + /api/v1_1/context.php#unassociate: + post: + summary: Unassociate SmartDataUnits or SmartDataSources from SmartDataContext + operationId: unassociateSmartDataContext + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + command: + type: string + example: /unassociate + request: + type: object + properties: + smartDataUnits: + type: array + items: + type: integer + description: The values of the SmartDataUnits to unassociate with this SmartDataContext + smartDataSources: + type: array + items: + oneOf: + - type: string + - type: array + items: + type: integer + description: The signatures (mobile) or space sphere (stationary) of the SmartDataSources to unassociate with this context + smartdataids: + type: array + items: + type: string + description: A list of SmartData IDs to unassociate + required: + - smartdataids + oneOf: + - required: + - smartDataUnits + - required: + - smartDataSources + example: + smartDataUnits: [98812] + smartDataSources: ["signature", [1,1,1,1]] + smartdataids: ['u3iu48-34323-d343-rffe', 'u3iu48-34323-d343-rffe'] + responses: + '200': + description: Update SmartDataContext entities or list of errors + content: + application/json: + schema: + oneOf: + - type: object + properties: + result: + type: array + items: + type: object + properties: + success: + type: boolean + result: + type: object + properties: + _id: + type: object + properties: + $oid: + type: string + example: "66c351f6fb8347b8c406db52" + id: + type: string + example: "88690087-a4f4-4e1d-8c8d-8c67d19ddaed" + t0: + type: integer + example: -1 + t1: + type: integer + example: -1 + content: + type: object + properties: + meta: + type: boolean + example: true + features: + type: object + properties: + tags: + type: array + items: + type: string + example: "sim" + identifier: + type: string + example: "0d8c9a721e46a26f31acce9a714620510c1722d1" + smartDataSources: + type: array + items: + oneOf: + - type: string + - type: array + items: + type: integer + smartDataUnits: + type: array + items: + type: integer + example: 111 + - type: object + properties: + result: + type: array + items: + type: object + properties: + errors: + type: array + items: + type: string + example: "Invalid id" + + /api/v1_1/context.php#context: + post: + summary: Retrieve context data for a given SmartData Unit or SmartDataSource + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + command: + type: string + example: /contexts + description: The command to execute. + request: + type: object + properties: + smartDataSources: + type: array + items: + type: array + items: + type: integer + description: An array of smart data source arrays. + example: [[4,5,6,7]] + smartDataUnits: + type: array + items: + type: integer + description: A list of smart data unit IDs. + example: [111] + t0: + type: integer + description: Start time for the query. + example: 10 + t1: + type: integer + description: End time for the query. + example: 300 + required: + - command + - request + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + properties: + result: + type: object + properties: + _id: + type: object + properties: + $oid: + type: string + example: "66c351f6fb8347b8c406db52" + id: + type: string + example: "88690087-a4f4-4e1d-8c8d-8c67d19ddaed" + t0: + type: integer + example: -1 + t1: + type: integer + example: -1 + content: + type: object + properties: + meta: + type: boolean + example: true + features: + type: object + properties: + tags: + type: array + items: + type: string + example: ["sim"] + identifier: + type: string + example: "0d8c9a721e46a26f31acce9a714620510c1722d1" + smartDataSources: + type: array + items: + oneOf: + - type: string + - type: array + items: + type: integer + example: + - "ffdaf2342" + - [1, 2, 3, 5] + - "ae7666" + - [4, 5, 6, 7] + smartDataUnits: + type: array + items: + type: integer + example: [111, 222] + + '400': + description: Invalid request + content: + application/json: + schema: + type: object + properties: + errors: + type: array + items: + type: string + example: ["Invalid id"] + /api/v1_1/context.php#query: + post: + summary: Execute a MongoDB query over the SmartDataContext entities + description: Endpoint to execute a MongoDB query and retrieve results. + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - command + - request + properties: + command: + type: string + description: Specifies the type of command to execute. + example: "/query" + request: + type: object + description: A MongoDB query should be passed here. + responses: + '200': + description: The MongoDB query result + content: + application/json: + schema: + type: object + properties: + result: + type: object + description: Response object containing the result + '400': + description: A list of errors + content: + application/json: + schema: + type: object + properties: + errors: + type: array + items: + type: string + example: ["Invalid id"] diff --git a/context/api/swagger/swagger-ui.html b/context/api/swagger/swagger-ui.html new file mode 100644 index 0000000000000000000000000000000000000000..936d1eac125db05aa513671989f05e8280d4892b --- /dev/null +++ b/context/api/swagger/swagger-ui.html @@ -0,0 +1,22 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="utf-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1" /> + <meta name="description" content="SwaggerUI" /> + <title>SwaggerUI</title> + <link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@5.11.0/swagger-ui.css" /> +</head> +<body> +<div id="swagger-ui"></div> +<script src="https://unpkg.com/swagger-ui-dist@5.11.0/swagger-ui-bundle.js" crossorigin></script> +<script> + window.onload = () => { + window.ui = SwaggerUIBundle({ + url: '?openapi', + dom_id: '#swagger-ui', + }); + }; +</script> +</body> +</html> \ No newline at end of file diff --git a/context/composer.json b/context/composer.json new file mode 100644 index 0000000000000000000000000000000000000000..b699ccf573868972cca5da81d35fc7f31cd893da --- /dev/null +++ b/context/composer.json @@ -0,0 +1,15 @@ +{ + "require": { + "mongodb/mongodb": "^1.19", + "ramsey/uuid": "^4.7", + "slim/psr7": "^1.7", + "slim/slim": "4.*", + "zircote/swagger-php": "^4.10" + }, + "autoload": { + "classmap": ["api/", "api/controller/", "api/domain/", "api/persistence/"] + }, + "require-dev": { + "phpunit/phpunit": "^10.5" + } +} diff --git a/context/composer.lock b/context/composer.lock new file mode 100644 index 0000000000000000000000000000000000000000..fcab4d4717fc071eaac0f6406a7a82d03fdfe6b7 --- /dev/null +++ b/context/composer.lock @@ -0,0 +1,3153 @@ +{ + "_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": "53aac8e4224f7b1186c221254ff8b879", + "packages": [ + { + "name": "brick/math", + "version": "0.12.1", + "source": { + "type": "git", + "url": "https://github.com/brick/math.git", + "reference": "f510c0a40911935b77b86859eb5223d58d660df1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/brick/math/zipball/f510c0a40911935b77b86859eb5223d58d660df1", + "reference": "f510c0a40911935b77b86859eb5223d58d660df1", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.2", + "phpunit/phpunit": "^10.1", + "vimeo/psalm": "5.16.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Brick\\Math\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Arbitrary-precision arithmetic library", + "keywords": [ + "Arbitrary-precision", + "BigInteger", + "BigRational", + "arithmetic", + "bigdecimal", + "bignum", + "bignumber", + "brick", + "decimal", + "integer", + "math", + "mathematics", + "rational" + ], + "support": { + "issues": "https://github.com/brick/math/issues", + "source": "https://github.com/brick/math/tree/0.12.1" + }, + "funding": [ + { + "url": "https://github.com/BenMorel", + "type": "github" + } + ], + "time": "2023-11-29T23:19:16+00:00" + }, + { + "name": "fig/http-message-util", + "version": "1.1.5", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message-util.git", + "reference": "9d94dc0154230ac39e5bf89398b324a86f63f765" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message-util/zipball/9d94dc0154230ac39e5bf89398b324a86f63f765", + "reference": "9d94dc0154230ac39e5bf89398b324a86f63f765", + "shasum": "" + }, + "require": { + "php": "^5.3 || ^7.0 || ^8.0" + }, + "suggest": { + "psr/http-message": "The package containing the PSR-7 interfaces" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Fig\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Utility classes and constants for use with PSR-7 (psr/http-message)", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "issues": "https://github.com/php-fig/http-message-util/issues", + "source": "https://github.com/php-fig/http-message-util/tree/1.1.5" + }, + "time": "2020-11-24T22:02:12+00:00" + }, + { + "name": "mongodb/mongodb", + "version": "1.19.1", + "source": { + "type": "git", + "url": "https://github.com/mongodb/mongo-php-library.git", + "reference": "afe425b629075fa597fa2d5645045cb20dc93d95" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mongodb/mongo-php-library/zipball/afe425b629075fa597fa2d5645045cb20dc93d95", + "reference": "afe425b629075fa597fa2d5645045cb20dc93d95", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2.0", + "ext-hash": "*", + "ext-json": "*", + "ext-mongodb": "^1.18.0", + "php": "^7.4 || ^8.0", + "psr/log": "^1.1.4|^2|^3", + "symfony/polyfill-php80": "^1.27", + "symfony/polyfill-php81": "^1.27" + }, + "require-dev": { + "doctrine/coding-standard": "^12.0", + "rector/rector": "^1.1", + "squizlabs/php_codesniffer": "^3.7", + "symfony/phpunit-bridge": "^5.2", + "vimeo/psalm": "^5.13" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "MongoDB\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Andreas Braun", + "email": "andreas.braun@mongodb.com" + }, + { + "name": "Jeremy Mikola", + "email": "jmikola@gmail.com" + }, + { + "name": "Jérôme Tamarelle", + "email": "jerome.tamarelle@mongodb.com" + } + ], + "description": "MongoDB driver library", + "homepage": "https://jira.mongodb.org/browse/PHPLIB", + "keywords": [ + "database", + "driver", + "mongodb", + "persistence" + ], + "support": { + "issues": "https://github.com/mongodb/mongo-php-library/issues", + "source": "https://github.com/mongodb/mongo-php-library/tree/1.19.1" + }, + "time": "2024-06-13T14:30:04+00:00" + }, + { + "name": "nikic/fast-route", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/nikic/FastRoute.git", + "reference": "181d480e08d9476e61381e04a71b34dc0432e812" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/FastRoute/zipball/181d480e08d9476e61381e04a71b34dc0432e812", + "reference": "181d480e08d9476e61381e04a71b34dc0432e812", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35|~5.7" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "FastRoute\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov", + "email": "nikic@php.net" + } + ], + "description": "Fast request router for PHP", + "keywords": [ + "router", + "routing" + ], + "support": { + "issues": "https://github.com/nikic/FastRoute/issues", + "source": "https://github.com/nikic/FastRoute/tree/master" + }, + "time": "2018-02-13T20:26:39+00:00" + }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory" + }, + "time": "2024-04-15T12:06:14+00:00" + }, + { + "name": "psr/http-message", + "version": "2.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/2.0" + }, + "time": "2023-04-04T09:54:51+00:00" + }, + { + "name": "psr/http-server-handler", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-server-handler.git", + "reference": "84c4fb66179be4caaf8e97bd239203245302e7d4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-server-handler/zipball/84c4fb66179be4caaf8e97bd239203245302e7d4", + "reference": "84c4fb66179be4caaf8e97bd239203245302e7d4", + "shasum": "" + }, + "require": { + "php": ">=7.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Server\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP server-side request handler", + "keywords": [ + "handler", + "http", + "http-interop", + "psr", + "psr-15", + "psr-7", + "request", + "response", + "server" + ], + "support": { + "source": "https://github.com/php-fig/http-server-handler/tree/1.0.2" + }, + "time": "2023-04-10T20:06:20+00:00" + }, + { + "name": "psr/http-server-middleware", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-server-middleware.git", + "reference": "c1481f747daaa6a0782775cd6a8c26a1bf4a3829" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-server-middleware/zipball/c1481f747daaa6a0782775cd6a8c26a1bf4a3829", + "reference": "c1481f747daaa6a0782775cd6a8c26a1bf4a3829", + "shasum": "" + }, + "require": { + "php": ">=7.0", + "psr/http-message": "^1.0 || ^2.0", + "psr/http-server-handler": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Server\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP server-side middleware", + "keywords": [ + "http", + "http-interop", + "middleware", + "psr", + "psr-15", + "psr-7", + "request", + "response" + ], + "support": { + "issues": "https://github.com/php-fig/http-server-middleware/issues", + "source": "https://github.com/php-fig/http-server-middleware/tree/1.0.2" + }, + "time": "2023-04-11T06:14:47+00:00" + }, + { + "name": "psr/log", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.0" + }, + "time": "2021-07-14T16:46:02+00:00" + }, + { + "name": "ralouphie/getallheaders", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, + "time": "2019-03-08T08:55:37+00:00" + }, + { + "name": "ramsey/collection", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/ramsey/collection.git", + "reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ramsey/collection/zipball/a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5", + "reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "captainhook/plugin-composer": "^5.3", + "ergebnis/composer-normalize": "^2.28.3", + "fakerphp/faker": "^1.21", + "hamcrest/hamcrest-php": "^2.0", + "jangregor/phpstan-prophecy": "^1.0", + "mockery/mockery": "^1.5", + "php-parallel-lint/php-console-highlighter": "^1.0", + "php-parallel-lint/php-parallel-lint": "^1.3", + "phpcsstandards/phpcsutils": "^1.0.0-rc1", + "phpspec/prophecy-phpunit": "^2.0", + "phpstan/extension-installer": "^1.2", + "phpstan/phpstan": "^1.9", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan-phpunit": "^1.3", + "phpunit/phpunit": "^9.5", + "psalm/plugin-mockery": "^1.1", + "psalm/plugin-phpunit": "^0.18.4", + "ramsey/coding-standard": "^2.0.3", + "ramsey/conventional-commits": "^1.3", + "vimeo/psalm": "^5.4" + }, + "type": "library", + "extra": { + "captainhook": { + "force-install": true + }, + "ramsey/conventional-commits": { + "configFile": "conventional-commits.json" + } + }, + "autoload": { + "psr-4": { + "Ramsey\\Collection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ben Ramsey", + "email": "ben@benramsey.com", + "homepage": "https://benramsey.com" + } + ], + "description": "A PHP library for representing and manipulating collections.", + "keywords": [ + "array", + "collection", + "hash", + "map", + "queue", + "set" + ], + "support": { + "issues": "https://github.com/ramsey/collection/issues", + "source": "https://github.com/ramsey/collection/tree/2.0.0" + }, + "funding": [ + { + "url": "https://github.com/ramsey", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/ramsey/collection", + "type": "tidelift" + } + ], + "time": "2022-12-31T21:50:55+00:00" + }, + { + "name": "ramsey/uuid", + "version": "4.7.6", + "source": { + "type": "git", + "url": "https://github.com/ramsey/uuid.git", + "reference": "91039bc1faa45ba123c4328958e620d382ec7088" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/91039bc1faa45ba123c4328958e620d382ec7088", + "reference": "91039bc1faa45ba123c4328958e620d382ec7088", + "shasum": "" + }, + "require": { + "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12", + "ext-json": "*", + "php": "^8.0", + "ramsey/collection": "^1.2 || ^2.0" + }, + "replace": { + "rhumsaa/uuid": "self.version" + }, + "require-dev": { + "captainhook/captainhook": "^5.10", + "captainhook/plugin-composer": "^5.3", + "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", + "doctrine/annotations": "^1.8", + "ergebnis/composer-normalize": "^2.15", + "mockery/mockery": "^1.3", + "paragonie/random-lib": "^2", + "php-mock/php-mock": "^2.2", + "php-mock/php-mock-mockery": "^1.3", + "php-parallel-lint/php-parallel-lint": "^1.1", + "phpbench/phpbench": "^1.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan-phpunit": "^1.1", + "phpunit/phpunit": "^8.5 || ^9", + "ramsey/composer-repl": "^1.4", + "slevomat/coding-standard": "^8.4", + "squizlabs/php_codesniffer": "^3.5", + "vimeo/psalm": "^4.9" + }, + "suggest": { + "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.", + "ext-gmp": "Enables faster math with arbitrary-precision integers using GMP.", + "ext-uuid": "Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.", + "paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter", + "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type." + }, + "type": "library", + "extra": { + "captainhook": { + "force-install": true + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Ramsey\\Uuid\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A PHP library for generating and working with universally unique identifiers (UUIDs).", + "keywords": [ + "guid", + "identifier", + "uuid" + ], + "support": { + "issues": "https://github.com/ramsey/uuid/issues", + "source": "https://github.com/ramsey/uuid/tree/4.7.6" + }, + "funding": [ + { + "url": "https://github.com/ramsey", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/ramsey/uuid", + "type": "tidelift" + } + ], + "time": "2024-04-27T21:32:50+00:00" + }, + { + "name": "slim/psr7", + "version": "1.7.0", + "source": { + "type": "git", + "url": "https://github.com/slimphp/Slim-Psr7.git", + "reference": "753e9646def5ff4db1a06e5cf4ef539bfd30f467" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/slimphp/Slim-Psr7/zipball/753e9646def5ff4db1a06e5cf4ef539bfd30f467", + "reference": "753e9646def5ff4db1a06e5cf4ef539bfd30f467", + "shasum": "" + }, + "require": { + "fig/http-message-util": "^1.1.5", + "php": "^8.0", + "psr/http-factory": "^1.1", + "psr/http-message": "^1.0 || ^2.0", + "ralouphie/getallheaders": "^3.0", + "symfony/polyfill-php80": "^1.29" + }, + "provide": { + "psr/http-factory-implementation": "^1.0", + "psr/http-message-implementation": "^1.0 || ^2.0" + }, + "require-dev": { + "adriansuter/php-autoload-override": "^1.4", + "ext-json": "*", + "http-interop/http-factory-tests": "^1.1.0", + "php-http/psr7-integration-tests": "1.3.0", + "phpspec/prophecy": "^1.19", + "phpspec/prophecy-phpunit": "^2.2", + "phpstan/phpstan": "^1.11", + "phpunit/phpunit": "^9.6", + "squizlabs/php_codesniffer": "^3.10" + }, + "type": "library", + "autoload": { + "psr-4": { + "Slim\\Psr7\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Josh Lockhart", + "email": "hello@joshlockhart.com", + "homepage": "http://joshlockhart.com" + }, + { + "name": "Andrew Smith", + "email": "a.smith@silentworks.co.uk", + "homepage": "http://silentworks.co.uk" + }, + { + "name": "Rob Allen", + "email": "rob@akrabat.com", + "homepage": "http://akrabat.com" + }, + { + "name": "Pierre Berube", + "email": "pierre@lgse.com", + "homepage": "http://www.lgse.com" + } + ], + "description": "Strict PSR-7 implementation", + "homepage": "https://www.slimframework.com", + "keywords": [ + "http", + "psr-7", + "psr7" + ], + "support": { + "issues": "https://github.com/slimphp/Slim-Psr7/issues", + "source": "https://github.com/slimphp/Slim-Psr7/tree/1.7.0" + }, + "time": "2024-06-08T14:48:17+00:00" + }, + { + "name": "slim/slim", + "version": "4.14.0", + "source": { + "type": "git", + "url": "https://github.com/slimphp/Slim.git", + "reference": "5943393b88716eb9e82c4161caa956af63423913" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/slimphp/Slim/zipball/5943393b88716eb9e82c4161caa956af63423913", + "reference": "5943393b88716eb9e82c4161caa956af63423913", + "shasum": "" + }, + "require": { + "ext-json": "*", + "nikic/fast-route": "^1.3", + "php": "^7.4 || ^8.0", + "psr/container": "^1.0 || ^2.0", + "psr/http-factory": "^1.1", + "psr/http-message": "^1.1 || ^2.0", + "psr/http-server-handler": "^1.0", + "psr/http-server-middleware": "^1.0", + "psr/log": "^1.1 || ^2.0 || ^3.0" + }, + "require-dev": { + "adriansuter/php-autoload-override": "^1.4", + "ext-simplexml": "*", + "guzzlehttp/psr7": "^2.6", + "httpsoft/http-message": "^1.1", + "httpsoft/http-server-request": "^1.1", + "laminas/laminas-diactoros": "^2.17 || ^3", + "nyholm/psr7": "^1.8", + "nyholm/psr7-server": "^1.1", + "phpspec/prophecy": "^1.19", + "phpspec/prophecy-phpunit": "^2.1", + "phpstan/phpstan": "^1.11", + "phpunit/phpunit": "^9.6", + "slim/http": "^1.3", + "slim/psr7": "^1.6", + "squizlabs/php_codesniffer": "^3.10", + "vimeo/psalm": "^5.24" + }, + "suggest": { + "ext-simplexml": "Needed to support XML format in BodyParsingMiddleware", + "ext-xml": "Needed to support XML format in BodyParsingMiddleware", + "php-di/php-di": "PHP-DI is the recommended container library to be used with Slim", + "slim/psr7": "Slim PSR-7 implementation. See https://www.slimframework.com/docs/v4/start/installation.html for more information." + }, + "type": "library", + "autoload": { + "psr-4": { + "Slim\\": "Slim" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Josh Lockhart", + "email": "hello@joshlockhart.com", + "homepage": "https://joshlockhart.com" + }, + { + "name": "Andrew Smith", + "email": "a.smith@silentworks.co.uk", + "homepage": "http://silentworks.co.uk" + }, + { + "name": "Rob Allen", + "email": "rob@akrabat.com", + "homepage": "http://akrabat.com" + }, + { + "name": "Pierre Berube", + "email": "pierre@lgse.com", + "homepage": "http://www.lgse.com" + }, + { + "name": "Gabriel Manricks", + "email": "gmanricks@me.com", + "homepage": "http://gabrielmanricks.com" + } + ], + "description": "Slim is a PHP micro framework that helps you quickly write simple yet powerful web applications and APIs", + "homepage": "https://www.slimframework.com", + "keywords": [ + "api", + "framework", + "micro", + "router" + ], + "support": { + "docs": "https://www.slimframework.com/docs/v4/", + "forum": "https://discourse.slimframework.com/", + "irc": "irc://irc.freenode.net:6667/slimphp", + "issues": "https://github.com/slimphp/Slim/issues", + "rss": "https://www.slimframework.com/blog/feed.rss", + "slack": "https://slimphp.slack.com/", + "source": "https://github.com/slimphp/Slim", + "wiki": "https://github.com/slimphp/Slim/wiki" + }, + "funding": [ + { + "url": "https://opencollective.com/slimphp", + "type": "open_collective" + }, + { + "url": "https://tidelift.com/funding/github/packagist/slim/slim", + "type": "tidelift" + } + ], + "time": "2024-06-13T08:54:48+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", + "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "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": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.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-04-18T09:32:20+00:00" + }, + { + "name": "symfony/finder", + "version": "v6.4.10", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "af29198d87112bebdd397bd7735fbd115997824c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/af29198d87112bebdd397bd7735fbd115997824c", + "reference": "af29198d87112bebdd397bd7735fbd115997824c", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "symfony/filesystem": "^6.0|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Finds files and directories via an intuitive fluent interface", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/finder/tree/v6.4.10" + }, + "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-07-24T07:06:38+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.30.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "0424dff1c58f028c451efff2045f5d92410bd540" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/0424dff1c58f028c451efff2045f5d92410bd540", + "reference": "0424dff1c58f028c451efff2045f5d92410bd540", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/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.30.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-05-31T15:07:36+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.30.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "77fa7995ac1b21ab60769b7323d600a991a90433" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/77fa7995ac1b21ab60769b7323d600a991a90433", + "reference": "77fa7995ac1b21ab60769b7323d600a991a90433", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/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.30.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-05-31T15:07:36+00:00" + }, + { + "name": "symfony/polyfill-php81", + "version": "v1.30.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php81.git", + "reference": "3fb075789fb91f9ad9af537c4012d523085bd5af" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/3fb075789fb91f9ad9af537c4012d523085bd5af", + "reference": "3fb075789fb91f9ad9af537c4012d523085bd5af", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php81\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "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 backporting some PHP 8.1+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php81/tree/v1.30.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-06-19T12:30:46+00:00" + }, + { + "name": "symfony/yaml", + "version": "v6.4.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "52903de178d542850f6f341ba92995d3d63e60c9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/52903de178d542850f6f341ba92995d3d63e60c9", + "reference": "52903de178d542850f6f341ba92995d3d63e60c9", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "symfony/console": "<5.4" + }, + "require-dev": { + "symfony/console": "^5.4|^6.0|^7.0" + }, + "bin": [ + "Resources/bin/yaml-lint" + ], + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Loads and dumps YAML files", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/yaml/tree/v6.4.8" + }, + "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-05-31T14:49:08+00:00" + }, + { + "name": "zircote/swagger-php", + "version": "4.10.6", + "source": { + "type": "git", + "url": "https://github.com/zircote/swagger-php.git", + "reference": "e462ff5269ea0ec91070edd5d51dc7215bdea3b6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zircote/swagger-php/zipball/e462ff5269ea0ec91070edd5d51dc7215bdea3b6", + "reference": "e462ff5269ea0ec91070edd5d51dc7215bdea3b6", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": ">=7.2", + "psr/log": "^1.1 || ^2.0 || ^3.0", + "symfony/deprecation-contracts": "^2 || ^3", + "symfony/finder": ">=2.2", + "symfony/yaml": ">=3.3" + }, + "require-dev": { + "composer/package-versions-deprecated": "^1.11", + "doctrine/annotations": "^1.7 || ^2.0", + "friendsofphp/php-cs-fixer": "^2.17 || ^3.47.1", + "phpstan/phpstan": "^1.6", + "phpunit/phpunit": ">=8", + "vimeo/psalm": "^4.23" + }, + "suggest": { + "doctrine/annotations": "^1.7 || ^2.0" + }, + "bin": [ + "bin/openapi" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.x-dev" + } + }, + "autoload": { + "psr-4": { + "OpenApi\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Robert Allen", + "email": "zircote@gmail.com" + }, + { + "name": "Bob Fanger", + "email": "bfanger@gmail.com", + "homepage": "https://bfanger.nl" + }, + { + "name": "Martin Rademacher", + "email": "mano@radebatz.net", + "homepage": "https://radebatz.net" + } + ], + "description": "swagger-php - Generate interactive documentation for your RESTful API using phpdoc annotations", + "homepage": "https://github.com/zircote/swagger-php/", + "keywords": [ + "api", + "json", + "rest", + "service discovery" + ], + "support": { + "issues": "https://github.com/zircote/swagger-php/issues", + "source": "https://github.com/zircote/swagger-php/tree/4.10.6" + }, + "time": "2024-07-26T03:04:43+00:00" + } + ], + "packages-dev": [ + { + "name": "myclabs/deep-copy", + "version": "1.12.0", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", + "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3 <3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.12.0" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2024-06-12T14:39:25+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v5.1.0", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "683130c2ff8c2739f4822ff7ac5c873ec529abd1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/683130c2ff8c2739f4822ff7ac5c873ec529abd1", + "reference": "683130c2ff8c2739f4822ff7ac5c873ec529abd1", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-json": "*", + "ext-tokenizer": "*", + "php": ">=7.4" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v5.1.0" + }, + "time": "2024-07-01T20:03:41+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "54750ef60c58e43759730615a392c31c80e23176" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "10.1.15", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "5da8b1728acd1e6ffdf2ff32ffbdfd04307f26ae" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/5da8b1728acd1e6ffdf2ff32ffbdfd04307f26ae", + "reference": "5da8b1728acd1e6ffdf2ff32ffbdfd04307f26ae", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=8.1", + "phpunit/php-file-iterator": "^4.0", + "phpunit/php-text-template": "^3.0", + "sebastian/code-unit-reverse-lookup": "^3.0", + "sebastian/complexity": "^3.0", + "sebastian/environment": "^6.0", + "sebastian/lines-of-code": "^2.0", + "sebastian/version": "^4.0", + "theseer/tokenizer": "^1.2.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.1" + }, + "suggest": { + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "10.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.15" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-06-29T08:25:15+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "4.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/a95037b6d9e608ba092da1b23931e537cadc3c3c", + "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/4.1.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-08-31T06:24:48+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", + "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^10.0" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/4.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:56:09+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/0c7b06ff49e3d5072f057eb1fa59258bf287a748", + "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/3.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-08-31T14:07:24+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "6.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/e2a2d67966e740530f4a3343fe2e030ffdc1161d", + "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/6.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:57:52+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "10.5.29", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "8e9e80872b4e8064401788ee8a32d40b4455318f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/8e9e80872b4e8064401788ee8a32d40b4455318f", + "reference": "8e9e80872b4e8064401788ee8a32d40b4455318f", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.12.0", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", + "php": ">=8.1", + "phpunit/php-code-coverage": "^10.1.15", + "phpunit/php-file-iterator": "^4.1.0", + "phpunit/php-invoker": "^4.0.0", + "phpunit/php-text-template": "^3.0.1", + "phpunit/php-timer": "^6.0.0", + "sebastian/cli-parser": "^2.0.1", + "sebastian/code-unit": "^2.0.0", + "sebastian/comparator": "^5.0.1", + "sebastian/diff": "^5.1.1", + "sebastian/environment": "^6.1.0", + "sebastian/exporter": "^5.1.2", + "sebastian/global-state": "^6.0.2", + "sebastian/object-enumerator": "^5.0.0", + "sebastian/recursion-context": "^5.0.0", + "sebastian/type": "^4.0.0", + "sebastian/version": "^4.0.1" + }, + "suggest": { + "ext-soap": "To be able to generate mocks based on WSDL files" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "10.5-dev" + } + }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.29" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" + } + ], + "time": "2024-07-30T11:08:00+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/c34583b87e7b7a8055bf6c450c2c77ce32a24084", + "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/2.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T07:12:49+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "a81fee9eef0b7a76af11d121767abc44c104e503" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/a81fee9eef0b7a76af11d121767abc44c104e503", + "reference": "a81fee9eef0b7a76af11d121767abc44c104e503", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/2.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:58:43+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", + "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/3.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:59:15+00:00" + }, + { + "name": "sebastian/comparator", + "version": "5.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "2d3e04c3b4c1e84a5e7382221ad8883c8fbc4f53" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2d3e04c3b4c1e84a5e7382221ad8883c8fbc4f53", + "reference": "2d3e04c3b4c1e84a5e7382221ad8883c8fbc4f53", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-mbstring": "*", + "php": ">=8.1", + "sebastian/diff": "^5.0", + "sebastian/exporter": "^5.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "security": "https://github.com/sebastianbergmann/comparator/security/policy", + "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-08-12T06:03:08+00:00" + }, + { + "name": "sebastian/complexity", + "version": "3.2.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "68ff824baeae169ec9f2137158ee529584553799" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68ff824baeae169ec9f2137158ee529584553799", + "reference": "68ff824baeae169ec9f2137158ee529584553799", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "security": "https://github.com/sebastianbergmann/complexity/security/policy", + "source": "https://github.com/sebastianbergmann/complexity/tree/3.2.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-12-21T08:37:17+00:00" + }, + { + "name": "sebastian/diff", + "version": "5.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/c41e007b4b62af48218231d6c2275e4c9b975b2e", + "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0", + "symfony/process": "^6.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "security": "https://github.com/sebastianbergmann/diff/security/policy", + "source": "https://github.com/sebastianbergmann/diff/tree/5.1.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T07:15:17+00:00" + }, + { + "name": "sebastian/environment", + "version": "6.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "8074dbcd93529b357029f5cc5058fd3e43666984" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/8074dbcd93529b357029f5cc5058fd3e43666984", + "reference": "8074dbcd93529b357029f5cc5058fd3e43666984", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "https://github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "security": "https://github.com/sebastianbergmann/environment/security/policy", + "source": "https://github.com/sebastianbergmann/environment/tree/6.1.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-23T08:47:14+00:00" + }, + { + "name": "sebastian/exporter", + "version": "5.1.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "955288482d97c19a372d3f31006ab3f37da47adf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/955288482d97c19a372d3f31006ab3f37da47adf", + "reference": "955288482d97c19a372d3f31006ab3f37da47adf", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=8.1", + "sebastian/recursion-context": "^5.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "security": "https://github.com/sebastianbergmann/exporter/security/policy", + "source": "https://github.com/sebastianbergmann/exporter/tree/5.1.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T07:17:12+00:00" + }, + { + "name": "sebastian/global-state", + "version": "6.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", + "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "sebastian/object-reflector": "^3.0", + "sebastian/recursion-context": "^5.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "https://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "security": "https://github.com/sebastianbergmann/global-state/security/policy", + "source": "https://github.com/sebastianbergmann/global-state/tree/6.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T07:19:19+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/856e7f6a75a84e339195d48c556f23be2ebf75d0", + "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-12-21T08:38:20+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/202d0e344a580d7f7d04b3fafce6933e59dae906", + "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "sebastian/object-reflector": "^3.0", + "sebastian/recursion-context": "^5.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:08:32+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "24ed13d98130f0e7122df55d06c5c4942a577957" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/24ed13d98130f0e7122df55d06c5c4942a577957", + "reference": "24ed13d98130f0e7122df55d06c5c4942a577957", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/3.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:06:18+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "05909fb5bc7df4c52992396d0116aed689f93712" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/05909fb5bc7df4c52992396d0116aed689f93712", + "reference": "05909fb5bc7df4c52992396d0116aed689f93712", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:05:40+00:00" + }, + { + "name": "sebastian/type", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "462699a16464c3944eefc02ebdd77882bd3925bf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/462699a16464c3944eefc02ebdd77882bd3925bf", + "reference": "462699a16464c3944eefc02ebdd77882bd3925bf", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/4.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:10:45+00:00" + }, + { + "name": "sebastian/version", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c51fa83a5d8f43f1402e3f32a005e6262244ef17", + "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-07T11:34:05+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.2.3", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.2.3" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:36:25+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [], + "plugin-api-version": "2.2.0" +} diff --git a/context/index.php b/context/index.php new file mode 100644 index 0000000000000000000000000000000000000000..9caa93c836c868349c34068c1e877ccf0f7660de --- /dev/null +++ b/context/index.php @@ -0,0 +1,7 @@ +<?php +require_once 'api/setup.php'; +require_once 'api/routes.php'; + +# Process request +$slim = setupRoutes(setupSlim()); +$slim->run(); \ No newline at end of file diff --git a/context/tests/BaseTest.php b/context/tests/BaseTest.php new file mode 100644 index 0000000000000000000000000000000000000000..61d848b7eac8d3b03c14a9311bb045c69ae32303 --- /dev/null +++ b/context/tests/BaseTest.php @@ -0,0 +1,27 @@ +<?php + +require_once 'api/setup.php'; +require_once 'api/routes.php'; + + +use PHPUnit\Framework\TestCase; +use Slim\Psr7\Factory\ServerRequestFactory; + +class BaseTest extends TestCase +{ + protected $app; + + public function setUp(): void + { + $this->app = setupRoutes(setupSlim()); + } + + protected function _createRequest($method, $url, $body = null) { + $request = (new ServerRequestFactory())->createServerRequest($method, $url)->withHeader('Content-Type', 'application/json'); + if ($body) { + $request->getBody()->write(json_encode($body)); + } + return $request; + } + +} diff --git a/context/tests/CrudTest.php b/context/tests/CrudTest.php new file mode 100644 index 0000000000000000000000000000000000000000..278742dc3008e5994fcdd0fcea26278519366c22 --- /dev/null +++ b/context/tests/CrudTest.php @@ -0,0 +1,220 @@ +<?php + +require_once 'api/setup.php'; +require_once 'api/routes.php'; +require 'BaseTest.php'; + +class CrudTest extends BaseTest +{ + + public function testCreateInvalid() + { + $response = $this->app->handle($this->_createRequest('POST', '/create/test')); + $this->assertEquals(400, $response->getStatusCode()); + } + + public function testCreateInvalidMissingContent() + { + $response = $this->app->handle($this->_createRequest('POST', '/create/test')); + $this->assertEquals(400, $response->getStatusCode()); + $this->assertContains("missing body", json_decode($response->getBody(), true)['errors']); + } + + public function testCreateInvalidMissingTags() + { + $body = array("content" => ["meta" => true, "features" => []]); + $response = $this->app->handle($this->_createRequest('POST', '/create/test', $body)); + + $this->assertEquals(400, $response->getStatusCode()); + $this->assertNotContains("content property empty", json_decode($response->getBody(), true)['errors']); + $this->assertContains("features missing tags entry", json_decode($response->getBody(), true)['errors']); + } + + public function testCreateInvalidInvalidT0T1() + { + $body = array("content" => ["meta" => true], "t0" => 'a'); + $response = $this->app->handle($this->_createRequest('POST', '/create/test', $body)); + + $this->assertEquals(400, $response->getStatusCode()); + $this->assertContains("t0 and t1 must time integer timestamps", json_decode($response->getBody(), true)['errors']); + + $body = array("content" => ["meta" => true], "t1" => 'a'); + $response = $this->app->handle($this->_createRequest('POST', '/create/test', $body)); + + $this->assertEquals(400, $response->getStatusCode()); + $this->assertContains("t0 and t1 must time integer timestamps", json_decode($response->getBody(), true)['errors']); + + } + + public function testCreate() + { + $body = array("content" => ["meta" => true], "features" => ["tags" => "sim"]); + $response = $this->app->handle($this->_createRequest('POST', '/create/test', $body)); + $this->assertEquals(200, $response->getStatusCode(), json_encode($response->getBody())); + } + + public function testCreateWithSmartDataUnit() + { + $body = array("content" => ["meta" => true], "features" => ["tags" => "sim"], "smartDataUnits" => [10, 12]); + $response = $this->app->handle($this->_createRequest('POST', '/create/test', $body)); + $this->assertEquals(200, $response->getStatusCode(), json_encode($response->getBody())); + } + + public function testCreateWithInvalidSmartDataUnit() + { + $body = array("content" => ["meta" => true], "features" => ["tags" => "sim"], "smartDataUnits" => ["aaa", "eee"]); + $response = $this->app->handle($this->_createRequest('POST', '/create/test', $body)); + $this->assertEquals(400, $response->getStatusCode()); + $this->assertContains("smartDataUnits should be a array of longs", json_decode($response->getBody(), true)['errors']); + } + + public function testCreateWithSmartDataSources() + { + $body = array("content" => ["meta" => true], "features" => ["tags" => "sim"], "smartDataSources" => ["ffdaf2342", [1,2,3,5]]); + $response = $this->app->handle($this->_createRequest('POST', '/create/test', $body)); + $this->assertEquals(200, $response->getStatusCode(), json_encode($response->getBody())); + } + + public function testCreateWithSmartDataSourcesInvalidSignature() + { + $body = array("content" => ["meta" => true], "features" => ["tags" => "sim"], "smartDataSources" => ["ffds-af-2342", [1,2,3,5]]); + $response = $this->app->handle($this->_createRequest('POST', '/create/test', $body)); + $this->assertEquals(400, $response->getStatusCode()); + $this->assertContains("ffds-af-2342 is an invalid signature", json_decode($response->getBody(), true)['errors']); + } + + public function testCreateWithSmartDataSourcesInvalidSphere() + { + $body = array("content" => ["meta" => true], "features" => ["tags" => "sim"], "smartDataSources" => ["ffdsaf2342", [1,2,3]]); + $response = $this->app->handle($this->_createRequest('POST', '/create/test', $body)); + $this->assertEquals(400, $response->getStatusCode()); + $this->assertContains("[1,2,3] is an invalid sphere", json_decode($response->getBody(), true)['errors']); + } + + public function testCreateGetWithSmartDataUnit() + { + + $body = array("content" => ["meta" => true], "features" => ["tags" => "sim"], "smartDataUnits" => [10, 12]); + $response = $this->app->handle($this->_createRequest('POST', '/create/test', $body)); + + $this->assertEquals(200, $response->getStatusCode(), json_encode($response->getBody())); + $response->getBody()->rewind(); + $body = json_decode($response->getBody()->getContents(), true); + $this->assertTrue(array_key_exists('result', $body), 'missing result'); + $this->assertTrue(array_key_exists('smartdataid', $body['result']), 'missing smartdataid'); + $id = $body['result']['smartdataid']; + $response = $this->app->handle($this->_createRequest('GET', "/get/test/$id")); + $this->assertEquals(200, $response->getStatusCode(), json_encode($response->getBody())); + $response->getBody()->rewind(); + $body = json_decode($response->getBody()->getContents(), true); + $this->assertTrue(array_key_exists('result', $body), 'missing result'); + $this->assertTrue(array_key_exists('content', $body['result']), 'missing content'); + $this->assertEquals($body['result']['id'], $id, 'wrong smartdataid'); + } + + public function testAssociateWithSmartDataSources() + { + $body = array("content" => ["meta" => true], "features" => ["tags" => "sim"], "smartDataSources" => ["ffdaf2342", [1,2,3,5]]); + $response = $this->app->handle($this->_createRequest('POST', '/create/test', $body)); + $this->assertEquals(200, $response->getStatusCode(), json_encode($response->getBody())); + $response->getBody()->rewind(); + $body = json_decode($response->getBody()->getContents(), true); + $this->assertTrue(array_key_exists('result', $body), 'missing result'); + $this->assertTrue(array_key_exists('smartdataid', $body['result']), 'missing smartdataid'); + $id = $body['result']['smartdataid']; + + $body = ["smartdataids" => [$id], "smartDataSources" => ["ae7666", [4,5,6,7], [1,2,3,5]], "smartDataUnits" => [111,222]]; + $response = $this->app->handle($this->_createRequest('POST', '/associate/test', $body)); + $this->assertEquals(200, $response->getStatusCode(), json_encode($response->getBody())); + $response->getBody()->rewind(); + $body = json_decode($response->getBody()->getContents(), true); + $this->assertEquals(4, count($body['result'][0]['result']['smartDataSources']), "Wrong number of smartDataSources associated"); + $this->assertEquals(2, count($body['result'][0]['result']['smartDataUnits']), "Wrong number of smartDataUnits associated"); + } + + + public function testUnassociateWithSmartDataSources() + { + $body = array("content" => ["meta" => true], "features" => ["tags" => "sim"], "smartDataSources" => ["ffdaf2342", [1,2,3,5]]); + $response = $this->app->handle($this->_createRequest('POST', '/create/test', $body)); + $this->assertEquals(200, $response->getStatusCode(), json_encode($response->getBody())); + $response->getBody()->rewind(); + $body = json_decode($response->getBody()->getContents(), true); + $this->assertTrue(array_key_exists('result', $body), 'missing result'); + $this->assertTrue(array_key_exists('smartdataid', $body['result']), 'missing smartdataid'); + $id = $body['result']['smartdataid']; + + $body = ["smartdataids" => [$id], "smartDataSources" => ["ae7666", [4,5,6,7], [1,2,3,5]], "smartDataUnits" => [111,222]]; + $response = $this->app->handle($this->_createRequest('POST', '/associate/test', $body)); + $this->assertEquals(200, $response->getStatusCode(), json_encode($response->getBody())); + $response->getBody()->rewind(); + $body = json_decode($response->getBody()->getContents(), true); + $this->assertEquals(4, count($body['result'][0]['result']['smartDataSources']), "Wrong number of smartDataSources associated"); + $this->assertEquals(2, count($body['result'][0]['result']['smartDataUnits']), "Wrong number of smartDataUnits associated"); + + $body = ["smartdataids" => [$id], "smartDataSources" => ["ae7666", [4,5,6,7]], "smartDataUnits" => [222]]; + $response = $this->app->handle($this->_createRequest('POST', '/unassociate/test', $body)); + $this->assertEquals(200, $response->getStatusCode(), json_encode($response->getBody())); + $response->getBody()->rewind(); + $body = json_decode($response->getBody()->getContents(), true); + $this->assertEquals(2, count($body['result'][0]['result']['smartDataSources']), "Wrong number of smartDataSources associated"); + $this->assertEquals(1, count($body['result'][0]['result']['smartDataUnits']), "Wrong number of smartDataUnits associated"); + } + + public function testQuery() + { + $body = array("content" => ["meta" => true], "features" => ["tags" => "sim"], "smartDataSources" => ["ffdaf2342", [1,2,3,5]]); + $response = $this->app->handle($this->_createRequest('POST', '/create/test', $body)); + $this->assertEquals(200, $response->getStatusCode(), json_encode($response->getBody())); + $response->getBody()->rewind(); + $body = json_decode($response->getBody()->getContents(), true); + $this->assertTrue(array_key_exists('result', $body), 'missing result'); + $this->assertTrue(array_key_exists('smartdataid', $body['result']), 'missing smartdataid'); + $id = $body['result']['smartdataid']; + + $body = ["smartdataids" => [$id], "smartDataSources" => ["ae7666", [4,5,6,7], [1,2,3,5]], "smartDataUnits" => [111,222]]; + $response = $this->app->handle($this->_createRequest('POST', '/associate/test', $body)); + $this->assertEquals(200, $response->getStatusCode(), json_encode($response->getBody())); + $response->getBody()->rewind(); + $body = json_decode($response->getBody()->getContents(), true); + $this->assertEquals(4, count($body['result'][0]['result']['smartDataSources']), "Wrong number of smartDataSources associated"); + $this->assertEquals(2, count($body['result'][0]['result']['smartDataUnits']), "Wrong number of smartDataUnits associated"); + + $body = ["smartDataSources" => [4,5,6,7]]; + $response = $this->app->handle($this->_createRequest('POST', '/query/test', $body)); + $this->assertEquals(200, $response->getStatusCode(), json_encode($response->getBody())); + $response->getBody()->rewind(); + $body = json_decode($response->getBody()->getContents(), true); + $this->assertGreaterThan(0, count($body['result']), "Wrong number of results"); + } + + public function testContexts() + { + $domain = "performance"; + + $body = array("content" => ["meta" => true], "features" => ["tags" => "sim"], "smartDataSources" => ["ffdaf2342", [1,2,3,5]]); + $response = $this->app->handle($this->_createRequest('POST', "/create/$domain", $body)); + $this->assertEquals(200, $response->getStatusCode(), json_encode($response->getBody())); + $response->getBody()->rewind(); + $body = json_decode($response->getBody()->getContents(), true); + $this->assertTrue(array_key_exists('result', $body), 'missing result'); + $this->assertTrue(array_key_exists('smartdataid', $body['result']), 'missing smartdataid'); + $id = $body['result']['smartdataid']; + + $body = ["smartdataids" => [$id], "smartDataSources" => ["ae7666", [4,5,6,7], [1,2,3,5]], "smartDataUnits" => [111,222]]; + $response = $this->app->handle($this->_createRequest('POST', "/associate/$domain", $body)); + $this->assertEquals(200, $response->getStatusCode(), json_encode($response->getBody())); + $response->getBody()->rewind(); + $body = json_decode($response->getBody()->getContents(), true); + $this->assertEquals(4, count($body['result'][0]['result']['smartDataSources']), "Wrong number of smartDataSources associated"); + $this->assertEquals(2, count($body['result'][0]['result']['smartDataUnits']), "Wrong number of smartDataUnits associated"); + + $start = time(); + $body = ["smartDataSources" => [[4,5,6,7]], "t0" => 10, "t1" => 300]; + $response = $this->app->handle($this->_createRequest('POST', "/contexts/$domain", $body)); + $this->assertEquals(200, $response->getStatusCode(), json_encode($response->getBody())); + $response->getBody()->rewind(); + $body = json_decode($response->getBody()->getContents(), true); + $this->assertGreaterThan(0, count($body['result']), "Wrong number of results"); + } +} diff --git a/context/tests/PerformanceTest.php b/context/tests/PerformanceTest.php new file mode 100644 index 0000000000000000000000000000000000000000..cb55bf5f2fa6ce360404ba48040ded0b42f5f9bd --- /dev/null +++ b/context/tests/PerformanceTest.php @@ -0,0 +1,94 @@ +<?php + +require_once 'api/setup.php'; +require_once 'api/routes.php'; +require 'BaseTest.php'; + +class PerformanceTest extends BaseTest +{ + + public function testCreateLargeDatasetWithSmartDataSources() + { + $totalContexts = 1000000; // 1 million context + $smartDataUnits = $this->createSampleSmartDataUnits(30); // 30 sample smart data units + $smartDataSourcesSignatures = $this->createSampleSmartDataSignatures(300); // 300 sample smart data units + $smartDataSourcesSpheres = $this->createSampleSmartDataSpheres(300); // 300 sample smart data units + $smartDataSourcesTimestamps = $this->createSampleSmartDataTimestamps(100); // 300 sample smart data units + + $lastTime = time(); + $pendingContexts = $totalContexts; + while ($pendingContexts > 0) { + $sources = array_merge($this->randomSelect($smartDataSourcesSignatures, 3), $this->randomSelect($smartDataSourcesSpheres, 2)); + $timestamp = $this->randomSelect($smartDataSourcesTimestamps, 1)[0]; + $units = $this->randomSelect($smartDataUnits, 1); + + $body = array("content" => ["meta" => true], "t0"=> $timestamp[0], "t1" => $timestamp[1], "features" => ["tags" => "sim"], "smartDataUnits" => $units, "smartDataSources" => $sources); + $response = $this->app->handle($this->_createRequest('POST', '/create/performance', $body)); + $this->assertEquals(200, $response->getStatusCode(), json_encode($response->getBody())); + if ($pendingContexts % 10000 == 0 && $pendingContexts != $totalContexts) { + $inserts = 10000 / (time() - $lastTime); + $lastTime = time(); + fwrite(STDERR, "Remaining $pendingContexts - $inserts inserts/s. \n"); + } + $pendingContexts--; + } + } + + private function randomSelect($items, $count) { + $keys = array_rand($items, $count); + + if ($count == 1) { + return [$items[$keys]]; + } else { + $random_values = array_map(function($key) use ($items) { + return $items[$key]; + }, $keys); + + return $random_values; + } + } + + private function createSampleSmartDataUnits($count) { + $result = []; + while ($count-- >= 0) { + $result[] = rand(); + } + return $result; + } + + private function createSampleSmartDataSignatures($count) { + $result = []; + while ($count-- >= 0) { + $result[] = bin2hex(random_bytes(18)); + } + return $result; + } + + private function createSampleSmartDataSpheres($count) { + $result = []; + while ($count-- >= 0) { + $result[] = [rand(), rand(), rand(), rand(1,10)]; + } + return $result; + } + + private function createSampleSmartDataTimestamps($count) { + $result = []; + while ($count-- >= 0) { + if ($count % 7 == 0) { + $result[] = [-1, rand()]; + } else if ($count % 5 == 0) { + $result[] = [rand(), -1]; + } else if ($count % 3 == 0) { + $result[] = [-1, -1]; + } else { + $timestamp = [rand(), rand()]; + if ($timestamp[1] < $timestamp[0]) { + $timestamp = array_reverse($timestamp); + } + $result[] = $timestamp; + } + } + return $result; + } +} diff --git a/docker-compose.yml b/docker-compose.yml index 8351ac692718169438d90d6af702ca4a7b8e6904..83b223d4429949495c2ae5b91f621f40d0ba68e9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -54,6 +54,20 @@ services: # - 3000:3000 # depends_on: # - cassandra + + mongo: + image: mongo:7.0.12 + volumes: + - mongo:/data/db + - ./docker/mongo:/docker-entrypoint-initdb.d/ + env_file: + - docker/variables.env + + smartdata-context-api: + build: ./context/ + env_file: + - docker/variables.env volumes: mariadb: cassandra: + mongo: diff --git a/docker/mongo/create-user.js b/docker/mongo/create-user.js new file mode 100755 index 0000000000000000000000000000000000000000..c4f9b531b51719cca1313dfc2d79aff924b3a511 --- /dev/null +++ b/docker/mongo/create-user.js @@ -0,0 +1,13 @@ +db.createUser( + { + user: process.env.MONGO_USERNAME, + pwd: process.env.MONGO_PASSWORD, + roles: [ + { + role: "readWrite", + db: process.env.MONGO_DATABASE + } + ] + } +); +db.createCollection("sample"); \ No newline at end of file diff --git a/docker/variables.env b/docker/variables.env index 8cfcd134f540e6e58abeee03e555a5cdf5705c31..730ca1ee7311cee43f44381981511a59575d27e5 100644 --- a/docker/variables.env +++ b/docker/variables.env @@ -14,3 +14,12 @@ JVM_OPTS=-Dcom.sun.management.jmxremote.authenticate=false CASSANDRA_USERNAME=cassandra CASSANDRA_PASSWORD=ch4ng3m3 +# MongoDB +MONGO_INITDB_ROOT_USERNAME=root +MONGO_INITDB_ROOT_PASSWORD=ch4ng3m3 +MONGO_INITDB_DATABASE=smartdatacontext + +MONGO_USERNAME=smartdatacontext +MONGO_PASSWORD=smartdatacontext +MONGO_DATABASE=smartdatacontext +MONGO_HOST=mongo