Compare commits
No commits in common. 'master' and 'UI_Mobile' have entirely different histories.
@ -1,6 +1,20 @@
|
|||||||
FROM php:8.1-apache
|
FROM aosapps/drone-sonar-plugin AS base
|
||||||
RUN apt-get update && apt-get install -y mariadb-client
|
FROM mcr.microsoft.com/dotnet/sdk:6.0-alpine
|
||||||
RUN docker-php-ext-install mysqli pdo pdo_mysql && docker-php-ext-enable pdo_mysql
|
|
||||||
COPY ./Sources/API /var/www/html/
|
COPY --from=base /bin/drone-sonar /bin/
|
||||||
COPY ./Sources/Data /sql/
|
WORKDIR /bin
|
||||||
RUN cd /sql/
|
|
||||||
|
RUN apk update && apk add openjdk11-jre nodejs && rm -rf /tmp/* /var/cache/apk/*
|
||||||
|
|
||||||
|
RUN dotnet tool install --global dotnet-sonarscanner
|
||||||
|
RUN dotnet tool install --global dotnet-reportgenerator-globaltool
|
||||||
|
|
||||||
|
ENV JAVA_HOME /usr/lib/jvm/default-jvm/
|
||||||
|
ENV PATH ${PATH}:${JAVA_HOME}/bin
|
||||||
|
ENV PATH $PATH:/root/.dotnet/tools
|
||||||
|
|
||||||
|
ENTRYPOINT /bin/drone-sonar
|
||||||
|
|
||||||
|
RUN dotnet sonarscanner begin /k:"ConsEco" /d:sonar.host.url="https://codefirst.iut.uca.fr/sonar" /d:sonar.login="sqp_ffc02968e133d03daeb917e8c2e6f243a80d087a"
|
||||||
|
RUN dotnet build
|
||||||
|
RUN dotnet sonarscanner end /d:sonar.login="sqp_ffc02968e133d03daeb917e8c2e6f243a80d087a"
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 52 KiB |
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"require": {
|
|
||||||
"slim/slim": "4.*",
|
|
||||||
"slim/psr7": "^1.6",
|
|
||||||
"zircote/swagger-php": "^4.5",
|
|
||||||
"doctrine/annotations": "^1.14"
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -1,22 +0,0 @@
|
|||||||
<?php
|
|
||||||
class Database {
|
|
||||||
private $host = 'localhost';
|
|
||||||
private $db_name = 'testAPI';
|
|
||||||
private $username = 'viastolfi';
|
|
||||||
private $password = 'MhhLeCaca1!';
|
|
||||||
private $conn;
|
|
||||||
|
|
||||||
public function connect(){
|
|
||||||
$this->conn = null;
|
|
||||||
|
|
||||||
try{
|
|
||||||
$this->conn = new PDO('mysql:host='.$this->host.';dbname='.$this->db_name, $this->username, $this->password);
|
|
||||||
$this->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
|
||||||
} catch(PDOException $e){
|
|
||||||
echo 'Connection Error :'.$e->getMessage();
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->conn;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
?>
|
|
@ -1,25 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
use Psr\Http\Message\ResponseInterface as Response;
|
|
||||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
|
||||||
use Slim\Factory\AppFactory;
|
|
||||||
|
|
||||||
require __DIR__ .'/../vendor/autoload.php';
|
|
||||||
require __DIR__.'/../config/Database.php';
|
|
||||||
|
|
||||||
$app = AppFactory::create();
|
|
||||||
|
|
||||||
$app->get('/', function (Request $request, Response $response, $args) {
|
|
||||||
$response->getBody()->write("Hello world!");
|
|
||||||
return $response;
|
|
||||||
});
|
|
||||||
|
|
||||||
require __DIR__.'/../routes/Inscrit.php';
|
|
||||||
require __DIR__.'/../routes/Banque.php';
|
|
||||||
require __DIR__.'/../routes/Compte.php';
|
|
||||||
require __DIR__.'/../routes/Operation.php';
|
|
||||||
require __DIR__.'/../routes/Planification.php';
|
|
||||||
require __DIR__.'/../routes/Echeance.php';
|
|
||||||
|
|
||||||
$app->run();
|
|
||||||
?>
|
|
@ -1,140 +0,0 @@
|
|||||||
<?php
|
|
||||||
use Psr\Http\Message\ResponseInterface as Response;
|
|
||||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
|
||||||
use Slim\Factory\AppFactory;
|
|
||||||
use OpenApi\Annotations as OA;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @OA\Info(title="My First API", version="0.1")
|
|
||||||
*/
|
|
||||||
|
|
||||||
$app->addBodyParsingMiddleware();
|
|
||||||
$app->addRoutingMiddleware();
|
|
||||||
$app->addErrorMiddleware(true, true, true);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @OA\Get(path="/api/Banque",
|
|
||||||
* @OA\Response(response="200", description="Succes")
|
|
||||||
* @OA\Response(response="500", description="Bdd Error")
|
|
||||||
* )
|
|
||||||
*/
|
|
||||||
$app->get('/Banque/', function(Request $request, Response $response){
|
|
||||||
$query = "SELECT * FROM Banque";
|
|
||||||
|
|
||||||
try{
|
|
||||||
$db = new Database();
|
|
||||||
$conn = $db->connect();
|
|
||||||
|
|
||||||
$stmt = $conn->query($query);
|
|
||||||
$inscrits = $stmt->fetchAll(PDO::FETCH_OBJ);
|
|
||||||
|
|
||||||
$db = null;
|
|
||||||
$response->getBody()->write(json_encode($inscrits));
|
|
||||||
return $response
|
|
||||||
->withHeader('content-type', 'application/json')
|
|
||||||
->withStatus(200);
|
|
||||||
} catch(PDOException $e){
|
|
||||||
$error = array("message" => $e->getMessage());
|
|
||||||
|
|
||||||
$response->getBody()->write(json_encode($error));
|
|
||||||
return $response
|
|
||||||
->withHeader('content-type', 'application/json')
|
|
||||||
->withStatus(500);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$app->post('/Banque/FromId/', function(Request $request, Response $response,array $args){
|
|
||||||
$id = $request->getParsedBody()["id"];
|
|
||||||
$query = 'SELECT id, nomBanque FROM InscrBanque WHERE idInscrit=:id';
|
|
||||||
|
|
||||||
try{
|
|
||||||
$db = new Database();
|
|
||||||
$conn = $db->connect();
|
|
||||||
|
|
||||||
$stmt = $conn->prepare($query);
|
|
||||||
$stmt->bindValue(':id', $id, PDO::PARAM_STR);
|
|
||||||
|
|
||||||
$stmt->execute();
|
|
||||||
$inscrit = $stmt->fetchAll(PDO::FETCH_OBJ);
|
|
||||||
|
|
||||||
$db = null;
|
|
||||||
$response->getBody()->write(json_encode($inscrit));
|
|
||||||
return $response
|
|
||||||
->withHeader('content-type', 'application/json')
|
|
||||||
->withStatus(200);
|
|
||||||
} catch(PDOException $e){
|
|
||||||
$error = array("message" => $e->getMessage());
|
|
||||||
|
|
||||||
$response->getBody()->write(json_encode($error));
|
|
||||||
return $response
|
|
||||||
->withHeader('content-type', 'application/json')
|
|
||||||
->withStatus(500);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$app->post('/Banque/add/', function(Request $request, Response $response, array $args){
|
|
||||||
$nom = $request->getParsedBody()["nom"];
|
|
||||||
$idInscrit = $request->getParsedBody()["idInscrit"];
|
|
||||||
|
|
||||||
$query = "INSERT INTO InscrBanque (nomBanque, idInscrit) VALUES (:nom, :idI)";
|
|
||||||
|
|
||||||
try{
|
|
||||||
$db = new Database();
|
|
||||||
$conn = $db->connect();
|
|
||||||
|
|
||||||
$stmt = $conn->prepare($query);
|
|
||||||
$stmt->bindValue(':nom', $nom, PDO::PARAM_STR);
|
|
||||||
$stmt->bindValue(':idI', $idInscrit, PDO::PARAM_STR);
|
|
||||||
|
|
||||||
$result = $stmt->execute();
|
|
||||||
|
|
||||||
$db = null;
|
|
||||||
$response->getBody()->write(json_encode($result));
|
|
||||||
return $response
|
|
||||||
->withHeader('content-type', 'application/json')
|
|
||||||
->withStatus(200);
|
|
||||||
} catch(PDOException $e){
|
|
||||||
$error = array("message" => $e->getMessage());
|
|
||||||
|
|
||||||
$response->getBody()->write(json_encode($error));
|
|
||||||
return $response
|
|
||||||
->withHeader('content-type', 'application/json')
|
|
||||||
->withStatus(500);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$app->delete('/Banque/delete/', function (Request $request, Response $response, array $args) {
|
|
||||||
$nom = $request->getParsedBody()["nom"];
|
|
||||||
$idInscrit = $request->getParsedBody()["idInscrit"];
|
|
||||||
|
|
||||||
$query = "DELETE FROM InscrBanque WHERE nomBanque=:nom AND idInscrit=:idI";
|
|
||||||
|
|
||||||
try{
|
|
||||||
$db = new Database();
|
|
||||||
$conn = $db->connect();
|
|
||||||
|
|
||||||
$stmt = $conn->prepare($query);
|
|
||||||
$stmt->bindValue(':nom', $nom, PDO::PARAM_STR);
|
|
||||||
$stmt->bindValue(':idI', $idInscrit, PDO::PARAM_STR);
|
|
||||||
|
|
||||||
$result = $stmt->execute();
|
|
||||||
|
|
||||||
$db = null;
|
|
||||||
$response->getBody()->write(json_encode($result));
|
|
||||||
return $response
|
|
||||||
->withHeader('content-type', 'application/json')
|
|
||||||
->withStatus(200);
|
|
||||||
|
|
||||||
} catch(PDOException $e){
|
|
||||||
$error = array("message" => $e->getMessage());
|
|
||||||
|
|
||||||
$response->getBody()->write(json_encode($error));
|
|
||||||
return $response
|
|
||||||
->withHeader('content-type', 'application/json')
|
|
||||||
->withStatus(500);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
?>
|
|
@ -1,116 +0,0 @@
|
|||||||
<?php
|
|
||||||
use Psr\Http\Message\ResponseInterface as Response;
|
|
||||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
|
||||||
use Slim\Factory\AppFactory;
|
|
||||||
use OpenApi\Annotations as OA;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @OA\Info(title="My First API", version="0.1")
|
|
||||||
*/
|
|
||||||
|
|
||||||
$app->addBodyParsingMiddleware();
|
|
||||||
$app->addRoutingMiddleware();
|
|
||||||
$app->addErrorMiddleware(true, true, true);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @OA\Get(path="/api/Compte",
|
|
||||||
* @OA\Response(response="200", description="Succes")
|
|
||||||
* @OA\Response(response="500", description="Bdd Error")
|
|
||||||
* )
|
|
||||||
*/
|
|
||||||
|
|
||||||
$app->post('/Compte/FromIdInscrit/', function(Request $request, Response $response,array $args){
|
|
||||||
$idInscrit = $request->getParsedBody()["id"];
|
|
||||||
$query = 'SELECT * FROM Compte WHERE idInscritBanque=:id';
|
|
||||||
|
|
||||||
try{
|
|
||||||
$db = new Database();
|
|
||||||
$conn = $db->connect();
|
|
||||||
|
|
||||||
$stmt = $conn->prepare($query);
|
|
||||||
$stmt->bindValue(':id', $idInscrit, PDO::PARAM_STR);
|
|
||||||
|
|
||||||
$stmt->execute();
|
|
||||||
$compte = $stmt->fetchAll(PDO::FETCH_OBJ);
|
|
||||||
|
|
||||||
$db = null;
|
|
||||||
$response->getBody()->write(json_encode($compte));
|
|
||||||
return $response
|
|
||||||
->withHeader('content-type', 'application/json')
|
|
||||||
->withStatus(200);
|
|
||||||
} catch(PDOException $e){
|
|
||||||
$error = array("message" => $e->getMessage());
|
|
||||||
|
|
||||||
$response->getBody()->write(json_encode($error));
|
|
||||||
return $response
|
|
||||||
->withHeader('content-type', 'application/json')
|
|
||||||
->withStatus(500);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$app->post('/Compte/add/', function(Request $request, Response $response, array $args){
|
|
||||||
$nom = $request->getParsedBody()["nom"];
|
|
||||||
$idInscrit = $request->getParsedBody()["idInscrit"];
|
|
||||||
|
|
||||||
$query = "INSERT INTO Compte (nom, idInscritBanque) VALUES (:nom, :idI)";
|
|
||||||
|
|
||||||
try{
|
|
||||||
$db = new Database();
|
|
||||||
$conn = $db->connect();
|
|
||||||
|
|
||||||
$stmt = $conn->prepare($query);
|
|
||||||
$stmt->bindValue(':nom', $nom, PDO::PARAM_STR);
|
|
||||||
$stmt->bindValue(':idI', $idInscrit, PDO::PARAM_STR);
|
|
||||||
|
|
||||||
$result = $stmt->execute();
|
|
||||||
|
|
||||||
$db = null;
|
|
||||||
$response->getBody()->write(json_encode($result));
|
|
||||||
return $response
|
|
||||||
->withHeader('content-type', 'application/json')
|
|
||||||
->withStatus(200);
|
|
||||||
} catch(PDOException $e){
|
|
||||||
$error = array("message" => $e->getMessage());
|
|
||||||
|
|
||||||
$response->getBody()->write(json_encode($error));
|
|
||||||
return $response
|
|
||||||
->withHeader('content-type', 'application/json')
|
|
||||||
->withStatus(500);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$app->delete('/Compte/delete/', function (Request $request, Response $response, array $args) {
|
|
||||||
$nom = $request->getParsedBody()["nom"];
|
|
||||||
$idInscrit = $request->getParsedBody()["idInscrit"];
|
|
||||||
|
|
||||||
$query = "DELETE FROM Compte WHERE nom=:nom AND idInscritBanque=:idI";
|
|
||||||
|
|
||||||
try{
|
|
||||||
$db = new Database();
|
|
||||||
$conn = $db->connect();
|
|
||||||
|
|
||||||
$stmt = $conn->prepare($query);
|
|
||||||
$stmt->bindValue(':nom', $nom, PDO::PARAM_STR);
|
|
||||||
$stmt->bindValue(':idI', $idInscrit, PDO::PARAM_STR);
|
|
||||||
|
|
||||||
$result = $stmt->execute();
|
|
||||||
|
|
||||||
$db = null;
|
|
||||||
$response->getBody()->write(json_encode($result));
|
|
||||||
return $response
|
|
||||||
->withHeader('content-type', 'application/json')
|
|
||||||
->withStatus(200);
|
|
||||||
|
|
||||||
} catch(PDOException $e){
|
|
||||||
$error = array("message" => $e->getMessage());
|
|
||||||
|
|
||||||
$response->getBody()->write(json_encode($error));
|
|
||||||
return $response
|
|
||||||
->withHeader('content-type', 'application/json')
|
|
||||||
->withStatus(500);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
?>
|
|
@ -1,125 +0,0 @@
|
|||||||
<?php
|
|
||||||
use Psr\Http\Message\ResponseInterface as Response;
|
|
||||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
|
||||||
use Slim\Factory\AppFactory;
|
|
||||||
use OpenApi\Annotations as OA;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @OA\Info(title="My First API", version="0.1")
|
|
||||||
*/
|
|
||||||
|
|
||||||
$app->addBodyParsingMiddleware();
|
|
||||||
$app->addRoutingMiddleware();
|
|
||||||
$app->addErrorMiddleware(true, true, true);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @OA\Get(path="/api/Echeance",
|
|
||||||
* @OA\Response(response="200", description="Succes")
|
|
||||||
* @OA\Response(response="500", description="Bdd Error")
|
|
||||||
* )
|
|
||||||
*/
|
|
||||||
|
|
||||||
$app->post('/Echeance/FromIdCompte/', function(Request $request, Response $response,array $args){
|
|
||||||
$idCompte = $request->getParsedBody()["id"];
|
|
||||||
$query = 'SELECT * FROM Echeancier WHERE compte=:id';
|
|
||||||
|
|
||||||
try{
|
|
||||||
$db = new Database();
|
|
||||||
$conn = $db->connect();
|
|
||||||
|
|
||||||
$stmt = $conn->prepare($query);
|
|
||||||
$stmt->bindValue(':id', $idCompte, PDO::PARAM_STR);
|
|
||||||
|
|
||||||
$stmt->execute();
|
|
||||||
$ope = $stmt->fetchAll(PDO::FETCH_OBJ);
|
|
||||||
|
|
||||||
$db = null;
|
|
||||||
$response->getBody()->write(json_encode($ope));
|
|
||||||
return $response
|
|
||||||
->withHeader('content-type', 'application/json')
|
|
||||||
->withStatus(200);
|
|
||||||
} catch(PDOException $e){
|
|
||||||
$error = array("message" => $e->getMessage());
|
|
||||||
|
|
||||||
$response->getBody()->write(json_encode($error));
|
|
||||||
return $response
|
|
||||||
->withHeader('content-type', 'application/json')
|
|
||||||
->withStatus(500);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$app->post('/Echeance/add/', function(Request $request, Response $response, array $args){
|
|
||||||
$compte = $request->getParsedBody()["compte"];
|
|
||||||
$nom = $request->getParsedBody()["nom"];
|
|
||||||
$montant = $request->getParsedBody()["montant"];
|
|
||||||
$dateO = $request->getParsedBody()["dateO"];
|
|
||||||
$methodePayement = $request->getParsedBody()["methodePayement"];
|
|
||||||
$isDebit = $request->getParsedBody()["isDebit"];
|
|
||||||
$tag = $request->getParsedBody()["tag"];
|
|
||||||
|
|
||||||
$query = "INSERT INTO Echeancier (compte, nom, montant, dateO, methodePayement, isDebit, tag) SELECT :compte,:nom,:montant, STR_TO_DATE(:dateO, '%d/%m/%Y %H:%i:%s' ), :methodePayement, :isD ,:tag;";
|
|
||||||
try{
|
|
||||||
$db = new Database();
|
|
||||||
$conn = $db->connect();
|
|
||||||
|
|
||||||
$stmt = $conn->prepare($query);
|
|
||||||
$stmt->bindValue(':compte', $compte, PDO::PARAM_STR);
|
|
||||||
$stmt->bindValue(':nom', $nom, PDO::PARAM_STR);
|
|
||||||
$stmt->bindValue(':montant', $montant, PDO::PARAM_STR);
|
|
||||||
$stmt->bindValue(':dateO', $dateO, PDO::PARAM_STR);
|
|
||||||
$stmt->bindValue(':methodePayement', $methodePayement, PDO::PARAM_STR);
|
|
||||||
$stmt->bindValue(':isD', $isDebit, PDO::PARAM_BOOL);
|
|
||||||
$stmt->bindValue(':tag', $tag, PDO::PARAM_STR);
|
|
||||||
|
|
||||||
$result = $stmt->execute();
|
|
||||||
|
|
||||||
$db = null;
|
|
||||||
$response->getBody()->write(json_encode($result));
|
|
||||||
return $response
|
|
||||||
->withHeader('content-type', 'application/json')
|
|
||||||
->withStatus(200);
|
|
||||||
} catch(PDOException $e){
|
|
||||||
$error = array("message" => $e->getMessage());
|
|
||||||
|
|
||||||
$response->getBody()->write(json_encode($error));
|
|
||||||
return $response
|
|
||||||
->withHeader('content-type', 'application/json')
|
|
||||||
->withStatus(500);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$app->delete('/Echeance/delete/', function (Request $request, Response $response, array $args) {
|
|
||||||
$compte = $request->getParsedBody()["compte"];
|
|
||||||
$nom = $request->getParsedBody()["nom"];
|
|
||||||
|
|
||||||
$query = "DELETE FROM Echeancier WHERE compte=:compte AND nom=:nom";
|
|
||||||
|
|
||||||
try{
|
|
||||||
$db = new Database();
|
|
||||||
$conn = $db->connect();
|
|
||||||
|
|
||||||
$stmt = $conn->prepare($query);
|
|
||||||
$stmt->bindValue(':compte', $compte, PDO::PARAM_STR);
|
|
||||||
$stmt->bindValue(':nom', $nom, PDO::PARAM_STR);
|
|
||||||
|
|
||||||
$result = $stmt->execute();
|
|
||||||
|
|
||||||
$db = null;
|
|
||||||
$response->getBody()->write(json_encode($result));
|
|
||||||
return $response
|
|
||||||
->withHeader('content-type', 'application/json')
|
|
||||||
->withStatus(200);
|
|
||||||
|
|
||||||
} catch(PDOException $e){
|
|
||||||
$error = array("message" => $e->getMessage());
|
|
||||||
|
|
||||||
$response->getBody()->write(json_encode($error));
|
|
||||||
return $response
|
|
||||||
->withHeader('content-type', 'application/json')
|
|
||||||
->withStatus(500);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
?>
|
|
@ -1,172 +0,0 @@
|
|||||||
<?php
|
|
||||||
use Psr\Http\Message\ResponseInterface as Response;
|
|
||||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
|
||||||
use Slim\Factory\AppFactory;
|
|
||||||
use OpenApi\Annotations as OA;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @OA\Info(title="My First API", version="0.1")
|
|
||||||
*/
|
|
||||||
|
|
||||||
$app->addBodyParsingMiddleware();
|
|
||||||
$app->addRoutingMiddleware();
|
|
||||||
$app->addErrorMiddleware(true, true, true);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @OA\Get(path="/api/Inscrit",
|
|
||||||
* @OA\Response(response="200", description="Succes")
|
|
||||||
* @OA\Response(response="500", description="Bdd Error")
|
|
||||||
* )
|
|
||||||
*/
|
|
||||||
$app->get('/Inscrit/', function(Request $request, Response $response){
|
|
||||||
$query = "SELECT * FROM Inscrit";
|
|
||||||
|
|
||||||
try{
|
|
||||||
$db = new Database();
|
|
||||||
$conn = $db->connect();
|
|
||||||
|
|
||||||
$stmt = $conn->query($query);
|
|
||||||
$inscrits = $stmt->fetchAll(PDO::FETCH_OBJ);
|
|
||||||
|
|
||||||
$db = null;
|
|
||||||
$response->getBody()->write(json_encode($inscrits));
|
|
||||||
return $response
|
|
||||||
->withHeader('content-type', 'application/json')
|
|
||||||
->withStatus(200);
|
|
||||||
} catch(PDOException $e){
|
|
||||||
$error = array("message" => $e->getMessage());
|
|
||||||
|
|
||||||
$response->getBody()->write(json_encode($error));
|
|
||||||
return $response
|
|
||||||
->withHeader('content-type', 'application/json')
|
|
||||||
->withStatus(500);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$app->post('/Inscrit/FromMail/', function(Request $request, Response $response,array $args){
|
|
||||||
$mail = $request->getParsedBody()["email"];
|
|
||||||
$query = 'SELECT * FROM Inscrit WHERE mail=:mail';
|
|
||||||
|
|
||||||
try{
|
|
||||||
$db = new Database();
|
|
||||||
$conn = $db->connect();
|
|
||||||
|
|
||||||
$stmt = $conn->prepare($query);
|
|
||||||
$stmt->bindValue(':mail', $mail, PDO::PARAM_STR);
|
|
||||||
|
|
||||||
$stmt->execute();
|
|
||||||
$inscrit = $stmt->fetchAll(PDO::FETCH_OBJ);
|
|
||||||
|
|
||||||
$db = null;
|
|
||||||
$response->getBody()->write(json_encode($inscrit));
|
|
||||||
return $response
|
|
||||||
->withHeader('content-type', 'application/json')
|
|
||||||
->withStatus(200);
|
|
||||||
} catch(PDOException $e){
|
|
||||||
$error = array("message" => $e->getMessage());
|
|
||||||
|
|
||||||
$response->getBody()->write(json_encode($error));
|
|
||||||
return $response
|
|
||||||
->withHeader('content-type', 'application/json')
|
|
||||||
->withStatus(500);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$app->put('/Inscrit/UpdatePassword/', function(Request $request, Response $response, array $args){
|
|
||||||
$mail = $request->getParsedBody()["email"];
|
|
||||||
$password = $request->getParsedBody()["password"];
|
|
||||||
$query = 'UPDATE Inscrit SET mdp=:password WHERE mail=:mail';
|
|
||||||
|
|
||||||
try{
|
|
||||||
$db = new Database();
|
|
||||||
$conn = $db->connect();
|
|
||||||
|
|
||||||
$stmt = $conn->prepare($query);
|
|
||||||
$stmt->bindValue(':mail', $mail, PDO::PARAM_STR);
|
|
||||||
$stmt->bindValue(':password', $password, PDO::PARAM_STR);
|
|
||||||
|
|
||||||
$result = $stmt->execute();
|
|
||||||
|
|
||||||
$db = null;
|
|
||||||
$response->getBody()->write(json_encode($result));
|
|
||||||
return $response
|
|
||||||
->withHeader('content-type', 'application/json')
|
|
||||||
->withStatus(200);
|
|
||||||
} catch(PDOException $e){
|
|
||||||
$error = array("message" => $e->getMessage());
|
|
||||||
|
|
||||||
$response->getBody()->write(json_encode($error));
|
|
||||||
return $response
|
|
||||||
->withHeader('content-type', 'application/json')
|
|
||||||
->withStatus(500);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$app->post('/Inscrit/add/', function(Request $request, Response $response, array $args){
|
|
||||||
$nom = $request->getParsedBody()["nom"];
|
|
||||||
$prenom = $request->getParsedBody()["prenom"];
|
|
||||||
$mail = $request->getParsedbody()["email"];
|
|
||||||
$password = $request->getParsedBody()["password"];
|
|
||||||
|
|
||||||
$query = "INSERT INTO Inscrit (nom, prenom, mail, mdp) VALUES (:nom, :prenom, :mail, :password);";
|
|
||||||
|
|
||||||
try{
|
|
||||||
$db = new Database();
|
|
||||||
$conn = $db->connect();
|
|
||||||
|
|
||||||
$stmt = $conn->prepare($query);
|
|
||||||
$stmt->bindValue(':nom', $nom, PDO::PARAM_STR);
|
|
||||||
$stmt->bindValue(':prenom', $prenom, PDO::PARAM_STR);
|
|
||||||
$stmt->bindValue(':mail', $mail, PDO::PARAM_STR);
|
|
||||||
$stmt->bindValue(':password', $password, PDO::PARAM_STR);
|
|
||||||
|
|
||||||
$result = $stmt->execute();
|
|
||||||
|
|
||||||
$db = null;
|
|
||||||
$response->getBody()->write(json_encode($result));
|
|
||||||
return $response
|
|
||||||
->withHeader('content-type', 'application/json')
|
|
||||||
->withStatus(200);
|
|
||||||
} catch(PDOException $e){
|
|
||||||
$error = array("message" => $e->getMessage());
|
|
||||||
|
|
||||||
$response->getBody()->write(json_encode($error));
|
|
||||||
return $response
|
|
||||||
->withHeader('content-type', 'application/json')
|
|
||||||
->withStatus(500);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
$app->delete('/Inscrit/delete/', function (Request $request, Response $response, array $args) {
|
|
||||||
$email = $request->getParsedBody()["email"];
|
|
||||||
|
|
||||||
$query = "DELETE FROM Inscrit WHERE mail=:mail";
|
|
||||||
|
|
||||||
try{
|
|
||||||
$db = new Database();
|
|
||||||
$conn = $db->connect();
|
|
||||||
|
|
||||||
$stmt = $conn->prepare($query);
|
|
||||||
$stmt->bindValue(':mail', $email, PDO::PARAM_STR);
|
|
||||||
|
|
||||||
$result = $stmt->execute();
|
|
||||||
|
|
||||||
$db = null;
|
|
||||||
$response->getBody()->write(json_encode($result));
|
|
||||||
return $response
|
|
||||||
->withHeader('content-type', 'application/json')
|
|
||||||
->withStatus(200);
|
|
||||||
|
|
||||||
} catch(PDOException $e){
|
|
||||||
$error = array("message" => $e->getMessage());
|
|
||||||
|
|
||||||
$response->getBody()->write(json_encode($error));
|
|
||||||
return $response
|
|
||||||
->withHeader('content-type', 'application/json')
|
|
||||||
->withStatus(500);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
?>
|
|
@ -1,127 +0,0 @@
|
|||||||
<?php
|
|
||||||
use Psr\Http\Message\ResponseInterface as Response;
|
|
||||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
|
||||||
use Slim\Factory\AppFactory;
|
|
||||||
use OpenApi\Annotations as OA;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @OA\Info(title="My First API", version="0.1")
|
|
||||||
*/
|
|
||||||
|
|
||||||
$app->addBodyParsingMiddleware();
|
|
||||||
$app->addRoutingMiddleware();
|
|
||||||
$app->addErrorMiddleware(true, true, true);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @OA\Get(path="/api/Operation",
|
|
||||||
* @OA\Response(response="200", description="Succes")
|
|
||||||
* @OA\Response(response="500", description="Bdd Error")
|
|
||||||
* )
|
|
||||||
*/
|
|
||||||
|
|
||||||
$app->post('/Operation/FromIdCompte/', function(Request $request, Response $response,array $args){
|
|
||||||
$idCompte = $request->getParsedBody()["id"];
|
|
||||||
$query = 'SELECT * FROM Operation WHERE compte=:id';
|
|
||||||
|
|
||||||
try{
|
|
||||||
$db = new Database();
|
|
||||||
$conn = $db->connect();
|
|
||||||
|
|
||||||
$stmt = $conn->prepare($query);
|
|
||||||
$stmt->bindValue(':id', $idCompte, PDO::PARAM_STR);
|
|
||||||
|
|
||||||
$stmt->execute();
|
|
||||||
$ope = $stmt->fetchAll(PDO::FETCH_OBJ);
|
|
||||||
|
|
||||||
$db = null;
|
|
||||||
$response->getBody()->write(json_encode($ope));
|
|
||||||
return $response
|
|
||||||
->withHeader('content-type', 'application/json')
|
|
||||||
->withStatus(200);
|
|
||||||
} catch(PDOException $e){
|
|
||||||
$error = array("message" => $e->getMessage());
|
|
||||||
|
|
||||||
$response->getBody()->write(json_encode($error));
|
|
||||||
return $response
|
|
||||||
->withHeader('content-type', 'application/json')
|
|
||||||
->withStatus(500);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$app->post('/Operation/add/', function(Request $request, Response $response, array $args){
|
|
||||||
$compte = $request->getParsedBody()["compte"];
|
|
||||||
$nom = $request->getParsedBody()["nom"];
|
|
||||||
$montant = $request->getParsedBody()["montant"];
|
|
||||||
$dateO = $request->getParsedBody()["dateO"];
|
|
||||||
$methodePayement = $request->getParsedBody()["methodePayement"];
|
|
||||||
$isDebit = $request->getParsedBody()["isDebit"];
|
|
||||||
$tag = $request->getParsedBody()["tag"];
|
|
||||||
$fromBanque = $request->getParsedBody()["fromBanque"];
|
|
||||||
|
|
||||||
$query = "INSERT INTO Operation (compte, nom, montant, dateO, methodePayement, isDebit, fromBanque, tag) SELECT :compte,:nom,:montant, STR_TO_DATE(:dateO, '%d/%m/%Y %H:%i:%s' ), :methodePayement, :isD, :fromBanque, :tag;";
|
|
||||||
try{
|
|
||||||
$db = new Database();
|
|
||||||
$conn = $db->connect();
|
|
||||||
|
|
||||||
$stmt = $conn->prepare($query);
|
|
||||||
$stmt->bindValue(':compte', $compte, PDO::PARAM_STR);
|
|
||||||
$stmt->bindValue(':nom', $nom, PDO::PARAM_STR);
|
|
||||||
$stmt->bindValue(':montant', $montant, PDO::PARAM_STR);
|
|
||||||
$stmt->bindValue(':dateO', $dateO, PDO::PARAM_STR);
|
|
||||||
$stmt->bindValue(':methodePayement', $methodePayement, PDO::PARAM_STR);
|
|
||||||
$stmt->bindValue(':isD', $isDebit, PDO::PARAM_BOOL);
|
|
||||||
$stmt->bindValue(':tag', $tag, PDO::PARAM_STR);
|
|
||||||
$stmt->bindValue(':fromBanque', $fromBanque, PDO::PARAM_BOOL);
|
|
||||||
|
|
||||||
$result = $stmt->execute();
|
|
||||||
|
|
||||||
$db = null;
|
|
||||||
$response->getBody()->write(json_encode($result));
|
|
||||||
return $response
|
|
||||||
->withHeader('content-type', 'application/json')
|
|
||||||
->withStatus(200);
|
|
||||||
} catch(PDOException $e){
|
|
||||||
$error = array("message" => $e->getMessage());
|
|
||||||
|
|
||||||
$response->getBody()->write(json_encode($error));
|
|
||||||
return $response
|
|
||||||
->withHeader('content-type', 'application/json')
|
|
||||||
->withStatus(500);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$app->delete('/Operation/delete/', function (Request $request, Response $response, array $args) {
|
|
||||||
$compte = $request->getParsedBody()["compte"];
|
|
||||||
$nom = $request->getParsedBody()["nom"];
|
|
||||||
|
|
||||||
$query = "DELETE FROM Operation WHERE compte=:compte AND nom=:nom";
|
|
||||||
|
|
||||||
try{
|
|
||||||
$db = new Database();
|
|
||||||
$conn = $db->connect();
|
|
||||||
|
|
||||||
$stmt = $conn->prepare($query);
|
|
||||||
$stmt->bindValue(':compte', $compte, PDO::PARAM_STR);
|
|
||||||
$stmt->bindValue(':nom', $nom, PDO::PARAM_STR);
|
|
||||||
|
|
||||||
$result = $stmt->execute();
|
|
||||||
|
|
||||||
$db = null;
|
|
||||||
$response->getBody()->write(json_encode($result));
|
|
||||||
return $response
|
|
||||||
->withHeader('content-type', 'application/json')
|
|
||||||
->withStatus(200);
|
|
||||||
|
|
||||||
} catch(PDOException $e){
|
|
||||||
$error = array("message" => $e->getMessage());
|
|
||||||
|
|
||||||
$response->getBody()->write(json_encode($error));
|
|
||||||
return $response
|
|
||||||
->withHeader('content-type', 'application/json')
|
|
||||||
->withStatus(500);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
?>
|
|
@ -1,125 +0,0 @@
|
|||||||
<?php
|
|
||||||
use Psr\Http\Message\ResponseInterface as Response;
|
|
||||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
|
||||||
use Slim\Factory\AppFactory;
|
|
||||||
use OpenApi\Annotations as OA;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @OA\Info(title="My First API", version="0.1")
|
|
||||||
*/
|
|
||||||
|
|
||||||
$app->addBodyParsingMiddleware();
|
|
||||||
$app->addRoutingMiddleware();
|
|
||||||
$app->addErrorMiddleware(true, true, true);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @OA\Get(path="/api/Planification",
|
|
||||||
* @OA\Response(response="200", description="Succes")
|
|
||||||
* @OA\Response(response="500", description="Bdd Error")
|
|
||||||
* )
|
|
||||||
*/
|
|
||||||
|
|
||||||
$app->post('/Planification/FromIdCompte/', function(Request $request, Response $response,array $args){
|
|
||||||
$idCompte = $request->getParsedBody()["id"];
|
|
||||||
$query = 'SELECT * FROM Planification WHERE compte=:id';
|
|
||||||
|
|
||||||
try{
|
|
||||||
$db = new Database();
|
|
||||||
$conn = $db->connect();
|
|
||||||
|
|
||||||
$stmt = $conn->prepare($query);
|
|
||||||
$stmt->bindValue(':id', $idCompte, PDO::PARAM_STR);
|
|
||||||
|
|
||||||
$stmt->execute();
|
|
||||||
$ope = $stmt->fetchAll(PDO::FETCH_OBJ);
|
|
||||||
|
|
||||||
$db = null;
|
|
||||||
$response->getBody()->write(json_encode($ope));
|
|
||||||
return $response
|
|
||||||
->withHeader('content-type', 'application/json')
|
|
||||||
->withStatus(200);
|
|
||||||
} catch(PDOException $e){
|
|
||||||
$error = array("message" => $e->getMessage());
|
|
||||||
|
|
||||||
$response->getBody()->write(json_encode($error));
|
|
||||||
return $response
|
|
||||||
->withHeader('content-type', 'application/json')
|
|
||||||
->withStatus(500);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$app->post('/Planification/add/', function(Request $request, Response $response, array $args){
|
|
||||||
$compte = $request->getParsedBody()["compte"];
|
|
||||||
$nom = $request->getParsedBody()["nom"];
|
|
||||||
$montant = $request->getParsedBody()["montant"];
|
|
||||||
$dateO = $request->getParsedBody()["dateO"];
|
|
||||||
$methodePayement = $request->getParsedBody()["methodePayement"];
|
|
||||||
$isDebit = $request->getParsedBody()["isDebit"];
|
|
||||||
$tag = $request->getParsedBody()["tag"];
|
|
||||||
|
|
||||||
$query = "INSERT INTO Planification (compte, nom, montant, dateO, methodePayement, isDebit, tag) SELECT :compte,:nom,:montant, STR_TO_DATE(:dateO, '%d/%m/%Y %H:%i:%s' ), :methodePayement, :isD ,:tag;";
|
|
||||||
try{
|
|
||||||
$db = new Database();
|
|
||||||
$conn = $db->connect();
|
|
||||||
|
|
||||||
$stmt = $conn->prepare($query);
|
|
||||||
$stmt->bindValue(':compte', $compte, PDO::PARAM_STR);
|
|
||||||
$stmt->bindValue(':nom', $nom, PDO::PARAM_STR);
|
|
||||||
$stmt->bindValue(':montant', $montant, PDO::PARAM_STR);
|
|
||||||
$stmt->bindValue(':dateO', $dateO, PDO::PARAM_STR);
|
|
||||||
$stmt->bindValue(':methodePayement', $methodePayement, PDO::PARAM_STR);
|
|
||||||
$stmt->bindValue(':isD', $isDebit, PDO::PARAM_BOOL);
|
|
||||||
$stmt->bindValue(':tag', $tag, PDO::PARAM_STR);
|
|
||||||
|
|
||||||
$result = $stmt->execute();
|
|
||||||
|
|
||||||
$db = null;
|
|
||||||
$response->getBody()->write(json_encode($result));
|
|
||||||
return $response
|
|
||||||
->withHeader('content-type', 'application/json')
|
|
||||||
->withStatus(200);
|
|
||||||
} catch(PDOException $e){
|
|
||||||
$error = array("message" => $e->getMessage());
|
|
||||||
|
|
||||||
$response->getBody()->write(json_encode($error));
|
|
||||||
return $response
|
|
||||||
->withHeader('content-type', 'application/json')
|
|
||||||
->withStatus(500);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$app->delete('/Planification/delete/', function (Request $request, Response $response, array $args) {
|
|
||||||
$compte = $request->getParsedBody()["compte"];
|
|
||||||
$nom = $request->getParsedBody()["nom"];
|
|
||||||
|
|
||||||
$query = "DELETE FROM Planification WHERE compte=:compte AND nom=:nom";
|
|
||||||
|
|
||||||
try{
|
|
||||||
$db = new Database();
|
|
||||||
$conn = $db->connect();
|
|
||||||
|
|
||||||
$stmt = $conn->prepare($query);
|
|
||||||
$stmt->bindValue(':compte', $compte, PDO::PARAM_STR);
|
|
||||||
$stmt->bindValue(':nom', $nom, PDO::PARAM_STR);
|
|
||||||
|
|
||||||
$result = $stmt->execute();
|
|
||||||
|
|
||||||
$db = null;
|
|
||||||
$response->getBody()->write(json_encode($result));
|
|
||||||
return $response
|
|
||||||
->withHeader('content-type', 'application/json')
|
|
||||||
->withStatus(200);
|
|
||||||
|
|
||||||
} catch(PDOException $e){
|
|
||||||
$error = array("message" => $e->getMessage());
|
|
||||||
|
|
||||||
$response->getBody()->write(json_encode($error));
|
|
||||||
return $response
|
|
||||||
->withHeader('content-type', 'application/json')
|
|
||||||
->withStatus(500);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
?>
|
|
@ -1,7 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
// autoload.php @generated by Composer
|
|
||||||
|
|
||||||
require_once __DIR__ . '/composer/autoload_real.php';
|
|
||||||
|
|
||||||
return ComposerAutoloaderInita934429c0ea4f4482346c5d296943a81::getLoader();
|
|
@ -1,572 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of Composer.
|
|
||||||
*
|
|
||||||
* (c) Nils Adermann <naderman@naderman.de>
|
|
||||||
* Jordi Boggiano <j.boggiano@seld.be>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Composer\Autoload;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
|
|
||||||
*
|
|
||||||
* $loader = new \Composer\Autoload\ClassLoader();
|
|
||||||
*
|
|
||||||
* // register classes with namespaces
|
|
||||||
* $loader->add('Symfony\Component', __DIR__.'/component');
|
|
||||||
* $loader->add('Symfony', __DIR__.'/framework');
|
|
||||||
*
|
|
||||||
* // activate the autoloader
|
|
||||||
* $loader->register();
|
|
||||||
*
|
|
||||||
* // to enable searching the include path (eg. for PEAR packages)
|
|
||||||
* $loader->setUseIncludePath(true);
|
|
||||||
*
|
|
||||||
* In this example, if you try to use a class in the Symfony\Component
|
|
||||||
* namespace or one of its children (Symfony\Component\Console for instance),
|
|
||||||
* the autoloader will first look for the class under the component/
|
|
||||||
* directory, and it will then fallback to the framework/ directory if not
|
|
||||||
* found before giving up.
|
|
||||||
*
|
|
||||||
* This class is loosely based on the Symfony UniversalClassLoader.
|
|
||||||
*
|
|
||||||
* @author Fabien Potencier <fabien@symfony.com>
|
|
||||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
|
||||||
* @see https://www.php-fig.org/psr/psr-0/
|
|
||||||
* @see https://www.php-fig.org/psr/psr-4/
|
|
||||||
*/
|
|
||||||
class ClassLoader
|
|
||||||
{
|
|
||||||
/** @var ?string */
|
|
||||||
private $vendorDir;
|
|
||||||
|
|
||||||
// PSR-4
|
|
||||||
/**
|
|
||||||
* @var array[]
|
|
||||||
* @psalm-var array<string, array<string, int>>
|
|
||||||
*/
|
|
||||||
private $prefixLengthsPsr4 = array();
|
|
||||||
/**
|
|
||||||
* @var array[]
|
|
||||||
* @psalm-var array<string, array<int, string>>
|
|
||||||
*/
|
|
||||||
private $prefixDirsPsr4 = array();
|
|
||||||
/**
|
|
||||||
* @var array[]
|
|
||||||
* @psalm-var array<string, string>
|
|
||||||
*/
|
|
||||||
private $fallbackDirsPsr4 = array();
|
|
||||||
|
|
||||||
// PSR-0
|
|
||||||
/**
|
|
||||||
* @var array[]
|
|
||||||
* @psalm-var array<string, array<string, string[]>>
|
|
||||||
*/
|
|
||||||
private $prefixesPsr0 = array();
|
|
||||||
/**
|
|
||||||
* @var array[]
|
|
||||||
* @psalm-var array<string, string>
|
|
||||||
*/
|
|
||||||
private $fallbackDirsPsr0 = array();
|
|
||||||
|
|
||||||
/** @var bool */
|
|
||||||
private $useIncludePath = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var string[]
|
|
||||||
* @psalm-var array<string, string>
|
|
||||||
*/
|
|
||||||
private $classMap = array();
|
|
||||||
|
|
||||||
/** @var bool */
|
|
||||||
private $classMapAuthoritative = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var bool[]
|
|
||||||
* @psalm-var array<string, bool>
|
|
||||||
*/
|
|
||||||
private $missingClasses = array();
|
|
||||||
|
|
||||||
/** @var ?string */
|
|
||||||
private $apcuPrefix;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var self[]
|
|
||||||
*/
|
|
||||||
private static $registeredLoaders = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param ?string $vendorDir
|
|
||||||
*/
|
|
||||||
public function __construct($vendorDir = null)
|
|
||||||
{
|
|
||||||
$this->vendorDir = $vendorDir;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string[]
|
|
||||||
*/
|
|
||||||
public function getPrefixes()
|
|
||||||
{
|
|
||||||
if (!empty($this->prefixesPsr0)) {
|
|
||||||
return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
|
|
||||||
}
|
|
||||||
|
|
||||||
return array();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array[]
|
|
||||||
* @psalm-return array<string, array<int, string>>
|
|
||||||
*/
|
|
||||||
public function getPrefixesPsr4()
|
|
||||||
{
|
|
||||||
return $this->prefixDirsPsr4;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array[]
|
|
||||||
* @psalm-return array<string, string>
|
|
||||||
*/
|
|
||||||
public function getFallbackDirs()
|
|
||||||
{
|
|
||||||
return $this->fallbackDirsPsr0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array[]
|
|
||||||
* @psalm-return array<string, string>
|
|
||||||
*/
|
|
||||||
public function getFallbackDirsPsr4()
|
|
||||||
{
|
|
||||||
return $this->fallbackDirsPsr4;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string[] Array of classname => path
|
|
||||||
* @psalm-return array<string, string>
|
|
||||||
*/
|
|
||||||
public function getClassMap()
|
|
||||||
{
|
|
||||||
return $this->classMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string[] $classMap Class to filename map
|
|
||||||
* @psalm-param array<string, string> $classMap
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function addClassMap(array $classMap)
|
|
||||||
{
|
|
||||||
if ($this->classMap) {
|
|
||||||
$this->classMap = array_merge($this->classMap, $classMap);
|
|
||||||
} else {
|
|
||||||
$this->classMap = $classMap;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers a set of PSR-0 directories for a given prefix, either
|
|
||||||
* appending or prepending to the ones previously set for this prefix.
|
|
||||||
*
|
|
||||||
* @param string $prefix The prefix
|
|
||||||
* @param string[]|string $paths The PSR-0 root directories
|
|
||||||
* @param bool $prepend Whether to prepend the directories
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function add($prefix, $paths, $prepend = false)
|
|
||||||
{
|
|
||||||
if (!$prefix) {
|
|
||||||
if ($prepend) {
|
|
||||||
$this->fallbackDirsPsr0 = array_merge(
|
|
||||||
(array) $paths,
|
|
||||||
$this->fallbackDirsPsr0
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
$this->fallbackDirsPsr0 = array_merge(
|
|
||||||
$this->fallbackDirsPsr0,
|
|
||||||
(array) $paths
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$first = $prefix[0];
|
|
||||||
if (!isset($this->prefixesPsr0[$first][$prefix])) {
|
|
||||||
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if ($prepend) {
|
|
||||||
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
|
||||||
(array) $paths,
|
|
||||||
$this->prefixesPsr0[$first][$prefix]
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
|
||||||
$this->prefixesPsr0[$first][$prefix],
|
|
||||||
(array) $paths
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers a set of PSR-4 directories for a given namespace, either
|
|
||||||
* appending or prepending to the ones previously set for this namespace.
|
|
||||||
*
|
|
||||||
* @param string $prefix The prefix/namespace, with trailing '\\'
|
|
||||||
* @param string[]|string $paths The PSR-4 base directories
|
|
||||||
* @param bool $prepend Whether to prepend the directories
|
|
||||||
*
|
|
||||||
* @throws \InvalidArgumentException
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function addPsr4($prefix, $paths, $prepend = false)
|
|
||||||
{
|
|
||||||
if (!$prefix) {
|
|
||||||
// Register directories for the root namespace.
|
|
||||||
if ($prepend) {
|
|
||||||
$this->fallbackDirsPsr4 = array_merge(
|
|
||||||
(array) $paths,
|
|
||||||
$this->fallbackDirsPsr4
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
$this->fallbackDirsPsr4 = array_merge(
|
|
||||||
$this->fallbackDirsPsr4,
|
|
||||||
(array) $paths
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
|
|
||||||
// Register directories for a new namespace.
|
|
||||||
$length = strlen($prefix);
|
|
||||||
if ('\\' !== $prefix[$length - 1]) {
|
|
||||||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
|
||||||
}
|
|
||||||
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
|
||||||
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
|
||||||
} elseif ($prepend) {
|
|
||||||
// Prepend directories for an already registered namespace.
|
|
||||||
$this->prefixDirsPsr4[$prefix] = array_merge(
|
|
||||||
(array) $paths,
|
|
||||||
$this->prefixDirsPsr4[$prefix]
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// Append directories for an already registered namespace.
|
|
||||||
$this->prefixDirsPsr4[$prefix] = array_merge(
|
|
||||||
$this->prefixDirsPsr4[$prefix],
|
|
||||||
(array) $paths
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers a set of PSR-0 directories for a given prefix,
|
|
||||||
* replacing any others previously set for this prefix.
|
|
||||||
*
|
|
||||||
* @param string $prefix The prefix
|
|
||||||
* @param string[]|string $paths The PSR-0 base directories
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function set($prefix, $paths)
|
|
||||||
{
|
|
||||||
if (!$prefix) {
|
|
||||||
$this->fallbackDirsPsr0 = (array) $paths;
|
|
||||||
} else {
|
|
||||||
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers a set of PSR-4 directories for a given namespace,
|
|
||||||
* replacing any others previously set for this namespace.
|
|
||||||
*
|
|
||||||
* @param string $prefix The prefix/namespace, with trailing '\\'
|
|
||||||
* @param string[]|string $paths The PSR-4 base directories
|
|
||||||
*
|
|
||||||
* @throws \InvalidArgumentException
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function setPsr4($prefix, $paths)
|
|
||||||
{
|
|
||||||
if (!$prefix) {
|
|
||||||
$this->fallbackDirsPsr4 = (array) $paths;
|
|
||||||
} else {
|
|
||||||
$length = strlen($prefix);
|
|
||||||
if ('\\' !== $prefix[$length - 1]) {
|
|
||||||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
|
||||||
}
|
|
||||||
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
|
||||||
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Turns on searching the include path for class files.
|
|
||||||
*
|
|
||||||
* @param bool $useIncludePath
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function setUseIncludePath($useIncludePath)
|
|
||||||
{
|
|
||||||
$this->useIncludePath = $useIncludePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Can be used to check if the autoloader uses the include path to check
|
|
||||||
* for classes.
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function getUseIncludePath()
|
|
||||||
{
|
|
||||||
return $this->useIncludePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Turns off searching the prefix and fallback directories for classes
|
|
||||||
* that have not been registered with the class map.
|
|
||||||
*
|
|
||||||
* @param bool $classMapAuthoritative
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function setClassMapAuthoritative($classMapAuthoritative)
|
|
||||||
{
|
|
||||||
$this->classMapAuthoritative = $classMapAuthoritative;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Should class lookup fail if not found in the current class map?
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function isClassMapAuthoritative()
|
|
||||||
{
|
|
||||||
return $this->classMapAuthoritative;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
|
|
||||||
*
|
|
||||||
* @param string|null $apcuPrefix
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function setApcuPrefix($apcuPrefix)
|
|
||||||
{
|
|
||||||
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The APCu prefix in use, or null if APCu caching is not enabled.
|
|
||||||
*
|
|
||||||
* @return string|null
|
|
||||||
*/
|
|
||||||
public function getApcuPrefix()
|
|
||||||
{
|
|
||||||
return $this->apcuPrefix;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers this instance as an autoloader.
|
|
||||||
*
|
|
||||||
* @param bool $prepend Whether to prepend the autoloader or not
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function register($prepend = false)
|
|
||||||
{
|
|
||||||
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
|
|
||||||
|
|
||||||
if (null === $this->vendorDir) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($prepend) {
|
|
||||||
self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
|
|
||||||
} else {
|
|
||||||
unset(self::$registeredLoaders[$this->vendorDir]);
|
|
||||||
self::$registeredLoaders[$this->vendorDir] = $this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unregisters this instance as an autoloader.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function unregister()
|
|
||||||
{
|
|
||||||
spl_autoload_unregister(array($this, 'loadClass'));
|
|
||||||
|
|
||||||
if (null !== $this->vendorDir) {
|
|
||||||
unset(self::$registeredLoaders[$this->vendorDir]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads the given class or interface.
|
|
||||||
*
|
|
||||||
* @param string $class The name of the class
|
|
||||||
* @return true|null True if loaded, null otherwise
|
|
||||||
*/
|
|
||||||
public function loadClass($class)
|
|
||||||
{
|
|
||||||
if ($file = $this->findFile($class)) {
|
|
||||||
includeFile($file);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Finds the path to the file where the class is defined.
|
|
||||||
*
|
|
||||||
* @param string $class The name of the class
|
|
||||||
*
|
|
||||||
* @return string|false The path if found, false otherwise
|
|
||||||
*/
|
|
||||||
public function findFile($class)
|
|
||||||
{
|
|
||||||
// class map lookup
|
|
||||||
if (isset($this->classMap[$class])) {
|
|
||||||
return $this->classMap[$class];
|
|
||||||
}
|
|
||||||
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (null !== $this->apcuPrefix) {
|
|
||||||
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
|
|
||||||
if ($hit) {
|
|
||||||
return $file;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$file = $this->findFileWithExtension($class, '.php');
|
|
||||||
|
|
||||||
// Search for Hack files if we are running on HHVM
|
|
||||||
if (false === $file && defined('HHVM_VERSION')) {
|
|
||||||
$file = $this->findFileWithExtension($class, '.hh');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (null !== $this->apcuPrefix) {
|
|
||||||
apcu_add($this->apcuPrefix.$class, $file);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (false === $file) {
|
|
||||||
// Remember that this class does not exist.
|
|
||||||
$this->missingClasses[$class] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $file;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the currently registered loaders indexed by their corresponding vendor directories.
|
|
||||||
*
|
|
||||||
* @return self[]
|
|
||||||
*/
|
|
||||||
public static function getRegisteredLoaders()
|
|
||||||
{
|
|
||||||
return self::$registeredLoaders;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $class
|
|
||||||
* @param string $ext
|
|
||||||
* @return string|false
|
|
||||||
*/
|
|
||||||
private function findFileWithExtension($class, $ext)
|
|
||||||
{
|
|
||||||
// PSR-4 lookup
|
|
||||||
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
|
|
||||||
|
|
||||||
$first = $class[0];
|
|
||||||
if (isset($this->prefixLengthsPsr4[$first])) {
|
|
||||||
$subPath = $class;
|
|
||||||
while (false !== $lastPos = strrpos($subPath, '\\')) {
|
|
||||||
$subPath = substr($subPath, 0, $lastPos);
|
|
||||||
$search = $subPath . '\\';
|
|
||||||
if (isset($this->prefixDirsPsr4[$search])) {
|
|
||||||
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
|
|
||||||
foreach ($this->prefixDirsPsr4[$search] as $dir) {
|
|
||||||
if (file_exists($file = $dir . $pathEnd)) {
|
|
||||||
return $file;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// PSR-4 fallback dirs
|
|
||||||
foreach ($this->fallbackDirsPsr4 as $dir) {
|
|
||||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
|
|
||||||
return $file;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// PSR-0 lookup
|
|
||||||
if (false !== $pos = strrpos($class, '\\')) {
|
|
||||||
// namespaced class name
|
|
||||||
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
|
|
||||||
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
|
|
||||||
} else {
|
|
||||||
// PEAR-like class name
|
|
||||||
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($this->prefixesPsr0[$first])) {
|
|
||||||
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
|
|
||||||
if (0 === strpos($class, $prefix)) {
|
|
||||||
foreach ($dirs as $dir) {
|
|
||||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
|
||||||
return $file;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// PSR-0 fallback dirs
|
|
||||||
foreach ($this->fallbackDirsPsr0 as $dir) {
|
|
||||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
|
||||||
return $file;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// PSR-0 include paths.
|
|
||||||
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
|
|
||||||
return $file;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Scope isolated include.
|
|
||||||
*
|
|
||||||
* Prevents access to $this/self from included files.
|
|
||||||
*
|
|
||||||
* @param string $file
|
|
||||||
* @return void
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
function includeFile($file)
|
|
||||||
{
|
|
||||||
include $file;
|
|
||||||
}
|
|
@ -1,350 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of Composer.
|
|
||||||
*
|
|
||||||
* (c) Nils Adermann <naderman@naderman.de>
|
|
||||||
* Jordi Boggiano <j.boggiano@seld.be>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Composer;
|
|
||||||
|
|
||||||
use Composer\Autoload\ClassLoader;
|
|
||||||
use Composer\Semver\VersionParser;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class is copied in every Composer installed project and available to all
|
|
||||||
*
|
|
||||||
* See also https://getcomposer.org/doc/07-runtime.md#installed-versions
|
|
||||||
*
|
|
||||||
* To require its presence, you can require `composer-runtime-api ^2.0`
|
|
||||||
*/
|
|
||||||
class InstalledVersions
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var mixed[]|null
|
|
||||||
* @psalm-var array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}|array{}|null
|
|
||||||
*/
|
|
||||||
private static $installed;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var bool|null
|
|
||||||
*/
|
|
||||||
private static $canGetVendors;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array[]
|
|
||||||
* @psalm-var array<string, array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
|
|
||||||
*/
|
|
||||||
private static $installedByVendor = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a list of all package names which are present, either by being installed, replaced or provided
|
|
||||||
*
|
|
||||||
* @return string[]
|
|
||||||
* @psalm-return list<string>
|
|
||||||
*/
|
|
||||||
public static function getInstalledPackages()
|
|
||||||
{
|
|
||||||
$packages = array();
|
|
||||||
foreach (self::getInstalled() as $installed) {
|
|
||||||
$packages[] = array_keys($installed['versions']);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (1 === \count($packages)) {
|
|
||||||
return $packages[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a list of all package names with a specific type e.g. 'library'
|
|
||||||
*
|
|
||||||
* @param string $type
|
|
||||||
* @return string[]
|
|
||||||
* @psalm-return list<string>
|
|
||||||
*/
|
|
||||||
public static function getInstalledPackagesByType($type)
|
|
||||||
{
|
|
||||||
$packagesByType = array();
|
|
||||||
|
|
||||||
foreach (self::getInstalled() as $installed) {
|
|
||||||
foreach ($installed['versions'] as $name => $package) {
|
|
||||||
if (isset($package['type']) && $package['type'] === $type) {
|
|
||||||
$packagesByType[] = $name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $packagesByType;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks whether the given package is installed
|
|
||||||
*
|
|
||||||
* This also returns true if the package name is provided or replaced by another package
|
|
||||||
*
|
|
||||||
* @param string $packageName
|
|
||||||
* @param bool $includeDevRequirements
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public static function isInstalled($packageName, $includeDevRequirements = true)
|
|
||||||
{
|
|
||||||
foreach (self::getInstalled() as $installed) {
|
|
||||||
if (isset($installed['versions'][$packageName])) {
|
|
||||||
return $includeDevRequirements || empty($installed['versions'][$packageName]['dev_requirement']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks whether the given package satisfies a version constraint
|
|
||||||
*
|
|
||||||
* e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
|
|
||||||
*
|
|
||||||
* Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
|
|
||||||
*
|
|
||||||
* @param VersionParser $parser Install composer/semver to have access to this class and functionality
|
|
||||||
* @param string $packageName
|
|
||||||
* @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public static function satisfies(VersionParser $parser, $packageName, $constraint)
|
|
||||||
{
|
|
||||||
$constraint = $parser->parseConstraints($constraint);
|
|
||||||
$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
|
|
||||||
|
|
||||||
return $provided->matches($constraint);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a version constraint representing all the range(s) which are installed for a given package
|
|
||||||
*
|
|
||||||
* It is easier to use this via isInstalled() with the $constraint argument if you need to check
|
|
||||||
* whether a given version of a package is installed, and not just whether it exists
|
|
||||||
*
|
|
||||||
* @param string $packageName
|
|
||||||
* @return string Version constraint usable with composer/semver
|
|
||||||
*/
|
|
||||||
public static function getVersionRanges($packageName)
|
|
||||||
{
|
|
||||||
foreach (self::getInstalled() as $installed) {
|
|
||||||
if (!isset($installed['versions'][$packageName])) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$ranges = array();
|
|
||||||
if (isset($installed['versions'][$packageName]['pretty_version'])) {
|
|
||||||
$ranges[] = $installed['versions'][$packageName]['pretty_version'];
|
|
||||||
}
|
|
||||||
if (array_key_exists('aliases', $installed['versions'][$packageName])) {
|
|
||||||
$ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
|
|
||||||
}
|
|
||||||
if (array_key_exists('replaced', $installed['versions'][$packageName])) {
|
|
||||||
$ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
|
|
||||||
}
|
|
||||||
if (array_key_exists('provided', $installed['versions'][$packageName])) {
|
|
||||||
$ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
|
|
||||||
}
|
|
||||||
|
|
||||||
return implode(' || ', $ranges);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $packageName
|
|
||||||
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
|
|
||||||
*/
|
|
||||||
public static function getVersion($packageName)
|
|
||||||
{
|
|
||||||
foreach (self::getInstalled() as $installed) {
|
|
||||||
if (!isset($installed['versions'][$packageName])) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isset($installed['versions'][$packageName]['version'])) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $installed['versions'][$packageName]['version'];
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $packageName
|
|
||||||
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
|
|
||||||
*/
|
|
||||||
public static function getPrettyVersion($packageName)
|
|
||||||
{
|
|
||||||
foreach (self::getInstalled() as $installed) {
|
|
||||||
if (!isset($installed['versions'][$packageName])) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isset($installed['versions'][$packageName]['pretty_version'])) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $installed['versions'][$packageName]['pretty_version'];
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $packageName
|
|
||||||
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
|
|
||||||
*/
|
|
||||||
public static function getReference($packageName)
|
|
||||||
{
|
|
||||||
foreach (self::getInstalled() as $installed) {
|
|
||||||
if (!isset($installed['versions'][$packageName])) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isset($installed['versions'][$packageName]['reference'])) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $installed['versions'][$packageName]['reference'];
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $packageName
|
|
||||||
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
|
|
||||||
*/
|
|
||||||
public static function getInstallPath($packageName)
|
|
||||||
{
|
|
||||||
foreach (self::getInstalled() as $installed) {
|
|
||||||
if (!isset($installed['versions'][$packageName])) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array
|
|
||||||
* @psalm-return array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}
|
|
||||||
*/
|
|
||||||
public static function getRootPackage()
|
|
||||||
{
|
|
||||||
$installed = self::getInstalled();
|
|
||||||
|
|
||||||
return $installed[0]['root'];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the raw installed.php data for custom implementations
|
|
||||||
*
|
|
||||||
* @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
|
|
||||||
* @return array[]
|
|
||||||
* @psalm-return array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}
|
|
||||||
*/
|
|
||||||
public static function getRawData()
|
|
||||||
{
|
|
||||||
@trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
|
|
||||||
|
|
||||||
if (null === self::$installed) {
|
|
||||||
// only require the installed.php file if this file is loaded from its dumped location,
|
|
||||||
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
|
|
||||||
if (substr(__DIR__, -8, 1) !== 'C') {
|
|
||||||
self::$installed = include __DIR__ . '/installed.php';
|
|
||||||
} else {
|
|
||||||
self::$installed = array();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return self::$installed;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the raw data of all installed.php which are currently loaded for custom implementations
|
|
||||||
*
|
|
||||||
* @return array[]
|
|
||||||
* @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
|
|
||||||
*/
|
|
||||||
public static function getAllRawData()
|
|
||||||
{
|
|
||||||
return self::getInstalled();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Lets you reload the static array from another file
|
|
||||||
*
|
|
||||||
* This is only useful for complex integrations in which a project needs to use
|
|
||||||
* this class but then also needs to execute another project's autoloader in process,
|
|
||||||
* and wants to ensure both projects have access to their version of installed.php.
|
|
||||||
*
|
|
||||||
* A typical case would be PHPUnit, where it would need to make sure it reads all
|
|
||||||
* the data it needs from this class, then call reload() with
|
|
||||||
* `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
|
|
||||||
* the project in which it runs can then also use this class safely, without
|
|
||||||
* interference between PHPUnit's dependencies and the project's dependencies.
|
|
||||||
*
|
|
||||||
* @param array[] $data A vendor/composer/installed.php data set
|
|
||||||
* @return void
|
|
||||||
*
|
|
||||||
* @psalm-param array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>} $data
|
|
||||||
*/
|
|
||||||
public static function reload($data)
|
|
||||||
{
|
|
||||||
self::$installed = $data;
|
|
||||||
self::$installedByVendor = array();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array[]
|
|
||||||
* @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
|
|
||||||
*/
|
|
||||||
private static function getInstalled()
|
|
||||||
{
|
|
||||||
if (null === self::$canGetVendors) {
|
|
||||||
self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
|
|
||||||
}
|
|
||||||
|
|
||||||
$installed = array();
|
|
||||||
|
|
||||||
if (self::$canGetVendors) {
|
|
||||||
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
|
|
||||||
if (isset(self::$installedByVendor[$vendorDir])) {
|
|
||||||
$installed[] = self::$installedByVendor[$vendorDir];
|
|
||||||
} elseif (is_file($vendorDir.'/composer/installed.php')) {
|
|
||||||
$installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php';
|
|
||||||
if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
|
|
||||||
self::$installed = $installed[count($installed) - 1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (null === self::$installed) {
|
|
||||||
// only require the installed.php file if this file is loaded from its dumped location,
|
|
||||||
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
|
|
||||||
if (substr(__DIR__, -8, 1) !== 'C') {
|
|
||||||
self::$installed = require __DIR__ . '/installed.php';
|
|
||||||
} else {
|
|
||||||
self::$installed = array();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$installed[] = self::$installed;
|
|
||||||
|
|
||||||
return $installed;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
Copyright (c) Nils Adermann, Jordi Boggiano
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is furnished
|
|
||||||
to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
@ -1,15 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
// autoload_classmap.php @generated by Composer
|
|
||||||
|
|
||||||
$vendorDir = dirname(dirname(__FILE__));
|
|
||||||
$baseDir = dirname($vendorDir);
|
|
||||||
|
|
||||||
return array(
|
|
||||||
'Attribute' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Attribute.php',
|
|
||||||
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
|
|
||||||
'PhpToken' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php',
|
|
||||||
'Stringable' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Stringable.php',
|
|
||||||
'UnhandledMatchError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php',
|
|
||||||
'ValueError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/ValueError.php',
|
|
||||||
);
|
|
@ -1,14 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
// autoload_files.php @generated by Composer
|
|
||||||
|
|
||||||
$vendorDir = dirname(dirname(__FILE__));
|
|
||||||
$baseDir = dirname($vendorDir);
|
|
||||||
|
|
||||||
return array(
|
|
||||||
'7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php',
|
|
||||||
'320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php',
|
|
||||||
'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php',
|
|
||||||
'253c157292f75eb38082b5acb06f3f01' => $vendorDir . '/nikic/fast-route/src/functions.php',
|
|
||||||
'6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php',
|
|
||||||
);
|
|
@ -1,9 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
// autoload_namespaces.php @generated by Composer
|
|
||||||
|
|
||||||
$vendorDir = dirname(dirname(__FILE__));
|
|
||||||
$baseDir = dirname($vendorDir);
|
|
||||||
|
|
||||||
return array(
|
|
||||||
);
|
|
@ -1,26 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
// autoload_psr4.php @generated by Composer
|
|
||||||
|
|
||||||
$vendorDir = dirname(dirname(__FILE__));
|
|
||||||
$baseDir = dirname($vendorDir);
|
|
||||||
|
|
||||||
return array(
|
|
||||||
'Symfony\\Polyfill\\Php80\\' => array($vendorDir . '/symfony/polyfill-php80'),
|
|
||||||
'Symfony\\Polyfill\\Ctype\\' => array($vendorDir . '/symfony/polyfill-ctype'),
|
|
||||||
'Symfony\\Component\\Yaml\\' => array($vendorDir . '/symfony/yaml'),
|
|
||||||
'Symfony\\Component\\Finder\\' => array($vendorDir . '/symfony/finder'),
|
|
||||||
'Slim\\Psr7\\' => array($vendorDir . '/slim/psr7/src'),
|
|
||||||
'Slim\\' => array($vendorDir . '/slim/slim/Slim'),
|
|
||||||
'Psr\\Log\\' => array($vendorDir . '/psr/log/src'),
|
|
||||||
'Psr\\Http\\Server\\' => array($vendorDir . '/psr/http-server-handler/src', $vendorDir . '/psr/http-server-middleware/src'),
|
|
||||||
'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-factory/src', $vendorDir . '/psr/http-message/src'),
|
|
||||||
'Psr\\Container\\' => array($vendorDir . '/psr/container/src'),
|
|
||||||
'Psr\\Cache\\' => array($vendorDir . '/psr/cache/src'),
|
|
||||||
'OpenApi\\' => array($vendorDir . '/zircote/swagger-php/src'),
|
|
||||||
'Fig\\Http\\Message\\' => array($vendorDir . '/fig/http-message-util/src'),
|
|
||||||
'FastRoute\\' => array($vendorDir . '/nikic/fast-route/src'),
|
|
||||||
'Doctrine\\Deprecations\\' => array($vendorDir . '/doctrine/deprecations/lib/Doctrine/Deprecations'),
|
|
||||||
'Doctrine\\Common\\Lexer\\' => array($vendorDir . '/doctrine/lexer/src'),
|
|
||||||
'Doctrine\\Common\\Annotations\\' => array($vendorDir . '/doctrine/annotations/lib/Doctrine/Common/Annotations'),
|
|
||||||
);
|
|
@ -1,80 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
// autoload_real.php @generated by Composer
|
|
||||||
|
|
||||||
class ComposerAutoloaderInita934429c0ea4f4482346c5d296943a81
|
|
||||||
{
|
|
||||||
private static $loader;
|
|
||||||
|
|
||||||
public static function loadClassLoader($class)
|
|
||||||
{
|
|
||||||
if ('Composer\Autoload\ClassLoader' === $class) {
|
|
||||||
require __DIR__ . '/ClassLoader.php';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return \Composer\Autoload\ClassLoader
|
|
||||||
*/
|
|
||||||
public static function getLoader()
|
|
||||||
{
|
|
||||||
if (null !== self::$loader) {
|
|
||||||
return self::$loader;
|
|
||||||
}
|
|
||||||
|
|
||||||
require __DIR__ . '/platform_check.php';
|
|
||||||
|
|
||||||
spl_autoload_register(array('ComposerAutoloaderInita934429c0ea4f4482346c5d296943a81', 'loadClassLoader'), true, true);
|
|
||||||
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
|
|
||||||
spl_autoload_unregister(array('ComposerAutoloaderInita934429c0ea4f4482346c5d296943a81', 'loadClassLoader'));
|
|
||||||
|
|
||||||
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
|
|
||||||
if ($useStaticLoader) {
|
|
||||||
require __DIR__ . '/autoload_static.php';
|
|
||||||
|
|
||||||
call_user_func(\Composer\Autoload\ComposerStaticInita934429c0ea4f4482346c5d296943a81::getInitializer($loader));
|
|
||||||
} else {
|
|
||||||
$map = require __DIR__ . '/autoload_namespaces.php';
|
|
||||||
foreach ($map as $namespace => $path) {
|
|
||||||
$loader->set($namespace, $path);
|
|
||||||
}
|
|
||||||
|
|
||||||
$map = require __DIR__ . '/autoload_psr4.php';
|
|
||||||
foreach ($map as $namespace => $path) {
|
|
||||||
$loader->setPsr4($namespace, $path);
|
|
||||||
}
|
|
||||||
|
|
||||||
$classMap = require __DIR__ . '/autoload_classmap.php';
|
|
||||||
if ($classMap) {
|
|
||||||
$loader->addClassMap($classMap);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$loader->register(true);
|
|
||||||
|
|
||||||
if ($useStaticLoader) {
|
|
||||||
$includeFiles = Composer\Autoload\ComposerStaticInita934429c0ea4f4482346c5d296943a81::$files;
|
|
||||||
} else {
|
|
||||||
$includeFiles = require __DIR__ . '/autoload_files.php';
|
|
||||||
}
|
|
||||||
foreach ($includeFiles as $fileIdentifier => $file) {
|
|
||||||
composerRequirea934429c0ea4f4482346c5d296943a81($fileIdentifier, $file);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $loader;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $fileIdentifier
|
|
||||||
* @param string $file
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
function composerRequirea934429c0ea4f4482346c5d296943a81($fileIdentifier, $file)
|
|
||||||
{
|
|
||||||
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
|
|
||||||
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
|
|
||||||
|
|
||||||
require $file;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,143 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
// autoload_static.php @generated by Composer
|
|
||||||
|
|
||||||
namespace Composer\Autoload;
|
|
||||||
|
|
||||||
class ComposerStaticInita934429c0ea4f4482346c5d296943a81
|
|
||||||
{
|
|
||||||
public static $files = array (
|
|
||||||
'7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php',
|
|
||||||
'320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php',
|
|
||||||
'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php',
|
|
||||||
'253c157292f75eb38082b5acb06f3f01' => __DIR__ . '/..' . '/nikic/fast-route/src/functions.php',
|
|
||||||
'6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php',
|
|
||||||
);
|
|
||||||
|
|
||||||
public static $prefixLengthsPsr4 = array (
|
|
||||||
'S' =>
|
|
||||||
array (
|
|
||||||
'Symfony\\Polyfill\\Php80\\' => 23,
|
|
||||||
'Symfony\\Polyfill\\Ctype\\' => 23,
|
|
||||||
'Symfony\\Component\\Yaml\\' => 23,
|
|
||||||
'Symfony\\Component\\Finder\\' => 25,
|
|
||||||
'Slim\\Psr7\\' => 10,
|
|
||||||
'Slim\\' => 5,
|
|
||||||
),
|
|
||||||
'P' =>
|
|
||||||
array (
|
|
||||||
'Psr\\Log\\' => 8,
|
|
||||||
'Psr\\Http\\Server\\' => 16,
|
|
||||||
'Psr\\Http\\Message\\' => 17,
|
|
||||||
'Psr\\Container\\' => 14,
|
|
||||||
'Psr\\Cache\\' => 10,
|
|
||||||
),
|
|
||||||
'O' =>
|
|
||||||
array (
|
|
||||||
'OpenApi\\' => 8,
|
|
||||||
),
|
|
||||||
'F' =>
|
|
||||||
array (
|
|
||||||
'Fig\\Http\\Message\\' => 17,
|
|
||||||
'FastRoute\\' => 10,
|
|
||||||
),
|
|
||||||
'D' =>
|
|
||||||
array (
|
|
||||||
'Doctrine\\Deprecations\\' => 22,
|
|
||||||
'Doctrine\\Common\\Lexer\\' => 22,
|
|
||||||
'Doctrine\\Common\\Annotations\\' => 28,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
public static $prefixDirsPsr4 = array (
|
|
||||||
'Symfony\\Polyfill\\Php80\\' =>
|
|
||||||
array (
|
|
||||||
0 => __DIR__ . '/..' . '/symfony/polyfill-php80',
|
|
||||||
),
|
|
||||||
'Symfony\\Polyfill\\Ctype\\' =>
|
|
||||||
array (
|
|
||||||
0 => __DIR__ . '/..' . '/symfony/polyfill-ctype',
|
|
||||||
),
|
|
||||||
'Symfony\\Component\\Yaml\\' =>
|
|
||||||
array (
|
|
||||||
0 => __DIR__ . '/..' . '/symfony/yaml',
|
|
||||||
),
|
|
||||||
'Symfony\\Component\\Finder\\' =>
|
|
||||||
array (
|
|
||||||
0 => __DIR__ . '/..' . '/symfony/finder',
|
|
||||||
),
|
|
||||||
'Slim\\Psr7\\' =>
|
|
||||||
array (
|
|
||||||
0 => __DIR__ . '/..' . '/slim/psr7/src',
|
|
||||||
),
|
|
||||||
'Slim\\' =>
|
|
||||||
array (
|
|
||||||
0 => __DIR__ . '/..' . '/slim/slim/Slim',
|
|
||||||
),
|
|
||||||
'Psr\\Log\\' =>
|
|
||||||
array (
|
|
||||||
0 => __DIR__ . '/..' . '/psr/log/src',
|
|
||||||
),
|
|
||||||
'Psr\\Http\\Server\\' =>
|
|
||||||
array (
|
|
||||||
0 => __DIR__ . '/..' . '/psr/http-server-handler/src',
|
|
||||||
1 => __DIR__ . '/..' . '/psr/http-server-middleware/src',
|
|
||||||
),
|
|
||||||
'Psr\\Http\\Message\\' =>
|
|
||||||
array (
|
|
||||||
0 => __DIR__ . '/..' . '/psr/http-factory/src',
|
|
||||||
1 => __DIR__ . '/..' . '/psr/http-message/src',
|
|
||||||
),
|
|
||||||
'Psr\\Container\\' =>
|
|
||||||
array (
|
|
||||||
0 => __DIR__ . '/..' . '/psr/container/src',
|
|
||||||
),
|
|
||||||
'Psr\\Cache\\' =>
|
|
||||||
array (
|
|
||||||
0 => __DIR__ . '/..' . '/psr/cache/src',
|
|
||||||
),
|
|
||||||
'OpenApi\\' =>
|
|
||||||
array (
|
|
||||||
0 => __DIR__ . '/..' . '/zircote/swagger-php/src',
|
|
||||||
),
|
|
||||||
'Fig\\Http\\Message\\' =>
|
|
||||||
array (
|
|
||||||
0 => __DIR__ . '/..' . '/fig/http-message-util/src',
|
|
||||||
),
|
|
||||||
'FastRoute\\' =>
|
|
||||||
array (
|
|
||||||
0 => __DIR__ . '/..' . '/nikic/fast-route/src',
|
|
||||||
),
|
|
||||||
'Doctrine\\Deprecations\\' =>
|
|
||||||
array (
|
|
||||||
0 => __DIR__ . '/..' . '/doctrine/deprecations/lib/Doctrine/Deprecations',
|
|
||||||
),
|
|
||||||
'Doctrine\\Common\\Lexer\\' =>
|
|
||||||
array (
|
|
||||||
0 => __DIR__ . '/..' . '/doctrine/lexer/src',
|
|
||||||
),
|
|
||||||
'Doctrine\\Common\\Annotations\\' =>
|
|
||||||
array (
|
|
||||||
0 => __DIR__ . '/..' . '/doctrine/annotations/lib/Doctrine/Common/Annotations',
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
public static $classMap = array (
|
|
||||||
'Attribute' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Attribute.php',
|
|
||||||
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
|
|
||||||
'PhpToken' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php',
|
|
||||||
'Stringable' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Stringable.php',
|
|
||||||
'UnhandledMatchError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php',
|
|
||||||
'ValueError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/ValueError.php',
|
|
||||||
);
|
|
||||||
|
|
||||||
public static function getInitializer(ClassLoader $loader)
|
|
||||||
{
|
|
||||||
return \Closure::bind(function () use ($loader) {
|
|
||||||
$loader->prefixLengthsPsr4 = ComposerStaticInita934429c0ea4f4482346c5d296943a81::$prefixLengthsPsr4;
|
|
||||||
$loader->prefixDirsPsr4 = ComposerStaticInita934429c0ea4f4482346c5d296943a81::$prefixDirsPsr4;
|
|
||||||
$loader->classMap = ComposerStaticInita934429c0ea4f4482346c5d296943a81::$classMap;
|
|
||||||
|
|
||||||
}, null, ClassLoader::class);
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -1,224 +0,0 @@
|
|||||||
<?php return array(
|
|
||||||
'root' => array(
|
|
||||||
'pretty_version' => 'dev-master',
|
|
||||||
'version' => 'dev-master',
|
|
||||||
'type' => 'library',
|
|
||||||
'install_path' => __DIR__ . '/../../',
|
|
||||||
'aliases' => array(),
|
|
||||||
'reference' => '0d6979f482a4dc4159775749125b647077c1dbcd',
|
|
||||||
'name' => '__root__',
|
|
||||||
'dev' => true,
|
|
||||||
),
|
|
||||||
'versions' => array(
|
|
||||||
'__root__' => array(
|
|
||||||
'pretty_version' => 'dev-master',
|
|
||||||
'version' => 'dev-master',
|
|
||||||
'type' => 'library',
|
|
||||||
'install_path' => __DIR__ . '/../../',
|
|
||||||
'aliases' => array(),
|
|
||||||
'reference' => '0d6979f482a4dc4159775749125b647077c1dbcd',
|
|
||||||
'dev_requirement' => false,
|
|
||||||
),
|
|
||||||
'doctrine/annotations' => array(
|
|
||||||
'pretty_version' => '1.14.1',
|
|
||||||
'version' => '1.14.1.0',
|
|
||||||
'type' => 'library',
|
|
||||||
'install_path' => __DIR__ . '/../doctrine/annotations',
|
|
||||||
'aliases' => array(),
|
|
||||||
'reference' => '9e034d7a70032d422169f27d8759e8d84abb4f51',
|
|
||||||
'dev_requirement' => false,
|
|
||||||
),
|
|
||||||
'doctrine/deprecations' => array(
|
|
||||||
'pretty_version' => 'v1.0.0',
|
|
||||||
'version' => '1.0.0.0',
|
|
||||||
'type' => 'library',
|
|
||||||
'install_path' => __DIR__ . '/../doctrine/deprecations',
|
|
||||||
'aliases' => array(),
|
|
||||||
'reference' => '0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de',
|
|
||||||
'dev_requirement' => false,
|
|
||||||
),
|
|
||||||
'doctrine/lexer' => array(
|
|
||||||
'pretty_version' => '2.0.0',
|
|
||||||
'version' => '2.0.0.0',
|
|
||||||
'type' => 'library',
|
|
||||||
'install_path' => __DIR__ . '/../doctrine/lexer',
|
|
||||||
'aliases' => array(),
|
|
||||||
'reference' => '3cf140b81e55d5d640f73367d829db7e3023ef69',
|
|
||||||
'dev_requirement' => false,
|
|
||||||
),
|
|
||||||
'fig/http-message-util' => array(
|
|
||||||
'pretty_version' => '1.1.5',
|
|
||||||
'version' => '1.1.5.0',
|
|
||||||
'type' => 'library',
|
|
||||||
'install_path' => __DIR__ . '/../fig/http-message-util',
|
|
||||||
'aliases' => array(),
|
|
||||||
'reference' => '9d94dc0154230ac39e5bf89398b324a86f63f765',
|
|
||||||
'dev_requirement' => false,
|
|
||||||
),
|
|
||||||
'nikic/fast-route' => array(
|
|
||||||
'pretty_version' => 'v1.3.0',
|
|
||||||
'version' => '1.3.0.0',
|
|
||||||
'type' => 'library',
|
|
||||||
'install_path' => __DIR__ . '/../nikic/fast-route',
|
|
||||||
'aliases' => array(),
|
|
||||||
'reference' => '181d480e08d9476e61381e04a71b34dc0432e812',
|
|
||||||
'dev_requirement' => false,
|
|
||||||
),
|
|
||||||
'psr/cache' => array(
|
|
||||||
'pretty_version' => '3.0.0',
|
|
||||||
'version' => '3.0.0.0',
|
|
||||||
'type' => 'library',
|
|
||||||
'install_path' => __DIR__ . '/../psr/cache',
|
|
||||||
'aliases' => array(),
|
|
||||||
'reference' => 'aa5030cfa5405eccfdcb1083ce040c2cb8d253bf',
|
|
||||||
'dev_requirement' => false,
|
|
||||||
),
|
|
||||||
'psr/container' => array(
|
|
||||||
'pretty_version' => '2.0.2',
|
|
||||||
'version' => '2.0.2.0',
|
|
||||||
'type' => 'library',
|
|
||||||
'install_path' => __DIR__ . '/../psr/container',
|
|
||||||
'aliases' => array(),
|
|
||||||
'reference' => 'c71ecc56dfe541dbd90c5360474fbc405f8d5963',
|
|
||||||
'dev_requirement' => false,
|
|
||||||
),
|
|
||||||
'psr/http-factory' => array(
|
|
||||||
'pretty_version' => '1.0.1',
|
|
||||||
'version' => '1.0.1.0',
|
|
||||||
'type' => 'library',
|
|
||||||
'install_path' => __DIR__ . '/../psr/http-factory',
|
|
||||||
'aliases' => array(),
|
|
||||||
'reference' => '12ac7fcd07e5b077433f5f2bee95b3a771bf61be',
|
|
||||||
'dev_requirement' => false,
|
|
||||||
),
|
|
||||||
'psr/http-factory-implementation' => array(
|
|
||||||
'dev_requirement' => false,
|
|
||||||
'provided' => array(
|
|
||||||
0 => '1.0',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
'psr/http-message' => array(
|
|
||||||
'pretty_version' => '1.0.1',
|
|
||||||
'version' => '1.0.1.0',
|
|
||||||
'type' => 'library',
|
|
||||||
'install_path' => __DIR__ . '/../psr/http-message',
|
|
||||||
'aliases' => array(),
|
|
||||||
'reference' => 'f6561bf28d520154e4b0ec72be95418abe6d9363',
|
|
||||||
'dev_requirement' => false,
|
|
||||||
),
|
|
||||||
'psr/http-message-implementation' => array(
|
|
||||||
'dev_requirement' => false,
|
|
||||||
'provided' => array(
|
|
||||||
0 => '1.0',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
'psr/http-server-handler' => array(
|
|
||||||
'pretty_version' => '1.0.1',
|
|
||||||
'version' => '1.0.1.0',
|
|
||||||
'type' => 'library',
|
|
||||||
'install_path' => __DIR__ . '/../psr/http-server-handler',
|
|
||||||
'aliases' => array(),
|
|
||||||
'reference' => 'aff2f80e33b7f026ec96bb42f63242dc50ffcae7',
|
|
||||||
'dev_requirement' => false,
|
|
||||||
),
|
|
||||||
'psr/http-server-middleware' => array(
|
|
||||||
'pretty_version' => '1.0.1',
|
|
||||||
'version' => '1.0.1.0',
|
|
||||||
'type' => 'library',
|
|
||||||
'install_path' => __DIR__ . '/../psr/http-server-middleware',
|
|
||||||
'aliases' => array(),
|
|
||||||
'reference' => '2296f45510945530b9dceb8bcedb5cb84d40c5f5',
|
|
||||||
'dev_requirement' => false,
|
|
||||||
),
|
|
||||||
'psr/log' => array(
|
|
||||||
'pretty_version' => '3.0.0',
|
|
||||||
'version' => '3.0.0.0',
|
|
||||||
'type' => 'library',
|
|
||||||
'install_path' => __DIR__ . '/../psr/log',
|
|
||||||
'aliases' => array(),
|
|
||||||
'reference' => 'fe5ea303b0887d5caefd3d431c3e61ad47037001',
|
|
||||||
'dev_requirement' => false,
|
|
||||||
),
|
|
||||||
'ralouphie/getallheaders' => array(
|
|
||||||
'pretty_version' => '3.0.3',
|
|
||||||
'version' => '3.0.3.0',
|
|
||||||
'type' => 'library',
|
|
||||||
'install_path' => __DIR__ . '/../ralouphie/getallheaders',
|
|
||||||
'aliases' => array(),
|
|
||||||
'reference' => '120b605dfeb996808c31b6477290a714d356e822',
|
|
||||||
'dev_requirement' => false,
|
|
||||||
),
|
|
||||||
'slim/psr7' => array(
|
|
||||||
'pretty_version' => '1.6',
|
|
||||||
'version' => '1.6.0.0',
|
|
||||||
'type' => 'library',
|
|
||||||
'install_path' => __DIR__ . '/../slim/psr7',
|
|
||||||
'aliases' => array(),
|
|
||||||
'reference' => '3471c22c1a0d26c51c78f6aeb06489d38cf46a4d',
|
|
||||||
'dev_requirement' => false,
|
|
||||||
),
|
|
||||||
'slim/slim' => array(
|
|
||||||
'pretty_version' => '4.11.0',
|
|
||||||
'version' => '4.11.0.0',
|
|
||||||
'type' => 'library',
|
|
||||||
'install_path' => __DIR__ . '/../slim/slim',
|
|
||||||
'aliases' => array(),
|
|
||||||
'reference' => 'b0f4ca393ea037be9ac7292ba7d0a34d18bac0c7',
|
|
||||||
'dev_requirement' => false,
|
|
||||||
),
|
|
||||||
'symfony/deprecation-contracts' => array(
|
|
||||||
'pretty_version' => 'v3.2.0',
|
|
||||||
'version' => '3.2.0.0',
|
|
||||||
'type' => 'library',
|
|
||||||
'install_path' => __DIR__ . '/../symfony/deprecation-contracts',
|
|
||||||
'aliases' => array(),
|
|
||||||
'reference' => '1ee04c65529dea5d8744774d474e7cbd2f1206d3',
|
|
||||||
'dev_requirement' => false,
|
|
||||||
),
|
|
||||||
'symfony/finder' => array(
|
|
||||||
'pretty_version' => 'v6.2.0',
|
|
||||||
'version' => '6.2.0.0',
|
|
||||||
'type' => 'library',
|
|
||||||
'install_path' => __DIR__ . '/../symfony/finder',
|
|
||||||
'aliases' => array(),
|
|
||||||
'reference' => 'eb2355f69519e4ef33f1835bca4c935f5d42e570',
|
|
||||||
'dev_requirement' => false,
|
|
||||||
),
|
|
||||||
'symfony/polyfill-ctype' => array(
|
|
||||||
'pretty_version' => 'v1.27.0',
|
|
||||||
'version' => '1.27.0.0',
|
|
||||||
'type' => 'library',
|
|
||||||
'install_path' => __DIR__ . '/../symfony/polyfill-ctype',
|
|
||||||
'aliases' => array(),
|
|
||||||
'reference' => '5bbc823adecdae860bb64756d639ecfec17b050a',
|
|
||||||
'dev_requirement' => false,
|
|
||||||
),
|
|
||||||
'symfony/polyfill-php80' => array(
|
|
||||||
'pretty_version' => 'v1.27.0',
|
|
||||||
'version' => '1.27.0.0',
|
|
||||||
'type' => 'library',
|
|
||||||
'install_path' => __DIR__ . '/../symfony/polyfill-php80',
|
|
||||||
'aliases' => array(),
|
|
||||||
'reference' => '7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936',
|
|
||||||
'dev_requirement' => false,
|
|
||||||
),
|
|
||||||
'symfony/yaml' => array(
|
|
||||||
'pretty_version' => 'v6.2.0',
|
|
||||||
'version' => '6.2.0.0',
|
|
||||||
'type' => 'library',
|
|
||||||
'install_path' => __DIR__ . '/../symfony/yaml',
|
|
||||||
'aliases' => array(),
|
|
||||||
'reference' => 'f2570f21bd4adc3589aa3133323273995109bae0',
|
|
||||||
'dev_requirement' => false,
|
|
||||||
),
|
|
||||||
'zircote/swagger-php' => array(
|
|
||||||
'pretty_version' => '4.5.1',
|
|
||||||
'version' => '4.5.1.0',
|
|
||||||
'type' => 'library',
|
|
||||||
'install_path' => __DIR__ . '/../zircote/swagger-php',
|
|
||||||
'aliases' => array(),
|
|
||||||
'reference' => 'eb84fb4d65a327e604812fbddc6c27f69b9ed6e2',
|
|
||||||
'dev_requirement' => false,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
@ -1,26 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
// platform_check.php @generated by Composer
|
|
||||||
|
|
||||||
$issues = array();
|
|
||||||
|
|
||||||
if (!(PHP_VERSION_ID >= 80100)) {
|
|
||||||
$issues[] = 'Your Composer dependencies require a PHP version ">= 8.1.0". You are running ' . PHP_VERSION . '.';
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($issues) {
|
|
||||||
if (!headers_sent()) {
|
|
||||||
header('HTTP/1.1 500 Internal Server Error');
|
|
||||||
}
|
|
||||||
if (!ini_get('display_errors')) {
|
|
||||||
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
|
|
||||||
fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
|
|
||||||
} elseif (!headers_sent()) {
|
|
||||||
echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
trigger_error(
|
|
||||||
'Composer detected issues in your platform: ' . implode(' ', $issues),
|
|
||||||
E_USER_ERROR
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
Copyright (c) 2006-2013 Doctrine Project
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
||||||
this software and associated documentation files (the "Software"), to deal in
|
|
||||||
the Software without restriction, including without limitation the rights to
|
|
||||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
||||||
of the Software, and to permit persons to whom the Software is furnished to do
|
|
||||||
so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
@ -1,24 +0,0 @@
|
|||||||
⚠️ PHP 8 introduced
|
|
||||||
[attributes](https://www.php.net/manual/en/language.attributes.overview.php),
|
|
||||||
which are a native replacement for annotations. As such, this library is
|
|
||||||
considered feature complete, and should receive exclusively bugfixes and
|
|
||||||
security fixes.
|
|
||||||
|
|
||||||
# Doctrine Annotations
|
|
||||||
|
|
||||||
[![Build Status](https://github.com/doctrine/annotations/workflows/Continuous%20Integration/badge.svg?label=build)](https://github.com/doctrine/persistence/actions)
|
|
||||||
[![Dependency Status](https://www.versioneye.com/package/php--doctrine--annotations/badge.png)](https://www.versioneye.com/package/php--doctrine--annotations)
|
|
||||||
[![Reference Status](https://www.versioneye.com/php/doctrine:annotations/reference_badge.svg)](https://www.versioneye.com/php/doctrine:annotations/references)
|
|
||||||
[![Total Downloads](https://poser.pugx.org/doctrine/annotations/downloads.png)](https://packagist.org/packages/doctrine/annotations)
|
|
||||||
[![Latest Stable Version](https://img.shields.io/packagist/v/doctrine/annotations.svg?label=stable)](https://packagist.org/packages/doctrine/annotations)
|
|
||||||
|
|
||||||
Docblock Annotations Parser library (extracted from [Doctrine Common](https://github.com/doctrine/common)).
|
|
||||||
|
|
||||||
## Documentation
|
|
||||||
|
|
||||||
See the [doctrine-project website](https://www.doctrine-project.org/projects/doctrine-annotations/en/latest/index.html).
|
|
||||||
|
|
||||||
## Contributing
|
|
||||||
|
|
||||||
When making a pull request, make sure your changes follow the
|
|
||||||
[Coding Standard Guidelines](https://www.doctrine-project.org/projects/doctrine-coding-standard/en/current/reference/index.html#introduction).
|
|
@ -1,72 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "doctrine/annotations",
|
|
||||||
"description": "Docblock Annotations Parser",
|
|
||||||
"license": "MIT",
|
|
||||||
"type": "library",
|
|
||||||
"keywords": [
|
|
||||||
"annotations",
|
|
||||||
"docblock",
|
|
||||||
"parser"
|
|
||||||
],
|
|
||||||
"authors": [
|
|
||||||
{
|
|
||||||
"name": "Guilherme Blanco",
|
|
||||||
"email": "guilhermeblanco@gmail.com"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Roman Borschel",
|
|
||||||
"email": "roman@code-factory.org"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Benjamin Eberlei",
|
|
||||||
"email": "kontakt@beberlei.de"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Jonathan Wage",
|
|
||||||
"email": "jonwage@gmail.com"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Johannes Schmitt",
|
|
||||||
"email": "schmittjoh@gmail.com"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"homepage": "https://www.doctrine-project.org/projects/annotations.html",
|
|
||||||
"require": {
|
|
||||||
"php": "^7.1 || ^8.0",
|
|
||||||
"ext-tokenizer": "*",
|
|
||||||
"doctrine/lexer": "^1 || ^2",
|
|
||||||
"psr/cache": "^1 || ^2 || ^3"
|
|
||||||
},
|
|
||||||
"require-dev": {
|
|
||||||
"doctrine/cache": "^1.11 || ^2.0",
|
|
||||||
"doctrine/coding-standard": "^9 || ^10",
|
|
||||||
"phpstan/phpstan": "~1.4.10 || ^1.8.0",
|
|
||||||
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
|
|
||||||
"symfony/cache": "^4.4 || ^5.4 || ^6",
|
|
||||||
"vimeo/psalm": "^4.10"
|
|
||||||
},
|
|
||||||
"suggest": {
|
|
||||||
"php": "PHP 8.0 or higher comes with attributes, a native replacement for annotations"
|
|
||||||
},
|
|
||||||
"autoload": {
|
|
||||||
"psr-4": {
|
|
||||||
"Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"autoload-dev": {
|
|
||||||
"psr-4": {
|
|
||||||
"Doctrine\\Performance\\Common\\Annotations\\": "tests/Doctrine/Performance/Common/Annotations",
|
|
||||||
"Doctrine\\Tests\\Common\\Annotations\\": "tests/Doctrine/Tests/Common/Annotations"
|
|
||||||
},
|
|
||||||
"files": [
|
|
||||||
"tests/Doctrine/Tests/Common/Annotations/Fixtures/functions.php",
|
|
||||||
"tests/Doctrine/Tests/Common/Annotations/Fixtures/SingleClassLOC1000.php"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"config": {
|
|
||||||
"allow-plugins": {
|
|
||||||
"dealerdirect/phpcodesniffer-composer-installer": true
|
|
||||||
},
|
|
||||||
"sort-packages": true
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,252 +0,0 @@
|
|||||||
Handling Annotations
|
|
||||||
====================
|
|
||||||
|
|
||||||
There are several different approaches to handling annotations in PHP.
|
|
||||||
Doctrine Annotations maps docblock annotations to PHP classes. Because
|
|
||||||
not all docblock annotations are used for metadata purposes a filter is
|
|
||||||
applied to ignore or skip classes that are not Doctrine annotations.
|
|
||||||
|
|
||||||
Take a look at the following code snippet:
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
namespace MyProject\Entities;
|
|
||||||
|
|
||||||
use Doctrine\ORM\Mapping AS ORM;
|
|
||||||
use Symfony\Component\Validator\Constraints AS Assert;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Benjamin Eberlei
|
|
||||||
* @ORM\Entity
|
|
||||||
* @MyProject\Annotations\Foobarable
|
|
||||||
*/
|
|
||||||
class User
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @ORM\Id @ORM\Column @ORM\GeneratedValue
|
|
||||||
* @dummy
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
private $id;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @ORM\Column(type="string")
|
|
||||||
* @Assert\NotEmpty
|
|
||||||
* @Assert\Email
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
private $email;
|
|
||||||
}
|
|
||||||
|
|
||||||
In this snippet you can see a variety of different docblock annotations:
|
|
||||||
|
|
||||||
- Documentation annotations such as ``@var`` and ``@author``. These
|
|
||||||
annotations are ignored and never considered for throwing an
|
|
||||||
exception due to wrongly used annotations.
|
|
||||||
- Annotations imported through use statements. The statement ``use
|
|
||||||
Doctrine\ORM\Mapping AS ORM`` makes all classes under that namespace
|
|
||||||
available as ``@ORM\ClassName``. Same goes for the import of
|
|
||||||
``@Assert``.
|
|
||||||
- The ``@dummy`` annotation. It is not a documentation annotation and
|
|
||||||
not ignored. For Doctrine Annotations it is not entirely clear how
|
|
||||||
to handle this annotation. Depending on the configuration an exception
|
|
||||||
(unknown annotation) will be thrown when parsing this annotation.
|
|
||||||
- The fully qualified annotation ``@MyProject\Annotations\Foobarable``.
|
|
||||||
This is transformed directly into the given class name.
|
|
||||||
|
|
||||||
How are these annotations loaded? From looking at the code you could
|
|
||||||
guess that the ORM Mapping, Assert Validation and the fully qualified
|
|
||||||
annotation can just be loaded using
|
|
||||||
the defined PHP autoloaders. This is not the case however: For error
|
|
||||||
handling reasons every check for class existence inside the
|
|
||||||
``AnnotationReader`` sets the second parameter $autoload
|
|
||||||
of ``class_exists($name, $autoload)`` to false. To work flawlessly the
|
|
||||||
``AnnotationReader`` requires silent autoloaders which many autoloaders are
|
|
||||||
not. Silent autoloading is NOT part of the `PSR-0 specification
|
|
||||||
<https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md>`_
|
|
||||||
for autoloading.
|
|
||||||
|
|
||||||
This is why Doctrine Annotations uses its own autoloading mechanism
|
|
||||||
through a global registry. If you are wondering about the annotation
|
|
||||||
registry being global, there is no other way to solve the architectural
|
|
||||||
problems of autoloading annotation classes in a straightforward fashion.
|
|
||||||
Additionally if you think about PHP autoloading then you recognize it is
|
|
||||||
a global as well.
|
|
||||||
|
|
||||||
To anticipate the configuration section, making the above PHP class work
|
|
||||||
with Doctrine Annotations requires this setup:
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
use Doctrine\Common\Annotations\AnnotationReader;
|
|
||||||
use Doctrine\Common\Annotations\AnnotationRegistry;
|
|
||||||
|
|
||||||
AnnotationRegistry::registerFile("/path/to/doctrine/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php");
|
|
||||||
AnnotationRegistry::registerAutoloadNamespace("Symfony\Component\Validator\Constraint", "/path/to/symfony/src");
|
|
||||||
AnnotationRegistry::registerAutoloadNamespace("MyProject\Annotations", "/path/to/myproject/src");
|
|
||||||
|
|
||||||
$reader = new AnnotationReader();
|
|
||||||
AnnotationReader::addGlobalIgnoredName('dummy');
|
|
||||||
|
|
||||||
The second block with the annotation registry calls registers all the
|
|
||||||
three different annotation namespaces that are used.
|
|
||||||
Doctrine Annotations saves all its annotations in a single file, that is
|
|
||||||
why ``AnnotationRegistry#registerFile`` is used in contrast to
|
|
||||||
``AnnotationRegistry#registerAutoloadNamespace`` which creates a PSR-0
|
|
||||||
compatible loading mechanism for class to file names.
|
|
||||||
|
|
||||||
In the third block, we create the actual ``AnnotationReader`` instance.
|
|
||||||
Note that we also add ``dummy`` to the global list of ignored
|
|
||||||
annotations for which we do not throw exceptions. Setting this is
|
|
||||||
necessary in our example case, otherwise ``@dummy`` would trigger an
|
|
||||||
exception to be thrown during the parsing of the docblock of
|
|
||||||
``MyProject\Entities\User#id``.
|
|
||||||
|
|
||||||
Setup and Configuration
|
|
||||||
-----------------------
|
|
||||||
|
|
||||||
To use the annotations library is simple, you just need to create a new
|
|
||||||
``AnnotationReader`` instance:
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
$reader = new \Doctrine\Common\Annotations\AnnotationReader();
|
|
||||||
|
|
||||||
This creates a simple annotation reader with no caching other than in
|
|
||||||
memory (in php arrays). Since parsing docblocks can be expensive you
|
|
||||||
should cache this process by using a caching reader.
|
|
||||||
|
|
||||||
To cache annotations, you can create a ``Doctrine\Common\Annotations\PsrCachedReader``.
|
|
||||||
This reader decorates the original reader and stores all annotations in a PSR-6
|
|
||||||
cache:
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
use Doctrine\Common\Annotations\AnnotationReader;
|
|
||||||
use Doctrine\Common\Annotations\PsrCachedReader;
|
|
||||||
|
|
||||||
$cache = ... // instantiate a PSR-6 Cache pool
|
|
||||||
|
|
||||||
$reader = new PsrCachedReader(
|
|
||||||
new AnnotationReader(),
|
|
||||||
$cache,
|
|
||||||
$debug = true
|
|
||||||
);
|
|
||||||
|
|
||||||
The ``debug`` flag is used here as well to invalidate the cache files
|
|
||||||
when the PHP class with annotations changed and should be used during
|
|
||||||
development.
|
|
||||||
|
|
||||||
.. warning ::
|
|
||||||
|
|
||||||
The ``AnnotationReader`` works and caches under the
|
|
||||||
assumption that all annotations of a doc-block are processed at
|
|
||||||
once. That means that annotation classes that do not exist and
|
|
||||||
aren't loaded and cannot be autoloaded (using the
|
|
||||||
AnnotationRegistry) would never be visible and not accessible if a
|
|
||||||
cache is used unless the cache is cleared and the annotations
|
|
||||||
requested again, this time with all annotations defined.
|
|
||||||
|
|
||||||
By default the annotation reader returns a list of annotations with
|
|
||||||
numeric indexes. If you want your annotations to be indexed by their
|
|
||||||
class name you can wrap the reader in an ``IndexedReader``:
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
use Doctrine\Common\Annotations\AnnotationReader;
|
|
||||||
use Doctrine\Common\Annotations\IndexedReader;
|
|
||||||
|
|
||||||
$reader = new IndexedReader(new AnnotationReader());
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
|
|
||||||
You should never wrap the indexed reader inside a cached reader,
|
|
||||||
only the other way around. This way you can re-use the cache with
|
|
||||||
indexed or numeric keys, otherwise your code may experience failures
|
|
||||||
due to caching in a numerical or indexed format.
|
|
||||||
|
|
||||||
Registering Annotations
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
As explained in the introduction, Doctrine Annotations uses its own
|
|
||||||
autoloading mechanism to determine if a given annotation has a
|
|
||||||
corresponding PHP class that can be autoloaded. For annotation
|
|
||||||
autoloading you have to configure the
|
|
||||||
``Doctrine\Common\Annotations\AnnotationRegistry``. There are three
|
|
||||||
different mechanisms to configure annotation autoloading:
|
|
||||||
|
|
||||||
- Calling ``AnnotationRegistry#registerFile($file)`` to register a file
|
|
||||||
that contains one or more annotation classes.
|
|
||||||
- Calling ``AnnotationRegistry#registerNamespace($namespace, $dirs =
|
|
||||||
null)`` to register that the given namespace contains annotations and
|
|
||||||
that their base directory is located at the given $dirs or in the
|
|
||||||
include path if ``NULL`` is passed. The given directories should *NOT*
|
|
||||||
be the directory where classes of the namespace are in, but the base
|
|
||||||
directory of the root namespace. The AnnotationRegistry uses a
|
|
||||||
namespace to directory separator approach to resolve the correct path.
|
|
||||||
- Calling ``AnnotationRegistry#registerLoader($callable)`` to register
|
|
||||||
an autoloader callback. The callback accepts the class as first and
|
|
||||||
only parameter and has to return ``true`` if the corresponding file
|
|
||||||
was found and included.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
Loaders have to fail silently, if a class is not found even if it
|
|
||||||
matches for example the namespace prefix of that loader. Never is a
|
|
||||||
loader to throw a warning or exception if the loading failed
|
|
||||||
otherwise parsing doc block annotations will become a huge pain.
|
|
||||||
|
|
||||||
A sample loader callback could look like:
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
use Doctrine\Common\Annotations\AnnotationRegistry;
|
|
||||||
use Symfony\Component\ClassLoader\UniversalClassLoader;
|
|
||||||
|
|
||||||
AnnotationRegistry::registerLoader(function($class) {
|
|
||||||
$file = str_replace("\\", DIRECTORY_SEPARATOR, $class) . ".php";
|
|
||||||
|
|
||||||
if (file_exists("/my/base/path/" . $file)) {
|
|
||||||
// file_exists() makes sure that the loader fails silently
|
|
||||||
require "/my/base/path/" . $file;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$loader = new UniversalClassLoader();
|
|
||||||
AnnotationRegistry::registerLoader(array($loader, "loadClass"));
|
|
||||||
|
|
||||||
|
|
||||||
Ignoring missing exceptions
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
By default an exception is thrown from the ``AnnotationReader`` if an
|
|
||||||
annotation was found that:
|
|
||||||
|
|
||||||
- is not part of the list of ignored "documentation annotations";
|
|
||||||
- was not imported through a use statement;
|
|
||||||
- is not a fully qualified class that exists.
|
|
||||||
|
|
||||||
You can disable this behavior for specific names if your docblocks do
|
|
||||||
not follow strict requirements:
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
$reader = new \Doctrine\Common\Annotations\AnnotationReader();
|
|
||||||
AnnotationReader::addGlobalIgnoredName('foo');
|
|
||||||
|
|
||||||
PHP Imports
|
|
||||||
~~~~~~~~~~~
|
|
||||||
|
|
||||||
By default the annotation reader parses the use-statement of a php file
|
|
||||||
to gain access to the import rules and register them for the annotation
|
|
||||||
processing. Only if you are using PHP Imports can you validate the
|
|
||||||
correct usage of annotations and throw exceptions if you misspelled an
|
|
||||||
annotation. This mechanism is enabled by default.
|
|
||||||
|
|
||||||
To ease the upgrade path, we still allow you to disable this mechanism.
|
|
||||||
Note however that we will remove this in future versions:
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
$reader = new \Doctrine\Common\Annotations\AnnotationReader();
|
|
||||||
$reader->setEnabledPhpImports(false);
|
|
@ -1,443 +0,0 @@
|
|||||||
Custom Annotation Classes
|
|
||||||
=========================
|
|
||||||
|
|
||||||
If you want to define your own annotations, you just have to group them
|
|
||||||
in a namespace and register this namespace in the ``AnnotationRegistry``.
|
|
||||||
Annotation classes have to contain a class-level docblock with the text
|
|
||||||
``@Annotation``:
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
namespace MyCompany\Annotations;
|
|
||||||
|
|
||||||
/** @Annotation */
|
|
||||||
class Bar
|
|
||||||
{
|
|
||||||
// some code
|
|
||||||
}
|
|
||||||
|
|
||||||
Inject annotation values
|
|
||||||
------------------------
|
|
||||||
|
|
||||||
The annotation parser checks if the annotation constructor has arguments,
|
|
||||||
if so then it will pass the value array, otherwise it will try to inject
|
|
||||||
values into public properties directly:
|
|
||||||
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
namespace MyCompany\Annotations;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Annotation
|
|
||||||
*
|
|
||||||
* Some Annotation using a constructor
|
|
||||||
*/
|
|
||||||
class Bar
|
|
||||||
{
|
|
||||||
private $foo;
|
|
||||||
|
|
||||||
public function __construct(array $values)
|
|
||||||
{
|
|
||||||
$this->foo = $values['foo'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Annotation
|
|
||||||
*
|
|
||||||
* Some Annotation without a constructor
|
|
||||||
*/
|
|
||||||
class Foo
|
|
||||||
{
|
|
||||||
public $bar;
|
|
||||||
}
|
|
||||||
|
|
||||||
Optional: Constructors with Named Parameters
|
|
||||||
--------------------------------------------
|
|
||||||
|
|
||||||
Starting with Annotations v1.11 a new annotation instantiation strategy
|
|
||||||
is available that aims at compatibility of Annotation classes with the PHP 8
|
|
||||||
attribute feature. You need to declare a constructor with regular parameter
|
|
||||||
names that match the named arguments in the annotation syntax.
|
|
||||||
|
|
||||||
To enable this feature, you can tag your annotation class with
|
|
||||||
``@NamedArgumentConstructor`` (available from v1.12) or implement the
|
|
||||||
``Doctrine\Common\Annotations\NamedArgumentConstructorAnnotation`` interface
|
|
||||||
(available from v1.11 and deprecated as of v1.12).
|
|
||||||
When using the ``@NamedArgumentConstructor`` tag, the first argument of the
|
|
||||||
constructor is considered as the default one.
|
|
||||||
|
|
||||||
|
|
||||||
Usage with the ``@NamedArgumentConstructor`` tag
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
namespace MyCompany\Annotations;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Annotation
|
|
||||||
* @NamedArgumentConstructor
|
|
||||||
*/
|
|
||||||
class Bar implements NamedArgumentConstructorAnnotation
|
|
||||||
{
|
|
||||||
private $foo;
|
|
||||||
|
|
||||||
public function __construct(string $foo)
|
|
||||||
{
|
|
||||||
$this->foo = $foo;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Usable with @Bar(foo="baz") */
|
|
||||||
/** Usable with @Bar("baz") */
|
|
||||||
|
|
||||||
In combination with PHP 8's constructor property promotion feature
|
|
||||||
you can simplify this to:
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
namespace MyCompany\Annotations;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Annotation
|
|
||||||
* @NamedArgumentConstructor
|
|
||||||
*/
|
|
||||||
class Bar implements NamedArgumentConstructorAnnotation
|
|
||||||
{
|
|
||||||
public function __construct(private string $foo) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Usage with the
|
|
||||||
``Doctrine\Common\Annotations\NamedArgumentConstructorAnnotation``
|
|
||||||
interface (v1.11, deprecated as of v1.12):
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
namespace MyCompany\Annotations;
|
|
||||||
|
|
||||||
use Doctrine\Common\Annotations\NamedArgumentConstructorAnnotation;
|
|
||||||
|
|
||||||
/** @Annotation */
|
|
||||||
class Bar implements NamedArgumentConstructorAnnotation
|
|
||||||
{
|
|
||||||
private $foo;
|
|
||||||
|
|
||||||
public function __construct(private string $foo) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Usable with @Bar(foo="baz") */
|
|
||||||
|
|
||||||
Annotation Target
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
``@Target`` indicates the kinds of class elements to which an annotation
|
|
||||||
type is applicable. Then you could define one or more targets:
|
|
||||||
|
|
||||||
- ``CLASS`` Allowed in class docblocks
|
|
||||||
- ``PROPERTY`` Allowed in property docblocks
|
|
||||||
- ``METHOD`` Allowed in the method docblocks
|
|
||||||
- ``FUNCTION`` Allowed in function dockblocks
|
|
||||||
- ``ALL`` Allowed in class, property, method and function docblocks
|
|
||||||
- ``ANNOTATION`` Allowed inside other annotations
|
|
||||||
|
|
||||||
If the annotations is not allowed in the current context, an
|
|
||||||
``AnnotationException`` is thrown.
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
namespace MyCompany\Annotations;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Annotation
|
|
||||||
* @Target({"METHOD","PROPERTY"})
|
|
||||||
*/
|
|
||||||
class Bar
|
|
||||||
{
|
|
||||||
// some code
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Annotation
|
|
||||||
* @Target("CLASS")
|
|
||||||
*/
|
|
||||||
class Foo
|
|
||||||
{
|
|
||||||
// some code
|
|
||||||
}
|
|
||||||
|
|
||||||
Attribute types
|
|
||||||
---------------
|
|
||||||
|
|
||||||
The annotation parser checks the given parameters using the phpdoc
|
|
||||||
annotation ``@var``, The data type could be validated using the ``@var``
|
|
||||||
annotation on the annotation properties or using the ``@Attributes`` and
|
|
||||||
``@Attribute`` annotations.
|
|
||||||
|
|
||||||
If the data type does not match you get an ``AnnotationException``
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
namespace MyCompany\Annotations;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Annotation
|
|
||||||
* @Target({"METHOD","PROPERTY"})
|
|
||||||
*/
|
|
||||||
class Bar
|
|
||||||
{
|
|
||||||
/** @var mixed */
|
|
||||||
public $mixed;
|
|
||||||
|
|
||||||
/** @var boolean */
|
|
||||||
public $boolean;
|
|
||||||
|
|
||||||
/** @var bool */
|
|
||||||
public $bool;
|
|
||||||
|
|
||||||
/** @var float */
|
|
||||||
public $float;
|
|
||||||
|
|
||||||
/** @var string */
|
|
||||||
public $string;
|
|
||||||
|
|
||||||
/** @var integer */
|
|
||||||
public $integer;
|
|
||||||
|
|
||||||
/** @var array */
|
|
||||||
public $array;
|
|
||||||
|
|
||||||
/** @var SomeAnnotationClass */
|
|
||||||
public $annotation;
|
|
||||||
|
|
||||||
/** @var array<integer> */
|
|
||||||
public $arrayOfIntegers;
|
|
||||||
|
|
||||||
/** @var array<SomeAnnotationClass> */
|
|
||||||
public $arrayOfAnnotations;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Annotation
|
|
||||||
* @Target({"METHOD","PROPERTY"})
|
|
||||||
* @Attributes({
|
|
||||||
* @Attribute("stringProperty", type = "string"),
|
|
||||||
* @Attribute("annotProperty", type = "SomeAnnotationClass"),
|
|
||||||
* })
|
|
||||||
*/
|
|
||||||
class Foo
|
|
||||||
{
|
|
||||||
public function __construct(array $values)
|
|
||||||
{
|
|
||||||
$this->stringProperty = $values['stringProperty'];
|
|
||||||
$this->annotProperty = $values['annotProperty'];
|
|
||||||
}
|
|
||||||
|
|
||||||
// some code
|
|
||||||
}
|
|
||||||
|
|
||||||
Annotation Required
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
``@Required`` indicates that the field must be specified when the
|
|
||||||
annotation is used. If it is not used you get an ``AnnotationException``
|
|
||||||
stating that this value can not be null.
|
|
||||||
|
|
||||||
Declaring a required field:
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Annotation
|
|
||||||
* @Target("ALL")
|
|
||||||
*/
|
|
||||||
class Foo
|
|
||||||
{
|
|
||||||
/** @Required */
|
|
||||||
public $requiredField;
|
|
||||||
}
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
/** @Foo(requiredField="value") */
|
|
||||||
public $direction; // Valid
|
|
||||||
|
|
||||||
/** @Foo */
|
|
||||||
public $direction; // Required field missing, throws an AnnotationException
|
|
||||||
|
|
||||||
|
|
||||||
Enumerated values
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
- An annotation property marked with ``@Enum`` is a field that accepts a
|
|
||||||
fixed set of scalar values.
|
|
||||||
- You should use ``@Enum`` fields any time you need to represent fixed
|
|
||||||
values.
|
|
||||||
- The annotation parser checks the given value and throws an
|
|
||||||
``AnnotationException`` if the value does not match.
|
|
||||||
|
|
||||||
|
|
||||||
Declaring an enumerated property:
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Annotation
|
|
||||||
* @Target("ALL")
|
|
||||||
*/
|
|
||||||
class Direction
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @Enum({"NORTH", "SOUTH", "EAST", "WEST"})
|
|
||||||
*/
|
|
||||||
public $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
Annotation usage:
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
/** @Direction("NORTH") */
|
|
||||||
public $direction; // Valid value
|
|
||||||
|
|
||||||
/** @Direction("NORTHEAST") */
|
|
||||||
public $direction; // Invalid value, throws an AnnotationException
|
|
||||||
|
|
||||||
|
|
||||||
Constants
|
|
||||||
---------
|
|
||||||
|
|
||||||
The use of constants and class constants is available on the annotations
|
|
||||||
parser.
|
|
||||||
|
|
||||||
The following usages are allowed:
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
namespace MyCompany\Entity;
|
|
||||||
|
|
||||||
use MyCompany\Annotations\Foo;
|
|
||||||
use MyCompany\Annotations\Bar;
|
|
||||||
use MyCompany\Entity\SomeClass;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Foo(PHP_EOL)
|
|
||||||
* @Bar(Bar::FOO)
|
|
||||||
* @Foo({SomeClass::FOO, SomeClass::BAR})
|
|
||||||
* @Bar({SomeClass::FOO_KEY = SomeClass::BAR_VALUE})
|
|
||||||
*/
|
|
||||||
class User
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Be careful with constants and the cache !
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
The cached reader will not re-evaluate each time an annotation is
|
|
||||||
loaded from cache. When a constant is changed the cache must be
|
|
||||||
cleaned.
|
|
||||||
|
|
||||||
|
|
||||||
Usage
|
|
||||||
-----
|
|
||||||
|
|
||||||
Using the library API is simple. Using the annotations described in the
|
|
||||||
previous section, you can now annotate other classes with your
|
|
||||||
annotations:
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
namespace MyCompany\Entity;
|
|
||||||
|
|
||||||
use MyCompany\Annotations\Foo;
|
|
||||||
use MyCompany\Annotations\Bar;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Foo(bar="foo")
|
|
||||||
* @Bar(foo="bar")
|
|
||||||
*/
|
|
||||||
class User
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Now we can write a script to get the annotations above:
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
$reflClass = new ReflectionClass('MyCompany\Entity\User');
|
|
||||||
$classAnnotations = $reader->getClassAnnotations($reflClass);
|
|
||||||
|
|
||||||
foreach ($classAnnotations AS $annot) {
|
|
||||||
if ($annot instanceof \MyCompany\Annotations\Foo) {
|
|
||||||
echo $annot->bar; // prints "foo";
|
|
||||||
} else if ($annot instanceof \MyCompany\Annotations\Bar) {
|
|
||||||
echo $annot->foo; // prints "bar";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
You have a complete API for retrieving annotation class instances from a
|
|
||||||
class, property or method docblock:
|
|
||||||
|
|
||||||
|
|
||||||
Reader API
|
|
||||||
~~~~~~~~~~
|
|
||||||
|
|
||||||
Access all annotations of a class
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
public function getClassAnnotations(\ReflectionClass $class);
|
|
||||||
|
|
||||||
Access one annotation of a class
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
public function getClassAnnotation(\ReflectionClass $class, $annotationName);
|
|
||||||
|
|
||||||
Access all annotations of a method
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
public function getMethodAnnotations(\ReflectionMethod $method);
|
|
||||||
|
|
||||||
Access one annotation of a method
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
public function getMethodAnnotation(\ReflectionMethod $method, $annotationName);
|
|
||||||
|
|
||||||
Access all annotations of a property
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
public function getPropertyAnnotations(\ReflectionProperty $property);
|
|
||||||
|
|
||||||
Access one annotation of a property
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
public function getPropertyAnnotation(\ReflectionProperty $property, $annotationName);
|
|
||||||
|
|
||||||
Access all annotations of a function
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
public function getFunctionAnnotations(\ReflectionFunction $property);
|
|
||||||
|
|
||||||
Access one annotation of a function
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
public function getFunctionAnnotation(\ReflectionFunction $property, $annotationName);
|
|
@ -1,6 +0,0 @@
|
|||||||
.. toctree::
|
|
||||||
:depth: 3
|
|
||||||
|
|
||||||
index
|
|
||||||
annotations
|
|
||||||
custom
|
|
@ -1,57 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Doctrine\Common\Annotations;
|
|
||||||
|
|
||||||
use BadMethodCallException;
|
|
||||||
|
|
||||||
use function sprintf;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Annotations class.
|
|
||||||
*/
|
|
||||||
class Annotation
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Value property. Common among all derived classes.
|
|
||||||
*
|
|
||||||
* @var mixed
|
|
||||||
*/
|
|
||||||
public $value;
|
|
||||||
|
|
||||||
/** @param array<string, mixed> $data Key-value for properties to be defined in this class. */
|
|
||||||
final public function __construct(array $data)
|
|
||||||
{
|
|
||||||
foreach ($data as $key => $value) {
|
|
||||||
$this->$key = $value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Error handler for unknown property accessor in Annotation class.
|
|
||||||
*
|
|
||||||
* @param string $name Unknown property name.
|
|
||||||
*
|
|
||||||
* @throws BadMethodCallException
|
|
||||||
*/
|
|
||||||
public function __get($name)
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException(
|
|
||||||
sprintf("Unknown property '%s' on annotation '%s'.", $name, static::class)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Error handler for unknown property mutator in Annotation class.
|
|
||||||
*
|
|
||||||
* @param string $name Unknown property name.
|
|
||||||
* @param mixed $value Property value.
|
|
||||||
*
|
|
||||||
* @throws BadMethodCallException
|
|
||||||
*/
|
|
||||||
public function __set($name, $value)
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException(
|
|
||||||
sprintf("Unknown property '%s' on annotation '%s'.", $name, static::class)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Doctrine\Common\Annotations\Annotation;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Annotation that can be used to signal to the parser
|
|
||||||
* to check the attribute type during the parsing process.
|
|
||||||
*
|
|
||||||
* @Annotation
|
|
||||||
*/
|
|
||||||
final class Attribute
|
|
||||||
{
|
|
||||||
/** @var string */
|
|
||||||
public $name;
|
|
||||||
|
|
||||||
/** @var string */
|
|
||||||
public $type;
|
|
||||||
|
|
||||||
/** @var bool */
|
|
||||||
public $required = false;
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Doctrine\Common\Annotations\Annotation;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Annotation that can be used to signal to the parser
|
|
||||||
* to check the types of all declared attributes during the parsing process.
|
|
||||||
*
|
|
||||||
* @Annotation
|
|
||||||
*/
|
|
||||||
final class Attributes
|
|
||||||
{
|
|
||||||
/** @var array<Attribute> */
|
|
||||||
public $value;
|
|
||||||
}
|
|
@ -1,69 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Doctrine\Common\Annotations\Annotation;
|
|
||||||
|
|
||||||
use InvalidArgumentException;
|
|
||||||
|
|
||||||
use function get_class;
|
|
||||||
use function gettype;
|
|
||||||
use function in_array;
|
|
||||||
use function is_object;
|
|
||||||
use function is_scalar;
|
|
||||||
use function sprintf;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Annotation that can be used to signal to the parser
|
|
||||||
* to check the available values during the parsing process.
|
|
||||||
*
|
|
||||||
* @Annotation
|
|
||||||
* @Attributes({
|
|
||||||
* @Attribute("value", required = true, type = "array"),
|
|
||||||
* @Attribute("literal", required = false, type = "array")
|
|
||||||
* })
|
|
||||||
*/
|
|
||||||
final class Enum
|
|
||||||
{
|
|
||||||
/** @phpstan-var list<scalar> */
|
|
||||||
public $value;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Literal target declaration.
|
|
||||||
*
|
|
||||||
* @var mixed[]
|
|
||||||
*/
|
|
||||||
public $literal;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @phpstan-param array{literal?: mixed[], value: list<scalar>} $values
|
|
||||||
*
|
|
||||||
* @throws InvalidArgumentException
|
|
||||||
*/
|
|
||||||
public function __construct(array $values)
|
|
||||||
{
|
|
||||||
if (! isset($values['literal'])) {
|
|
||||||
$values['literal'] = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($values['value'] as $var) {
|
|
||||||
if (! is_scalar($var)) {
|
|
||||||
throw new InvalidArgumentException(sprintf(
|
|
||||||
'@Enum supports only scalar values "%s" given.',
|
|
||||||
is_object($var) ? get_class($var) : gettype($var)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($values['literal'] as $key => $var) {
|
|
||||||
if (! in_array($key, $values['value'])) {
|
|
||||||
throw new InvalidArgumentException(sprintf(
|
|
||||||
'Undefined enumerator value "%s" for literal "%s".',
|
|
||||||
$key,
|
|
||||||
$var
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->value = $values['value'];
|
|
||||||
$this->literal = $values['literal'];
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,43 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Doctrine\Common\Annotations\Annotation;
|
|
||||||
|
|
||||||
use RuntimeException;
|
|
||||||
|
|
||||||
use function is_array;
|
|
||||||
use function is_string;
|
|
||||||
use function json_encode;
|
|
||||||
use function sprintf;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Annotation that can be used to signal to the parser to ignore specific
|
|
||||||
* annotations during the parsing process.
|
|
||||||
*
|
|
||||||
* @Annotation
|
|
||||||
*/
|
|
||||||
final class IgnoreAnnotation
|
|
||||||
{
|
|
||||||
/** @phpstan-var list<string> */
|
|
||||||
public $names;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @phpstan-param array{value: string|list<string>} $values
|
|
||||||
*
|
|
||||||
* @throws RuntimeException
|
|
||||||
*/
|
|
||||||
public function __construct(array $values)
|
|
||||||
{
|
|
||||||
if (is_string($values['value'])) {
|
|
||||||
$values['value'] = [$values['value']];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! is_array($values['value'])) {
|
|
||||||
throw new RuntimeException(sprintf(
|
|
||||||
'@IgnoreAnnotation expects either a string name, or an array of strings, but got %s.',
|
|
||||||
json_encode($values['value'])
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->names = $values['value'];
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Doctrine\Common\Annotations\Annotation;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Annotation that indicates that the annotated class should be constructed with a named argument call.
|
|
||||||
*
|
|
||||||
* @Annotation
|
|
||||||
* @Target("CLASS")
|
|
||||||
*/
|
|
||||||
final class NamedArgumentConstructor
|
|
||||||
{
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Doctrine\Common\Annotations\Annotation;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Annotation that can be used to signal to the parser
|
|
||||||
* to check if that attribute is required during the parsing process.
|
|
||||||
*
|
|
||||||
* @Annotation
|
|
||||||
*/
|
|
||||||
final class Required
|
|
||||||
{
|
|
||||||
}
|
|
@ -1,101 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Doctrine\Common\Annotations\Annotation;
|
|
||||||
|
|
||||||
use InvalidArgumentException;
|
|
||||||
|
|
||||||
use function array_keys;
|
|
||||||
use function get_class;
|
|
||||||
use function gettype;
|
|
||||||
use function implode;
|
|
||||||
use function is_array;
|
|
||||||
use function is_object;
|
|
||||||
use function is_string;
|
|
||||||
use function sprintf;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Annotation that can be used to signal to the parser
|
|
||||||
* to check the annotation target during the parsing process.
|
|
||||||
*
|
|
||||||
* @Annotation
|
|
||||||
*/
|
|
||||||
final class Target
|
|
||||||
{
|
|
||||||
public const TARGET_CLASS = 1;
|
|
||||||
public const TARGET_METHOD = 2;
|
|
||||||
public const TARGET_PROPERTY = 4;
|
|
||||||
public const TARGET_ANNOTATION = 8;
|
|
||||||
public const TARGET_FUNCTION = 16;
|
|
||||||
public const TARGET_ALL = 31;
|
|
||||||
|
|
||||||
/** @var array<string, int> */
|
|
||||||
private static $map = [
|
|
||||||
'ALL' => self::TARGET_ALL,
|
|
||||||
'CLASS' => self::TARGET_CLASS,
|
|
||||||
'METHOD' => self::TARGET_METHOD,
|
|
||||||
'PROPERTY' => self::TARGET_PROPERTY,
|
|
||||||
'FUNCTION' => self::TARGET_FUNCTION,
|
|
||||||
'ANNOTATION' => self::TARGET_ANNOTATION,
|
|
||||||
];
|
|
||||||
|
|
||||||
/** @phpstan-var list<string> */
|
|
||||||
public $value;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Targets as bitmask.
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public $targets;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Literal target declaration.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $literal;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @phpstan-param array{value?: string|list<string>} $values
|
|
||||||
*
|
|
||||||
* @throws InvalidArgumentException
|
|
||||||
*/
|
|
||||||
public function __construct(array $values)
|
|
||||||
{
|
|
||||||
if (! isset($values['value'])) {
|
|
||||||
$values['value'] = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_string($values['value'])) {
|
|
||||||
$values['value'] = [$values['value']];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! is_array($values['value'])) {
|
|
||||||
throw new InvalidArgumentException(
|
|
||||||
sprintf(
|
|
||||||
'@Target expects either a string value, or an array of strings, "%s" given.',
|
|
||||||
is_object($values['value']) ? get_class($values['value']) : gettype($values['value'])
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
$bitmask = 0;
|
|
||||||
foreach ($values['value'] as $literal) {
|
|
||||||
if (! isset(self::$map[$literal])) {
|
|
||||||
throw new InvalidArgumentException(
|
|
||||||
sprintf(
|
|
||||||
'Invalid Target "%s". Available targets: [%s]',
|
|
||||||
$literal,
|
|
||||||
implode(', ', array_keys(self::$map))
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
$bitmask |= self::$map[$literal];
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->targets = $bitmask;
|
|
||||||
$this->value = $values['value'];
|
|
||||||
$this->literal = implode(', ', $this->value);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,167 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Doctrine\Common\Annotations;
|
|
||||||
|
|
||||||
use Exception;
|
|
||||||
use Throwable;
|
|
||||||
|
|
||||||
use function get_class;
|
|
||||||
use function gettype;
|
|
||||||
use function implode;
|
|
||||||
use function is_object;
|
|
||||||
use function sprintf;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Description of AnnotationException
|
|
||||||
*/
|
|
||||||
class AnnotationException extends Exception
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Creates a new AnnotationException describing a Syntax error.
|
|
||||||
*
|
|
||||||
* @param string $message Exception message
|
|
||||||
*
|
|
||||||
* @return AnnotationException
|
|
||||||
*/
|
|
||||||
public static function syntaxError($message)
|
|
||||||
{
|
|
||||||
return new self('[Syntax Error] ' . $message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new AnnotationException describing a Semantical error.
|
|
||||||
*
|
|
||||||
* @param string $message Exception message
|
|
||||||
*
|
|
||||||
* @return AnnotationException
|
|
||||||
*/
|
|
||||||
public static function semanticalError($message)
|
|
||||||
{
|
|
||||||
return new self('[Semantical Error] ' . $message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new AnnotationException describing an error which occurred during
|
|
||||||
* the creation of the annotation.
|
|
||||||
*
|
|
||||||
* @param string $message
|
|
||||||
*
|
|
||||||
* @return AnnotationException
|
|
||||||
*/
|
|
||||||
public static function creationError($message, ?Throwable $previous = null)
|
|
||||||
{
|
|
||||||
return new self('[Creation Error] ' . $message, 0, $previous);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new AnnotationException describing a type error.
|
|
||||||
*
|
|
||||||
* @param string $message
|
|
||||||
*
|
|
||||||
* @return AnnotationException
|
|
||||||
*/
|
|
||||||
public static function typeError($message)
|
|
||||||
{
|
|
||||||
return new self('[Type Error] ' . $message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new AnnotationException describing a constant semantical error.
|
|
||||||
*
|
|
||||||
* @param string $identifier
|
|
||||||
* @param string $context
|
|
||||||
*
|
|
||||||
* @return AnnotationException
|
|
||||||
*/
|
|
||||||
public static function semanticalErrorConstants($identifier, $context = null)
|
|
||||||
{
|
|
||||||
return self::semanticalError(sprintf(
|
|
||||||
"Couldn't find constant %s%s.",
|
|
||||||
$identifier,
|
|
||||||
$context ? ', ' . $context : ''
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new AnnotationException describing an type error of an attribute.
|
|
||||||
*
|
|
||||||
* @param string $attributeName
|
|
||||||
* @param string $annotationName
|
|
||||||
* @param string $context
|
|
||||||
* @param string $expected
|
|
||||||
* @param mixed $actual
|
|
||||||
*
|
|
||||||
* @return AnnotationException
|
|
||||||
*/
|
|
||||||
public static function attributeTypeError($attributeName, $annotationName, $context, $expected, $actual)
|
|
||||||
{
|
|
||||||
return self::typeError(sprintf(
|
|
||||||
'Attribute "%s" of @%s declared on %s expects %s, but got %s.',
|
|
||||||
$attributeName,
|
|
||||||
$annotationName,
|
|
||||||
$context,
|
|
||||||
$expected,
|
|
||||||
is_object($actual) ? 'an instance of ' . get_class($actual) : gettype($actual)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new AnnotationException describing an required error of an attribute.
|
|
||||||
*
|
|
||||||
* @param string $attributeName
|
|
||||||
* @param string $annotationName
|
|
||||||
* @param string $context
|
|
||||||
* @param string $expected
|
|
||||||
*
|
|
||||||
* @return AnnotationException
|
|
||||||
*/
|
|
||||||
public static function requiredError($attributeName, $annotationName, $context, $expected)
|
|
||||||
{
|
|
||||||
return self::typeError(sprintf(
|
|
||||||
'Attribute "%s" of @%s declared on %s expects %s. This value should not be null.',
|
|
||||||
$attributeName,
|
|
||||||
$annotationName,
|
|
||||||
$context,
|
|
||||||
$expected
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new AnnotationException describing a invalid enummerator.
|
|
||||||
*
|
|
||||||
* @param string $attributeName
|
|
||||||
* @param string $annotationName
|
|
||||||
* @param string $context
|
|
||||||
* @param mixed $given
|
|
||||||
* @phpstan-param list<string> $available
|
|
||||||
*
|
|
||||||
* @return AnnotationException
|
|
||||||
*/
|
|
||||||
public static function enumeratorError($attributeName, $annotationName, $context, $available, $given)
|
|
||||||
{
|
|
||||||
return new self(sprintf(
|
|
||||||
'[Enum Error] Attribute "%s" of @%s declared on %s accepts only [%s], but got %s.',
|
|
||||||
$attributeName,
|
|
||||||
$annotationName,
|
|
||||||
$context,
|
|
||||||
implode(', ', $available),
|
|
||||||
is_object($given) ? get_class($given) : $given
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @return AnnotationException */
|
|
||||||
public static function optimizerPlusSaveComments()
|
|
||||||
{
|
|
||||||
return new self(
|
|
||||||
'You have to enable opcache.save_comments=1 or zend_optimizerplus.save_comments=1.'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @return AnnotationException */
|
|
||||||
public static function optimizerPlusLoadComments()
|
|
||||||
{
|
|
||||||
return new self(
|
|
||||||
'You have to enable opcache.load_comments=1 or zend_optimizerplus.load_comments=1.'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,389 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Doctrine\Common\Annotations;
|
|
||||||
|
|
||||||
use Doctrine\Common\Annotations\Annotation\IgnoreAnnotation;
|
|
||||||
use Doctrine\Common\Annotations\Annotation\Target;
|
|
||||||
use ReflectionClass;
|
|
||||||
use ReflectionFunction;
|
|
||||||
use ReflectionMethod;
|
|
||||||
use ReflectionProperty;
|
|
||||||
|
|
||||||
use function array_merge;
|
|
||||||
use function class_exists;
|
|
||||||
use function extension_loaded;
|
|
||||||
use function ini_get;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A reader for docblock annotations.
|
|
||||||
*/
|
|
||||||
class AnnotationReader implements Reader
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Global map for imports.
|
|
||||||
*
|
|
||||||
* @var array<string, class-string>
|
|
||||||
*/
|
|
||||||
private static $globalImports = [
|
|
||||||
'ignoreannotation' => Annotation\IgnoreAnnotation::class,
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A list with annotations that are not causing exceptions when not resolved to an annotation class.
|
|
||||||
*
|
|
||||||
* The names are case sensitive.
|
|
||||||
*
|
|
||||||
* @var array<string, true>
|
|
||||||
*/
|
|
||||||
private static $globalIgnoredNames = ImplicitlyIgnoredAnnotationNames::LIST;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A list with annotations that are not causing exceptions when not resolved to an annotation class.
|
|
||||||
*
|
|
||||||
* The names are case sensitive.
|
|
||||||
*
|
|
||||||
* @var array<string, true>
|
|
||||||
*/
|
|
||||||
private static $globalIgnoredNamespaces = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a new annotation to the globally ignored annotation names with regard to exception handling.
|
|
||||||
*
|
|
||||||
* @param string $name
|
|
||||||
*/
|
|
||||||
public static function addGlobalIgnoredName($name)
|
|
||||||
{
|
|
||||||
self::$globalIgnoredNames[$name] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a new annotation to the globally ignored annotation namespaces with regard to exception handling.
|
|
||||||
*
|
|
||||||
* @param string $namespace
|
|
||||||
*/
|
|
||||||
public static function addGlobalIgnoredNamespace($namespace)
|
|
||||||
{
|
|
||||||
self::$globalIgnoredNamespaces[$namespace] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Annotations parser.
|
|
||||||
*
|
|
||||||
* @var DocParser
|
|
||||||
*/
|
|
||||||
private $parser;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Annotations parser used to collect parsing metadata.
|
|
||||||
*
|
|
||||||
* @var DocParser
|
|
||||||
*/
|
|
||||||
private $preParser;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* PHP parser used to collect imports.
|
|
||||||
*
|
|
||||||
* @var PhpParser
|
|
||||||
*/
|
|
||||||
private $phpParser;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* In-memory cache mechanism to store imported annotations per class.
|
|
||||||
*
|
|
||||||
* @psalm-var array<'class'|'function', array<string, array<string, class-string>>>
|
|
||||||
*/
|
|
||||||
private $imports = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* In-memory cache mechanism to store ignored annotations per class.
|
|
||||||
*
|
|
||||||
* @psalm-var array<'class'|'function', array<string, array<string, true>>>
|
|
||||||
*/
|
|
||||||
private $ignoredAnnotationNames = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes a new AnnotationReader.
|
|
||||||
*
|
|
||||||
* @throws AnnotationException
|
|
||||||
*/
|
|
||||||
public function __construct(?DocParser $parser = null)
|
|
||||||
{
|
|
||||||
if (
|
|
||||||
extension_loaded('Zend Optimizer+') && (ini_get('zend_optimizerplus.save_comments') === '0' ||
|
|
||||||
ini_get('opcache.save_comments') === '0')
|
|
||||||
) {
|
|
||||||
throw AnnotationException::optimizerPlusSaveComments();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (extension_loaded('Zend OPcache') && ini_get('opcache.save_comments') === 0) {
|
|
||||||
throw AnnotationException::optimizerPlusSaveComments();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure that the IgnoreAnnotation annotation is loaded
|
|
||||||
class_exists(IgnoreAnnotation::class);
|
|
||||||
|
|
||||||
$this->parser = $parser ?: new DocParser();
|
|
||||||
|
|
||||||
$this->preParser = new DocParser();
|
|
||||||
|
|
||||||
$this->preParser->setImports(self::$globalImports);
|
|
||||||
$this->preParser->setIgnoreNotImportedAnnotations(true);
|
|
||||||
$this->preParser->setIgnoredAnnotationNames(self::$globalIgnoredNames);
|
|
||||||
|
|
||||||
$this->phpParser = new PhpParser();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public function getClassAnnotations(ReflectionClass $class)
|
|
||||||
{
|
|
||||||
$this->parser->setTarget(Target::TARGET_CLASS);
|
|
||||||
$this->parser->setImports($this->getImports($class));
|
|
||||||
$this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class));
|
|
||||||
$this->parser->setIgnoredAnnotationNamespaces(self::$globalIgnoredNamespaces);
|
|
||||||
|
|
||||||
return $this->parser->parse($class->getDocComment(), 'class ' . $class->getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public function getClassAnnotation(ReflectionClass $class, $annotationName)
|
|
||||||
{
|
|
||||||
$annotations = $this->getClassAnnotations($class);
|
|
||||||
|
|
||||||
foreach ($annotations as $annotation) {
|
|
||||||
if ($annotation instanceof $annotationName) {
|
|
||||||
return $annotation;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public function getPropertyAnnotations(ReflectionProperty $property)
|
|
||||||
{
|
|
||||||
$class = $property->getDeclaringClass();
|
|
||||||
$context = 'property ' . $class->getName() . '::$' . $property->getName();
|
|
||||||
|
|
||||||
$this->parser->setTarget(Target::TARGET_PROPERTY);
|
|
||||||
$this->parser->setImports($this->getPropertyImports($property));
|
|
||||||
$this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class));
|
|
||||||
$this->parser->setIgnoredAnnotationNamespaces(self::$globalIgnoredNamespaces);
|
|
||||||
|
|
||||||
return $this->parser->parse($property->getDocComment(), $context);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public function getPropertyAnnotation(ReflectionProperty $property, $annotationName)
|
|
||||||
{
|
|
||||||
$annotations = $this->getPropertyAnnotations($property);
|
|
||||||
|
|
||||||
foreach ($annotations as $annotation) {
|
|
||||||
if ($annotation instanceof $annotationName) {
|
|
||||||
return $annotation;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public function getMethodAnnotations(ReflectionMethod $method)
|
|
||||||
{
|
|
||||||
$class = $method->getDeclaringClass();
|
|
||||||
$context = 'method ' . $class->getName() . '::' . $method->getName() . '()';
|
|
||||||
|
|
||||||
$this->parser->setTarget(Target::TARGET_METHOD);
|
|
||||||
$this->parser->setImports($this->getMethodImports($method));
|
|
||||||
$this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class));
|
|
||||||
$this->parser->setIgnoredAnnotationNamespaces(self::$globalIgnoredNamespaces);
|
|
||||||
|
|
||||||
return $this->parser->parse($method->getDocComment(), $context);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public function getMethodAnnotation(ReflectionMethod $method, $annotationName)
|
|
||||||
{
|
|
||||||
$annotations = $this->getMethodAnnotations($method);
|
|
||||||
|
|
||||||
foreach ($annotations as $annotation) {
|
|
||||||
if ($annotation instanceof $annotationName) {
|
|
||||||
return $annotation;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the annotations applied to a function.
|
|
||||||
*
|
|
||||||
* @phpstan-return list<object> An array of Annotations.
|
|
||||||
*/
|
|
||||||
public function getFunctionAnnotations(ReflectionFunction $function): array
|
|
||||||
{
|
|
||||||
$context = 'function ' . $function->getName();
|
|
||||||
|
|
||||||
$this->parser->setTarget(Target::TARGET_FUNCTION);
|
|
||||||
$this->parser->setImports($this->getImports($function));
|
|
||||||
$this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($function));
|
|
||||||
$this->parser->setIgnoredAnnotationNamespaces(self::$globalIgnoredNamespaces);
|
|
||||||
|
|
||||||
return $this->parser->parse($function->getDocComment(), $context);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a function annotation.
|
|
||||||
*
|
|
||||||
* @return object|null The Annotation or NULL, if the requested annotation does not exist.
|
|
||||||
*/
|
|
||||||
public function getFunctionAnnotation(ReflectionFunction $function, string $annotationName)
|
|
||||||
{
|
|
||||||
$annotations = $this->getFunctionAnnotations($function);
|
|
||||||
|
|
||||||
foreach ($annotations as $annotation) {
|
|
||||||
if ($annotation instanceof $annotationName) {
|
|
||||||
return $annotation;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the ignored annotations for the given class or function.
|
|
||||||
*
|
|
||||||
* @param ReflectionClass|ReflectionFunction $reflection
|
|
||||||
*
|
|
||||||
* @return array<string, true>
|
|
||||||
*/
|
|
||||||
private function getIgnoredAnnotationNames($reflection): array
|
|
||||||
{
|
|
||||||
$type = $reflection instanceof ReflectionClass ? 'class' : 'function';
|
|
||||||
$name = $reflection->getName();
|
|
||||||
|
|
||||||
if (isset($this->ignoredAnnotationNames[$type][$name])) {
|
|
||||||
return $this->ignoredAnnotationNames[$type][$name];
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->collectParsingMetadata($reflection);
|
|
||||||
|
|
||||||
return $this->ignoredAnnotationNames[$type][$name];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves imports for a class or a function.
|
|
||||||
*
|
|
||||||
* @param ReflectionClass|ReflectionFunction $reflection
|
|
||||||
*
|
|
||||||
* @return array<string, class-string>
|
|
||||||
*/
|
|
||||||
private function getImports($reflection): array
|
|
||||||
{
|
|
||||||
$type = $reflection instanceof ReflectionClass ? 'class' : 'function';
|
|
||||||
$name = $reflection->getName();
|
|
||||||
|
|
||||||
if (isset($this->imports[$type][$name])) {
|
|
||||||
return $this->imports[$type][$name];
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->collectParsingMetadata($reflection);
|
|
||||||
|
|
||||||
return $this->imports[$type][$name];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves imports for methods.
|
|
||||||
*
|
|
||||||
* @return array<string, class-string>
|
|
||||||
*/
|
|
||||||
private function getMethodImports(ReflectionMethod $method)
|
|
||||||
{
|
|
||||||
$class = $method->getDeclaringClass();
|
|
||||||
$classImports = $this->getImports($class);
|
|
||||||
|
|
||||||
$traitImports = [];
|
|
||||||
|
|
||||||
foreach ($class->getTraits() as $trait) {
|
|
||||||
if (
|
|
||||||
! $trait->hasMethod($method->getName())
|
|
||||||
|| $trait->getFileName() !== $method->getFileName()
|
|
||||||
) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$traitImports = array_merge($traitImports, $this->phpParser->parseUseStatements($trait));
|
|
||||||
}
|
|
||||||
|
|
||||||
return array_merge($classImports, $traitImports);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves imports for properties.
|
|
||||||
*
|
|
||||||
* @return array<string, class-string>
|
|
||||||
*/
|
|
||||||
private function getPropertyImports(ReflectionProperty $property)
|
|
||||||
{
|
|
||||||
$class = $property->getDeclaringClass();
|
|
||||||
$classImports = $this->getImports($class);
|
|
||||||
|
|
||||||
$traitImports = [];
|
|
||||||
|
|
||||||
foreach ($class->getTraits() as $trait) {
|
|
||||||
if (! $trait->hasProperty($property->getName())) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$traitImports = array_merge($traitImports, $this->phpParser->parseUseStatements($trait));
|
|
||||||
}
|
|
||||||
|
|
||||||
return array_merge($classImports, $traitImports);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Collects parsing metadata for a given class or function.
|
|
||||||
*
|
|
||||||
* @param ReflectionClass|ReflectionFunction $reflection
|
|
||||||
*/
|
|
||||||
private function collectParsingMetadata($reflection): void
|
|
||||||
{
|
|
||||||
$type = $reflection instanceof ReflectionClass ? 'class' : 'function';
|
|
||||||
$name = $reflection->getName();
|
|
||||||
|
|
||||||
$ignoredAnnotationNames = self::$globalIgnoredNames;
|
|
||||||
$annotations = $this->preParser->parse($reflection->getDocComment(), $type . ' ' . $name);
|
|
||||||
|
|
||||||
foreach ($annotations as $annotation) {
|
|
||||||
if (! ($annotation instanceof IgnoreAnnotation)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($annotation->names as $annot) {
|
|
||||||
$ignoredAnnotationNames[$annot] = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->imports[$type][$name] = array_merge(
|
|
||||||
self::$globalImports,
|
|
||||||
$this->phpParser->parseUseStatements($reflection),
|
|
||||||
[
|
|
||||||
'__NAMESPACE__' => $reflection->getNamespaceName(),
|
|
||||||
'self' => $name,
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
$this->ignoredAnnotationNames[$type][$name] = $ignoredAnnotationNames;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,190 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Doctrine\Common\Annotations;
|
|
||||||
|
|
||||||
use function array_key_exists;
|
|
||||||
use function array_merge;
|
|
||||||
use function class_exists;
|
|
||||||
use function in_array;
|
|
||||||
use function is_file;
|
|
||||||
use function str_replace;
|
|
||||||
use function stream_resolve_include_path;
|
|
||||||
use function strpos;
|
|
||||||
|
|
||||||
use const DIRECTORY_SEPARATOR;
|
|
||||||
|
|
||||||
final class AnnotationRegistry
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* A map of namespaces to use for autoloading purposes based on a PSR-0 convention.
|
|
||||||
*
|
|
||||||
* Contains the namespace as key and an array of directories as value. If the value is NULL
|
|
||||||
* the include path is used for checking for the corresponding file.
|
|
||||||
*
|
|
||||||
* This autoloading mechanism does not utilize the PHP autoloading but implements autoloading on its own.
|
|
||||||
*
|
|
||||||
* @var string[][]|string[]|null[]
|
|
||||||
*/
|
|
||||||
private static $autoloadNamespaces = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A map of autoloader callables.
|
|
||||||
*
|
|
||||||
* @var callable[]
|
|
||||||
*/
|
|
||||||
private static $loaders = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An array of classes which cannot be found
|
|
||||||
*
|
|
||||||
* @var null[] indexed by class name
|
|
||||||
*/
|
|
||||||
private static $failedToAutoload = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whenever registerFile() was used. Disables use of standard autoloader.
|
|
||||||
*
|
|
||||||
* @var bool
|
|
||||||
*/
|
|
||||||
private static $registerFileUsed = false;
|
|
||||||
|
|
||||||
public static function reset(): void
|
|
||||||
{
|
|
||||||
self::$autoloadNamespaces = [];
|
|
||||||
self::$loaders = [];
|
|
||||||
self::$failedToAutoload = [];
|
|
||||||
self::$registerFileUsed = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers file.
|
|
||||||
*
|
|
||||||
* @deprecated This method is deprecated and will be removed in
|
|
||||||
* doctrine/annotations 2.0. Annotations will be autoloaded in 2.0.
|
|
||||||
*/
|
|
||||||
public static function registerFile(string $file): void
|
|
||||||
{
|
|
||||||
self::$registerFileUsed = true;
|
|
||||||
|
|
||||||
require_once $file;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a namespace with one or many directories to look for files or null for the include path.
|
|
||||||
*
|
|
||||||
* Loading of this namespaces will be done with a PSR-0 namespace loading algorithm.
|
|
||||||
*
|
|
||||||
* @deprecated This method is deprecated and will be removed in
|
|
||||||
* doctrine/annotations 2.0. Annotations will be autoloaded in 2.0.
|
|
||||||
*
|
|
||||||
* @phpstan-param string|list<string>|null $dirs
|
|
||||||
*/
|
|
||||||
public static function registerAutoloadNamespace(string $namespace, $dirs = null): void
|
|
||||||
{
|
|
||||||
self::$autoloadNamespaces[$namespace] = $dirs;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers multiple namespaces.
|
|
||||||
*
|
|
||||||
* Loading of this namespaces will be done with a PSR-0 namespace loading algorithm.
|
|
||||||
*
|
|
||||||
* @deprecated This method is deprecated and will be removed in
|
|
||||||
* doctrine/annotations 2.0. Annotations will be autoloaded in 2.0.
|
|
||||||
*
|
|
||||||
* @param string[][]|string[]|null[] $namespaces indexed by namespace name
|
|
||||||
*/
|
|
||||||
public static function registerAutoloadNamespaces(array $namespaces): void
|
|
||||||
{
|
|
||||||
self::$autoloadNamespaces = array_merge(self::$autoloadNamespaces, $namespaces);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers an autoloading callable for annotations, much like spl_autoload_register().
|
|
||||||
*
|
|
||||||
* NOTE: These class loaders HAVE to be silent when a class was not found!
|
|
||||||
* IMPORTANT: Loaders have to return true if they loaded a class that could contain the searched annotation class.
|
|
||||||
*
|
|
||||||
* @deprecated This method is deprecated and will be removed in
|
|
||||||
* doctrine/annotations 2.0. Annotations will be autoloaded in 2.0.
|
|
||||||
*/
|
|
||||||
public static function registerLoader(callable $callable): void
|
|
||||||
{
|
|
||||||
// Reset our static cache now that we have a new loader to work with
|
|
||||||
self::$failedToAutoload = [];
|
|
||||||
self::$loaders[] = $callable;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers an autoloading callable for annotations, if it is not already registered
|
|
||||||
*
|
|
||||||
* @deprecated This method is deprecated and will be removed in
|
|
||||||
* doctrine/annotations 2.0. Annotations will be autoloaded in 2.0.
|
|
||||||
*/
|
|
||||||
public static function registerUniqueLoader(callable $callable): void
|
|
||||||
{
|
|
||||||
if (in_array($callable, self::$loaders, true)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
self::registerLoader($callable);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Autoloads an annotation class silently.
|
|
||||||
*/
|
|
||||||
public static function loadAnnotationClass(string $class): bool
|
|
||||||
{
|
|
||||||
if (class_exists($class, false)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (array_key_exists($class, self::$failedToAutoload)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (self::$autoloadNamespaces as $namespace => $dirs) {
|
|
||||||
if (strpos($class, $namespace) !== 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$file = str_replace('\\', DIRECTORY_SEPARATOR, $class) . '.php';
|
|
||||||
|
|
||||||
if ($dirs === null) {
|
|
||||||
$path = stream_resolve_include_path($file);
|
|
||||||
if ($path) {
|
|
||||||
require $path;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
foreach ((array) $dirs as $dir) {
|
|
||||||
if (is_file($dir . DIRECTORY_SEPARATOR . $file)) {
|
|
||||||
require $dir . DIRECTORY_SEPARATOR . $file;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (self::$loaders as $loader) {
|
|
||||||
if ($loader($class) === true) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
self::$loaders === [] &&
|
|
||||||
self::$autoloadNamespaces === [] &&
|
|
||||||
self::$registerFileUsed === false &&
|
|
||||||
class_exists($class)
|
|
||||||
) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
self::$failedToAutoload[$class] = null;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,266 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Doctrine\Common\Annotations;
|
|
||||||
|
|
||||||
use Doctrine\Common\Cache\Cache;
|
|
||||||
use ReflectionClass;
|
|
||||||
use ReflectionMethod;
|
|
||||||
use ReflectionProperty;
|
|
||||||
|
|
||||||
use function array_map;
|
|
||||||
use function array_merge;
|
|
||||||
use function assert;
|
|
||||||
use function filemtime;
|
|
||||||
use function max;
|
|
||||||
use function time;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A cache aware annotation reader.
|
|
||||||
*
|
|
||||||
* @deprecated the CachedReader is deprecated and will be removed
|
|
||||||
* in version 2.0.0 of doctrine/annotations. Please use the
|
|
||||||
* {@see \Doctrine\Common\Annotations\PsrCachedReader} instead.
|
|
||||||
*/
|
|
||||||
final class CachedReader implements Reader
|
|
||||||
{
|
|
||||||
/** @var Reader */
|
|
||||||
private $delegate;
|
|
||||||
|
|
||||||
/** @var Cache */
|
|
||||||
private $cache;
|
|
||||||
|
|
||||||
/** @var bool */
|
|
||||||
private $debug;
|
|
||||||
|
|
||||||
/** @var array<string, array<object>> */
|
|
||||||
private $loadedAnnotations = [];
|
|
||||||
|
|
||||||
/** @var int[] */
|
|
||||||
private $loadedFilemtimes = [];
|
|
||||||
|
|
||||||
/** @param bool $debug */
|
|
||||||
public function __construct(Reader $reader, Cache $cache, $debug = false)
|
|
||||||
{
|
|
||||||
$this->delegate = $reader;
|
|
||||||
$this->cache = $cache;
|
|
||||||
$this->debug = (bool) $debug;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public function getClassAnnotations(ReflectionClass $class)
|
|
||||||
{
|
|
||||||
$cacheKey = $class->getName();
|
|
||||||
|
|
||||||
if (isset($this->loadedAnnotations[$cacheKey])) {
|
|
||||||
return $this->loadedAnnotations[$cacheKey];
|
|
||||||
}
|
|
||||||
|
|
||||||
$annots = $this->fetchFromCache($cacheKey, $class);
|
|
||||||
if ($annots === false) {
|
|
||||||
$annots = $this->delegate->getClassAnnotations($class);
|
|
||||||
$this->saveToCache($cacheKey, $annots);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->loadedAnnotations[$cacheKey] = $annots;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public function getClassAnnotation(ReflectionClass $class, $annotationName)
|
|
||||||
{
|
|
||||||
foreach ($this->getClassAnnotations($class) as $annot) {
|
|
||||||
if ($annot instanceof $annotationName) {
|
|
||||||
return $annot;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public function getPropertyAnnotations(ReflectionProperty $property)
|
|
||||||
{
|
|
||||||
$class = $property->getDeclaringClass();
|
|
||||||
$cacheKey = $class->getName() . '$' . $property->getName();
|
|
||||||
|
|
||||||
if (isset($this->loadedAnnotations[$cacheKey])) {
|
|
||||||
return $this->loadedAnnotations[$cacheKey];
|
|
||||||
}
|
|
||||||
|
|
||||||
$annots = $this->fetchFromCache($cacheKey, $class);
|
|
||||||
if ($annots === false) {
|
|
||||||
$annots = $this->delegate->getPropertyAnnotations($property);
|
|
||||||
$this->saveToCache($cacheKey, $annots);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->loadedAnnotations[$cacheKey] = $annots;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public function getPropertyAnnotation(ReflectionProperty $property, $annotationName)
|
|
||||||
{
|
|
||||||
foreach ($this->getPropertyAnnotations($property) as $annot) {
|
|
||||||
if ($annot instanceof $annotationName) {
|
|
||||||
return $annot;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public function getMethodAnnotations(ReflectionMethod $method)
|
|
||||||
{
|
|
||||||
$class = $method->getDeclaringClass();
|
|
||||||
$cacheKey = $class->getName() . '#' . $method->getName();
|
|
||||||
|
|
||||||
if (isset($this->loadedAnnotations[$cacheKey])) {
|
|
||||||
return $this->loadedAnnotations[$cacheKey];
|
|
||||||
}
|
|
||||||
|
|
||||||
$annots = $this->fetchFromCache($cacheKey, $class);
|
|
||||||
if ($annots === false) {
|
|
||||||
$annots = $this->delegate->getMethodAnnotations($method);
|
|
||||||
$this->saveToCache($cacheKey, $annots);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->loadedAnnotations[$cacheKey] = $annots;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public function getMethodAnnotation(ReflectionMethod $method, $annotationName)
|
|
||||||
{
|
|
||||||
foreach ($this->getMethodAnnotations($method) as $annot) {
|
|
||||||
if ($annot instanceof $annotationName) {
|
|
||||||
return $annot;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clears loaded annotations.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function clearLoadedAnnotations()
|
|
||||||
{
|
|
||||||
$this->loadedAnnotations = [];
|
|
||||||
$this->loadedFilemtimes = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetches a value from the cache.
|
|
||||||
*
|
|
||||||
* @param string $cacheKey The cache key.
|
|
||||||
*
|
|
||||||
* @return mixed The cached value or false when the value is not in cache.
|
|
||||||
*/
|
|
||||||
private function fetchFromCache($cacheKey, ReflectionClass $class)
|
|
||||||
{
|
|
||||||
$data = $this->cache->fetch($cacheKey);
|
|
||||||
if ($data !== false) {
|
|
||||||
if (! $this->debug || $this->isCacheFresh($cacheKey, $class)) {
|
|
||||||
return $data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Saves a value to the cache.
|
|
||||||
*
|
|
||||||
* @param string $cacheKey The cache key.
|
|
||||||
* @param mixed $value The value.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
private function saveToCache($cacheKey, $value)
|
|
||||||
{
|
|
||||||
$this->cache->save($cacheKey, $value);
|
|
||||||
if (! $this->debug) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->cache->save('[C]' . $cacheKey, time());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the cache is fresh.
|
|
||||||
*
|
|
||||||
* @param string $cacheKey
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
private function isCacheFresh($cacheKey, ReflectionClass $class)
|
|
||||||
{
|
|
||||||
$lastModification = $this->getLastModification($class);
|
|
||||||
if ($lastModification === 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->cache->fetch('[C]' . $cacheKey) >= $lastModification;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the time the class was last modified, testing traits and parents
|
|
||||||
*/
|
|
||||||
private function getLastModification(ReflectionClass $class): int
|
|
||||||
{
|
|
||||||
$filename = $class->getFileName();
|
|
||||||
|
|
||||||
if (isset($this->loadedFilemtimes[$filename])) {
|
|
||||||
return $this->loadedFilemtimes[$filename];
|
|
||||||
}
|
|
||||||
|
|
||||||
$parent = $class->getParentClass();
|
|
||||||
|
|
||||||
$lastModification = max(array_merge(
|
|
||||||
[$filename ? filemtime($filename) : 0],
|
|
||||||
array_map(function (ReflectionClass $reflectionTrait): int {
|
|
||||||
return $this->getTraitLastModificationTime($reflectionTrait);
|
|
||||||
}, $class->getTraits()),
|
|
||||||
array_map(function (ReflectionClass $class): int {
|
|
||||||
return $this->getLastModification($class);
|
|
||||||
}, $class->getInterfaces()),
|
|
||||||
$parent ? [$this->getLastModification($parent)] : []
|
|
||||||
));
|
|
||||||
|
|
||||||
assert($lastModification !== false);
|
|
||||||
|
|
||||||
return $this->loadedFilemtimes[$filename] = $lastModification;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getTraitLastModificationTime(ReflectionClass $reflectionTrait): int
|
|
||||||
{
|
|
||||||
$fileName = $reflectionTrait->getFileName();
|
|
||||||
|
|
||||||
if (isset($this->loadedFilemtimes[$fileName])) {
|
|
||||||
return $this->loadedFilemtimes[$fileName];
|
|
||||||
}
|
|
||||||
|
|
||||||
$lastModificationTime = max(array_merge(
|
|
||||||
[$fileName ? filemtime($fileName) : 0],
|
|
||||||
array_map(function (ReflectionClass $reflectionTrait): int {
|
|
||||||
return $this->getTraitLastModificationTime($reflectionTrait);
|
|
||||||
}, $reflectionTrait->getTraits())
|
|
||||||
));
|
|
||||||
|
|
||||||
assert($lastModificationTime !== false);
|
|
||||||
|
|
||||||
return $this->loadedFilemtimes[$fileName] = $lastModificationTime;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,143 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Doctrine\Common\Annotations;
|
|
||||||
|
|
||||||
use Doctrine\Common\Lexer\AbstractLexer;
|
|
||||||
|
|
||||||
use function ctype_alpha;
|
|
||||||
use function is_numeric;
|
|
||||||
use function str_replace;
|
|
||||||
use function stripos;
|
|
||||||
use function strlen;
|
|
||||||
use function strpos;
|
|
||||||
use function strtolower;
|
|
||||||
use function substr;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Simple lexer for docblock annotations.
|
|
||||||
*
|
|
||||||
* @template-extends AbstractLexer<DocLexer::T_*>
|
|
||||||
*/
|
|
||||||
final class DocLexer extends AbstractLexer
|
|
||||||
{
|
|
||||||
public const T_NONE = 1;
|
|
||||||
public const T_INTEGER = 2;
|
|
||||||
public const T_STRING = 3;
|
|
||||||
public const T_FLOAT = 4;
|
|
||||||
|
|
||||||
// All tokens that are also identifiers should be >= 100
|
|
||||||
public const T_IDENTIFIER = 100;
|
|
||||||
public const T_AT = 101;
|
|
||||||
public const T_CLOSE_CURLY_BRACES = 102;
|
|
||||||
public const T_CLOSE_PARENTHESIS = 103;
|
|
||||||
public const T_COMMA = 104;
|
|
||||||
public const T_EQUALS = 105;
|
|
||||||
public const T_FALSE = 106;
|
|
||||||
public const T_NAMESPACE_SEPARATOR = 107;
|
|
||||||
public const T_OPEN_CURLY_BRACES = 108;
|
|
||||||
public const T_OPEN_PARENTHESIS = 109;
|
|
||||||
public const T_TRUE = 110;
|
|
||||||
public const T_NULL = 111;
|
|
||||||
public const T_COLON = 112;
|
|
||||||
public const T_MINUS = 113;
|
|
||||||
|
|
||||||
/** @var array<string, self::T*> */
|
|
||||||
protected $noCase = [
|
|
||||||
'@' => self::T_AT,
|
|
||||||
',' => self::T_COMMA,
|
|
||||||
'(' => self::T_OPEN_PARENTHESIS,
|
|
||||||
')' => self::T_CLOSE_PARENTHESIS,
|
|
||||||
'{' => self::T_OPEN_CURLY_BRACES,
|
|
||||||
'}' => self::T_CLOSE_CURLY_BRACES,
|
|
||||||
'=' => self::T_EQUALS,
|
|
||||||
':' => self::T_COLON,
|
|
||||||
'-' => self::T_MINUS,
|
|
||||||
'\\' => self::T_NAMESPACE_SEPARATOR,
|
|
||||||
];
|
|
||||||
|
|
||||||
/** @var array<string, self::T*> */
|
|
||||||
protected $withCase = [
|
|
||||||
'true' => self::T_TRUE,
|
|
||||||
'false' => self::T_FALSE,
|
|
||||||
'null' => self::T_NULL,
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether the next token starts immediately, or if there were
|
|
||||||
* non-captured symbols before that
|
|
||||||
*/
|
|
||||||
public function nextTokenIsAdjacent(): bool
|
|
||||||
{
|
|
||||||
return $this->token === null
|
|
||||||
|| ($this->lookahead !== null
|
|
||||||
&& ($this->lookahead['position'] - $this->token['position']) === strlen($this->token['value']));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
protected function getCatchablePatterns()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'[a-z_\\\][a-z0-9_\:\\\]*[a-z_][a-z0-9_]*',
|
|
||||||
'(?:[+-]?[0-9]+(?:[\.][0-9]+)*)(?:[eE][+-]?[0-9]+)?',
|
|
||||||
'"(?:""|[^"])*+"',
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
protected function getNonCatchablePatterns()
|
|
||||||
{
|
|
||||||
return ['\s+', '\*+', '(.)'];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
protected function getType(&$value)
|
|
||||||
{
|
|
||||||
$type = self::T_NONE;
|
|
||||||
|
|
||||||
if ($value[0] === '"') {
|
|
||||||
$value = str_replace('""', '"', substr($value, 1, strlen($value) - 2));
|
|
||||||
|
|
||||||
return self::T_STRING;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($this->noCase[$value])) {
|
|
||||||
return $this->noCase[$value];
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($value[0] === '_' || $value[0] === '\\' || ctype_alpha($value[0])) {
|
|
||||||
return self::T_IDENTIFIER;
|
|
||||||
}
|
|
||||||
|
|
||||||
$lowerValue = strtolower($value);
|
|
||||||
|
|
||||||
if (isset($this->withCase[$lowerValue])) {
|
|
||||||
return $this->withCase[$lowerValue];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Checking numeric value
|
|
||||||
if (is_numeric($value)) {
|
|
||||||
return strpos($value, '.') !== false || stripos($value, 'e') !== false
|
|
||||||
? self::T_FLOAT : self::T_INTEGER;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $type;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @return array{value: int|string, type:self::T_*|null, position:int} */
|
|
||||||
public function peek(): ?array
|
|
||||||
{
|
|
||||||
$token = parent::peek();
|
|
||||||
|
|
||||||
if ($token === null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (array) $token;
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -1,315 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Doctrine\Common\Annotations;
|
|
||||||
|
|
||||||
use InvalidArgumentException;
|
|
||||||
use ReflectionClass;
|
|
||||||
use ReflectionMethod;
|
|
||||||
use ReflectionProperty;
|
|
||||||
use RuntimeException;
|
|
||||||
|
|
||||||
use function chmod;
|
|
||||||
use function file_put_contents;
|
|
||||||
use function filemtime;
|
|
||||||
use function gettype;
|
|
||||||
use function is_dir;
|
|
||||||
use function is_file;
|
|
||||||
use function is_int;
|
|
||||||
use function is_writable;
|
|
||||||
use function mkdir;
|
|
||||||
use function rename;
|
|
||||||
use function rtrim;
|
|
||||||
use function serialize;
|
|
||||||
use function sha1;
|
|
||||||
use function sprintf;
|
|
||||||
use function strtr;
|
|
||||||
use function tempnam;
|
|
||||||
use function uniqid;
|
|
||||||
use function unlink;
|
|
||||||
use function var_export;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* File cache reader for annotations.
|
|
||||||
*
|
|
||||||
* @deprecated the FileCacheReader is deprecated and will be removed
|
|
||||||
* in version 2.0.0 of doctrine/annotations. Please use the
|
|
||||||
* {@see \Doctrine\Common\Annotations\PsrCachedReader} instead.
|
|
||||||
*/
|
|
||||||
class FileCacheReader implements Reader
|
|
||||||
{
|
|
||||||
/** @var Reader */
|
|
||||||
private $reader;
|
|
||||||
|
|
||||||
/** @var string */
|
|
||||||
private $dir;
|
|
||||||
|
|
||||||
/** @var bool */
|
|
||||||
private $debug;
|
|
||||||
|
|
||||||
/** @phpstan-var array<string, list<object>> */
|
|
||||||
private $loadedAnnotations = [];
|
|
||||||
|
|
||||||
/** @var array<string, string> */
|
|
||||||
private $classNameHashes = [];
|
|
||||||
|
|
||||||
/** @var int */
|
|
||||||
private $umask;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $cacheDir
|
|
||||||
* @param bool $debug
|
|
||||||
* @param int $umask
|
|
||||||
*
|
|
||||||
* @throws InvalidArgumentException
|
|
||||||
*/
|
|
||||||
public function __construct(Reader $reader, $cacheDir, $debug = false, $umask = 0002)
|
|
||||||
{
|
|
||||||
if (! is_int($umask)) {
|
|
||||||
throw new InvalidArgumentException(sprintf(
|
|
||||||
'The parameter umask must be an integer, was: %s',
|
|
||||||
gettype($umask)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->reader = $reader;
|
|
||||||
$this->umask = $umask;
|
|
||||||
|
|
||||||
if (! is_dir($cacheDir) && ! @mkdir($cacheDir, 0777 & (~$this->umask), true)) {
|
|
||||||
throw new InvalidArgumentException(sprintf(
|
|
||||||
'The directory "%s" does not exist and could not be created.',
|
|
||||||
$cacheDir
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->dir = rtrim($cacheDir, '\\/');
|
|
||||||
$this->debug = $debug;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public function getClassAnnotations(ReflectionClass $class)
|
|
||||||
{
|
|
||||||
if (! isset($this->classNameHashes[$class->name])) {
|
|
||||||
$this->classNameHashes[$class->name] = sha1($class->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
$key = $this->classNameHashes[$class->name];
|
|
||||||
|
|
||||||
if (isset($this->loadedAnnotations[$key])) {
|
|
||||||
return $this->loadedAnnotations[$key];
|
|
||||||
}
|
|
||||||
|
|
||||||
$path = $this->dir . '/' . strtr($key, '\\', '-') . '.cache.php';
|
|
||||||
if (! is_file($path)) {
|
|
||||||
$annot = $this->reader->getClassAnnotations($class);
|
|
||||||
$this->saveCacheFile($path, $annot);
|
|
||||||
|
|
||||||
return $this->loadedAnnotations[$key] = $annot;
|
|
||||||
}
|
|
||||||
|
|
||||||
$filename = $class->getFilename();
|
|
||||||
if (
|
|
||||||
$this->debug
|
|
||||||
&& $filename !== false
|
|
||||||
&& filemtime($path) < filemtime($filename)
|
|
||||||
) {
|
|
||||||
@unlink($path);
|
|
||||||
|
|
||||||
$annot = $this->reader->getClassAnnotations($class);
|
|
||||||
$this->saveCacheFile($path, $annot);
|
|
||||||
|
|
||||||
return $this->loadedAnnotations[$key] = $annot;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->loadedAnnotations[$key] = include $path;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public function getPropertyAnnotations(ReflectionProperty $property)
|
|
||||||
{
|
|
||||||
$class = $property->getDeclaringClass();
|
|
||||||
if (! isset($this->classNameHashes[$class->name])) {
|
|
||||||
$this->classNameHashes[$class->name] = sha1($class->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
$key = $this->classNameHashes[$class->name] . '$' . $property->getName();
|
|
||||||
|
|
||||||
if (isset($this->loadedAnnotations[$key])) {
|
|
||||||
return $this->loadedAnnotations[$key];
|
|
||||||
}
|
|
||||||
|
|
||||||
$path = $this->dir . '/' . strtr($key, '\\', '-') . '.cache.php';
|
|
||||||
if (! is_file($path)) {
|
|
||||||
$annot = $this->reader->getPropertyAnnotations($property);
|
|
||||||
$this->saveCacheFile($path, $annot);
|
|
||||||
|
|
||||||
return $this->loadedAnnotations[$key] = $annot;
|
|
||||||
}
|
|
||||||
|
|
||||||
$filename = $class->getFilename();
|
|
||||||
if (
|
|
||||||
$this->debug
|
|
||||||
&& $filename !== false
|
|
||||||
&& filemtime($path) < filemtime($filename)
|
|
||||||
) {
|
|
||||||
@unlink($path);
|
|
||||||
|
|
||||||
$annot = $this->reader->getPropertyAnnotations($property);
|
|
||||||
$this->saveCacheFile($path, $annot);
|
|
||||||
|
|
||||||
return $this->loadedAnnotations[$key] = $annot;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->loadedAnnotations[$key] = include $path;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public function getMethodAnnotations(ReflectionMethod $method)
|
|
||||||
{
|
|
||||||
$class = $method->getDeclaringClass();
|
|
||||||
if (! isset($this->classNameHashes[$class->name])) {
|
|
||||||
$this->classNameHashes[$class->name] = sha1($class->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
$key = $this->classNameHashes[$class->name] . '#' . $method->getName();
|
|
||||||
|
|
||||||
if (isset($this->loadedAnnotations[$key])) {
|
|
||||||
return $this->loadedAnnotations[$key];
|
|
||||||
}
|
|
||||||
|
|
||||||
$path = $this->dir . '/' . strtr($key, '\\', '-') . '.cache.php';
|
|
||||||
if (! is_file($path)) {
|
|
||||||
$annot = $this->reader->getMethodAnnotations($method);
|
|
||||||
$this->saveCacheFile($path, $annot);
|
|
||||||
|
|
||||||
return $this->loadedAnnotations[$key] = $annot;
|
|
||||||
}
|
|
||||||
|
|
||||||
$filename = $class->getFilename();
|
|
||||||
if (
|
|
||||||
$this->debug
|
|
||||||
&& $filename !== false
|
|
||||||
&& filemtime($path) < filemtime($filename)
|
|
||||||
) {
|
|
||||||
@unlink($path);
|
|
||||||
|
|
||||||
$annot = $this->reader->getMethodAnnotations($method);
|
|
||||||
$this->saveCacheFile($path, $annot);
|
|
||||||
|
|
||||||
return $this->loadedAnnotations[$key] = $annot;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->loadedAnnotations[$key] = include $path;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Saves the cache file.
|
|
||||||
*
|
|
||||||
* @param string $path
|
|
||||||
* @param mixed $data
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
private function saveCacheFile($path, $data)
|
|
||||||
{
|
|
||||||
if (! is_writable($this->dir)) {
|
|
||||||
throw new InvalidArgumentException(sprintf(
|
|
||||||
<<<'EXCEPTION'
|
|
||||||
The directory "%s" is not writable. Both the webserver and the console user need access.
|
|
||||||
You can manage access rights for multiple users with "chmod +a".
|
|
||||||
If your system does not support this, check out the acl package.,
|
|
||||||
EXCEPTION
|
|
||||||
,
|
|
||||||
$this->dir
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
$tempfile = tempnam($this->dir, uniqid('', true));
|
|
||||||
|
|
||||||
if ($tempfile === false) {
|
|
||||||
throw new RuntimeException(sprintf('Unable to create tempfile in directory: %s', $this->dir));
|
|
||||||
}
|
|
||||||
|
|
||||||
@chmod($tempfile, 0666 & (~$this->umask));
|
|
||||||
|
|
||||||
$written = file_put_contents(
|
|
||||||
$tempfile,
|
|
||||||
'<?php return unserialize(' . var_export(serialize($data), true) . ');'
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($written === false) {
|
|
||||||
throw new RuntimeException(sprintf('Unable to write cached file to: %s', $tempfile));
|
|
||||||
}
|
|
||||||
|
|
||||||
@chmod($tempfile, 0666 & (~$this->umask));
|
|
||||||
|
|
||||||
if (rename($tempfile, $path) === false) {
|
|
||||||
@unlink($tempfile);
|
|
||||||
|
|
||||||
throw new RuntimeException(sprintf('Unable to rename %s to %s', $tempfile, $path));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public function getClassAnnotation(ReflectionClass $class, $annotationName)
|
|
||||||
{
|
|
||||||
$annotations = $this->getClassAnnotations($class);
|
|
||||||
|
|
||||||
foreach ($annotations as $annotation) {
|
|
||||||
if ($annotation instanceof $annotationName) {
|
|
||||||
return $annotation;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public function getMethodAnnotation(ReflectionMethod $method, $annotationName)
|
|
||||||
{
|
|
||||||
$annotations = $this->getMethodAnnotations($method);
|
|
||||||
|
|
||||||
foreach ($annotations as $annotation) {
|
|
||||||
if ($annotation instanceof $annotationName) {
|
|
||||||
return $annotation;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public function getPropertyAnnotation(ReflectionProperty $property, $annotationName)
|
|
||||||
{
|
|
||||||
$annotations = $this->getPropertyAnnotations($property);
|
|
||||||
|
|
||||||
foreach ($annotations as $annotation) {
|
|
||||||
if ($annotation instanceof $annotationName) {
|
|
||||||
return $annotation;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clears loaded annotations.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function clearLoadedAnnotations()
|
|
||||||
{
|
|
||||||
$this->loadedAnnotations = [];
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,178 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Doctrine\Common\Annotations;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A list of annotations that are implicitly ignored during the parsing process.
|
|
||||||
*
|
|
||||||
* All names are case sensitive.
|
|
||||||
*/
|
|
||||||
final class ImplicitlyIgnoredAnnotationNames
|
|
||||||
{
|
|
||||||
private const Reserved = [
|
|
||||||
'Annotation' => true,
|
|
||||||
'Attribute' => true,
|
|
||||||
'Attributes' => true,
|
|
||||||
/* Can we enable this? 'Enum' => true, */
|
|
||||||
'Required' => true,
|
|
||||||
'Target' => true,
|
|
||||||
'NamedArgumentConstructor' => true,
|
|
||||||
];
|
|
||||||
|
|
||||||
private const WidelyUsedNonStandard = [
|
|
||||||
'fix' => true,
|
|
||||||
'fixme' => true,
|
|
||||||
'override' => true,
|
|
||||||
];
|
|
||||||
|
|
||||||
private const PhpDocumentor1 = [
|
|
||||||
'abstract' => true,
|
|
||||||
'access' => true,
|
|
||||||
'code' => true,
|
|
||||||
'deprec' => true,
|
|
||||||
'endcode' => true,
|
|
||||||
'exception' => true,
|
|
||||||
'final' => true,
|
|
||||||
'ingroup' => true,
|
|
||||||
'inheritdoc' => true,
|
|
||||||
'inheritDoc' => true,
|
|
||||||
'magic' => true,
|
|
||||||
'name' => true,
|
|
||||||
'private' => true,
|
|
||||||
'static' => true,
|
|
||||||
'staticvar' => true,
|
|
||||||
'staticVar' => true,
|
|
||||||
'toc' => true,
|
|
||||||
'tutorial' => true,
|
|
||||||
'throw' => true,
|
|
||||||
];
|
|
||||||
|
|
||||||
private const PhpDocumentor2 = [
|
|
||||||
'api' => true,
|
|
||||||
'author' => true,
|
|
||||||
'category' => true,
|
|
||||||
'copyright' => true,
|
|
||||||
'deprecated' => true,
|
|
||||||
'example' => true,
|
|
||||||
'filesource' => true,
|
|
||||||
'global' => true,
|
|
||||||
'ignore' => true,
|
|
||||||
/* Can we enable this? 'index' => true, */
|
|
||||||
'internal' => true,
|
|
||||||
'license' => true,
|
|
||||||
'link' => true,
|
|
||||||
'method' => true,
|
|
||||||
'package' => true,
|
|
||||||
'param' => true,
|
|
||||||
'property' => true,
|
|
||||||
'property-read' => true,
|
|
||||||
'property-write' => true,
|
|
||||||
'return' => true,
|
|
||||||
'see' => true,
|
|
||||||
'since' => true,
|
|
||||||
'source' => true,
|
|
||||||
'subpackage' => true,
|
|
||||||
'throws' => true,
|
|
||||||
'todo' => true,
|
|
||||||
'TODO' => true,
|
|
||||||
'usedby' => true,
|
|
||||||
'uses' => true,
|
|
||||||
'var' => true,
|
|
||||||
'version' => true,
|
|
||||||
];
|
|
||||||
|
|
||||||
private const PHPUnit = [
|
|
||||||
'author' => true,
|
|
||||||
'after' => true,
|
|
||||||
'afterClass' => true,
|
|
||||||
'backupGlobals' => true,
|
|
||||||
'backupStaticAttributes' => true,
|
|
||||||
'before' => true,
|
|
||||||
'beforeClass' => true,
|
|
||||||
'codeCoverageIgnore' => true,
|
|
||||||
'codeCoverageIgnoreStart' => true,
|
|
||||||
'codeCoverageIgnoreEnd' => true,
|
|
||||||
'covers' => true,
|
|
||||||
'coversDefaultClass' => true,
|
|
||||||
'coversNothing' => true,
|
|
||||||
'dataProvider' => true,
|
|
||||||
'depends' => true,
|
|
||||||
'doesNotPerformAssertions' => true,
|
|
||||||
'expectedException' => true,
|
|
||||||
'expectedExceptionCode' => true,
|
|
||||||
'expectedExceptionMessage' => true,
|
|
||||||
'expectedExceptionMessageRegExp' => true,
|
|
||||||
'group' => true,
|
|
||||||
'large' => true,
|
|
||||||
'medium' => true,
|
|
||||||
'preserveGlobalState' => true,
|
|
||||||
'requires' => true,
|
|
||||||
'runTestsInSeparateProcesses' => true,
|
|
||||||
'runInSeparateProcess' => true,
|
|
||||||
'small' => true,
|
|
||||||
'test' => true,
|
|
||||||
'testdox' => true,
|
|
||||||
'testWith' => true,
|
|
||||||
'ticket' => true,
|
|
||||||
'uses' => true,
|
|
||||||
];
|
|
||||||
|
|
||||||
private const PhpCheckStyle = ['SuppressWarnings' => true];
|
|
||||||
|
|
||||||
private const PhpStorm = ['noinspection' => true];
|
|
||||||
|
|
||||||
private const PEAR = ['package_version' => true];
|
|
||||||
|
|
||||||
private const PlainUML = [
|
|
||||||
'startuml' => true,
|
|
||||||
'enduml' => true,
|
|
||||||
];
|
|
||||||
|
|
||||||
private const Symfony = ['experimental' => true];
|
|
||||||
|
|
||||||
private const PhpCodeSniffer = [
|
|
||||||
'codingStandardsIgnoreStart' => true,
|
|
||||||
'codingStandardsIgnoreEnd' => true,
|
|
||||||
];
|
|
||||||
|
|
||||||
private const SlevomatCodingStandard = ['phpcsSuppress' => true];
|
|
||||||
|
|
||||||
private const Phan = ['suppress' => true];
|
|
||||||
|
|
||||||
private const Rector = ['noRector' => true];
|
|
||||||
|
|
||||||
private const StaticAnalysis = [
|
|
||||||
// PHPStan, Psalm
|
|
||||||
'extends' => true,
|
|
||||||
'implements' => true,
|
|
||||||
'readonly' => true,
|
|
||||||
'template' => true,
|
|
||||||
'use' => true,
|
|
||||||
|
|
||||||
// Psalm
|
|
||||||
'pure' => true,
|
|
||||||
'immutable' => true,
|
|
||||||
];
|
|
||||||
|
|
||||||
public const LIST = self::Reserved
|
|
||||||
+ self::WidelyUsedNonStandard
|
|
||||||
+ self::PhpDocumentor1
|
|
||||||
+ self::PhpDocumentor2
|
|
||||||
+ self::PHPUnit
|
|
||||||
+ self::PhpCheckStyle
|
|
||||||
+ self::PhpStorm
|
|
||||||
+ self::PEAR
|
|
||||||
+ self::PlainUML
|
|
||||||
+ self::Symfony
|
|
||||||
+ self::SlevomatCodingStandard
|
|
||||||
+ self::PhpCodeSniffer
|
|
||||||
+ self::Phan
|
|
||||||
+ self::Rector
|
|
||||||
+ self::StaticAnalysis;
|
|
||||||
|
|
||||||
private function __construct()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,100 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Doctrine\Common\Annotations;
|
|
||||||
|
|
||||||
use ReflectionClass;
|
|
||||||
use ReflectionMethod;
|
|
||||||
use ReflectionProperty;
|
|
||||||
|
|
||||||
use function call_user_func_array;
|
|
||||||
use function get_class;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Allows the reader to be used in-place of Doctrine's reader.
|
|
||||||
*/
|
|
||||||
class IndexedReader implements Reader
|
|
||||||
{
|
|
||||||
/** @var Reader */
|
|
||||||
private $delegate;
|
|
||||||
|
|
||||||
public function __construct(Reader $reader)
|
|
||||||
{
|
|
||||||
$this->delegate = $reader;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public function getClassAnnotations(ReflectionClass $class)
|
|
||||||
{
|
|
||||||
$annotations = [];
|
|
||||||
foreach ($this->delegate->getClassAnnotations($class) as $annot) {
|
|
||||||
$annotations[get_class($annot)] = $annot;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $annotations;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public function getClassAnnotation(ReflectionClass $class, $annotationName)
|
|
||||||
{
|
|
||||||
return $this->delegate->getClassAnnotation($class, $annotationName);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public function getMethodAnnotations(ReflectionMethod $method)
|
|
||||||
{
|
|
||||||
$annotations = [];
|
|
||||||
foreach ($this->delegate->getMethodAnnotations($method) as $annot) {
|
|
||||||
$annotations[get_class($annot)] = $annot;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $annotations;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public function getMethodAnnotation(ReflectionMethod $method, $annotationName)
|
|
||||||
{
|
|
||||||
return $this->delegate->getMethodAnnotation($method, $annotationName);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public function getPropertyAnnotations(ReflectionProperty $property)
|
|
||||||
{
|
|
||||||
$annotations = [];
|
|
||||||
foreach ($this->delegate->getPropertyAnnotations($property) as $annot) {
|
|
||||||
$annotations[get_class($annot)] = $annot;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $annotations;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public function getPropertyAnnotation(ReflectionProperty $property, $annotationName)
|
|
||||||
{
|
|
||||||
return $this->delegate->getPropertyAnnotation($property, $annotationName);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Proxies all methods to the delegate.
|
|
||||||
*
|
|
||||||
* @param string $method
|
|
||||||
* @param mixed[] $args
|
|
||||||
*
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function __call($method, $args)
|
|
||||||
{
|
|
||||||
return call_user_func_array([$this->delegate, $method], $args);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Doctrine\Common\Annotations;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Marker interface for PHP7/PHP8 compatible support
|
|
||||||
* for named arguments (and constructor property promotion).
|
|
||||||
*
|
|
||||||
* @deprecated Implementing this interface is deprecated
|
|
||||||
* Use the Annotation @NamedArgumentConstructor instead
|
|
||||||
*/
|
|
||||||
interface NamedArgumentConstructorAnnotation
|
|
||||||
{
|
|
||||||
}
|
|
@ -1,92 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Doctrine\Common\Annotations;
|
|
||||||
|
|
||||||
use ReflectionClass;
|
|
||||||
use ReflectionFunction;
|
|
||||||
use SplFileObject;
|
|
||||||
|
|
||||||
use function is_file;
|
|
||||||
use function method_exists;
|
|
||||||
use function preg_quote;
|
|
||||||
use function preg_replace;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses a file for namespaces/use/class declarations.
|
|
||||||
*/
|
|
||||||
final class PhpParser
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Parses a class.
|
|
||||||
*
|
|
||||||
* @deprecated use parseUseStatements instead
|
|
||||||
*
|
|
||||||
* @param ReflectionClass $class A <code>ReflectionClass</code> object.
|
|
||||||
*
|
|
||||||
* @return array<string, class-string> A list with use statements in the form (Alias => FQN).
|
|
||||||
*/
|
|
||||||
public function parseClass(ReflectionClass $class)
|
|
||||||
{
|
|
||||||
return $this->parseUseStatements($class);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse a class or function for use statements.
|
|
||||||
*
|
|
||||||
* @param ReflectionClass|ReflectionFunction $reflection
|
|
||||||
*
|
|
||||||
* @psalm-return array<string, string> a list with use statements in the form (Alias => FQN).
|
|
||||||
*/
|
|
||||||
public function parseUseStatements($reflection): array
|
|
||||||
{
|
|
||||||
if (method_exists($reflection, 'getUseStatements')) {
|
|
||||||
return $reflection->getUseStatements();
|
|
||||||
}
|
|
||||||
|
|
||||||
$filename = $reflection->getFileName();
|
|
||||||
|
|
||||||
if ($filename === false) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
$content = $this->getFileContent($filename, $reflection->getStartLine());
|
|
||||||
|
|
||||||
if ($content === null) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
$namespace = preg_quote($reflection->getNamespaceName());
|
|
||||||
$content = preg_replace('/^.*?(\bnamespace\s+' . $namespace . '\s*[;{].*)$/s', '\\1', $content);
|
|
||||||
$tokenizer = new TokenParser('<?php ' . $content);
|
|
||||||
|
|
||||||
return $tokenizer->parseUseStatements($reflection->getNamespaceName());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the content of the file right up to the given line number.
|
|
||||||
*
|
|
||||||
* @param string $filename The name of the file to load.
|
|
||||||
* @param int $lineNumber The number of lines to read from file.
|
|
||||||
*
|
|
||||||
* @return string|null The content of the file or null if the file does not exist.
|
|
||||||
*/
|
|
||||||
private function getFileContent($filename, $lineNumber)
|
|
||||||
{
|
|
||||||
if (! is_file($filename)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$content = '';
|
|
||||||
$lineCnt = 0;
|
|
||||||
$file = new SplFileObject($filename);
|
|
||||||
while (! $file->eof()) {
|
|
||||||
if ($lineCnt++ === $lineNumber) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
$content .= $file->fgets();
|
|
||||||
}
|
|
||||||
|
|
||||||
return $content;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,232 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Doctrine\Common\Annotations;
|
|
||||||
|
|
||||||
use Psr\Cache\CacheItemPoolInterface;
|
|
||||||
use ReflectionClass;
|
|
||||||
use ReflectionMethod;
|
|
||||||
use ReflectionProperty;
|
|
||||||
use Reflector;
|
|
||||||
|
|
||||||
use function array_map;
|
|
||||||
use function array_merge;
|
|
||||||
use function assert;
|
|
||||||
use function filemtime;
|
|
||||||
use function max;
|
|
||||||
use function rawurlencode;
|
|
||||||
use function time;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A cache aware annotation reader.
|
|
||||||
*/
|
|
||||||
final class PsrCachedReader implements Reader
|
|
||||||
{
|
|
||||||
/** @var Reader */
|
|
||||||
private $delegate;
|
|
||||||
|
|
||||||
/** @var CacheItemPoolInterface */
|
|
||||||
private $cache;
|
|
||||||
|
|
||||||
/** @var bool */
|
|
||||||
private $debug;
|
|
||||||
|
|
||||||
/** @var array<string, array<object>> */
|
|
||||||
private $loadedAnnotations = [];
|
|
||||||
|
|
||||||
/** @var int[] */
|
|
||||||
private $loadedFilemtimes = [];
|
|
||||||
|
|
||||||
public function __construct(Reader $reader, CacheItemPoolInterface $cache, bool $debug = false)
|
|
||||||
{
|
|
||||||
$this->delegate = $reader;
|
|
||||||
$this->cache = $cache;
|
|
||||||
$this->debug = (bool) $debug;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public function getClassAnnotations(ReflectionClass $class)
|
|
||||||
{
|
|
||||||
$cacheKey = $class->getName();
|
|
||||||
|
|
||||||
if (isset($this->loadedAnnotations[$cacheKey])) {
|
|
||||||
return $this->loadedAnnotations[$cacheKey];
|
|
||||||
}
|
|
||||||
|
|
||||||
$annots = $this->fetchFromCache($cacheKey, $class, 'getClassAnnotations', $class);
|
|
||||||
|
|
||||||
return $this->loadedAnnotations[$cacheKey] = $annots;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public function getClassAnnotation(ReflectionClass $class, $annotationName)
|
|
||||||
{
|
|
||||||
foreach ($this->getClassAnnotations($class) as $annot) {
|
|
||||||
if ($annot instanceof $annotationName) {
|
|
||||||
return $annot;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public function getPropertyAnnotations(ReflectionProperty $property)
|
|
||||||
{
|
|
||||||
$class = $property->getDeclaringClass();
|
|
||||||
$cacheKey = $class->getName() . '$' . $property->getName();
|
|
||||||
|
|
||||||
if (isset($this->loadedAnnotations[$cacheKey])) {
|
|
||||||
return $this->loadedAnnotations[$cacheKey];
|
|
||||||
}
|
|
||||||
|
|
||||||
$annots = $this->fetchFromCache($cacheKey, $class, 'getPropertyAnnotations', $property);
|
|
||||||
|
|
||||||
return $this->loadedAnnotations[$cacheKey] = $annots;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public function getPropertyAnnotation(ReflectionProperty $property, $annotationName)
|
|
||||||
{
|
|
||||||
foreach ($this->getPropertyAnnotations($property) as $annot) {
|
|
||||||
if ($annot instanceof $annotationName) {
|
|
||||||
return $annot;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public function getMethodAnnotations(ReflectionMethod $method)
|
|
||||||
{
|
|
||||||
$class = $method->getDeclaringClass();
|
|
||||||
$cacheKey = $class->getName() . '#' . $method->getName();
|
|
||||||
|
|
||||||
if (isset($this->loadedAnnotations[$cacheKey])) {
|
|
||||||
return $this->loadedAnnotations[$cacheKey];
|
|
||||||
}
|
|
||||||
|
|
||||||
$annots = $this->fetchFromCache($cacheKey, $class, 'getMethodAnnotations', $method);
|
|
||||||
|
|
||||||
return $this->loadedAnnotations[$cacheKey] = $annots;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public function getMethodAnnotation(ReflectionMethod $method, $annotationName)
|
|
||||||
{
|
|
||||||
foreach ($this->getMethodAnnotations($method) as $annot) {
|
|
||||||
if ($annot instanceof $annotationName) {
|
|
||||||
return $annot;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function clearLoadedAnnotations(): void
|
|
||||||
{
|
|
||||||
$this->loadedAnnotations = [];
|
|
||||||
$this->loadedFilemtimes = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @return mixed[] */
|
|
||||||
private function fetchFromCache(
|
|
||||||
string $cacheKey,
|
|
||||||
ReflectionClass $class,
|
|
||||||
string $method,
|
|
||||||
Reflector $reflector
|
|
||||||
): array {
|
|
||||||
$cacheKey = rawurlencode($cacheKey);
|
|
||||||
|
|
||||||
$item = $this->cache->getItem($cacheKey);
|
|
||||||
if (($this->debug && ! $this->refresh($cacheKey, $class)) || ! $item->isHit()) {
|
|
||||||
$this->cache->save($item->set($this->delegate->{$method}($reflector)));
|
|
||||||
}
|
|
||||||
|
|
||||||
return $item->get();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used in debug mode to check if the cache is fresh.
|
|
||||||
*
|
|
||||||
* @return bool Returns true if the cache was fresh, or false if the class
|
|
||||||
* being read was modified since writing to the cache.
|
|
||||||
*/
|
|
||||||
private function refresh(string $cacheKey, ReflectionClass $class): bool
|
|
||||||
{
|
|
||||||
$lastModification = $this->getLastModification($class);
|
|
||||||
if ($lastModification === 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
$item = $this->cache->getItem('[C]' . $cacheKey);
|
|
||||||
if ($item->isHit() && $item->get() >= $lastModification) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->cache->save($item->set(time()));
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the time the class was last modified, testing traits and parents
|
|
||||||
*/
|
|
||||||
private function getLastModification(ReflectionClass $class): int
|
|
||||||
{
|
|
||||||
$filename = $class->getFileName();
|
|
||||||
|
|
||||||
if (isset($this->loadedFilemtimes[$filename])) {
|
|
||||||
return $this->loadedFilemtimes[$filename];
|
|
||||||
}
|
|
||||||
|
|
||||||
$parent = $class->getParentClass();
|
|
||||||
|
|
||||||
$lastModification = max(array_merge(
|
|
||||||
[$filename ? filemtime($filename) : 0],
|
|
||||||
array_map(function (ReflectionClass $reflectionTrait): int {
|
|
||||||
return $this->getTraitLastModificationTime($reflectionTrait);
|
|
||||||
}, $class->getTraits()),
|
|
||||||
array_map(function (ReflectionClass $class): int {
|
|
||||||
return $this->getLastModification($class);
|
|
||||||
}, $class->getInterfaces()),
|
|
||||||
$parent ? [$this->getLastModification($parent)] : []
|
|
||||||
));
|
|
||||||
|
|
||||||
assert($lastModification !== false);
|
|
||||||
|
|
||||||
return $this->loadedFilemtimes[$filename] = $lastModification;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getTraitLastModificationTime(ReflectionClass $reflectionTrait): int
|
|
||||||
{
|
|
||||||
$fileName = $reflectionTrait->getFileName();
|
|
||||||
|
|
||||||
if (isset($this->loadedFilemtimes[$fileName])) {
|
|
||||||
return $this->loadedFilemtimes[$fileName];
|
|
||||||
}
|
|
||||||
|
|
||||||
$lastModificationTime = max(array_merge(
|
|
||||||
[$fileName ? filemtime($fileName) : 0],
|
|
||||||
array_map(function (ReflectionClass $reflectionTrait): int {
|
|
||||||
return $this->getTraitLastModificationTime($reflectionTrait);
|
|
||||||
}, $reflectionTrait->getTraits())
|
|
||||||
));
|
|
||||||
|
|
||||||
assert($lastModificationTime !== false);
|
|
||||||
|
|
||||||
return $this->loadedFilemtimes[$fileName] = $lastModificationTime;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,80 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Doctrine\Common\Annotations;
|
|
||||||
|
|
||||||
use ReflectionClass;
|
|
||||||
use ReflectionMethod;
|
|
||||||
use ReflectionProperty;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interface for annotation readers.
|
|
||||||
*/
|
|
||||||
interface Reader
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Gets the annotations applied to a class.
|
|
||||||
*
|
|
||||||
* @param ReflectionClass $class The ReflectionClass of the class from which
|
|
||||||
* the class annotations should be read.
|
|
||||||
*
|
|
||||||
* @return array<object> An array of Annotations.
|
|
||||||
*/
|
|
||||||
public function getClassAnnotations(ReflectionClass $class);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a class annotation.
|
|
||||||
*
|
|
||||||
* @param ReflectionClass $class The ReflectionClass of the class from which
|
|
||||||
* the class annotations should be read.
|
|
||||||
* @param class-string<T> $annotationName The name of the annotation.
|
|
||||||
*
|
|
||||||
* @return T|null The Annotation or NULL, if the requested annotation does not exist.
|
|
||||||
*
|
|
||||||
* @template T
|
|
||||||
*/
|
|
||||||
public function getClassAnnotation(ReflectionClass $class, $annotationName);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the annotations applied to a method.
|
|
||||||
*
|
|
||||||
* @param ReflectionMethod $method The ReflectionMethod of the method from which
|
|
||||||
* the annotations should be read.
|
|
||||||
*
|
|
||||||
* @return array<object> An array of Annotations.
|
|
||||||
*/
|
|
||||||
public function getMethodAnnotations(ReflectionMethod $method);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a method annotation.
|
|
||||||
*
|
|
||||||
* @param ReflectionMethod $method The ReflectionMethod to read the annotations from.
|
|
||||||
* @param class-string<T> $annotationName The name of the annotation.
|
|
||||||
*
|
|
||||||
* @return T|null The Annotation or NULL, if the requested annotation does not exist.
|
|
||||||
*
|
|
||||||
* @template T
|
|
||||||
*/
|
|
||||||
public function getMethodAnnotation(ReflectionMethod $method, $annotationName);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the annotations applied to a property.
|
|
||||||
*
|
|
||||||
* @param ReflectionProperty $property The ReflectionProperty of the property
|
|
||||||
* from which the annotations should be read.
|
|
||||||
*
|
|
||||||
* @return array<object> An array of Annotations.
|
|
||||||
*/
|
|
||||||
public function getPropertyAnnotations(ReflectionProperty $property);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a property annotation.
|
|
||||||
*
|
|
||||||
* @param ReflectionProperty $property The ReflectionProperty to read the annotations from.
|
|
||||||
* @param class-string<T> $annotationName The name of the annotation.
|
|
||||||
*
|
|
||||||
* @return T|null The Annotation or NULL, if the requested annotation does not exist.
|
|
||||||
*
|
|
||||||
* @template T
|
|
||||||
*/
|
|
||||||
public function getPropertyAnnotation(ReflectionProperty $property, $annotationName);
|
|
||||||
}
|
|
@ -1,114 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Doctrine\Common\Annotations;
|
|
||||||
|
|
||||||
use ReflectionClass;
|
|
||||||
use ReflectionMethod;
|
|
||||||
use ReflectionProperty;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Simple Annotation Reader.
|
|
||||||
*
|
|
||||||
* This annotation reader is intended to be used in projects where you have
|
|
||||||
* full-control over all annotations that are available.
|
|
||||||
*
|
|
||||||
* @deprecated Deprecated in favour of using AnnotationReader
|
|
||||||
*/
|
|
||||||
class SimpleAnnotationReader implements Reader
|
|
||||||
{
|
|
||||||
/** @var DocParser */
|
|
||||||
private $parser;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes a new SimpleAnnotationReader.
|
|
||||||
*/
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
$this->parser = new DocParser();
|
|
||||||
$this->parser->setIgnoreNotImportedAnnotations(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a namespace in which we will look for annotations.
|
|
||||||
*
|
|
||||||
* @param string $namespace
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function addNamespace($namespace)
|
|
||||||
{
|
|
||||||
$this->parser->addNamespace($namespace);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public function getClassAnnotations(ReflectionClass $class)
|
|
||||||
{
|
|
||||||
return $this->parser->parse($class->getDocComment(), 'class ' . $class->getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public function getMethodAnnotations(ReflectionMethod $method)
|
|
||||||
{
|
|
||||||
return $this->parser->parse(
|
|
||||||
$method->getDocComment(),
|
|
||||||
'method ' . $method->getDeclaringClass()->name . '::' . $method->getName() . '()'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public function getPropertyAnnotations(ReflectionProperty $property)
|
|
||||||
{
|
|
||||||
return $this->parser->parse(
|
|
||||||
$property->getDocComment(),
|
|
||||||
'property ' . $property->getDeclaringClass()->name . '::$' . $property->getName()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public function getClassAnnotation(ReflectionClass $class, $annotationName)
|
|
||||||
{
|
|
||||||
foreach ($this->getClassAnnotations($class) as $annot) {
|
|
||||||
if ($annot instanceof $annotationName) {
|
|
||||||
return $annot;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public function getMethodAnnotation(ReflectionMethod $method, $annotationName)
|
|
||||||
{
|
|
||||||
foreach ($this->getMethodAnnotations($method) as $annot) {
|
|
||||||
if ($annot instanceof $annotationName) {
|
|
||||||
return $annot;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public function getPropertyAnnotation(ReflectionProperty $property, $annotationName)
|
|
||||||
{
|
|
||||||
foreach ($this->getPropertyAnnotations($property) as $annot) {
|
|
||||||
if ($annot instanceof $annotationName) {
|
|
||||||
return $annot;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,206 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Doctrine\Common\Annotations;
|
|
||||||
|
|
||||||
use function array_merge;
|
|
||||||
use function count;
|
|
||||||
use function explode;
|
|
||||||
use function strtolower;
|
|
||||||
use function token_get_all;
|
|
||||||
|
|
||||||
use const PHP_VERSION_ID;
|
|
||||||
use const T_AS;
|
|
||||||
use const T_COMMENT;
|
|
||||||
use const T_DOC_COMMENT;
|
|
||||||
use const T_NAME_FULLY_QUALIFIED;
|
|
||||||
use const T_NAME_QUALIFIED;
|
|
||||||
use const T_NAMESPACE;
|
|
||||||
use const T_NS_SEPARATOR;
|
|
||||||
use const T_STRING;
|
|
||||||
use const T_USE;
|
|
||||||
use const T_WHITESPACE;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses a file for namespaces/use/class declarations.
|
|
||||||
*/
|
|
||||||
class TokenParser
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The token list.
|
|
||||||
*
|
|
||||||
* @phpstan-var list<mixed[]>
|
|
||||||
*/
|
|
||||||
private $tokens;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The number of tokens.
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
private $numTokens;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The current array pointer.
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
private $pointer = 0;
|
|
||||||
|
|
||||||
/** @param string $contents */
|
|
||||||
public function __construct($contents)
|
|
||||||
{
|
|
||||||
$this->tokens = token_get_all($contents);
|
|
||||||
|
|
||||||
// The PHP parser sets internal compiler globals for certain things. Annoyingly, the last docblock comment it
|
|
||||||
// saw gets stored in doc_comment. When it comes to compile the next thing to be include()d this stored
|
|
||||||
// doc_comment becomes owned by the first thing the compiler sees in the file that it considers might have a
|
|
||||||
// docblock. If the first thing in the file is a class without a doc block this would cause calls to
|
|
||||||
// getDocBlock() on said class to return our long lost doc_comment. Argh.
|
|
||||||
// To workaround, cause the parser to parse an empty docblock. Sure getDocBlock() will return this, but at least
|
|
||||||
// it's harmless to us.
|
|
||||||
token_get_all("<?php\n/**\n *\n */");
|
|
||||||
|
|
||||||
$this->numTokens = count($this->tokens);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the next non whitespace and non comment token.
|
|
||||||
*
|
|
||||||
* @param bool $docCommentIsComment If TRUE then a doc comment is considered a comment and skipped.
|
|
||||||
* If FALSE then only whitespace and normal comments are skipped.
|
|
||||||
*
|
|
||||||
* @return mixed[]|string|null The token if exists, null otherwise.
|
|
||||||
*/
|
|
||||||
public function next($docCommentIsComment = true)
|
|
||||||
{
|
|
||||||
for ($i = $this->pointer; $i < $this->numTokens; $i++) {
|
|
||||||
$this->pointer++;
|
|
||||||
if (
|
|
||||||
$this->tokens[$i][0] === T_WHITESPACE ||
|
|
||||||
$this->tokens[$i][0] === T_COMMENT ||
|
|
||||||
($docCommentIsComment && $this->tokens[$i][0] === T_DOC_COMMENT)
|
|
||||||
) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->tokens[$i];
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses a single use statement.
|
|
||||||
*
|
|
||||||
* @return array<string, string> A list with all found class names for a use statement.
|
|
||||||
*/
|
|
||||||
public function parseUseStatement()
|
|
||||||
{
|
|
||||||
$groupRoot = '';
|
|
||||||
$class = '';
|
|
||||||
$alias = '';
|
|
||||||
$statements = [];
|
|
||||||
$explicitAlias = false;
|
|
||||||
while (($token = $this->next())) {
|
|
||||||
if (! $explicitAlias && $token[0] === T_STRING) {
|
|
||||||
$class .= $token[1];
|
|
||||||
$alias = $token[1];
|
|
||||||
} elseif ($explicitAlias && $token[0] === T_STRING) {
|
|
||||||
$alias = $token[1];
|
|
||||||
} elseif (
|
|
||||||
PHP_VERSION_ID >= 80000 &&
|
|
||||||
($token[0] === T_NAME_QUALIFIED || $token[0] === T_NAME_FULLY_QUALIFIED)
|
|
||||||
) {
|
|
||||||
$class .= $token[1];
|
|
||||||
|
|
||||||
$classSplit = explode('\\', $token[1]);
|
|
||||||
$alias = $classSplit[count($classSplit) - 1];
|
|
||||||
} elseif ($token[0] === T_NS_SEPARATOR) {
|
|
||||||
$class .= '\\';
|
|
||||||
$alias = '';
|
|
||||||
} elseif ($token[0] === T_AS) {
|
|
||||||
$explicitAlias = true;
|
|
||||||
$alias = '';
|
|
||||||
} elseif ($token === ',') {
|
|
||||||
$statements[strtolower($alias)] = $groupRoot . $class;
|
|
||||||
$class = '';
|
|
||||||
$alias = '';
|
|
||||||
$explicitAlias = false;
|
|
||||||
} elseif ($token === ';') {
|
|
||||||
$statements[strtolower($alias)] = $groupRoot . $class;
|
|
||||||
break;
|
|
||||||
} elseif ($token === '{') {
|
|
||||||
$groupRoot = $class;
|
|
||||||
$class = '';
|
|
||||||
} elseif ($token === '}') {
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $statements;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets all use statements.
|
|
||||||
*
|
|
||||||
* @param string $namespaceName The namespace name of the reflected class.
|
|
||||||
*
|
|
||||||
* @return array<string, string> A list with all found use statements.
|
|
||||||
*/
|
|
||||||
public function parseUseStatements($namespaceName)
|
|
||||||
{
|
|
||||||
$statements = [];
|
|
||||||
while (($token = $this->next())) {
|
|
||||||
if ($token[0] === T_USE) {
|
|
||||||
$statements = array_merge($statements, $this->parseUseStatement());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($token[0] !== T_NAMESPACE || $this->parseNamespace() !== $namespaceName) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get fresh array for new namespace. This is to prevent the parser to collect the use statements
|
|
||||||
// for a previous namespace with the same name. This is the case if a namespace is defined twice
|
|
||||||
// or if a namespace with the same name is commented out.
|
|
||||||
$statements = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $statements;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the namespace.
|
|
||||||
*
|
|
||||||
* @return string The found namespace.
|
|
||||||
*/
|
|
||||||
public function parseNamespace()
|
|
||||||
{
|
|
||||||
$name = '';
|
|
||||||
while (
|
|
||||||
($token = $this->next()) && ($token[0] === T_STRING || $token[0] === T_NS_SEPARATOR || (
|
|
||||||
PHP_VERSION_ID >= 80000 &&
|
|
||||||
($token[0] === T_NAME_QUALIFIED || $token[0] === T_NAME_FULLY_QUALIFIED)
|
|
||||||
))
|
|
||||||
) {
|
|
||||||
$name .= $token[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $name;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the class name.
|
|
||||||
*
|
|
||||||
* @return string The found class name.
|
|
||||||
*/
|
|
||||||
public function parseClass()
|
|
||||||
{
|
|
||||||
// Namespaces and class names are tokenized the same: T_STRINGs
|
|
||||||
// separated by T_NS_SEPARATOR so we can use one function to provide
|
|
||||||
// both.
|
|
||||||
return $this->parseNamespace();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
<?xml version="1.0"?>
|
|
||||||
<psalm
|
|
||||||
errorLevel="7"
|
|
||||||
resolveFromConfigFile="true"
|
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xmlns="https://getpsalm.org/schema/config"
|
|
||||||
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
|
|
||||||
>
|
|
||||||
<projectFiles>
|
|
||||||
<directory name="lib/Doctrine/Common/Annotations" />
|
|
||||||
<ignoreFiles>
|
|
||||||
<directory name="vendor" />
|
|
||||||
</ignoreFiles>
|
|
||||||
</projectFiles>
|
|
||||||
</psalm>
|
|
@ -1,19 +0,0 @@
|
|||||||
Copyright (c) 2020-2021 Doctrine Project
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
||||||
this software and associated documentation files (the "Software"), to deal in
|
|
||||||
the Software without restriction, including without limitation the rights to
|
|
||||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
||||||
of the Software, and to permit persons to whom the Software is furnished to do
|
|
||||||
so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
@ -1,154 +0,0 @@
|
|||||||
# Doctrine Deprecations
|
|
||||||
|
|
||||||
A small (side-effect free by default) layer on top of
|
|
||||||
`trigger_error(E_USER_DEPRECATED)` or PSR-3 logging.
|
|
||||||
|
|
||||||
- no side-effects by default, making it a perfect fit for libraries that don't know how the error handler works they operate under
|
|
||||||
- options to avoid having to rely on error handlers global state by using PSR-3 logging
|
|
||||||
- deduplicate deprecation messages to avoid excessive triggering and reduce overhead
|
|
||||||
|
|
||||||
We recommend to collect Deprecations using a PSR logger instead of relying on
|
|
||||||
the global error handler.
|
|
||||||
|
|
||||||
## Usage from consumer perspective:
|
|
||||||
|
|
||||||
Enable Doctrine deprecations to be sent to a PSR3 logger:
|
|
||||||
|
|
||||||
```php
|
|
||||||
\Doctrine\Deprecations\Deprecation::enableWithPsrLogger($logger);
|
|
||||||
```
|
|
||||||
|
|
||||||
Enable Doctrine deprecations to be sent as `@trigger_error($message, E_USER_DEPRECATED)`
|
|
||||||
messages.
|
|
||||||
|
|
||||||
```php
|
|
||||||
\Doctrine\Deprecations\Deprecation::enableWithTriggerError();
|
|
||||||
```
|
|
||||||
|
|
||||||
If you only want to enable deprecation tracking, without logging or calling `trigger_error` then call:
|
|
||||||
|
|
||||||
```php
|
|
||||||
\Doctrine\Deprecations\Deprecation::enableTrackingDeprecations();
|
|
||||||
```
|
|
||||||
|
|
||||||
Tracking is enabled with all three modes and provides access to all triggered
|
|
||||||
deprecations and their individual count:
|
|
||||||
|
|
||||||
```php
|
|
||||||
$deprecations = \Doctrine\Deprecations\Deprecation::getTriggeredDeprecations();
|
|
||||||
|
|
||||||
foreach ($deprecations as $identifier => $count) {
|
|
||||||
echo $identifier . " was triggered " . $count . " times\n";
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Suppressing Specific Deprecations
|
|
||||||
|
|
||||||
Disable triggering about specific deprecations:
|
|
||||||
|
|
||||||
```php
|
|
||||||
\Doctrine\Deprecations\Deprecation::ignoreDeprecations("https://link/to/deprecations-description-identifier");
|
|
||||||
```
|
|
||||||
|
|
||||||
Disable all deprecations from a package
|
|
||||||
|
|
||||||
```php
|
|
||||||
\Doctrine\Deprecations\Deprecation::ignorePackage("doctrine/orm");
|
|
||||||
```
|
|
||||||
|
|
||||||
### Other Operations
|
|
||||||
|
|
||||||
When used within PHPUnit or other tools that could collect multiple instances of the same deprecations
|
|
||||||
the deduplication can be disabled:
|
|
||||||
|
|
||||||
```php
|
|
||||||
\Doctrine\Deprecations\Deprecation::withoutDeduplication();
|
|
||||||
```
|
|
||||||
|
|
||||||
Disable deprecation tracking again:
|
|
||||||
|
|
||||||
```php
|
|
||||||
\Doctrine\Deprecations\Deprecation::disable();
|
|
||||||
```
|
|
||||||
|
|
||||||
## Usage from a library/producer perspective:
|
|
||||||
|
|
||||||
When you want to unconditionally trigger a deprecation even when called
|
|
||||||
from the library itself then the `trigger` method is the way to go:
|
|
||||||
|
|
||||||
```php
|
|
||||||
\Doctrine\Deprecations\Deprecation::trigger(
|
|
||||||
"doctrine/orm",
|
|
||||||
"https://link/to/deprecations-description",
|
|
||||||
"message"
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
If variable arguments are provided at the end, they are used with `sprintf` on
|
|
||||||
the message.
|
|
||||||
|
|
||||||
```php
|
|
||||||
\Doctrine\Deprecations\Deprecation::trigger(
|
|
||||||
"doctrine/orm",
|
|
||||||
"https://github.com/doctrine/orm/issue/1234",
|
|
||||||
"message %s %d",
|
|
||||||
"foo",
|
|
||||||
1234
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
When you want to trigger a deprecation only when it is called by a function
|
|
||||||
outside of the current package, but not trigger when the package itself is the cause,
|
|
||||||
then use:
|
|
||||||
|
|
||||||
```php
|
|
||||||
\Doctrine\Deprecations\Deprecation::triggerIfCalledFromOutside(
|
|
||||||
"doctrine/orm",
|
|
||||||
"https://link/to/deprecations-description",
|
|
||||||
"message"
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
Based on the issue link each deprecation message is only triggered once per
|
|
||||||
request.
|
|
||||||
|
|
||||||
A limited stacktrace is included in the deprecation message to find the
|
|
||||||
offending location.
|
|
||||||
|
|
||||||
Note: A producer/library should never call `Deprecation::enableWith` methods
|
|
||||||
and leave the decision how to handle deprecations to application and
|
|
||||||
frameworks.
|
|
||||||
|
|
||||||
## Usage in PHPUnit tests
|
|
||||||
|
|
||||||
There is a `VerifyDeprecations` trait that you can use to make assertions on
|
|
||||||
the occurrence of deprecations within a test.
|
|
||||||
|
|
||||||
```php
|
|
||||||
use Doctrine\Deprecations\PHPUnit\VerifyDeprecations;
|
|
||||||
|
|
||||||
class MyTest extends TestCase
|
|
||||||
{
|
|
||||||
use VerifyDeprecations;
|
|
||||||
|
|
||||||
public function testSomethingDeprecation()
|
|
||||||
{
|
|
||||||
$this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/issue/1234');
|
|
||||||
|
|
||||||
triggerTheCodeWithDeprecation();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testSomethingDeprecationFixed()
|
|
||||||
{
|
|
||||||
$this->expectNoDeprecationWithIdentifier('https://github.com/doctrine/orm/issue/1234');
|
|
||||||
|
|
||||||
triggerTheCodeWithoutDeprecation();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## What is a deprecation identifier?
|
|
||||||
|
|
||||||
An identifier for deprecations is just a link to any resource, most often a
|
|
||||||
Github Issue or Pull Request explaining the deprecation and potentially its
|
|
||||||
alternative.
|
|
@ -1,32 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "doctrine/deprecations",
|
|
||||||
"type": "library",
|
|
||||||
"description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.",
|
|
||||||
"homepage": "https://www.doctrine-project.org/",
|
|
||||||
"license": "MIT",
|
|
||||||
"require": {
|
|
||||||
"php": "^7.1|^8.0"
|
|
||||||
},
|
|
||||||
"require-dev": {
|
|
||||||
"phpunit/phpunit": "^7.5|^8.5|^9.5",
|
|
||||||
"psr/log": "^1|^2|^3",
|
|
||||||
"doctrine/coding-standard": "^9"
|
|
||||||
},
|
|
||||||
"suggest": {
|
|
||||||
"psr/log": "Allows logging deprecations via PSR-3 logger implementation"
|
|
||||||
},
|
|
||||||
"autoload": {
|
|
||||||
"psr-4": {"Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations"}
|
|
||||||
},
|
|
||||||
"autoload-dev": {
|
|
||||||
"psr-4": {
|
|
||||||
"DeprecationTests\\": "test_fixtures/src",
|
|
||||||
"Doctrine\\Foo\\": "test_fixtures/vendor/doctrine/foo"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"config": {
|
|
||||||
"allow-plugins": {
|
|
||||||
"dealerdirect/phpcodesniffer-composer-installer": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,266 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Doctrine\Deprecations;
|
|
||||||
|
|
||||||
use Psr\Log\LoggerInterface;
|
|
||||||
|
|
||||||
use function array_key_exists;
|
|
||||||
use function array_reduce;
|
|
||||||
use function debug_backtrace;
|
|
||||||
use function sprintf;
|
|
||||||
use function strpos;
|
|
||||||
use function strrpos;
|
|
||||||
use function substr;
|
|
||||||
use function trigger_error;
|
|
||||||
|
|
||||||
use const DEBUG_BACKTRACE_IGNORE_ARGS;
|
|
||||||
use const DIRECTORY_SEPARATOR;
|
|
||||||
use const E_USER_DEPRECATED;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Manages Deprecation logging in different ways.
|
|
||||||
*
|
|
||||||
* By default triggered exceptions are not logged.
|
|
||||||
*
|
|
||||||
* To enable different deprecation logging mechanisms you can call the
|
|
||||||
* following methods:
|
|
||||||
*
|
|
||||||
* - Minimal collection of deprecations via getTriggeredDeprecations()
|
|
||||||
* \Doctrine\Deprecations\Deprecation::enableTrackingDeprecations();
|
|
||||||
*
|
|
||||||
* - Uses @trigger_error with E_USER_DEPRECATED
|
|
||||||
* \Doctrine\Deprecations\Deprecation::enableWithTriggerError();
|
|
||||||
*
|
|
||||||
* - Sends deprecation messages via a PSR-3 logger
|
|
||||||
* \Doctrine\Deprecations\Deprecation::enableWithPsrLogger($logger);
|
|
||||||
*
|
|
||||||
* Packages that trigger deprecations should use the `trigger()` or
|
|
||||||
* `triggerIfCalledFromOutside()` methods.
|
|
||||||
*/
|
|
||||||
class Deprecation
|
|
||||||
{
|
|
||||||
private const TYPE_NONE = 0;
|
|
||||||
private const TYPE_TRACK_DEPRECATIONS = 1;
|
|
||||||
private const TYPE_TRIGGER_ERROR = 2;
|
|
||||||
private const TYPE_PSR_LOGGER = 4;
|
|
||||||
|
|
||||||
/** @var int */
|
|
||||||
private static $type = self::TYPE_NONE;
|
|
||||||
|
|
||||||
/** @var LoggerInterface|null */
|
|
||||||
private static $logger;
|
|
||||||
|
|
||||||
/** @var array<string,bool> */
|
|
||||||
private static $ignoredPackages = [];
|
|
||||||
|
|
||||||
/** @var array<string,int> */
|
|
||||||
private static $ignoredLinks = [];
|
|
||||||
|
|
||||||
/** @var bool */
|
|
||||||
private static $deduplication = true;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Trigger a deprecation for the given package and identfier.
|
|
||||||
*
|
|
||||||
* The link should point to a Github issue or Wiki entry detailing the
|
|
||||||
* deprecation. It is additionally used to de-duplicate the trigger of the
|
|
||||||
* same deprecation during a request.
|
|
||||||
*
|
|
||||||
* @param mixed $args
|
|
||||||
*/
|
|
||||||
public static function trigger(string $package, string $link, string $message, ...$args): void
|
|
||||||
{
|
|
||||||
if (self::$type === self::TYPE_NONE) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (array_key_exists($link, self::$ignoredLinks)) {
|
|
||||||
self::$ignoredLinks[$link]++;
|
|
||||||
} else {
|
|
||||||
self::$ignoredLinks[$link] = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self::$deduplication === true && self::$ignoredLinks[$link] > 1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset(self::$ignoredPackages[$package])) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
|
|
||||||
|
|
||||||
$message = sprintf($message, ...$args);
|
|
||||||
|
|
||||||
self::delegateTriggerToBackend($message, $backtrace, $link, $package);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Trigger a deprecation for the given package and identifier when called from outside.
|
|
||||||
*
|
|
||||||
* "Outside" means we assume that $package is currently installed as a
|
|
||||||
* dependency and the caller is not a file in that package. When $package
|
|
||||||
* is installed as a root package then deprecations triggered from the
|
|
||||||
* tests folder are also considered "outside".
|
|
||||||
*
|
|
||||||
* This deprecation method assumes that you are using Composer to install
|
|
||||||
* the dependency and are using the default /vendor/ folder and not a
|
|
||||||
* Composer plugin to change the install location. The assumption is also
|
|
||||||
* that $package is the exact composer packge name.
|
|
||||||
*
|
|
||||||
* Compared to {@link trigger()} this method causes some overhead when
|
|
||||||
* deprecation tracking is enabled even during deduplication, because it
|
|
||||||
* needs to call {@link debug_backtrace()}
|
|
||||||
*
|
|
||||||
* @param mixed $args
|
|
||||||
*/
|
|
||||||
public static function triggerIfCalledFromOutside(string $package, string $link, string $message, ...$args): void
|
|
||||||
{
|
|
||||||
if (self::$type === self::TYPE_NONE) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
|
|
||||||
|
|
||||||
// first check that the caller is not from a tests folder, in which case we always let deprecations pass
|
|
||||||
if (strpos($backtrace[1]['file'], DIRECTORY_SEPARATOR . 'tests' . DIRECTORY_SEPARATOR) === false) {
|
|
||||||
$path = DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . $package . DIRECTORY_SEPARATOR;
|
|
||||||
|
|
||||||
if (strpos($backtrace[0]['file'], $path) === false) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strpos($backtrace[1]['file'], $path) !== false) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (array_key_exists($link, self::$ignoredLinks)) {
|
|
||||||
self::$ignoredLinks[$link]++;
|
|
||||||
} else {
|
|
||||||
self::$ignoredLinks[$link] = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self::$deduplication === true && self::$ignoredLinks[$link] > 1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset(self::$ignoredPackages[$package])) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$message = sprintf($message, ...$args);
|
|
||||||
|
|
||||||
self::delegateTriggerToBackend($message, $backtrace, $link, $package);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array<mixed> $backtrace
|
|
||||||
*/
|
|
||||||
private static function delegateTriggerToBackend(string $message, array $backtrace, string $link, string $package): void
|
|
||||||
{
|
|
||||||
if ((self::$type & self::TYPE_PSR_LOGGER) > 0) {
|
|
||||||
$context = [
|
|
||||||
'file' => $backtrace[0]['file'],
|
|
||||||
'line' => $backtrace[0]['line'],
|
|
||||||
'package' => $package,
|
|
||||||
'link' => $link,
|
|
||||||
];
|
|
||||||
|
|
||||||
self::$logger->notice($message, $context);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! ((self::$type & self::TYPE_TRIGGER_ERROR) > 0)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$message .= sprintf(
|
|
||||||
' (%s:%d called by %s:%d, %s, package %s)',
|
|
||||||
self::basename($backtrace[0]['file']),
|
|
||||||
$backtrace[0]['line'],
|
|
||||||
self::basename($backtrace[1]['file']),
|
|
||||||
$backtrace[1]['line'],
|
|
||||||
$link,
|
|
||||||
$package
|
|
||||||
);
|
|
||||||
|
|
||||||
@trigger_error($message, E_USER_DEPRECATED);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A non-local-aware version of PHPs basename function.
|
|
||||||
*/
|
|
||||||
private static function basename(string $filename): string
|
|
||||||
{
|
|
||||||
$pos = strrpos($filename, DIRECTORY_SEPARATOR);
|
|
||||||
|
|
||||||
if ($pos === false) {
|
|
||||||
return $filename;
|
|
||||||
}
|
|
||||||
|
|
||||||
return substr($filename, $pos + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function enableTrackingDeprecations(): void
|
|
||||||
{
|
|
||||||
self::$type |= self::TYPE_TRACK_DEPRECATIONS;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function enableWithTriggerError(): void
|
|
||||||
{
|
|
||||||
self::$type |= self::TYPE_TRIGGER_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function enableWithPsrLogger(LoggerInterface $logger): void
|
|
||||||
{
|
|
||||||
self::$type |= self::TYPE_PSR_LOGGER;
|
|
||||||
self::$logger = $logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function withoutDeduplication(): void
|
|
||||||
{
|
|
||||||
self::$deduplication = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function disable(): void
|
|
||||||
{
|
|
||||||
self::$type = self::TYPE_NONE;
|
|
||||||
self::$logger = null;
|
|
||||||
self::$deduplication = true;
|
|
||||||
|
|
||||||
foreach (self::$ignoredLinks as $link => $count) {
|
|
||||||
self::$ignoredLinks[$link] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function ignorePackage(string $packageName): void
|
|
||||||
{
|
|
||||||
self::$ignoredPackages[$packageName] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function ignoreDeprecations(string ...$links): void
|
|
||||||
{
|
|
||||||
foreach ($links as $link) {
|
|
||||||
self::$ignoredLinks[$link] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getUniqueTriggeredDeprecationsCount(): int
|
|
||||||
{
|
|
||||||
return array_reduce(self::$ignoredLinks, static function (int $carry, int $count) {
|
|
||||||
return $carry + $count;
|
|
||||||
}, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns each triggered deprecation link identifier and the amount of occurrences.
|
|
||||||
*
|
|
||||||
* @return array<string,int>
|
|
||||||
*/
|
|
||||||
public static function getTriggeredDeprecations(): array
|
|
||||||
{
|
|
||||||
return self::$ignoredLinks;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,66 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Doctrine\Deprecations\PHPUnit;
|
|
||||||
|
|
||||||
use Doctrine\Deprecations\Deprecation;
|
|
||||||
|
|
||||||
use function sprintf;
|
|
||||||
|
|
||||||
trait VerifyDeprecations
|
|
||||||
{
|
|
||||||
/** @var array<string,int> */
|
|
||||||
private $doctrineDeprecationsExpectations = [];
|
|
||||||
|
|
||||||
/** @var array<string,int> */
|
|
||||||
private $doctrineNoDeprecationsExpectations = [];
|
|
||||||
|
|
||||||
public function expectDeprecationWithIdentifier(string $identifier): void
|
|
||||||
{
|
|
||||||
$this->doctrineDeprecationsExpectations[$identifier] = Deprecation::getTriggeredDeprecations()[$identifier] ?? 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function expectNoDeprecationWithIdentifier(string $identifier): void
|
|
||||||
{
|
|
||||||
$this->doctrineNoDeprecationsExpectations[$identifier] = Deprecation::getTriggeredDeprecations()[$identifier] ?? 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @before
|
|
||||||
*/
|
|
||||||
public function enableDeprecationTracking(): void
|
|
||||||
{
|
|
||||||
Deprecation::enableTrackingDeprecations();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @after
|
|
||||||
*/
|
|
||||||
public function verifyDeprecationsAreTriggered(): void
|
|
||||||
{
|
|
||||||
foreach ($this->doctrineDeprecationsExpectations as $identifier => $expectation) {
|
|
||||||
$actualCount = Deprecation::getTriggeredDeprecations()[$identifier] ?? 0;
|
|
||||||
|
|
||||||
$this->assertTrue(
|
|
||||||
$actualCount > $expectation,
|
|
||||||
sprintf(
|
|
||||||
"Expected deprecation with identifier '%s' was not triggered by code executed in test.",
|
|
||||||
$identifier
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($this->doctrineNoDeprecationsExpectations as $identifier => $expectation) {
|
|
||||||
$actualCount = Deprecation::getTriggeredDeprecations()[$identifier] ?? 0;
|
|
||||||
|
|
||||||
$this->assertTrue(
|
|
||||||
$actualCount === $expectation,
|
|
||||||
sprintf(
|
|
||||||
"Expected deprecation with identifier '%s' was triggered by code executed in test, but expected not to.",
|
|
||||||
$identifier
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
<?xml version="1.0"?>
|
|
||||||
<ruleset>
|
|
||||||
<arg name="basepath" value="."/>
|
|
||||||
<arg name="extensions" value="php"/>
|
|
||||||
<arg name="parallel" value="80"/>
|
|
||||||
<arg name="cache" value=".phpcs-cache"/>
|
|
||||||
<arg name="colors"/>
|
|
||||||
|
|
||||||
<!-- Ignore warnings, show progress of the run and show sniff names -->
|
|
||||||
<arg value="nps"/>
|
|
||||||
|
|
||||||
<config name="php_version" value="70100"/>
|
|
||||||
|
|
||||||
<!-- Directories to be checked -->
|
|
||||||
<file>lib</file>
|
|
||||||
<file>tests</file>
|
|
||||||
|
|
||||||
<!-- Include full Doctrine Coding Standard -->
|
|
||||||
<rule ref="Doctrine">
|
|
||||||
<exclude name="SlevomatCodingStandard.TypeHints.PropertyTypeHint.MissingNativeTypeHint" />
|
|
||||||
</rule>
|
|
||||||
</ruleset>
|
|
@ -1,19 +0,0 @@
|
|||||||
Copyright (c) 2006-2018 Doctrine Project
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
||||||
this software and associated documentation files (the "Software"), to deal in
|
|
||||||
the Software without restriction, including without limitation the rights to
|
|
||||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
||||||
of the Software, and to permit persons to whom the Software is furnished to do
|
|
||||||
so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
@ -1,9 +0,0 @@
|
|||||||
# Doctrine Lexer
|
|
||||||
|
|
||||||
[![Build Status](https://github.com/doctrine/lexer/workflows/Continuous%20Integration/badge.svg)](https://github.com/doctrine/lexer/actions)
|
|
||||||
|
|
||||||
Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers.
|
|
||||||
|
|
||||||
This lexer is used in Doctrine Annotations and in Doctrine ORM (DQL).
|
|
||||||
|
|
||||||
https://www.doctrine-project.org/projects/lexer.html
|
|
@ -1,14 +0,0 @@
|
|||||||
Note about upgrading: Doctrine uses static and runtime mechanisms to raise
|
|
||||||
awareness about deprecated code.
|
|
||||||
|
|
||||||
- Use of `@deprecated` docblock that is detected by IDEs (like PHPStorm) or
|
|
||||||
Static Analysis tools (like Psalm, phpstan)
|
|
||||||
- Use of our low-overhead runtime deprecation API, details:
|
|
||||||
https://github.com/doctrine/deprecations/
|
|
||||||
|
|
||||||
# Upgrade to 2.0.0
|
|
||||||
|
|
||||||
`AbstractLexer::glimpse()` and `AbstractLexer::peek()` now return
|
|
||||||
instances of `Doctrine\Common\Lexer\Token`, which is an array-like class
|
|
||||||
Using it as an array is deprecated in favor of using properties of that class.
|
|
||||||
Using `count()` on it is deprecated with no replacement.
|
|
@ -1,56 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "doctrine/lexer",
|
|
||||||
"description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.",
|
|
||||||
"license": "MIT",
|
|
||||||
"type": "library",
|
|
||||||
"keywords": [
|
|
||||||
"php",
|
|
||||||
"parser",
|
|
||||||
"lexer",
|
|
||||||
"annotations",
|
|
||||||
"docblock"
|
|
||||||
],
|
|
||||||
"authors": [
|
|
||||||
{
|
|
||||||
"name": "Guilherme Blanco",
|
|
||||||
"email": "guilhermeblanco@gmail.com"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Roman Borschel",
|
|
||||||
"email": "roman@code-factory.org"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Johannes Schmitt",
|
|
||||||
"email": "schmittjoh@gmail.com"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"homepage": "https://www.doctrine-project.org/projects/lexer.html",
|
|
||||||
"require": {
|
|
||||||
"php": "^7.1 || ^8.0",
|
|
||||||
"doctrine/deprecations": "^1.0"
|
|
||||||
},
|
|
||||||
"require-dev": {
|
|
||||||
"doctrine/coding-standard": "^9 || ^10",
|
|
||||||
"phpstan/phpstan": "^1.3",
|
|
||||||
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
|
|
||||||
"psalm/plugin-phpunit": "^0.18.3",
|
|
||||||
"vimeo/psalm": "^4.11 || ^5.0"
|
|
||||||
},
|
|
||||||
"autoload": {
|
|
||||||
"psr-4": {
|
|
||||||
"Doctrine\\Common\\Lexer\\": "src"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"autoload-dev": {
|
|
||||||
"psr-4": {
|
|
||||||
"Doctrine\\Tests\\Common\\Lexer\\": "tests"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"config": {
|
|
||||||
"allow-plugins": {
|
|
||||||
"composer/package-versions-deprecated": true,
|
|
||||||
"dealerdirect/phpcodesniffer-composer-installer": true
|
|
||||||
},
|
|
||||||
"sort-packages": true
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,336 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Doctrine\Common\Lexer;
|
|
||||||
|
|
||||||
use ReflectionClass;
|
|
||||||
use UnitEnum;
|
|
||||||
|
|
||||||
use function get_class;
|
|
||||||
use function implode;
|
|
||||||
use function preg_split;
|
|
||||||
use function sprintf;
|
|
||||||
use function substr;
|
|
||||||
|
|
||||||
use const PREG_SPLIT_DELIM_CAPTURE;
|
|
||||||
use const PREG_SPLIT_NO_EMPTY;
|
|
||||||
use const PREG_SPLIT_OFFSET_CAPTURE;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Base class for writing simple lexers, i.e. for creating small DSLs.
|
|
||||||
*
|
|
||||||
* @template T of UnitEnum|string|int
|
|
||||||
*/
|
|
||||||
abstract class AbstractLexer
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Lexer original input string.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
private $input;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Array of scanned tokens.
|
|
||||||
*
|
|
||||||
* @var list<Token<T>>
|
|
||||||
*/
|
|
||||||
private $tokens = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Current lexer position in input string.
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
private $position = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Current peek of current lexer position.
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
private $peek = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The next token in the input.
|
|
||||||
*
|
|
||||||
* @var mixed[]|null
|
|
||||||
* @psalm-var Token<T>|null
|
|
||||||
*/
|
|
||||||
public $lookahead;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The last matched/seen token.
|
|
||||||
*
|
|
||||||
* @var mixed[]|null
|
|
||||||
* @psalm-var Token<T>|null
|
|
||||||
*/
|
|
||||||
public $token;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Composed regex for input parsing.
|
|
||||||
*
|
|
||||||
* @var string|null
|
|
||||||
*/
|
|
||||||
private $regex;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the input data to be tokenized.
|
|
||||||
*
|
|
||||||
* The Lexer is immediately reset and the new input tokenized.
|
|
||||||
* Any unprocessed tokens from any previous input are lost.
|
|
||||||
*
|
|
||||||
* @param string $input The input to be tokenized.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function setInput($input)
|
|
||||||
{
|
|
||||||
$this->input = $input;
|
|
||||||
$this->tokens = [];
|
|
||||||
|
|
||||||
$this->reset();
|
|
||||||
$this->scan($input);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resets the lexer.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function reset()
|
|
||||||
{
|
|
||||||
$this->lookahead = null;
|
|
||||||
$this->token = null;
|
|
||||||
$this->peek = 0;
|
|
||||||
$this->position = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resets the peek pointer to 0.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function resetPeek()
|
|
||||||
{
|
|
||||||
$this->peek = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resets the lexer position on the input to the given position.
|
|
||||||
*
|
|
||||||
* @param int $position Position to place the lexical scanner.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function resetPosition($position = 0)
|
|
||||||
{
|
|
||||||
$this->position = $position;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the original lexer's input until a given position.
|
|
||||||
*
|
|
||||||
* @param int $position
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getInputUntilPosition($position)
|
|
||||||
{
|
|
||||||
return substr($this->input, 0, $position);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks whether a given token matches the current lookahead.
|
|
||||||
*
|
|
||||||
* @param T $type
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function isNextToken($type)
|
|
||||||
{
|
|
||||||
return $this->lookahead !== null && $this->lookahead->isA($type);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks whether any of the given tokens matches the current lookahead.
|
|
||||||
*
|
|
||||||
* @param list<T> $types
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function isNextTokenAny(array $types)
|
|
||||||
{
|
|
||||||
return $this->lookahead !== null && $this->lookahead->isA(...$types);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Moves to the next token in the input string.
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function moveNext()
|
|
||||||
{
|
|
||||||
$this->peek = 0;
|
|
||||||
$this->token = $this->lookahead;
|
|
||||||
$this->lookahead = isset($this->tokens[$this->position])
|
|
||||||
? $this->tokens[$this->position++] : null;
|
|
||||||
|
|
||||||
return $this->lookahead !== null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tells the lexer to skip input tokens until it sees a token with the given value.
|
|
||||||
*
|
|
||||||
* @param T $type The token type to skip until.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function skipUntil($type)
|
|
||||||
{
|
|
||||||
while ($this->lookahead !== null && ! $this->lookahead->isA($type)) {
|
|
||||||
$this->moveNext();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if given value is identical to the given token.
|
|
||||||
*
|
|
||||||
* @param string $value
|
|
||||||
* @param int|string $token
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function isA($value, $token)
|
|
||||||
{
|
|
||||||
return $this->getType($value) === $token;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Moves the lookahead token forward.
|
|
||||||
*
|
|
||||||
* @return mixed[]|null The next token or NULL if there are no more tokens ahead.
|
|
||||||
* @psalm-return Token<T>|null
|
|
||||||
*/
|
|
||||||
public function peek()
|
|
||||||
{
|
|
||||||
if (isset($this->tokens[$this->position + $this->peek])) {
|
|
||||||
return $this->tokens[$this->position + $this->peek++];
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Peeks at the next token, returns it and immediately resets the peek.
|
|
||||||
*
|
|
||||||
* @return mixed[]|null The next token or NULL if there are no more tokens ahead.
|
|
||||||
* @psalm-return Token<T>|null
|
|
||||||
*/
|
|
||||||
public function glimpse()
|
|
||||||
{
|
|
||||||
$peek = $this->peek();
|
|
||||||
$this->peek = 0;
|
|
||||||
|
|
||||||
return $peek;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Scans the input string for tokens.
|
|
||||||
*
|
|
||||||
* @param string $input A query string.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
protected function scan($input)
|
|
||||||
{
|
|
||||||
if (! isset($this->regex)) {
|
|
||||||
$this->regex = sprintf(
|
|
||||||
'/(%s)|%s/%s',
|
|
||||||
implode(')|(', $this->getCatchablePatterns()),
|
|
||||||
implode('|', $this->getNonCatchablePatterns()),
|
|
||||||
$this->getModifiers()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
$flags = PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_OFFSET_CAPTURE;
|
|
||||||
$matches = preg_split($this->regex, $input, -1, $flags);
|
|
||||||
|
|
||||||
if ($matches === false) {
|
|
||||||
// Work around https://bugs.php.net/78122
|
|
||||||
$matches = [[$input, 0]];
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($matches as $match) {
|
|
||||||
// Must remain before 'value' assignment since it can change content
|
|
||||||
$type = $this->getType($match[0]);
|
|
||||||
|
|
||||||
$this->tokens[] = new Token(
|
|
||||||
$match[0],
|
|
||||||
$type,
|
|
||||||
$match[1]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the literal for a given token.
|
|
||||||
*
|
|
||||||
* @param T $token
|
|
||||||
*
|
|
||||||
* @return int|string
|
|
||||||
*/
|
|
||||||
public function getLiteral($token)
|
|
||||||
{
|
|
||||||
if ($token instanceof UnitEnum) {
|
|
||||||
return get_class($token) . '::' . $token->name;
|
|
||||||
}
|
|
||||||
|
|
||||||
$className = static::class;
|
|
||||||
|
|
||||||
$reflClass = new ReflectionClass($className);
|
|
||||||
$constants = $reflClass->getConstants();
|
|
||||||
|
|
||||||
foreach ($constants as $name => $value) {
|
|
||||||
if ($value === $token) {
|
|
||||||
return $className . '::' . $name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $token;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Regex modifiers
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
protected function getModifiers()
|
|
||||||
{
|
|
||||||
return 'iu';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Lexical catchable patterns.
|
|
||||||
*
|
|
||||||
* @return string[]
|
|
||||||
*/
|
|
||||||
abstract protected function getCatchablePatterns();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Lexical non-catchable patterns.
|
|
||||||
*
|
|
||||||
* @return string[]
|
|
||||||
*/
|
|
||||||
abstract protected function getNonCatchablePatterns();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve token type. Also processes the token value if necessary.
|
|
||||||
*
|
|
||||||
* @param string $value
|
|
||||||
*
|
|
||||||
* @return T|null
|
|
||||||
*/
|
|
||||||
abstract protected function getType(&$value);
|
|
||||||
}
|
|
@ -1,129 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Doctrine\Common\Lexer;
|
|
||||||
|
|
||||||
use ArrayAccess;
|
|
||||||
use Doctrine\Deprecations\Deprecation;
|
|
||||||
use ReturnTypeWillChange;
|
|
||||||
use UnitEnum;
|
|
||||||
|
|
||||||
use function in_array;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @template T of UnitEnum|string|int
|
|
||||||
* @implements ArrayAccess<string,mixed>
|
|
||||||
*/
|
|
||||||
final class Token implements ArrayAccess
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The string value of the token in the input string
|
|
||||||
*
|
|
||||||
* @readonly
|
|
||||||
* @var string|int
|
|
||||||
*/
|
|
||||||
public $value;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The type of the token (identifier, numeric, string, input parameter, none)
|
|
||||||
*
|
|
||||||
* @readonly
|
|
||||||
* @var T|null
|
|
||||||
*/
|
|
||||||
public $type;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The position of the token in the input string
|
|
||||||
*
|
|
||||||
* @readonly
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public $position;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string|int $value
|
|
||||||
* @param T|null $type
|
|
||||||
*/
|
|
||||||
public function __construct($value, $type, int $position)
|
|
||||||
{
|
|
||||||
$this->value = $value;
|
|
||||||
$this->type = $type;
|
|
||||||
$this->position = $position;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @param T ...$types */
|
|
||||||
public function isA(...$types): bool
|
|
||||||
{
|
|
||||||
return in_array($this->type, $types, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated Use the value, type or position property instead
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public function offsetExists($offset): bool
|
|
||||||
{
|
|
||||||
Deprecation::trigger(
|
|
||||||
'doctrine/lexer',
|
|
||||||
'https://github.com/doctrine/lexer/pull/79',
|
|
||||||
'Accessing %s properties via ArrayAccess is deprecated, use the value, type or position property instead',
|
|
||||||
self::class
|
|
||||||
);
|
|
||||||
|
|
||||||
return in_array($offset, ['value', 'type', 'position'], true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated Use the value, type or position property instead
|
|
||||||
* {@inheritDoc}
|
|
||||||
*
|
|
||||||
* @param array-key $offset
|
|
||||||
*
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
#[ReturnTypeWillChange]
|
|
||||||
public function offsetGet($offset)
|
|
||||||
{
|
|
||||||
Deprecation::trigger(
|
|
||||||
'doctrine/lexer',
|
|
||||||
'https://github.com/doctrine/lexer/pull/79',
|
|
||||||
'Accessing %s properties via ArrayAccess is deprecated, use the value, type or position property instead',
|
|
||||||
self::class
|
|
||||||
);
|
|
||||||
|
|
||||||
return $this->$offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated no replacement planned
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public function offsetSet($offset, $value): void
|
|
||||||
{
|
|
||||||
Deprecation::trigger(
|
|
||||||
'doctrine/lexer',
|
|
||||||
'https://github.com/doctrine/lexer/pull/79',
|
|
||||||
'Setting %s properties via ArrayAccess is deprecated',
|
|
||||||
self::class
|
|
||||||
);
|
|
||||||
|
|
||||||
$this->$offset = $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated no replacement planned
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public function offsetUnset($offset): void
|
|
||||||
{
|
|
||||||
Deprecation::trigger(
|
|
||||||
'doctrine/lexer',
|
|
||||||
'https://github.com/doctrine/lexer/pull/79',
|
|
||||||
'Setting %s properties via ArrayAccess is deprecated',
|
|
||||||
self::class
|
|
||||||
);
|
|
||||||
|
|
||||||
$this->$offset = null;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1 +0,0 @@
|
|||||||
vendor/
|
|
@ -1,147 +0,0 @@
|
|||||||
# Changelog
|
|
||||||
|
|
||||||
All notable changes to this project will be documented in this file, in reverse chronological order by release.
|
|
||||||
|
|
||||||
## 1.1.5 - 2020-11-24
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- [#19](https://github.com/php-fig/http-message-util/pull/19) adds support for PHP 8.
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Nothing.
|
|
||||||
|
|
||||||
### Deprecated
|
|
||||||
|
|
||||||
- Nothing.
|
|
||||||
|
|
||||||
### Removed
|
|
||||||
|
|
||||||
- Nothing.
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Nothing.
|
|
||||||
|
|
||||||
## 1.1.4 - 2020-02-05
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Nothing.
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Nothing.
|
|
||||||
|
|
||||||
### Deprecated
|
|
||||||
|
|
||||||
- Nothing.
|
|
||||||
|
|
||||||
### Removed
|
|
||||||
|
|
||||||
- [#15](https://github.com/php-fig/http-message-util/pull/15) removes the dependency on psr/http-message, as it is not technically necessary for usage of this package.
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Nothing.
|
|
||||||
|
|
||||||
## 1.1.3 - 2018-11-19
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- [#10](https://github.com/php-fig/http-message-util/pull/10) adds the constants `StatusCodeInterface::STATUS_EARLY_HINTS` (103) and
|
|
||||||
`StatusCodeInterface::STATUS_TOO_EARLY` (425).
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Nothing.
|
|
||||||
|
|
||||||
### Deprecated
|
|
||||||
|
|
||||||
- Nothing.
|
|
||||||
|
|
||||||
### Removed
|
|
||||||
|
|
||||||
- Nothing.
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Nothing.
|
|
||||||
|
|
||||||
## 1.1.2 - 2017-02-09
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- [#4](https://github.com/php-fig/http-message-util/pull/4) adds the constant
|
|
||||||
`StatusCodeInterface::STATUS_MISDIRECTED_REQUEST` (421).
|
|
||||||
|
|
||||||
### Deprecated
|
|
||||||
|
|
||||||
- Nothing.
|
|
||||||
|
|
||||||
### Removed
|
|
||||||
|
|
||||||
- Nothing.
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Nothing.
|
|
||||||
|
|
||||||
## 1.1.1 - 2017-02-06
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- [#3](https://github.com/php-fig/http-message-util/pull/3) adds the constant
|
|
||||||
`StatusCodeInterface::STATUS_IM_A_TEAPOT` (418).
|
|
||||||
|
|
||||||
### Deprecated
|
|
||||||
|
|
||||||
- Nothing.
|
|
||||||
|
|
||||||
### Removed
|
|
||||||
|
|
||||||
- Nothing.
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Nothing.
|
|
||||||
|
|
||||||
## 1.1.0 - 2016-09-19
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- [#1](https://github.com/php-fig/http-message-util/pull/1) adds
|
|
||||||
`Fig\Http\Message\StatusCodeInterface`, with constants named after common
|
|
||||||
status reason phrases, with values indicating the status codes themselves.
|
|
||||||
|
|
||||||
### Deprecated
|
|
||||||
|
|
||||||
- Nothing.
|
|
||||||
|
|
||||||
### Removed
|
|
||||||
|
|
||||||
- Nothing.
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Nothing.
|
|
||||||
|
|
||||||
## 1.0.0 - 2017-08-05
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Adds `Fig\Http\Message\RequestMethodInterface`, with constants covering the
|
|
||||||
most common HTTP request methods as specified by the IETF.
|
|
||||||
|
|
||||||
### Deprecated
|
|
||||||
|
|
||||||
- Nothing.
|
|
||||||
|
|
||||||
### Removed
|
|
||||||
|
|
||||||
- Nothing.
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Nothing.
|
|
@ -1,19 +0,0 @@
|
|||||||
Copyright (c) 2016 PHP Framework Interoperability Group
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
@ -1,17 +0,0 @@
|
|||||||
# PSR Http Message Util
|
|
||||||
|
|
||||||
This repository holds utility classes and constants to facilitate common
|
|
||||||
operations of [PSR-7](https://www.php-fig.org/psr/psr-7/); the primary purpose is
|
|
||||||
to provide constants for referring to request methods, response status codes and
|
|
||||||
messages, and potentially common headers.
|
|
||||||
|
|
||||||
Implementation of PSR-7 interfaces is **not** within the scope of this package.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
Install by adding the package as a [Composer](https://getcomposer.org)
|
|
||||||
requirement:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ composer require fig/http-message-util
|
|
||||||
```
|
|
@ -1,28 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "fig/http-message-util",
|
|
||||||
"description": "Utility classes and constants for use with PSR-7 (psr/http-message)",
|
|
||||||
"keywords": ["psr", "psr-7", "http", "http-message", "request", "response"],
|
|
||||||
"license": "MIT",
|
|
||||||
"authors": [
|
|
||||||
{
|
|
||||||
"name": "PHP-FIG",
|
|
||||||
"homepage": "https://www.php-fig.org/"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"require": {
|
|
||||||
"php": "^5.3 || ^7.0 || ^8.0"
|
|
||||||
},
|
|
||||||
"suggest": {
|
|
||||||
"psr/http-message": "The package containing the PSR-7 interfaces"
|
|
||||||
},
|
|
||||||
"autoload": {
|
|
||||||
"psr-4": {
|
|
||||||
"Fig\\Http\\Message\\": "src/"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"extra": {
|
|
||||||
"branch-alias": {
|
|
||||||
"dev-master": "1.1.x-dev"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,34 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Fig\Http\Message;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Defines constants for common HTTP request methods.
|
|
||||||
*
|
|
||||||
* Usage:
|
|
||||||
*
|
|
||||||
* <code>
|
|
||||||
* class RequestFactory implements RequestMethodInterface
|
|
||||||
* {
|
|
||||||
* public static function factory(
|
|
||||||
* $uri = '/',
|
|
||||||
* $method = self::METHOD_GET,
|
|
||||||
* $data = []
|
|
||||||
* ) {
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* </code>
|
|
||||||
*/
|
|
||||||
interface RequestMethodInterface
|
|
||||||
{
|
|
||||||
const METHOD_HEAD = 'HEAD';
|
|
||||||
const METHOD_GET = 'GET';
|
|
||||||
const METHOD_POST = 'POST';
|
|
||||||
const METHOD_PUT = 'PUT';
|
|
||||||
const METHOD_PATCH = 'PATCH';
|
|
||||||
const METHOD_DELETE = 'DELETE';
|
|
||||||
const METHOD_PURGE = 'PURGE';
|
|
||||||
const METHOD_OPTIONS = 'OPTIONS';
|
|
||||||
const METHOD_TRACE = 'TRACE';
|
|
||||||
const METHOD_CONNECT = 'CONNECT';
|
|
||||||
}
|
|
@ -1,107 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Fig\Http\Message;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Defines constants for common HTTP status code.
|
|
||||||
*
|
|
||||||
* @see https://tools.ietf.org/html/rfc2295#section-8.1
|
|
||||||
* @see https://tools.ietf.org/html/rfc2324#section-2.3
|
|
||||||
* @see https://tools.ietf.org/html/rfc2518#section-9.7
|
|
||||||
* @see https://tools.ietf.org/html/rfc2774#section-7
|
|
||||||
* @see https://tools.ietf.org/html/rfc3229#section-10.4
|
|
||||||
* @see https://tools.ietf.org/html/rfc4918#section-11
|
|
||||||
* @see https://tools.ietf.org/html/rfc5842#section-7.1
|
|
||||||
* @see https://tools.ietf.org/html/rfc5842#section-7.2
|
|
||||||
* @see https://tools.ietf.org/html/rfc6585#section-3
|
|
||||||
* @see https://tools.ietf.org/html/rfc6585#section-4
|
|
||||||
* @see https://tools.ietf.org/html/rfc6585#section-5
|
|
||||||
* @see https://tools.ietf.org/html/rfc6585#section-6
|
|
||||||
* @see https://tools.ietf.org/html/rfc7231#section-6
|
|
||||||
* @see https://tools.ietf.org/html/rfc7238#section-3
|
|
||||||
* @see https://tools.ietf.org/html/rfc7725#section-3
|
|
||||||
* @see https://tools.ietf.org/html/rfc7540#section-9.1.2
|
|
||||||
* @see https://tools.ietf.org/html/rfc8297#section-2
|
|
||||||
* @see https://tools.ietf.org/html/rfc8470#section-7
|
|
||||||
* Usage:
|
|
||||||
*
|
|
||||||
* <code>
|
|
||||||
* class ResponseFactory implements StatusCodeInterface
|
|
||||||
* {
|
|
||||||
* public function createResponse($code = self::STATUS_OK)
|
|
||||||
* {
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* </code>
|
|
||||||
*/
|
|
||||||
interface StatusCodeInterface
|
|
||||||
{
|
|
||||||
// Informational 1xx
|
|
||||||
const STATUS_CONTINUE = 100;
|
|
||||||
const STATUS_SWITCHING_PROTOCOLS = 101;
|
|
||||||
const STATUS_PROCESSING = 102;
|
|
||||||
const STATUS_EARLY_HINTS = 103;
|
|
||||||
// Successful 2xx
|
|
||||||
const STATUS_OK = 200;
|
|
||||||
const STATUS_CREATED = 201;
|
|
||||||
const STATUS_ACCEPTED = 202;
|
|
||||||
const STATUS_NON_AUTHORITATIVE_INFORMATION = 203;
|
|
||||||
const STATUS_NO_CONTENT = 204;
|
|
||||||
const STATUS_RESET_CONTENT = 205;
|
|
||||||
const STATUS_PARTIAL_CONTENT = 206;
|
|
||||||
const STATUS_MULTI_STATUS = 207;
|
|
||||||
const STATUS_ALREADY_REPORTED = 208;
|
|
||||||
const STATUS_IM_USED = 226;
|
|
||||||
// Redirection 3xx
|
|
||||||
const STATUS_MULTIPLE_CHOICES = 300;
|
|
||||||
const STATUS_MOVED_PERMANENTLY = 301;
|
|
||||||
const STATUS_FOUND = 302;
|
|
||||||
const STATUS_SEE_OTHER = 303;
|
|
||||||
const STATUS_NOT_MODIFIED = 304;
|
|
||||||
const STATUS_USE_PROXY = 305;
|
|
||||||
const STATUS_RESERVED = 306;
|
|
||||||
const STATUS_TEMPORARY_REDIRECT = 307;
|
|
||||||
const STATUS_PERMANENT_REDIRECT = 308;
|
|
||||||
// Client Errors 4xx
|
|
||||||
const STATUS_BAD_REQUEST = 400;
|
|
||||||
const STATUS_UNAUTHORIZED = 401;
|
|
||||||
const STATUS_PAYMENT_REQUIRED = 402;
|
|
||||||
const STATUS_FORBIDDEN = 403;
|
|
||||||
const STATUS_NOT_FOUND = 404;
|
|
||||||
const STATUS_METHOD_NOT_ALLOWED = 405;
|
|
||||||
const STATUS_NOT_ACCEPTABLE = 406;
|
|
||||||
const STATUS_PROXY_AUTHENTICATION_REQUIRED = 407;
|
|
||||||
const STATUS_REQUEST_TIMEOUT = 408;
|
|
||||||
const STATUS_CONFLICT = 409;
|
|
||||||
const STATUS_GONE = 410;
|
|
||||||
const STATUS_LENGTH_REQUIRED = 411;
|
|
||||||
const STATUS_PRECONDITION_FAILED = 412;
|
|
||||||
const STATUS_PAYLOAD_TOO_LARGE = 413;
|
|
||||||
const STATUS_URI_TOO_LONG = 414;
|
|
||||||
const STATUS_UNSUPPORTED_MEDIA_TYPE = 415;
|
|
||||||
const STATUS_RANGE_NOT_SATISFIABLE = 416;
|
|
||||||
const STATUS_EXPECTATION_FAILED = 417;
|
|
||||||
const STATUS_IM_A_TEAPOT = 418;
|
|
||||||
const STATUS_MISDIRECTED_REQUEST = 421;
|
|
||||||
const STATUS_UNPROCESSABLE_ENTITY = 422;
|
|
||||||
const STATUS_LOCKED = 423;
|
|
||||||
const STATUS_FAILED_DEPENDENCY = 424;
|
|
||||||
const STATUS_TOO_EARLY = 425;
|
|
||||||
const STATUS_UPGRADE_REQUIRED = 426;
|
|
||||||
const STATUS_PRECONDITION_REQUIRED = 428;
|
|
||||||
const STATUS_TOO_MANY_REQUESTS = 429;
|
|
||||||
const STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE = 431;
|
|
||||||
const STATUS_UNAVAILABLE_FOR_LEGAL_REASONS = 451;
|
|
||||||
// Server Errors 5xx
|
|
||||||
const STATUS_INTERNAL_SERVER_ERROR = 500;
|
|
||||||
const STATUS_NOT_IMPLEMENTED = 501;
|
|
||||||
const STATUS_BAD_GATEWAY = 502;
|
|
||||||
const STATUS_SERVICE_UNAVAILABLE = 503;
|
|
||||||
const STATUS_GATEWAY_TIMEOUT = 504;
|
|
||||||
const STATUS_VERSION_NOT_SUPPORTED = 505;
|
|
||||||
const STATUS_VARIANT_ALSO_NEGOTIATES = 506;
|
|
||||||
const STATUS_INSUFFICIENT_STORAGE = 507;
|
|
||||||
const STATUS_LOOP_DETECTED = 508;
|
|
||||||
const STATUS_NOT_EXTENDED = 510;
|
|
||||||
const STATUS_NETWORK_AUTHENTICATION_REQUIRED = 511;
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
/vendor/
|
|
||||||
.idea/
|
|
||||||
|
|
||||||
# ignore lock file since we have no extra dependencies
|
|
||||||
composer.lock
|
|
@ -1 +0,0 @@
|
|||||||
assume_php=false
|
|
@ -1,20 +0,0 @@
|
|||||||
sudo: false
|
|
||||||
language: php
|
|
||||||
|
|
||||||
php:
|
|
||||||
- 5.4
|
|
||||||
- 5.5
|
|
||||||
- 5.6
|
|
||||||
- 7.0
|
|
||||||
- 7.1
|
|
||||||
- 7.2
|
|
||||||
- hhvm
|
|
||||||
|
|
||||||
script:
|
|
||||||
- ./vendor/bin/phpunit
|
|
||||||
|
|
||||||
before_install:
|
|
||||||
- travis_retry composer self-update
|
|
||||||
|
|
||||||
install:
|
|
||||||
- composer install
|
|
@ -1,126 +0,0 @@
|
|||||||
<?hh // decl
|
|
||||||
|
|
||||||
namespace FastRoute {
|
|
||||||
class BadRouteException extends \LogicException {
|
|
||||||
}
|
|
||||||
|
|
||||||
interface RouteParser {
|
|
||||||
public function parse(string $route): array<array>;
|
|
||||||
}
|
|
||||||
|
|
||||||
class RouteCollector {
|
|
||||||
public function __construct(RouteParser $routeParser, DataGenerator $dataGenerator);
|
|
||||||
public function addRoute(mixed $httpMethod, string $route, mixed $handler): void;
|
|
||||||
public function getData(): array;
|
|
||||||
}
|
|
||||||
|
|
||||||
class Route {
|
|
||||||
public function __construct(string $httpMethod, mixed $handler, string $regex, array $variables);
|
|
||||||
public function matches(string $str): bool;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DataGenerator {
|
|
||||||
public function addRoute(string $httpMethod, array $routeData, mixed $handler);
|
|
||||||
public function getData(): array;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Dispatcher {
|
|
||||||
const int NOT_FOUND = 0;
|
|
||||||
const int FOUND = 1;
|
|
||||||
const int METHOD_NOT_ALLOWED = 2;
|
|
||||||
public function dispatch(string $httpMethod, string $uri): array;
|
|
||||||
}
|
|
||||||
|
|
||||||
function simpleDispatcher(
|
|
||||||
(function(RouteCollector): void) $routeDefinitionCallback,
|
|
||||||
shape(
|
|
||||||
?'routeParser' => classname<RouteParser>,
|
|
||||||
?'dataGenerator' => classname<DataGenerator>,
|
|
||||||
?'dispatcher' => classname<Dispatcher>,
|
|
||||||
?'routeCollector' => classname<RouteCollector>,
|
|
||||||
) $options = shape()): Dispatcher;
|
|
||||||
|
|
||||||
function cachedDispatcher(
|
|
||||||
(function(RouteCollector): void) $routeDefinitionCallback,
|
|
||||||
shape(
|
|
||||||
?'routeParser' => classname<RouteParser>,
|
|
||||||
?'dataGenerator' => classname<DataGenerator>,
|
|
||||||
?'dispatcher' => classname<Dispatcher>,
|
|
||||||
?'routeCollector' => classname<RouteCollector>,
|
|
||||||
?'cacheDisabled' => bool,
|
|
||||||
?'cacheFile' => string,
|
|
||||||
) $options = shape()): Dispatcher;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace FastRoute\DataGenerator {
|
|
||||||
abstract class RegexBasedAbstract implements \FastRoute\DataGenerator {
|
|
||||||
protected abstract function getApproxChunkSize();
|
|
||||||
protected abstract function processChunk($regexToRoutesMap);
|
|
||||||
|
|
||||||
public function addRoute(string $httpMethod, array $routeData, mixed $handler): void;
|
|
||||||
public function getData(): array;
|
|
||||||
}
|
|
||||||
|
|
||||||
class CharCountBased extends RegexBasedAbstract {
|
|
||||||
protected function getApproxChunkSize(): int;
|
|
||||||
protected function processChunk(array<string, string> $regexToRoutesMap): array<string, mixed>;
|
|
||||||
}
|
|
||||||
|
|
||||||
class GroupCountBased extends RegexBasedAbstract {
|
|
||||||
protected function getApproxChunkSize(): int;
|
|
||||||
protected function processChunk(array<string, string> $regexToRoutesMap): array<string, mixed>;
|
|
||||||
}
|
|
||||||
|
|
||||||
class GroupPosBased extends RegexBasedAbstract {
|
|
||||||
protected function getApproxChunkSize(): int;
|
|
||||||
protected function processChunk(array<string, string> $regexToRoutesMap): array<string, mixed>;
|
|
||||||
}
|
|
||||||
|
|
||||||
class MarkBased extends RegexBasedAbstract {
|
|
||||||
protected function getApproxChunkSize(): int;
|
|
||||||
protected function processChunk(array<string, string> $regexToRoutesMap): array<string, mixed>;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace FastRoute\Dispatcher {
|
|
||||||
abstract class RegexBasedAbstract implements \FastRoute\Dispatcher {
|
|
||||||
protected abstract function dispatchVariableRoute(array<array> $routeData, string $uri): array;
|
|
||||||
|
|
||||||
public function dispatch(string $httpMethod, string $uri): array;
|
|
||||||
}
|
|
||||||
|
|
||||||
class GroupPosBased extends RegexBasedAbstract {
|
|
||||||
public function __construct(array $data);
|
|
||||||
protected function dispatchVariableRoute(array<array> $routeData, string $uri): array;
|
|
||||||
}
|
|
||||||
|
|
||||||
class GroupCountBased extends RegexBasedAbstract {
|
|
||||||
public function __construct(array $data);
|
|
||||||
protected function dispatchVariableRoute(array<array> $routeData, string $uri): array;
|
|
||||||
}
|
|
||||||
|
|
||||||
class CharCountBased extends RegexBasedAbstract {
|
|
||||||
public function __construct(array $data);
|
|
||||||
protected function dispatchVariableRoute(array<array> $routeData, string $uri): array;
|
|
||||||
}
|
|
||||||
|
|
||||||
class MarkBased extends RegexBasedAbstract {
|
|
||||||
public function __construct(array $data);
|
|
||||||
protected function dispatchVariableRoute(array<array> $routeData, string $uri): array;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace FastRoute\RouteParser {
|
|
||||||
class Std implements \FastRoute\RouteParser {
|
|
||||||
const string VARIABLE_REGEX = <<<'REGEX'
|
|
||||||
\{
|
|
||||||
\s* ([a-zA-Z][a-zA-Z0-9_]*) \s*
|
|
||||||
(?:
|
|
||||||
: \s* ([^{}]*(?:\{(?-1)\}[^{}]*)*)
|
|
||||||
)?
|
|
||||||
\}
|
|
||||||
REGEX;
|
|
||||||
const string DEFAULT_DISPATCH_REGEX = '[^/]+';
|
|
||||||
public function parse(string $route): array<array>;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
Copyright (c) 2013 by Nikita Popov.
|
|
||||||
|
|
||||||
Some rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are
|
|
||||||
met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
* Redistributions in binary form must reproduce the above
|
|
||||||
copyright notice, this list of conditions and the following
|
|
||||||
disclaimer in the documentation and/or other materials provided
|
|
||||||
with the distribution.
|
|
||||||
|
|
||||||
* The names of the contributors may not be used to endorse or
|
|
||||||
promote products derived from this software without specific
|
|
||||||
prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -1,313 +0,0 @@
|
|||||||
FastRoute - Fast request router for PHP
|
|
||||||
=======================================
|
|
||||||
|
|
||||||
This library provides a fast implementation of a regular expression based router. [Blog post explaining how the
|
|
||||||
implementation works and why it is fast.][blog_post]
|
|
||||||
|
|
||||||
Install
|
|
||||||
-------
|
|
||||||
|
|
||||||
To install with composer:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
composer require nikic/fast-route
|
|
||||||
```
|
|
||||||
|
|
||||||
Requires PHP 5.4 or newer.
|
|
||||||
|
|
||||||
Usage
|
|
||||||
-----
|
|
||||||
|
|
||||||
Here's a basic usage example:
|
|
||||||
|
|
||||||
```php
|
|
||||||
<?php
|
|
||||||
|
|
||||||
require '/path/to/vendor/autoload.php';
|
|
||||||
|
|
||||||
$dispatcher = FastRoute\simpleDispatcher(function(FastRoute\RouteCollector $r) {
|
|
||||||
$r->addRoute('GET', '/users', 'get_all_users_handler');
|
|
||||||
// {id} must be a number (\d+)
|
|
||||||
$r->addRoute('GET', '/user/{id:\d+}', 'get_user_handler');
|
|
||||||
// The /{title} suffix is optional
|
|
||||||
$r->addRoute('GET', '/articles/{id:\d+}[/{title}]', 'get_article_handler');
|
|
||||||
});
|
|
||||||
|
|
||||||
// Fetch method and URI from somewhere
|
|
||||||
$httpMethod = $_SERVER['REQUEST_METHOD'];
|
|
||||||
$uri = $_SERVER['REQUEST_URI'];
|
|
||||||
|
|
||||||
// Strip query string (?foo=bar) and decode URI
|
|
||||||
if (false !== $pos = strpos($uri, '?')) {
|
|
||||||
$uri = substr($uri, 0, $pos);
|
|
||||||
}
|
|
||||||
$uri = rawurldecode($uri);
|
|
||||||
|
|
||||||
$routeInfo = $dispatcher->dispatch($httpMethod, $uri);
|
|
||||||
switch ($routeInfo[0]) {
|
|
||||||
case FastRoute\Dispatcher::NOT_FOUND:
|
|
||||||
// ... 404 Not Found
|
|
||||||
break;
|
|
||||||
case FastRoute\Dispatcher::METHOD_NOT_ALLOWED:
|
|
||||||
$allowedMethods = $routeInfo[1];
|
|
||||||
// ... 405 Method Not Allowed
|
|
||||||
break;
|
|
||||||
case FastRoute\Dispatcher::FOUND:
|
|
||||||
$handler = $routeInfo[1];
|
|
||||||
$vars = $routeInfo[2];
|
|
||||||
// ... call $handler with $vars
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Defining routes
|
|
||||||
|
|
||||||
The routes are defined by calling the `FastRoute\simpleDispatcher()` function, which accepts
|
|
||||||
a callable taking a `FastRoute\RouteCollector` instance. The routes are added by calling
|
|
||||||
`addRoute()` on the collector instance:
|
|
||||||
|
|
||||||
```php
|
|
||||||
$r->addRoute($method, $routePattern, $handler);
|
|
||||||
```
|
|
||||||
|
|
||||||
The `$method` is an uppercase HTTP method string for which a certain route should match. It
|
|
||||||
is possible to specify multiple valid methods using an array:
|
|
||||||
|
|
||||||
```php
|
|
||||||
// These two calls
|
|
||||||
$r->addRoute('GET', '/test', 'handler');
|
|
||||||
$r->addRoute('POST', '/test', 'handler');
|
|
||||||
// Are equivalent to this one call
|
|
||||||
$r->addRoute(['GET', 'POST'], '/test', 'handler');
|
|
||||||
```
|
|
||||||
|
|
||||||
By default the `$routePattern` uses a syntax where `{foo}` specifies a placeholder with name `foo`
|
|
||||||
and matching the regex `[^/]+`. To adjust the pattern the placeholder matches, you can specify
|
|
||||||
a custom pattern by writing `{bar:[0-9]+}`. Some examples:
|
|
||||||
|
|
||||||
```php
|
|
||||||
// Matches /user/42, but not /user/xyz
|
|
||||||
$r->addRoute('GET', '/user/{id:\d+}', 'handler');
|
|
||||||
|
|
||||||
// Matches /user/foobar, but not /user/foo/bar
|
|
||||||
$r->addRoute('GET', '/user/{name}', 'handler');
|
|
||||||
|
|
||||||
// Matches /user/foo/bar as well
|
|
||||||
$r->addRoute('GET', '/user/{name:.+}', 'handler');
|
|
||||||
```
|
|
||||||
|
|
||||||
Custom patterns for route placeholders cannot use capturing groups. For example `{lang:(en|de)}`
|
|
||||||
is not a valid placeholder, because `()` is a capturing group. Instead you can use either
|
|
||||||
`{lang:en|de}` or `{lang:(?:en|de)}`.
|
|
||||||
|
|
||||||
Furthermore parts of the route enclosed in `[...]` are considered optional, so that `/foo[bar]`
|
|
||||||
will match both `/foo` and `/foobar`. Optional parts are only supported in a trailing position,
|
|
||||||
not in the middle of a route.
|
|
||||||
|
|
||||||
```php
|
|
||||||
// This route
|
|
||||||
$r->addRoute('GET', '/user/{id:\d+}[/{name}]', 'handler');
|
|
||||||
// Is equivalent to these two routes
|
|
||||||
$r->addRoute('GET', '/user/{id:\d+}', 'handler');
|
|
||||||
$r->addRoute('GET', '/user/{id:\d+}/{name}', 'handler');
|
|
||||||
|
|
||||||
// Multiple nested optional parts are possible as well
|
|
||||||
$r->addRoute('GET', '/user[/{id:\d+}[/{name}]]', 'handler');
|
|
||||||
|
|
||||||
// This route is NOT valid, because optional parts can only occur at the end
|
|
||||||
$r->addRoute('GET', '/user[/{id:\d+}]/{name}', 'handler');
|
|
||||||
```
|
|
||||||
|
|
||||||
The `$handler` parameter does not necessarily have to be a callback, it could also be a controller
|
|
||||||
class name or any other kind of data you wish to associate with the route. FastRoute only tells you
|
|
||||||
which handler corresponds to your URI, how you interpret it is up to you.
|
|
||||||
|
|
||||||
#### Shorcut methods for common request methods
|
|
||||||
|
|
||||||
For the `GET`, `POST`, `PUT`, `PATCH`, `DELETE` and `HEAD` request methods shortcut methods are available. For example:
|
|
||||||
|
|
||||||
```php
|
|
||||||
$r->get('/get-route', 'get_handler');
|
|
||||||
$r->post('/post-route', 'post_handler');
|
|
||||||
```
|
|
||||||
|
|
||||||
Is equivalent to:
|
|
||||||
|
|
||||||
```php
|
|
||||||
$r->addRoute('GET', '/get-route', 'get_handler');
|
|
||||||
$r->addRoute('POST', '/post-route', 'post_handler');
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Route Groups
|
|
||||||
|
|
||||||
Additionally, you can specify routes inside of a group. All routes defined inside a group will have a common prefix.
|
|
||||||
|
|
||||||
For example, defining your routes as:
|
|
||||||
|
|
||||||
```php
|
|
||||||
$r->addGroup('/admin', function (RouteCollector $r) {
|
|
||||||
$r->addRoute('GET', '/do-something', 'handler');
|
|
||||||
$r->addRoute('GET', '/do-another-thing', 'handler');
|
|
||||||
$r->addRoute('GET', '/do-something-else', 'handler');
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
Will have the same result as:
|
|
||||||
|
|
||||||
```php
|
|
||||||
$r->addRoute('GET', '/admin/do-something', 'handler');
|
|
||||||
$r->addRoute('GET', '/admin/do-another-thing', 'handler');
|
|
||||||
$r->addRoute('GET', '/admin/do-something-else', 'handler');
|
|
||||||
```
|
|
||||||
|
|
||||||
Nested groups are also supported, in which case the prefixes of all the nested groups are combined.
|
|
||||||
|
|
||||||
### Caching
|
|
||||||
|
|
||||||
The reason `simpleDispatcher` accepts a callback for defining the routes is to allow seamless
|
|
||||||
caching. By using `cachedDispatcher` instead of `simpleDispatcher` you can cache the generated
|
|
||||||
routing data and construct the dispatcher from the cached information:
|
|
||||||
|
|
||||||
```php
|
|
||||||
<?php
|
|
||||||
|
|
||||||
$dispatcher = FastRoute\cachedDispatcher(function(FastRoute\RouteCollector $r) {
|
|
||||||
$r->addRoute('GET', '/user/{name}/{id:[0-9]+}', 'handler0');
|
|
||||||
$r->addRoute('GET', '/user/{id:[0-9]+}', 'handler1');
|
|
||||||
$r->addRoute('GET', '/user/{name}', 'handler2');
|
|
||||||
}, [
|
|
||||||
'cacheFile' => __DIR__ . '/route.cache', /* required */
|
|
||||||
'cacheDisabled' => IS_DEBUG_ENABLED, /* optional, enabled by default */
|
|
||||||
]);
|
|
||||||
```
|
|
||||||
|
|
||||||
The second parameter to the function is an options array, which can be used to specify the cache
|
|
||||||
file location, among other things.
|
|
||||||
|
|
||||||
### Dispatching a URI
|
|
||||||
|
|
||||||
A URI is dispatched by calling the `dispatch()` method of the created dispatcher. This method
|
|
||||||
accepts the HTTP method and a URI. Getting those two bits of information (and normalizing them
|
|
||||||
appropriately) is your job - this library is not bound to the PHP web SAPIs.
|
|
||||||
|
|
||||||
The `dispatch()` method returns an array whose first element contains a status code. It is one
|
|
||||||
of `Dispatcher::NOT_FOUND`, `Dispatcher::METHOD_NOT_ALLOWED` and `Dispatcher::FOUND`. For the
|
|
||||||
method not allowed status the second array element contains a list of HTTP methods allowed for
|
|
||||||
the supplied URI. For example:
|
|
||||||
|
|
||||||
[FastRoute\Dispatcher::METHOD_NOT_ALLOWED, ['GET', 'POST']]
|
|
||||||
|
|
||||||
> **NOTE:** The HTTP specification requires that a `405 Method Not Allowed` response include the
|
|
||||||
`Allow:` header to detail available methods for the requested resource. Applications using FastRoute
|
|
||||||
should use the second array element to add this header when relaying a 405 response.
|
|
||||||
|
|
||||||
For the found status the second array element is the handler that was associated with the route
|
|
||||||
and the third array element is a dictionary of placeholder names to their values. For example:
|
|
||||||
|
|
||||||
/* Routing against GET /user/nikic/42 */
|
|
||||||
|
|
||||||
[FastRoute\Dispatcher::FOUND, 'handler0', ['name' => 'nikic', 'id' => '42']]
|
|
||||||
|
|
||||||
### Overriding the route parser and dispatcher
|
|
||||||
|
|
||||||
The routing process makes use of three components: A route parser, a data generator and a
|
|
||||||
dispatcher. The three components adhere to the following interfaces:
|
|
||||||
|
|
||||||
```php
|
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace FastRoute;
|
|
||||||
|
|
||||||
interface RouteParser {
|
|
||||||
public function parse($route);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DataGenerator {
|
|
||||||
public function addRoute($httpMethod, $routeData, $handler);
|
|
||||||
public function getData();
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Dispatcher {
|
|
||||||
const NOT_FOUND = 0, FOUND = 1, METHOD_NOT_ALLOWED = 2;
|
|
||||||
|
|
||||||
public function dispatch($httpMethod, $uri);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The route parser takes a route pattern string and converts it into an array of route infos, where
|
|
||||||
each route info is again an array of it's parts. The structure is best understood using an example:
|
|
||||||
|
|
||||||
/* The route /user/{id:\d+}[/{name}] converts to the following array: */
|
|
||||||
[
|
|
||||||
[
|
|
||||||
'/user/',
|
|
||||||
['id', '\d+'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'/user/',
|
|
||||||
['id', '\d+'],
|
|
||||||
'/',
|
|
||||||
['name', '[^/]+'],
|
|
||||||
],
|
|
||||||
]
|
|
||||||
|
|
||||||
This array can then be passed to the `addRoute()` method of a data generator. After all routes have
|
|
||||||
been added the `getData()` of the generator is invoked, which returns all the routing data required
|
|
||||||
by the dispatcher. The format of this data is not further specified - it is tightly coupled to
|
|
||||||
the corresponding dispatcher.
|
|
||||||
|
|
||||||
The dispatcher accepts the routing data via a constructor and provides a `dispatch()` method, which
|
|
||||||
you're already familiar with.
|
|
||||||
|
|
||||||
The route parser can be overwritten individually (to make use of some different pattern syntax),
|
|
||||||
however the data generator and dispatcher should always be changed as a pair, as the output from
|
|
||||||
the former is tightly coupled to the input of the latter. The reason the generator and the
|
|
||||||
dispatcher are separate is that only the latter is needed when using caching (as the output of
|
|
||||||
the former is what is being cached.)
|
|
||||||
|
|
||||||
When using the `simpleDispatcher` / `cachedDispatcher` functions from above the override happens
|
|
||||||
through the options array:
|
|
||||||
|
|
||||||
```php
|
|
||||||
<?php
|
|
||||||
|
|
||||||
$dispatcher = FastRoute\simpleDispatcher(function(FastRoute\RouteCollector $r) {
|
|
||||||
/* ... */
|
|
||||||
}, [
|
|
||||||
'routeParser' => 'FastRoute\\RouteParser\\Std',
|
|
||||||
'dataGenerator' => 'FastRoute\\DataGenerator\\GroupCountBased',
|
|
||||||
'dispatcher' => 'FastRoute\\Dispatcher\\GroupCountBased',
|
|
||||||
]);
|
|
||||||
```
|
|
||||||
|
|
||||||
The above options array corresponds to the defaults. By replacing `GroupCountBased` by
|
|
||||||
`GroupPosBased` you could switch to a different dispatching strategy.
|
|
||||||
|
|
||||||
### A Note on HEAD Requests
|
|
||||||
|
|
||||||
The HTTP spec requires servers to [support both GET and HEAD methods][2616-511]:
|
|
||||||
|
|
||||||
> The methods GET and HEAD MUST be supported by all general-purpose servers
|
|
||||||
|
|
||||||
To avoid forcing users to manually register HEAD routes for each resource we fallback to matching an
|
|
||||||
available GET route for a given resource. The PHP web SAPI transparently removes the entity body
|
|
||||||
from HEAD responses so this behavior has no effect on the vast majority of users.
|
|
||||||
|
|
||||||
However, implementers using FastRoute outside the web SAPI environment (e.g. a custom server) MUST
|
|
||||||
NOT send entity bodies generated in response to HEAD requests. If you are a non-SAPI user this is
|
|
||||||
*your responsibility*; FastRoute has no purview to prevent you from breaking HTTP in such cases.
|
|
||||||
|
|
||||||
Finally, note that applications MAY always specify their own HEAD method route for a given
|
|
||||||
resource to bypass this behavior entirely.
|
|
||||||
|
|
||||||
### Credits
|
|
||||||
|
|
||||||
This library is based on a router that [Levi Morrison][levi] implemented for the Aerys server.
|
|
||||||
|
|
||||||
A large number of tests, as well as HTTP compliance considerations, were provided by [Daniel Lowrey][rdlowrey].
|
|
||||||
|
|
||||||
|
|
||||||
[2616-511]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.1 "RFC 2616 Section 5.1.1"
|
|
||||||
[blog_post]: http://nikic.github.io/2014/02/18/Fast-request-routing-using-regular-expressions.html
|
|
||||||
[levi]: https://github.com/morrisonlevi
|
|
||||||
[rdlowrey]: https://github.com/rdlowrey
|
|
@ -1,24 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "nikic/fast-route",
|
|
||||||
"description": "Fast request router for PHP",
|
|
||||||
"keywords": ["routing", "router"],
|
|
||||||
"license": "BSD-3-Clause",
|
|
||||||
"authors": [
|
|
||||||
{
|
|
||||||
"name": "Nikita Popov",
|
|
||||||
"email": "nikic@php.net"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"autoload": {
|
|
||||||
"psr-4": {
|
|
||||||
"FastRoute\\": "src/"
|
|
||||||
},
|
|
||||||
"files": ["src/functions.php"]
|
|
||||||
},
|
|
||||||
"require": {
|
|
||||||
"php": ">=5.4.0"
|
|
||||||
},
|
|
||||||
"require-dev": {
|
|
||||||
"phpunit/phpunit": "^4.8.35|~5.7"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
|
|
||||||
<phpunit backupGlobals="false"
|
|
||||||
backupStaticAttributes="false"
|
|
||||||
colors="true"
|
|
||||||
convertErrorsToExceptions="true"
|
|
||||||
convertNoticesToExceptions="true"
|
|
||||||
convertWarningsToExceptions="true"
|
|
||||||
processIsolation="false"
|
|
||||||
syntaxCheck="false"
|
|
||||||
bootstrap="test/bootstrap.php"
|
|
||||||
>
|
|
||||||
<testsuites>
|
|
||||||
<testsuite name="FastRoute Tests">
|
|
||||||
<directory>./test/</directory>
|
|
||||||
</testsuite>
|
|
||||||
</testsuites>
|
|
||||||
|
|
||||||
<filter>
|
|
||||||
<whitelist>
|
|
||||||
<directory>./src/</directory>
|
|
||||||
</whitelist>
|
|
||||||
</filter>
|
|
||||||
</phpunit>
|
|
@ -1,28 +0,0 @@
|
|||||||
<?xml version="1.0"?>
|
|
||||||
<psalm
|
|
||||||
name="Example Psalm config with recommended defaults"
|
|
||||||
stopOnFirstError="false"
|
|
||||||
useDocblockTypes="true"
|
|
||||||
totallyTyped="false"
|
|
||||||
requireVoidReturnType="false"
|
|
||||||
>
|
|
||||||
<projectFiles>
|
|
||||||
<directory name="src" />
|
|
||||||
</projectFiles>
|
|
||||||
|
|
||||||
<issueHandlers>
|
|
||||||
<LessSpecificReturnType errorLevel="info" />
|
|
||||||
|
|
||||||
<!-- level 3 issues - slightly lazy code writing, but provably low false-negatives -->
|
|
||||||
<DeprecatedMethod errorLevel="info" />
|
|
||||||
|
|
||||||
<MissingClosureReturnType errorLevel="info" />
|
|
||||||
<MissingReturnType errorLevel="info" />
|
|
||||||
<MissingPropertyType errorLevel="info" />
|
|
||||||
<InvalidDocblock errorLevel="info" />
|
|
||||||
<MisplacedRequiredParam errorLevel="info" />
|
|
||||||
|
|
||||||
<PropertyNotSetInConstructor errorLevel="info" />
|
|
||||||
<MissingConstructor errorLevel="info" />
|
|
||||||
</issueHandlers>
|
|
||||||
</psalm>
|
|
@ -1,7 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace FastRoute;
|
|
||||||
|
|
||||||
class BadRouteException extends \LogicException
|
|
||||||
{
|
|
||||||
}
|
|
@ -1,26 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace FastRoute;
|
|
||||||
|
|
||||||
interface DataGenerator
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Adds a route to the data generator. The route data uses the
|
|
||||||
* same format that is returned by RouterParser::parser().
|
|
||||||
*
|
|
||||||
* The handler doesn't necessarily need to be a callable, it
|
|
||||||
* can be arbitrary data that will be returned when the route
|
|
||||||
* matches.
|
|
||||||
*
|
|
||||||
* @param string $httpMethod
|
|
||||||
* @param array $routeData
|
|
||||||
* @param mixed $handler
|
|
||||||
*/
|
|
||||||
public function addRoute($httpMethod, $routeData, $handler);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns dispatcher data in some unspecified format, which
|
|
||||||
* depends on the used method of dispatch.
|
|
||||||
*/
|
|
||||||
public function getData();
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace FastRoute\DataGenerator;
|
|
||||||
|
|
||||||
class CharCountBased extends RegexBasedAbstract
|
|
||||||
{
|
|
||||||
protected function getApproxChunkSize()
|
|
||||||
{
|
|
||||||
return 30;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function processChunk($regexToRoutesMap)
|
|
||||||
{
|
|
||||||
$routeMap = [];
|
|
||||||
$regexes = [];
|
|
||||||
|
|
||||||
$suffixLen = 0;
|
|
||||||
$suffix = '';
|
|
||||||
$count = count($regexToRoutesMap);
|
|
||||||
foreach ($regexToRoutesMap as $regex => $route) {
|
|
||||||
$suffixLen++;
|
|
||||||
$suffix .= "\t";
|
|
||||||
|
|
||||||
$regexes[] = '(?:' . $regex . '/(\t{' . $suffixLen . '})\t{' . ($count - $suffixLen) . '})';
|
|
||||||
$routeMap[$suffix] = [$route->handler, $route->variables];
|
|
||||||
}
|
|
||||||
|
|
||||||
$regex = '~^(?|' . implode('|', $regexes) . ')$~';
|
|
||||||
return ['regex' => $regex, 'suffix' => '/' . $suffix, 'routeMap' => $routeMap];
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,30 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace FastRoute\DataGenerator;
|
|
||||||
|
|
||||||
class GroupCountBased extends RegexBasedAbstract
|
|
||||||
{
|
|
||||||
protected function getApproxChunkSize()
|
|
||||||
{
|
|
||||||
return 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function processChunk($regexToRoutesMap)
|
|
||||||
{
|
|
||||||
$routeMap = [];
|
|
||||||
$regexes = [];
|
|
||||||
$numGroups = 0;
|
|
||||||
foreach ($regexToRoutesMap as $regex => $route) {
|
|
||||||
$numVariables = count($route->variables);
|
|
||||||
$numGroups = max($numGroups, $numVariables);
|
|
||||||
|
|
||||||
$regexes[] = $regex . str_repeat('()', $numGroups - $numVariables);
|
|
||||||
$routeMap[$numGroups + 1] = [$route->handler, $route->variables];
|
|
||||||
|
|
||||||
++$numGroups;
|
|
||||||
}
|
|
||||||
|
|
||||||
$regex = '~^(?|' . implode('|', $regexes) . ')$~';
|
|
||||||
return ['regex' => $regex, 'routeMap' => $routeMap];
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,27 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace FastRoute\DataGenerator;
|
|
||||||
|
|
||||||
class GroupPosBased extends RegexBasedAbstract
|
|
||||||
{
|
|
||||||
protected function getApproxChunkSize()
|
|
||||||
{
|
|
||||||
return 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function processChunk($regexToRoutesMap)
|
|
||||||
{
|
|
||||||
$routeMap = [];
|
|
||||||
$regexes = [];
|
|
||||||
$offset = 1;
|
|
||||||
foreach ($regexToRoutesMap as $regex => $route) {
|
|
||||||
$regexes[] = $regex;
|
|
||||||
$routeMap[$offset] = [$route->handler, $route->variables];
|
|
||||||
|
|
||||||
$offset += count($route->variables);
|
|
||||||
}
|
|
||||||
|
|
||||||
$regex = '~^(?:' . implode('|', $regexes) . ')$~';
|
|
||||||
return ['regex' => $regex, 'routeMap' => $routeMap];
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,27 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace FastRoute\DataGenerator;
|
|
||||||
|
|
||||||
class MarkBased extends RegexBasedAbstract
|
|
||||||
{
|
|
||||||
protected function getApproxChunkSize()
|
|
||||||
{
|
|
||||||
return 30;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function processChunk($regexToRoutesMap)
|
|
||||||
{
|
|
||||||
$routeMap = [];
|
|
||||||
$regexes = [];
|
|
||||||
$markName = 'a';
|
|
||||||
foreach ($regexToRoutesMap as $regex => $route) {
|
|
||||||
$regexes[] = $regex . '(*MARK:' . $markName . ')';
|
|
||||||
$routeMap[$markName] = [$route->handler, $route->variables];
|
|
||||||
|
|
||||||
++$markName;
|
|
||||||
}
|
|
||||||
|
|
||||||
$regex = '~^(?|' . implode('|', $regexes) . ')$~';
|
|
||||||
return ['regex' => $regex, 'routeMap' => $routeMap];
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,186 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace FastRoute\DataGenerator;
|
|
||||||
|
|
||||||
use FastRoute\BadRouteException;
|
|
||||||
use FastRoute\DataGenerator;
|
|
||||||
use FastRoute\Route;
|
|
||||||
|
|
||||||
abstract class RegexBasedAbstract implements DataGenerator
|
|
||||||
{
|
|
||||||
/** @var mixed[][] */
|
|
||||||
protected $staticRoutes = [];
|
|
||||||
|
|
||||||
/** @var Route[][] */
|
|
||||||
protected $methodToRegexToRoutesMap = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
abstract protected function getApproxChunkSize();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return mixed[]
|
|
||||||
*/
|
|
||||||
abstract protected function processChunk($regexToRoutesMap);
|
|
||||||
|
|
||||||
public function addRoute($httpMethod, $routeData, $handler)
|
|
||||||
{
|
|
||||||
if ($this->isStaticRoute($routeData)) {
|
|
||||||
$this->addStaticRoute($httpMethod, $routeData, $handler);
|
|
||||||
} else {
|
|
||||||
$this->addVariableRoute($httpMethod, $routeData, $handler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return mixed[]
|
|
||||||
*/
|
|
||||||
public function getData()
|
|
||||||
{
|
|
||||||
if (empty($this->methodToRegexToRoutesMap)) {
|
|
||||||
return [$this->staticRoutes, []];
|
|
||||||
}
|
|
||||||
|
|
||||||
return [$this->staticRoutes, $this->generateVariableRouteData()];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return mixed[]
|
|
||||||
*/
|
|
||||||
private function generateVariableRouteData()
|
|
||||||
{
|
|
||||||
$data = [];
|
|
||||||
foreach ($this->methodToRegexToRoutesMap as $method => $regexToRoutesMap) {
|
|
||||||
$chunkSize = $this->computeChunkSize(count($regexToRoutesMap));
|
|
||||||
$chunks = array_chunk($regexToRoutesMap, $chunkSize, true);
|
|
||||||
$data[$method] = array_map([$this, 'processChunk'], $chunks);
|
|
||||||
}
|
|
||||||
return $data;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param int
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
private function computeChunkSize($count)
|
|
||||||
{
|
|
||||||
$numParts = max(1, round($count / $this->getApproxChunkSize()));
|
|
||||||
return (int) ceil($count / $numParts);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param mixed[]
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
private function isStaticRoute($routeData)
|
|
||||||
{
|
|
||||||
return count($routeData) === 1 && is_string($routeData[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function addStaticRoute($httpMethod, $routeData, $handler)
|
|
||||||
{
|
|
||||||
$routeStr = $routeData[0];
|
|
||||||
|
|
||||||
if (isset($this->staticRoutes[$httpMethod][$routeStr])) {
|
|
||||||
throw new BadRouteException(sprintf(
|
|
||||||
'Cannot register two routes matching "%s" for method "%s"',
|
|
||||||
$routeStr, $httpMethod
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($this->methodToRegexToRoutesMap[$httpMethod])) {
|
|
||||||
foreach ($this->methodToRegexToRoutesMap[$httpMethod] as $route) {
|
|
||||||
if ($route->matches($routeStr)) {
|
|
||||||
throw new BadRouteException(sprintf(
|
|
||||||
'Static route "%s" is shadowed by previously defined variable route "%s" for method "%s"',
|
|
||||||
$routeStr, $route->regex, $httpMethod
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->staticRoutes[$httpMethod][$routeStr] = $handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function addVariableRoute($httpMethod, $routeData, $handler)
|
|
||||||
{
|
|
||||||
list($regex, $variables) = $this->buildRegexForRoute($routeData);
|
|
||||||
|
|
||||||
if (isset($this->methodToRegexToRoutesMap[$httpMethod][$regex])) {
|
|
||||||
throw new BadRouteException(sprintf(
|
|
||||||
'Cannot register two routes matching "%s" for method "%s"',
|
|
||||||
$regex, $httpMethod
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->methodToRegexToRoutesMap[$httpMethod][$regex] = new Route(
|
|
||||||
$httpMethod, $handler, $regex, $variables
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param mixed[]
|
|
||||||
* @return mixed[]
|
|
||||||
*/
|
|
||||||
private function buildRegexForRoute($routeData)
|
|
||||||
{
|
|
||||||
$regex = '';
|
|
||||||
$variables = [];
|
|
||||||
foreach ($routeData as $part) {
|
|
||||||
if (is_string($part)) {
|
|
||||||
$regex .= preg_quote($part, '~');
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
list($varName, $regexPart) = $part;
|
|
||||||
|
|
||||||
if (isset($variables[$varName])) {
|
|
||||||
throw new BadRouteException(sprintf(
|
|
||||||
'Cannot use the same placeholder "%s" twice', $varName
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->regexHasCapturingGroups($regexPart)) {
|
|
||||||
throw new BadRouteException(sprintf(
|
|
||||||
'Regex "%s" for parameter "%s" contains a capturing group',
|
|
||||||
$regexPart, $varName
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
$variables[$varName] = $varName;
|
|
||||||
$regex .= '(' . $regexPart . ')';
|
|
||||||
}
|
|
||||||
|
|
||||||
return [$regex, $variables];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
private function regexHasCapturingGroups($regex)
|
|
||||||
{
|
|
||||||
if (false === strpos($regex, '(')) {
|
|
||||||
// Needs to have at least a ( to contain a capturing group
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Semi-accurate detection for capturing groups
|
|
||||||
return (bool) preg_match(
|
|
||||||
'~
|
|
||||||
(?:
|
|
||||||
\(\?\(
|
|
||||||
| \[ [^\]\\\\]* (?: \\\\ . [^\]\\\\]* )* \]
|
|
||||||
| \\\\ .
|
|
||||||
) (*SKIP)(*FAIL) |
|
|
||||||
\(
|
|
||||||
(?!
|
|
||||||
\? (?! <(?![!=]) | P< | \' )
|
|
||||||
| \*
|
|
||||||
)
|
|
||||||
~x',
|
|
||||||
$regex
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,26 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace FastRoute;
|
|
||||||
|
|
||||||
interface Dispatcher
|
|
||||||
{
|
|
||||||
const NOT_FOUND = 0;
|
|
||||||
const FOUND = 1;
|
|
||||||
const METHOD_NOT_ALLOWED = 2;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dispatches against the provided HTTP method verb and URI.
|
|
||||||
*
|
|
||||||
* Returns array with one of the following formats:
|
|
||||||
*
|
|
||||||
* [self::NOT_FOUND]
|
|
||||||
* [self::METHOD_NOT_ALLOWED, ['GET', 'OTHER_ALLOWED_METHODS']]
|
|
||||||
* [self::FOUND, $handler, ['varName' => 'value', ...]]
|
|
||||||
*
|
|
||||||
* @param string $httpMethod
|
|
||||||
* @param string $uri
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function dispatch($httpMethod, $uri);
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace FastRoute\Dispatcher;
|
|
||||||
|
|
||||||
class CharCountBased extends RegexBasedAbstract
|
|
||||||
{
|
|
||||||
public function __construct($data)
|
|
||||||
{
|
|
||||||
list($this->staticRouteMap, $this->variableRouteData) = $data;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function dispatchVariableRoute($routeData, $uri)
|
|
||||||
{
|
|
||||||
foreach ($routeData as $data) {
|
|
||||||
if (!preg_match($data['regex'], $uri . $data['suffix'], $matches)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
list($handler, $varNames) = $data['routeMap'][end($matches)];
|
|
||||||
|
|
||||||
$vars = [];
|
|
||||||
$i = 0;
|
|
||||||
foreach ($varNames as $varName) {
|
|
||||||
$vars[$varName] = $matches[++$i];
|
|
||||||
}
|
|
||||||
return [self::FOUND, $handler, $vars];
|
|
||||||
}
|
|
||||||
|
|
||||||
return [self::NOT_FOUND];
|
|
||||||
}
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue