Compare commits

..

No commits in common. 'master' and 'UI_Mobile' have entirely different histories.

@ -10,13 +10,11 @@ steps:
- name: build
image: mcr.microsoft.com/dotnet/sdk:6.0
commands:
- dotnet add package Newtonsoft.Json
- cd Sources
- dotnet workload restore
- dotnet restore CI_MAUI.sln
- dotnet build CI_MAUI.sln -c Release --no-restore
- dotnet publish CI_MAUI.sln -c Release --no-restore -o CI_PROJECT_DIR/build/release
- name: tests
image: mcr.microsoft.com/dotnet/sdk:6.0
commands:
@ -24,11 +22,9 @@ steps:
- dotnet restore CI_MAUI.sln
- dotnet test CI_MAUI.sln --no-restore
depends_on: [build]
- name: code-analysis
image: hub.codefirst.iut.uca.fr/thomas.bellembois/codefirst-dronesonarplugin-dotnet6
commands:
- dotnet add package Newtonsoft.Json
- cd Sources/
- dotnet workload restore
- dotnet restore CI_MAUI.sln
@ -46,52 +42,13 @@ steps:
sonar_token:
from_secret: SECRET_SONAR_LOGIN
depends_on: [tests]
# database container deployment
- name: deploy-container-mysql
image: hub.codefirst.iut.uca.fr/thomas.bellembois/codefirst-dockerproxy-clientdrone:latest
environment:
IMAGENAME: mariadb:latest
CONTAINERNAME: mariadb
IMAGENAME: postgres
CONTAINERNAME: mysql
COMMAND: create
OVERWRITE: true
PRIVATE: false
CODEFIRST_CLIENTDRONE_ENV_MARIADB_ROOT_PASSWORD:
from_secret: db_root_password
CODEFIRST_CLIENTDRONE_ENV_MARIADB_DATABASE:
from_secret: db_database
CODEFIRST_CLIENTDRONE_ENV_MARIADB_USER:
from_secret: db_user
CODEFIRST_CLIENTDRONE_ENV_MARIADB_PASSWORD:
from_secret: db_password
- name: web-API
image: plugins/docker
settings:
dockerfile: ./Dockerfile
context: ./
registry: hub.codefirst.iut.uca.fr
repo: hub.codefirst.iut.uca.fr/vincent.astolfi/conseco
username:
from_secret: secret-registry-username
password:
from_secret: secret-registry-password
PRIVATE: true
POSTGRES_PASSWORD: oui
- name: deploy-web-container
image: hub.codefirst.iut.uca.fr/thomas.bellembois/codefirst-dockerproxy-clientdrone:latest
environment:
IMAGENAME: hub.codefirst.iut.uca.fr/vincent.astolfi/conseco:latest
CONTAINERNAME: conseco
COMMAND: create
OVERWRITE: true
CODEFIRST_CLIENTDRONE_ENV_DB_SERVER:
from_secret: db_server
CODEFIRST_CLIENTDRONE_ENV_MARIADB_ROOT_PASSWORD:
from_secret: db_root_password
CODEFIRST_CLIENTDRONE_ENV_MARIADB_DATABASE:
from_secret: db_database
CODEFIRST_CLIENTDRONE_ENV_MARIADB_USER:
from_secret: db_user
CODEFIRST_CLIENTDRONE_ENV_MARIADB_PASSWORD:
from_secret: db_password
depends_on: [ web-API, deploy-container-mysql ]

@ -1,6 +1,20 @@
FROM php:8.1-apache
RUN apt-get update && apt-get install -y mariadb-client
RUN docker-php-ext-install mysqli pdo pdo_mysql && docker-php-ext-enable pdo_mysql
COPY ./Sources/API /var/www/html/
COPY ./Sources/Data /sql/
RUN cd /sql/
FROM aosapps/drone-sonar-plugin AS base
FROM mcr.microsoft.com/dotnet/sdk:6.0-alpine
COPY --from=base /bin/drone-sonar /bin/
WORKDIR /bin
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.

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,7 +1,7 @@
# Cons'Eco
[![Build Status](https://codefirst.iut.uca.fr/api/badges/ConsEcoTeam/ConsEco/status.svg)](https://codefirst.iut.uca.fr/hugo.livet/ConsEco)
[![Build Status](https://codefirst.iut.uca.fr/api/badges/hugo.livet/ConsEco/status.svg)](https://codefirst.iut.uca.fr/hugo.livet/ConsEco)
[![Csharp](https://img.shields.io/badge/-CSharp-50C878?style=for-the-badge&logo=csharp)](https://learn.microsoft.com/fr-fr/dotnet/csharp/) [![Xaml](https://img.shields.io/badge/-XAML-6495ED?style=for-the-badge&logo=xaml)](https://learn.microsoft.com/fr-fr/dotnet/desktop/wpf/xaml/?view=netdesktop-6.0) [![.NET/WPF](https://img.shields.io/badge/-.NET/MAUI-B87333?style=for-the-badge&logo=dotnet)](https://learn.microsoft.com/fr-fr/dotnet/maui/what-is-maui?view=net-maui-6.0)
[![Csharp](https://img.shields.io/badge/-CSharp-50C878?style=for-the-badge&logo=csharp)](https://learn.microsoft.com/fr-fr/dotnet/csharp/) [![Xaml](https://img.shields.io/badge/-XAML-6495ED?style=for-the-badge&logo=xaml)](https://learn.microsoft.com/fr-fr/dotnet/desktop/wpf/xaml/?view=netdesktop-6.0) [![.NET/WPF](https://img.shields.io/badge/-.NET/WPF-B87333?style=for-the-badge&logo=dotnet)](https://learn.microsoft.com/fr-fr/dotnet/desktop/wpf/?view=netdesktop-6.0)
<br />
<div align="center">

@ -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,110 +0,0 @@
Deprecation notice
==================
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.
Introduction
============
Doctrine Annotations allows to implement custom annotation
functionality for PHP classes and functions.
.. code-block:: php
class Foo
{
/**
* @MyAnnotation(myProperty="value")
*/
private $bar;
}
Annotations aren't implemented in PHP itself which is why this component
offers a way to use the PHP doc-blocks as a place for the well known
annotation syntax using the ``@`` char.
Annotations in Doctrine are used for the ORM configuration to build the
class mapping, but it can be used in other projects for other purposes
too.
Installation
============
You can install the Annotation component with composer:
.. code-block::
  $ composer require doctrine/annotations
Create an annotation class
==========================
An annotation class is a representation of the later used annotation
configuration in classes. The annotation class of the previous example
looks like this:
.. code-block:: php
/**
* @Annotation
*/
final class MyAnnotation
{
public $myProperty;
}
The annotation class is declared as an annotation by ``@Annotation``.
:ref:`Read more about custom annotations. <custom>`
Reading annotations
===================
The access to the annotations happens by reflection of the class or function
containing them. There are multiple reader-classes implementing the
``Doctrine\Common\Annotations\Reader`` interface, that can access the
annotations of a class. A common one is
``Doctrine\Common\Annotations\AnnotationReader``:
.. code-block:: php
use Doctrine\Common\Annotations\AnnotationReader;
use Doctrine\Common\Annotations\AnnotationRegistry;
// Deprecated and will be removed in 2.0 but currently needed
AnnotationRegistry::registerLoader('class_exists');
$reflectionClass = new ReflectionClass(Foo::class);
$property = $reflectionClass->getProperty('bar');
$reader = new AnnotationReader();
$myAnnotation = $reader->getPropertyAnnotation(
$property,
MyAnnotation::class
);
echo $myAnnotation->myProperty; // result: "value"
Note that ``AnnotationRegistry::registerLoader('class_exists')`` only works
if you already have an autoloader configured (i.e. composer autoloader).
Otherwise, :ref:`please take a look to the other annotation autoload mechanisms <annotations>`.
A reader has multiple methods to access the annotations of a class or
function.
:ref:`Read more about handling annotations. <annotations>`
IDE Support
-----------
Some IDEs already provide support for annotations:
- Eclipse via the `Symfony2 Plugin <https://github.com/pulse00/Symfony-2-Eclipse-Plugin>`_
- PhpStorm via the `PHP Annotations Plugin <https://plugins.jetbrains.com/plugin/7320-php-annotations>`_ or the `Symfony Plugin <https://plugins.jetbrains.com/plugin/7219-symfony-support>`_
.. _Read more about handling annotations.: annotations
.. _Read more about custom annotations.: custom

@ -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;
}
}

@ -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,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,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…
Cancel
Save