merge with API

clean_web
David D'ALMEIDA 1 year ago
commit ef572e5a2b

@ -1,61 +0,0 @@
kind: pipeline
type: docker
name: HeartWave
trigger:
event:
- push
steps:
# Test ✔️
- name: test
image: composer:2.6
commands:
- cd Sources
# Installe les dépendances PHP si nécessaire
- composer install --no-interaction
- ./vendor/bin/phpunit tests
# Sonar static code analisis deployment
# TODO : use an image that already have unzip
- name: code-analysis
image: php:8.1-cli
environment:
SONAR_TOKEN:
from_secret: SONAR_TOKEN
commands:
- apt-get update && apt-get install -y curl unzip
- export SONAR_SCANNER_VERSION=4.7.0.2747
- export SONAR_SCANNER_HOME=$HOME/.sonar/sonar-scanner-$SONAR_SCANNER_VERSION-linux
- curl --create-dirs -sSLo $HOME/.sonar/sonar-scanner.zip https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-$SONAR_SCANNER_VERSION-linux.zip
- unzip -o $HOME/.sonar/sonar-scanner.zip -d $HOME/.sonar/
- export PATH=$SONAR_SCANNER_HOME/bin:$PATH
- export SONAR_SCANNER_OPTS="-server"
- cd Sources
- sonar-scanner -D sonar.projectKey=HeartTrack -D sonar.host.url=https://codefirst.iut.uca.fr/sonar
depends_on: [ test ]
# build image and push on the registry ✔️
- name: docker-build-and-push
image: plugins/docker
settings:
dockerfile: Sources/config/Dockerfile
context: Sources
registry: hub.codefirst.iut.uca.fr
repo: hub.codefirst.iut.uca.fr/david.d_almeida/web
username:
from_secret: SECRET_REGISTRY_USERNAME
password:
from_secret: SECRET_REGISTRY_PASSWORD
- name: notify
image: ruby:2.1
when:
status: [ success ]
ref:
include:
- refs/tags/*-demo
commands:
- sh ./notifymail.sh
depends_on: [ docker-build-and-push ]

5
.gitignore vendored

@ -6,7 +6,12 @@ dist
*.swo *.swo
.env .env
loginDatabase.php
# Cache file on macOS
.DS_Store .DS_Store
.phpunit.cache
# Cache files for Sublime Text # Cache files for Sublime Text
*.tmlanguage.cache *.tmlanguage.cache
*.tmPreferences.cache *.tmPreferences.cache

@ -176,3 +176,118 @@ notif --> athlete
athlete <-- friendship athlete <-- friendship
@enduml @enduml
``` ```
```plantuml
@startuml
class Athlete {
idAthlete
username
nom
prenom
email
sexe
taille
poids
motDePasse
dateNaissance
isCoach
}
class Amitie {
idAthlete1
idAthlete2
}
class Notification {
idNotif
message
date
statut
urgence
athleteId
}
class Envoi {
idAthlete
idNotif
}
class Statistique {
idStatistique
poids
fcMoyenne
fcMax
caloriesBruleesMoy
date
athleteId
}
class Entrainement {
idEntrainement
date
description
latitude
longitude
feedback
athleteId
}
class Participe {
athleteId
entrainementId
}
class Donne {
coachId
entrainementId
}
class SourceDonnee {
idSource
type
modele
precision
athleteId
}
class Activite {
idActivite
type
date
heureDeDebut
heureDeFin
effortRessent
variabilite
variance
ecartType
moyenne
maximum
minimum
temperatureMoyenne
athleteId
sourceId
}
class FrequenceCardiaque {
idFc
altitude
temps
temperature
bpm
longitude
latitude
activiteId
}
Athlete "1" --o "0..*" Amitie
Athlete "1" --o "0..*" Notification
Athlete "1" --o "0..*" Statistique
Athlete "1" --o "0..*" Entrainement
Entrainement "0..*" --o "0..*" Athlete : Participants
Entrainement "0..*" --o "0..*" Athlete : Coachs
Athlete "1" --o "0..*" SourceDonnee
Activite "1" --o "0..*" FrequenceCardiaque
@enduml
```

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "1887e85fc3cfddacf8d7e17588dae6f1", "content-hash": "5a409c8bab8a792898aeb0f32ad5cf48",
"packages": [ "packages": [
{ {
"name": "adriangibbons/php-fit-file-analysis", "name": "adriangibbons/php-fit-file-analysis",
@ -756,25 +756,27 @@
}, },
{ {
"name": "nikic/php-parser", "name": "nikic/php-parser",
"version": "v4.17.1", "version": "v5.0.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/nikic/PHP-Parser.git", "url": "https://github.com/nikic/PHP-Parser.git",
"reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d" "reference": "4a21235f7e56e713259a6f76bf4b5ea08502b9dc"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/4a21235f7e56e713259a6f76bf4b5ea08502b9dc",
"reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", "reference": "4a21235f7e56e713259a6f76bf4b5ea08502b9dc",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"ext-ctype": "*",
"ext-json": "*",
"ext-tokenizer": "*", "ext-tokenizer": "*",
"php": ">=7.0" "php": ">=7.4"
}, },
"require-dev": { "require-dev": {
"ircmaxell/php-yacc": "^0.0.7", "ircmaxell/php-yacc": "^0.0.7",
"phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0"
}, },
"bin": [ "bin": [
"bin/php-parse" "bin/php-parse"
@ -782,7 +784,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "4.9-dev" "dev-master": "5.0-dev"
} }
}, },
"autoload": { "autoload": {
@ -806,9 +808,9 @@
], ],
"support": { "support": {
"issues": "https://github.com/nikic/PHP-Parser/issues", "issues": "https://github.com/nikic/PHP-Parser/issues",
"source": "https://github.com/nikic/PHP-Parser/tree/v4.17.1" "source": "https://github.com/nikic/PHP-Parser/tree/v5.0.0"
}, },
"time": "2023-08-13T19:53:39+00:00" "time": "2024-01-07T17:17:35+00:00"
}, },
{ {
"name": "phar-io/manifest", "name": "phar-io/manifest",
@ -923,23 +925,23 @@
}, },
{ {
"name": "phpunit/php-code-coverage", "name": "phpunit/php-code-coverage",
"version": "10.1.9", "version": "10.1.11",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "a56a9ab2f680246adcf3db43f38ddf1765774735" "reference": "78c3b7625965c2513ee96569a4dbb62601784145"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/a56a9ab2f680246adcf3db43f38ddf1765774735", "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/78c3b7625965c2513ee96569a4dbb62601784145",
"reference": "a56a9ab2f680246adcf3db43f38ddf1765774735", "reference": "78c3b7625965c2513ee96569a4dbb62601784145",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"ext-dom": "*", "ext-dom": "*",
"ext-libxml": "*", "ext-libxml": "*",
"ext-xmlwriter": "*", "ext-xmlwriter": "*",
"nikic/php-parser": "^4.15", "nikic/php-parser": "^4.18 || ^5.0",
"php": ">=8.1", "php": ">=8.1",
"phpunit/php-file-iterator": "^4.0", "phpunit/php-file-iterator": "^4.0",
"phpunit/php-text-template": "^3.0", "phpunit/php-text-template": "^3.0",
@ -989,7 +991,7 @@
"support": { "support": {
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
"security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.9" "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.11"
}, },
"funding": [ "funding": [
{ {
@ -997,7 +999,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2023-11-23T12:23:20+00:00" "time": "2023-12-21T15:38:30+00:00"
}, },
{ {
"name": "phpunit/php-file-iterator", "name": "phpunit/php-file-iterator",
@ -1244,16 +1246,16 @@
}, },
{ {
"name": "phpunit/phpunit", "name": "phpunit/phpunit",
"version": "10.4.2", "version": "10.5.5",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git", "url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "cacd8b9dd224efa8eb28beb69004126c7ca1a1a1" "reference": "ed21115d505b4b4f7dc7b5651464e19a2c7f7856"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/cacd8b9dd224efa8eb28beb69004126c7ca1a1a1", "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/ed21115d505b4b4f7dc7b5651464e19a2c7f7856",
"reference": "cacd8b9dd224efa8eb28beb69004126c7ca1a1a1", "reference": "ed21115d505b4b4f7dc7b5651464e19a2c7f7856",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1293,7 +1295,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "10.4-dev" "dev-main": "10.5-dev"
} }
}, },
"autoload": { "autoload": {
@ -1325,7 +1327,7 @@
"support": { "support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues", "issues": "https://github.com/sebastianbergmann/phpunit/issues",
"security": "https://github.com/sebastianbergmann/phpunit/security/policy", "security": "https://github.com/sebastianbergmann/phpunit/security/policy",
"source": "https://github.com/sebastianbergmann/phpunit/tree/10.4.2" "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.5"
}, },
"funding": [ "funding": [
{ {
@ -1341,7 +1343,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2023-10-26T07:21:45+00:00" "time": "2023-12-27T15:13:52+00:00"
}, },
{ {
"name": "sebastian/cli-parser", "name": "sebastian/cli-parser",
@ -1589,20 +1591,20 @@
}, },
{ {
"name": "sebastian/complexity", "name": "sebastian/complexity",
"version": "3.1.0", "version": "3.2.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/complexity.git", "url": "https://github.com/sebastianbergmann/complexity.git",
"reference": "68cfb347a44871f01e33ab0ef8215966432f6957" "reference": "68ff824baeae169ec9f2137158ee529584553799"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68cfb347a44871f01e33ab0ef8215966432f6957", "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68ff824baeae169ec9f2137158ee529584553799",
"reference": "68cfb347a44871f01e33ab0ef8215966432f6957", "reference": "68ff824baeae169ec9f2137158ee529584553799",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"nikic/php-parser": "^4.10", "nikic/php-parser": "^4.18 || ^5.0",
"php": ">=8.1" "php": ">=8.1"
}, },
"require-dev": { "require-dev": {
@ -1611,7 +1613,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "3.1-dev" "dev-main": "3.2-dev"
} }
}, },
"autoload": { "autoload": {
@ -1635,7 +1637,7 @@
"support": { "support": {
"issues": "https://github.com/sebastianbergmann/complexity/issues", "issues": "https://github.com/sebastianbergmann/complexity/issues",
"security": "https://github.com/sebastianbergmann/complexity/security/policy", "security": "https://github.com/sebastianbergmann/complexity/security/policy",
"source": "https://github.com/sebastianbergmann/complexity/tree/3.1.0" "source": "https://github.com/sebastianbergmann/complexity/tree/3.2.0"
}, },
"funding": [ "funding": [
{ {
@ -1643,20 +1645,20 @@
"type": "github" "type": "github"
} }
], ],
"time": "2023-09-28T11:50:59+00:00" "time": "2023-12-21T08:37:17+00:00"
}, },
{ {
"name": "sebastian/diff", "name": "sebastian/diff",
"version": "5.0.3", "version": "5.1.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/diff.git", "url": "https://github.com/sebastianbergmann/diff.git",
"reference": "912dc2fbe3e3c1e7873313cc801b100b6c68c87b" "reference": "fbf413a49e54f6b9b17e12d900ac7f6101591b7f"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/912dc2fbe3e3c1e7873313cc801b100b6c68c87b", "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/fbf413a49e54f6b9b17e12d900ac7f6101591b7f",
"reference": "912dc2fbe3e3c1e7873313cc801b100b6c68c87b", "reference": "fbf413a49e54f6b9b17e12d900ac7f6101591b7f",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1669,7 +1671,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "5.0-dev" "dev-main": "5.1-dev"
} }
}, },
"autoload": { "autoload": {
@ -1702,7 +1704,7 @@
"support": { "support": {
"issues": "https://github.com/sebastianbergmann/diff/issues", "issues": "https://github.com/sebastianbergmann/diff/issues",
"security": "https://github.com/sebastianbergmann/diff/security/policy", "security": "https://github.com/sebastianbergmann/diff/security/policy",
"source": "https://github.com/sebastianbergmann/diff/tree/5.0.3" "source": "https://github.com/sebastianbergmann/diff/tree/5.1.0"
}, },
"funding": [ "funding": [
{ {
@ -1710,7 +1712,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2023-05-01T07:48:21+00:00" "time": "2023-12-22T10:55:06+00:00"
}, },
{ {
"name": "sebastian/environment", "name": "sebastian/environment",
@ -1918,20 +1920,20 @@
}, },
{ {
"name": "sebastian/lines-of-code", "name": "sebastian/lines-of-code",
"version": "2.0.1", "version": "2.0.2",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/lines-of-code.git", "url": "https://github.com/sebastianbergmann/lines-of-code.git",
"reference": "649e40d279e243d985aa8fb6e74dd5bb28dc185d" "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/649e40d279e243d985aa8fb6e74dd5bb28dc185d", "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/856e7f6a75a84e339195d48c556f23be2ebf75d0",
"reference": "649e40d279e243d985aa8fb6e74dd5bb28dc185d", "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"nikic/php-parser": "^4.10", "nikic/php-parser": "^4.18 || ^5.0",
"php": ">=8.1" "php": ">=8.1"
}, },
"require-dev": { "require-dev": {
@ -1964,7 +1966,7 @@
"support": { "support": {
"issues": "https://github.com/sebastianbergmann/lines-of-code/issues", "issues": "https://github.com/sebastianbergmann/lines-of-code/issues",
"security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy",
"source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.1" "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.2"
}, },
"funding": [ "funding": [
{ {
@ -1972,7 +1974,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2023-08-31T09:25:50+00:00" "time": "2023-12-21T08:38:20+00:00"
}, },
{ {
"name": "sebastian/object-enumerator", "name": "sebastian/object-enumerator",
@ -2314,7 +2316,10 @@
"stability-flags": [], "stability-flags": [],
"prefer-stable": false, "prefer-stable": false,
"prefer-lowest": false, "prefer-lowest": false,
"platform": [], "platform": {
"ext-couchbase": "*",
"ext-pdo": "*"
},
"platform-dev": [], "platform-dev": [],
"plugin-api-version": "2.6.0" "plugin-api-version": "2.6.0"
} }

@ -88,7 +88,6 @@ class AppCreator
return $this; return $this;
} }
public function getServiceRegistered(): array public function getServiceRegistered(): array
{ {
return $this->services; return $this->services;

@ -0,0 +1,131 @@
<?php
namespace App\Controller;
use App\Router\Request\IRequest;
use App\Router\Response\Response;
use Database\ActivityGateway;
use Database\ActivityMapper;
use Database\Connexion;
use Json\JsonSerializer;
use Manager\UserManager;
use Shared\Attributes\Route;
use Shared\Log;
class ApiController extends BaseController
{
private UserManager $userMgr;
public function __construct(UserManager $manager){
parent::__construct();
$this->userMgr = $manager;
}
#[Route(path: '/api/activities', name: 'api-activities', methods: ['GET'])]
public function apiActivities(IRequest $request)
{
$activityGateway = new ActivityGateway(new Connexion(DSN, DB_USER, DB_PASSWORD));
$listSearch = $activityGateway->getActivity();
$map = new ActivityMapper();
$activityGateway = $map->activitySqlToEntity($listSearch);
$listActivity = [];
foreach ($activityGateway as $entity) {
$activity = $map->activityEntityToModel($entity);
$listActivity[] = ['idactivity' => number_format($activity->getIdActivity()), 'type' => $activity->getType(),
'date' => $activity->getDate()->format("Y-m-d"), 'heureDebut' => $activity->getHeureDebut()->format("Y-m-d"), 'heureFin' => $activity->getHeureFin()->format("Y-m-d"),
'effortRessenti' => $activity->getEffortRessenti(), 'variabilite' => $activity->getVariability(), 'variance' => $activity->getVariance(),
'ecartType' => $activity->getStandardDeviation(), 'moyenne' => $activity->getAverage(),
'max' => $activity->getMaximum(), 'min' => $activity->getMinimum(), 'temperature' => $activity->getAvrTemperature()];
}
$jsonSerializer = new JsonSerializer();
$jsonData = $jsonSerializer::serialize($listActivity);
$response = new Response();
$response->setContent($jsonData);
$response->setHeader('Content-Type', 'application/json');
return $response;
}
// cela ne trouve pas la methode avec la route '/api/activities/1'
#[Route(path: '/api/activities/{id}', name: 'api-activities-by-id', methods: ['GET'])]
public function apiActivityById(IRequest $request, int $id)
{
$activityGateway = new ActivityGateway(new Connexion(DSN, DB_USER, DB_PASSWORD));
$listSearch = $activityGateway->getActivityById($id);
Log::dd($listSearch);
$map = new ActivityMapper();
$activityGateway = $map->activitySqlToEntity($listSearch);
$listActivity = [];
foreach ($activityGateway as $entity) {
$activity = $map->activityEntityToModel($entity);
$listActivity[] = ['idactivity' => number_format($activity->getIdActivity()), 'type' => $activity->getType(),
'date' => $activity->getDate()->format("Y-m-d"), 'heureDebut' => $activity->getHeureDebut()->format("Y-m-d"), 'heureFin' => $activity->getHeureFin()->format("Y-m-d"),
'effortRessenti' => $activity->getEffortRessenti(), 'variabilite' => $activity->getVariability(), 'variance' => $activity->getVariance(),
'ecartType' => $activity->getStandardDeviation(), 'moyenne' => $activity->getAverage(),
'max' => $activity->getMaximum(), 'min' => $activity->getMinimum(), 'temperature' => $activity->getAvrTemperature()];
}
$jsonSerializer = new JsonSerializer();
$jsonData = $jsonSerializer::serialize($listActivity);
$response = new Response();
$response->setContent($jsonData);
$response->setHeader('Content-Type', 'application/json');
// pour delete renvoyer 204 pour dire ok et supprimer
// update 200
return $response;
}
//
// #[Route(path: '/api/activities', name: 'api-activities-post', methods: ['POST'])]
// public function apiAddActivity(IRequest $request)
// {
// $activityGateway = new ActivityGateway(new Connexion(DSN, DB_USER, DB_PASSWORD));
// $listSearch = $activityGateway->getActivity();
// $map = new ActivityMapper();
// $activityGateway = $map->activitySqlToEntity($listSearch);
// $listActivity = [];
// foreach ($activityGateway as $entity) {
// $activity = $map->activityEntityToModel($entity);
// $listActivity[] = ['idactivity' => number_format($activity->getIdActivity()), 'type' => $activity->getType(),
// 'date' => $activity->getDate()->format("Y-m-d"), 'heureDebut' => $activity->getHeureDebut()->format("Y-m-d"), 'heureFin' => $activity->getHeureFin()->format("Y-m-d"),
// 'effortRessenti' => $activity->getEffortRessenti(), 'variabilite' => $activity->getVariability(), 'variance' => $activity->getVariance(),
// 'ecartType' => $activity->getStandardDeviation(), 'moyenne' => $activity->getAverage(),
// 'max' => $activity->getMaximum(), 'min' => $activity->getMinimum(), 'temperature' => $activity->getAvrTemperature()];
// }
//
// $jsonSerializer = new JsonSerializer();
// $jsonData = $jsonSerializer::serialize($listActivity);
//
// $response = new Response();
// $response->setContent($jsonData);
// $response->setHeader('Content-Type', 'application/json');
//
// return $response;
// }
// #[Route(path: '/api/activities/{id}', name: 'api-activities-post', methods: ['DELETE'])]
// public function apiDeleteActivity(IRequest $request, int $id)
// {
// $activityGateway = new ActivityGateway(new Connexion(DSN, DB_USER, DB_PASSWORD));
// $listSearch = $activityGateway->removeActivityById($id);
//
// $response = new Response();
// $response->setContent($jsonData);
// $response->setHeader('Content-Type', 'application/json');
//
// return $response;
// }
// #[Route(path: '/api/activities/{id}', name: 'api-activities-post', methods: ['PUT'])]
// public function apiUpdateActivity(IRequest $request, int $id)
// {
// $activityGateway = new ActivityGateway(new Connexion(DSN, DB_USER, DB_PASSWORD));
// $listSearch = $activityGateway->updateActivity($id);
//
// $response = new Response();
// $response->setContent($jsonData);
// $response->setHeader('Content-Type', 'application/json');
//
// return $response;
// }
}

@ -15,7 +15,6 @@ use Shared\Log;
class HeartRateController extends BaseController class HeartRateController extends BaseController
{ {
private ActivityManager $activityMgr; private ActivityManager $activityMgr;
public function __construct(ActivityManager $manager) public function __construct(ActivityManager $manager)
@ -59,7 +58,7 @@ class HeartRateController extends BaseController
try { try {
if ($this->activityMgr->uploadFile($activityType, 5, $content)) { if ($this->activityMgr->uploadFile($activityType, 5, $content)) {
return new RedirectResponse('/'); return new RedirectResponse('/home');
} }
} catch (\Exception $e) { } catch (\Exception $e) {
return $this->renderError([$e->getMessage()]); return $this->renderError([$e->getMessage()]);

@ -5,6 +5,11 @@ namespace App\Controller;
use App\Container; use App\Container;
use App\Router\Request\IRequest; use App\Router\Request\IRequest;
use App\Router\Response\Response; use App\Router\Response\Response;
use Database\AthleteGateway;
use Database\AthleteMapper;
use Database\Connexion;
use Database\NotificationGateway;
use Database\NotificationMapper;
use Shared\Attributes\Route; use Shared\Attributes\Route;
use Twig\Environment; use Twig\Environment;
use Data\Core\Preferences; use Data\Core\Preferences;
@ -12,33 +17,49 @@ use Shared\Log;
class SocialController extends BaseController class SocialController extends BaseController
{ {
private Environment $twig;
protected Preferences $preference; protected Preferences $preference;
public function __construct() public function __construct(){
{
session_start();
$this->preference = new Preferences(); $this->preference = new Preferences();
} }
#[Route(path: '/notification', name: 'notification', methods: ['GET'])]
#[Route(path: '/mail', name: 'mail', methods: ['GET'])]
public function mail(): Response public function mail(): Response
{ {
return $this->render('./page/mail.html.twig',[ try {
$notificationGateway = new NotificationGateway(new Connexion(DSN, DB_USER, DB_PASSWORD));
$listSearch = $notificationGateway->getNotifications();
$map = new NotificationMapper();
$notificationEntity = $map->notificationSqlToEntity($listSearch);
$listUsers = [];
foreach ($notificationEntity as $entity) {
$notification = $map->notificationEntityToModel($entity);
$listUsers[] = ['idnotif' => $notification->getId(), 'message' => $notification->getMessage(),
'date' => $notification->getDate()->format("D j F Y"),'statut' => $notification->getStatut(), 'urgence' => $notification->getUrgence(),
'idathlete' => $notification->getToUserId()];
}
$response = $this->render('./page/notification.html.twig',[
'css' => $this->preference->getCookie(), 'css' => $this->preference->getCookie(),
'pp' => "test2", 'pp' => "test2",
'user' => "Doe", 'user' => "Doe",
'role' => "Athlète", 'role' => "Athlète",
'friendship' => [], 'friendship' => [],
'analyzes' => [], 'analyzes' => [],
'mails' => [], 'mails' => $listUsers,
'users' => [], 'users' => [],
'infoUser' => [], 'infoUser' => [],
'exos' => [], 'exos' => [],
'member' => [] 'member' => []
]); ]);
} catch (\Throwable $th) {
throw $th;
return $this->render("notification.html.twig", ['tabError' => $taberror]);
}
return $response;
} }

@ -12,6 +12,7 @@
<link href="https://cdn.jsdelivr.net/npm/simple-datatables@7.1.2/dist/style.min.css" rel="stylesheet" /> <link href="https://cdn.jsdelivr.net/npm/simple-datatables@7.1.2/dist/style.min.css" rel="stylesheet" />
<link href="/css/{% block css %}styles{% endblock %}.css" rel="stylesheet" /> <link href="/css/{% block css %}styles{% endblock %}.css" rel="stylesheet" />
<script src="https://use.fontawesome.com/releases/v6.3.0/js/all.js" crossorigin="anonymous"></script> <script src="https://use.fontawesome.com/releases/v6.3.0/js/all.js" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://unpkg.com/leaflet/dist/leaflet.css" />
</head> </head>
<body class="sb-nav-fixed"> <body class="sb-nav-fixed">
<nav class="sb-topnav navbar navbar-expand navbar-dark bg-dark"> <nav class="sb-topnav navbar navbar-expand navbar-dark bg-dark">
@ -57,9 +58,9 @@
<div class="sb-nav-link-icon"><img src="/assets/img/sprinter.png"></div> <div class="sb-nav-link-icon"><img src="/assets/img/sprinter.png"></div>
Exercices Exercices
</a> </a>
<a class="nav-link" href="/analyses"> <a class="nav-link" href="/activity">
<div class="sb-nav-link-icon"><i class="fas fa-chart-area"></i></div> <div class="sb-nav-link-icon"><i class="fas fa-chart-area"></i></div>
Analyses Activités
</a> </a>
<div class="sb-sidenav-menu-heading">Social</div> <div class="sb-sidenav-menu-heading">Social</div>
<a class="nav-link" href="/friendlist"> <a class="nav-link" href="/friendlist">
@ -70,7 +71,7 @@
<div class="sb-nav-link-icon"><img src="/assets/img/coaching.png"></div> <div class="sb-nav-link-icon"><img src="/assets/img/coaching.png"></div>
Coaching Coaching
</a> </a>
<a class="nav-link" href="/mail"> <a class="nav-link" href="/notification">
<div class="sb-nav-link-icon"><img src="/assets/img/letter.png"></div> <div class="sb-nav-link-icon"><img src="/assets/img/letter.png"></div>
Messagerie Messagerie
</a> </a>

@ -21,7 +21,7 @@
<h1 class="display-1">{% block nb %}{% endblock %}</h1> <h1 class="display-1">{% block nb %}{% endblock %}</h1>
<p class="lead">{% block name %}{% endblock %}</p> <p class="lead">{% block name %}{% endblock %}</p>
<p>{% block descr %}{% endblock %}</p> <p>{% block descr %}{% endblock %}</p>
<a href="/"> <a href="/home">
<i class="fas fa-arrow-left me-1"></i> <i class="fas fa-arrow-left me-1"></i>
Retour à l'accueil Retour à l'accueil
</a> </a>

@ -4,30 +4,84 @@
{% block css %}{{css}}{% endblock %} {% block css %}{{css}}{% endblock %}
{% block title %}Exercices - HearthTrack{% endblock %} {% block title %}Analyses - HearthTrack{% endblock %}
{% block user %}{{user}} - {{role}}{% endblock %} {% block user %}{{user}} - {{role}}{% endblock %}
{% block body %} {% block body %}
<div class="container-fluid px-4"> <div class="container-fluid px-4">
<h1 class="mt-4">Exercices</h1> <h1 class="mt-4">Analyses</h1>
<ol class="breadcrumb mb-4"> <ol class="breadcrumb mb-4">
<li class="breadcrumb-item"><a href="/">Accueil</a></li> <li class="breadcrumb-item"><a href="/home">Accueil</a></li>
<li class="breadcrumb-item active">Exercices</li> <li class="breadcrumb-item active">Analyses</li>
</ol> </ol>
<div class="card-body"> <div class="card-body">
<div class="datatable-container"> <div class="datatable-container">
<form method="post" action="/exercices"> <table id="datatablesSimple" class="datatable-table">
Type : <input type="text" name="type"/> <thead>
Intensité : <input type="text" name="intensite"/> <tr>
Date : <input type="text" name="date"/> <th>Date</th>
<button class="btn btn-primary btn-mrg" id="btnNavbarSearch" type="submit">Ajouter l'exercice</button> <th>Heure de début</th>
</form> <th>Heure de fin</th>
<th>Type</th>
<th>effort ressenti</th>
<th>Variabilité</th>
<th>Variance</th>
<th>Ecart type</th>
<th>Moyenne</th>
<th>Maximum</th>
<th>Minimum</th>
<th>Temperature moyenne</th>
<th></th>
</tr>
</thead>
<tfoot>
<tr>
<th>Date</th>
<th>Heure de début</th>
<th>Heure de fin</th>
<th>Type</th>
<th>effort ressenti</th>
<th>Variabilité</th>
<th>Variance</th>
<th>Ecart type</th>
<th>Moyenne</th>
<th>Maximum</th>
<th>Minimum</th>
<th>Temperature moyenne</th>
</tr><tr></tr>
</tfoot>
<tbody>
{% for analyze in analyzes %}
<tr>
<td>{{analyze.date}}</td>
<td>{{analyze.heureDebut}}</td>
<td>{{analyze.heureFin}}</td>
<td>{{analyze.type}}</td>
<td>{{analyze.effortRessenti}}</td>
<td>{{analyze.variabilite}}</td>
<td>{{analyze.variance}}</td>
<td>{{analyze.ecartType}}</td>
<td>{{analyze.moyenne}} Bpm</td>
<td>{{analyze.max}} Bpm</td>
<td>{{analyze.min}} Bpm</td>
<td>{{analyze.temperature}} °C</td>
<td><a href="/analyze?id={{ analyze.idactivity }}">En savoir plus</a></td>
</tr>
{% endfor %}
</tbody>
</table>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<h1>{{responce}}</h1> {% endblock %}
{% block script %}
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js" crossorigin="anonymous"></script>
<script src="js/scripts.js"></script>
<script src="https://cdn.jsdelivr.net/npm/simple-datatables@7.1.2/dist/umd/simple-datatables.min.js" crossorigin="anonymous"></script>
<script src="js/datatables-simple-demo.js"></script>
{% endblock %} {% endblock %}

@ -12,7 +12,7 @@
<div class="container-fluid px-4"> <div class="container-fluid px-4">
<h1 class="mt-4">Liste d'ami</h1> <h1 class="mt-4">Liste d'ami</h1>
<ol class="breadcrumb mb-4"> <ol class="breadcrumb mb-4">
<li class="breadcrumb-item"><a href="/">Accueil</a></li> <li class="breadcrumb-item"><a href="/home">Accueil</a></li>
<li class="breadcrumb-item active">Ami</li> <li class="breadcrumb-item active">Ami</li>
</ol> </ol>

@ -12,7 +12,7 @@
<div class="container-fluid px-4"> <div class="container-fluid px-4">
<h1 class="mt-4">Coaching</h1> <h1 class="mt-4">Coaching</h1>
<ol class="breadcrumb mb-4"> <ol class="breadcrumb mb-4">
<li class="breadcrumb-item"><a href="/">Accueil</a></li> <li class="breadcrumb-item"><a href="/home">Accueil</a></li>
<li class="breadcrumb-item active">Coaching</li> <li class="breadcrumb-item active">Coaching</li>
</ol> </ol>

@ -4,63 +4,151 @@
{% block css %}{{css}}{% endblock %} {% block css %}{{css}}{% endblock %}
{% block title %}Analyses - HearthTrack{% endblock %} {% block title %}Exercices - HearthTrack{% endblock %}
{% block user %}{{user}} - {{role}}{% endblock %} {% block user %}{{user}} - {{role}}{% endblock %}
{% block body %} {% block body %}
<div class="container-fluid px-4"> <div class="container-fluid px-4">
<h1 class="mt-4">Analyses</h1> <h1 class="mt-4">Exercices</h1>
<ol class="breadcrumb mb-4"> <ol class="breadcrumb mb-4">
<li class="breadcrumb-item"><a href="/">Accueil</a></li> <li class="breadcrumb-item"><a href="/home">Accueil</a></li>
<li class="breadcrumb-item active">Analyses</li> <li class="breadcrumb-item active">Exercices</li>
<script src="https://unpkg.com/leaflet/dist/leaflet.js"></script>
</ol> </ol>
<div class="card-body"> <div class="card-body">
<div class="datatable-container"> {# <table id="datatablesSimple" class="datatable-table">#}
<table id="datatablesSimple" class="datatable-table"> {# <thead>#}
<thead> {# <tr>#}
<tr> {# <th>id FC</th>#}
<th>Date</th> {# <th>altitude</th>#}
<th>Type</th> {# <th>temps</th>#}
<th>BMP</th> {# <th>Temperature</th>#}
<th>KM/H</th> {# <th>bpm</th>#}
<th>Distance</th> {# <th>longitude</th>#}
<th></th> {# <th>latitude</th>#}
</tr> {# <th>activiteid</th>#}
</thead> {# <th></th>#}
<tfoot> {# </tr>#}
<tr> {# </thead>#}
<th>Date</th> {# {% for analyze in analyzes %}#}
<th>Type</th> {# <tbody>#}
<th>BPM</th> {# <tr>#}
<th>KM/H</th> {# <td>{{analyze.idfc}}</td>#}
<th>Distance</th> {# <td>{{analyze.altitude}}</td>#}
</tr><tr></tr> {# <td>{{analyze.temps}}</td>#}
{# <td>{{analyze.temperature}}</td>#}
{# <td>{{analyze.bpm}}</td>#}
{# <td>{{analyze.longitude}}</td>#}
{# <td>{{analyze.latitude}}</td>#}
{# <td>{{analyze.activiteid}}</td>#}
{# <td><a href="/home">Home</a></td>#}
{# </tr>#}
{# </tbody>#}
{# {% endfor %}#}
{# </table>#}
{# <div>#}
{# <canvas id="myChart" width="400" height="200">#}
{# <p>Hello Fallback World</p>#}
{# </canvas>#}
{# </div>#}
{# <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>#}
{# <script>#}
{# const ctx = document.getElementById('myChart');#}
{# new Chart(ctx, {#}
{# type: 'line',#}
{# data: {#}
{# labels: [#}
{# {% for analyze in analyzes %}#}
{# {{analyze.temps}}#}
{# {% endfor %}#}
{# ],#}
{# datasets: [{#}
{# label: 'Batement par minute',#}
{# data: [#}
{# {% for analyze in analyzes %}#}
{# {{analyze.bpm}}#}
{# {% endfor %}#}
{# ],#}
{# borderWidth: 1#}
{# }]#}
{# },#}
{# options: {#}
{# scales: {#}
{# y: {#}
{# beginAtZero: true#}
{# }#}
{# }#}
{# }#}
{# });#}
{# </script>#}
<div>
<canvas id="myChart" height="50">
<p>Hello Fallback World</p>
</canvas>
</div>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
const ctx = document.getElementById('myChart');
const labels = [
{% for analyze in analyzes %}
'{{ analyze.temps }}',
{% endfor %}
];
const data = [
{% for analyze in analyzes %}
{{ analyze.bpm }},
{% endfor %}
];
new Chart(ctx, {
type: 'line',
data: {
labels: labels,
datasets: [{
label: 'Batement par minute',
data: data,
borderWidth: 1,
tension: 0.2,
fill: false,
borderColor: 'rgb(255, 0, 0)'
}]
},
options: {
scales: {
y: {
beginAtZero: false
}
}
}
});
</script>
<div class="card-body">
<div id="map" style="height: 500px;"></div>
<script>
document.addEventListener("DOMContentLoaded", function () {
{#var lat = {{ analyze.latitude }}#}
var map = L.map('map').setView([45.75771709151474, 3.113484980409329], 14);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors'
}).addTo(map);
</tfoot>
<tbody>
{% for analyze in analyzes %} {% for analyze in analyzes %}
<tr> var marker = L.marker([{{ analyze.latitude }}, {{ analyze.longitude }}]).addTo(map);
<td>{{analyze.date}}</td> marker.bindPopup(`
<td>{{analyze.type}}</td> <strong>Date:</strong> {{ analyze.temps }}<br>
<td>{{analyze.bpm}}</td> <strong>FC:</strong> {{ analyze.bpm }}<br>
<td>{{analyze.kmh}} Km/H</td> <strong>Altitude:</strong> {{ analyze.altitude }}<br>
<td>{{analyze.distance}} Km</td> <strong>Temperature:</strong> {{ analyze.temperature }}
<td><a href="#about">En savoir plus</a></td> `);
</tr>
{% endfor %} {% endfor %}
</tbody> });
</table> </script>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{% endblock %} <h1>{{responce}}</h1>
{% block script %}
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js" crossorigin="anonymous"></script>
<script src="js/scripts.js"></script>
<script src="https://cdn.jsdelivr.net/npm/simple-datatables@7.1.2/dist/umd/simple-datatables.min.js" crossorigin="anonymous"></script>
<script src="js/datatables-simple-demo.js"></script>
{% endblock %} {% endblock %}

@ -13,7 +13,7 @@
<div class="container-fluid px-4"> <div class="container-fluid px-4">
<h1 class="mt-4">Coaching</h1> <h1 class="mt-4">Coaching</h1>
<ol class="breadcrumb mb-4"> <ol class="breadcrumb mb-4">
<li class="breadcrumb-item"><a href="/">Accueil</a></li> <li class="breadcrumb-item"><a href="/home">Accueil</a></li>
<li class="breadcrumb-item active">Coaching</li> <li class="breadcrumb-item active">Coaching</li>
</ol> </ol>
<style>.btn-mrg{margin:15px; margin-left:83%}</style> <style>.btn-mrg{margin:15px; margin-left:83%}</style>

@ -9,10 +9,11 @@
{% block user %}{{user}} - {{role}}{% endblock %} {% block user %}{{user}} - {{role}}{% endblock %}
{% block body %} {% block body %}
<div class="container-fluid px-4"> <div class="container-fluid px-4">
<h1 class="mt-4">Exercices</h1> <h1 class="mt-4">Exercices</h1>
<ol class="breadcrumb mb-4"> <ol class="breadcrumb mb-4">
<li class="breadcrumb-item"><a href="/">Accueil</a></li> <li class="breadcrumb-item"><a href="/home">Accueil</a></li>
<li class="breadcrumb-item active">Exercices</li> <li class="breadcrumb-item active">Exercices</li>
</ol> </ol>
<style>.btn-mrg{margin:15px; margin-left:85%}</style> <style>.btn-mrg{margin:15px; margin-left:85%}</style>
@ -25,18 +26,39 @@
<thead> <thead>
<tr> <tr>
<th>Date</th> <th>Date</th>
<th>Type</th> <th>Description</th>
<th>Intensité prévue</th> <th>Localisation</th>
<th>Status</th> <th>FeedBack</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<script src="https://unpkg.com/leaflet/dist/leaflet.js"></script>
{% for exo in exos %} {% for exo in exos %}
<tr> <tr>
<td>{{ exo.date }}</td> <td>{{ exo.date }}</td>
<td>{{exo.type}}</td> <td>{{ exo.description }}</td>
<td>{{exo.intensite}}</td> <td>
<td>{{exo.status}}</td> <div id="map-{{ loop.index }}"
data-lat=45.758285982369415
data-lng=3.110223414416445
style="height: 300px; width: 650px;"></div>
<script>
document.addEventListener("DOMContentLoaded", function() {
var map{{ loop.index }} = L.map('map-{{ loop.index }}').setView([45.75771709151474, 3.113484980409329], 14);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors'
}).addTo(map{{ loop.index }});
var marker{{ loop.index }} = L.marker([45.75771709151474, 3.113484980409329]).addTo(map{{ loop.index }});
marker{{ loop.index }}.bindPopup(`
<strong>Date:</strong> {{ exo.date }}<br>
<strong>Description:</strong> {{ exo.description }}<br>
<strong>Feedback:</strong> {{ exo.feedback }}
`);
});
</script>
</td>
<td>{{ exo.feedback }}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>

@ -12,7 +12,7 @@
<div class="container-fluid px-4"> <div class="container-fluid px-4">
<h1 class="mt-4">Liste d'ami</h1> <h1 class="mt-4">Liste d'ami</h1>
<ol class="breadcrumb mb-4"> <ol class="breadcrumb mb-4">
<li class="breadcrumb-item"><a href="/">Accueil</a></li> <li class="breadcrumb-item"><a href="/home">Accueil</a></li>
<li class="breadcrumb-item active">Ami</li> <li class="breadcrumb-item active">Ami</li>
</ol> </ol>
<style>.btn-mrg{margin:15px; margin-left:87%}</style> <style>.btn-mrg{margin:15px; margin-left:87%}</style>

@ -44,7 +44,7 @@
<span class="text-blue-600 hover:underline">select a file</span> <span class="text-blue-600 hover:underline">select a file</span>
from your computer</p> from your computer</p>
</div> </div>
<input type="file" class="hidden" id="file-input" name="uploaded_file"> <input type="file" class="hidden" id="file-input" name="uploaded_file" accept=".fit">
</label> </label>
<p id="file-name-display" class="text-sm text-gray-500"></p> <p id="file-name-display" class="text-sm text-gray-500"></p>
</div> </div>

@ -5,6 +5,7 @@
{% block title %}Connexion - HearthTrack{% endblock %} {% block title %}Connexion - HearthTrack{% endblock %}
{% block main %} {% block main %}
{#<<<<<<< HEAD#}
<div class="container"> <div class="container">
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-lg-5"> <div class="col-lg-5">
@ -15,6 +16,36 @@
{% for value in login_error %} {% for value in login_error %}
<div class="alert alert-danger" role="alert"> <div class="alert alert-danger" role="alert">
{{ value }} {{ value }}
{#=======#}
{# <div class="container">#}
{# <div class="row justify-content-center">#}
{# <div class="col-lg-5">#}
{# <div class="card shadow-lg border-0 rounded-lg mt-5">#}
{# <div class="card-header"><h3 class="text-center font-weight-light my-4">Connexion</h3></div>#}
{# <div class="card-body">#}
{# <form>#}
{# <div class="form-floating mb-3">#}
{# <input class="form-control" id="inputEmail" type="email" placeholder="nom@exemple.com" />#}
{# <label for="inputEmail">Adresse eMail</label>#}
{# </div>#}
{# <div class="form-floating mb-3">#}
{# <input class="form-control" id="inputPassword" type="password" placeholder="Mot de passe" />#}
{# <label for="inputPassword">Mot de passe</label>#}
{# </div>#}
{# <div class="form-check mb-3">#}
{# <input class="form-check-input" id="inputRememberPassword" type="checkbox" value="" />#}
{# <label class="form-check-label" for="inputRememberPassword">Mémoriser le mot de passe</label>#}
{# </div>#}
{# <div class="d-flex align-items-center justify-content-between mt-4 mb-0">#}
{# <a class="small" href="/pass">Mot de passe oublié ?</a>#}
{# <a class="btn btn-primary" href="/home">Se connecter</a>#}
{# </div>#}
{# </form>#}
{# </div>#}
{# <div class="card-footer text-center py-3">#}
{# <div class="small"><a href="/regist">Besoin d'un compte ? Inscrivez-vous !</a></div>#}
{# </div>#}
{#>>>>>>> origin/merged_PLE#}
</div> </div>
{% endfor %} {% endfor %}
{% endif %} {% endif %}

@ -23,14 +23,16 @@
<th>Lu</th> <th>Lu</th>
<th>De:</th> <th>De:</th>
<th>Description</th> <th>Description</th>
<th>Date</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for mail in mails %} {% for mail in mails %}
<tr> <tr>
<td><img src="/assets/img/verif/{{mail.lu}}.png" width="25px" height="25px"></td> <td><img src="/assets/img/verif/{{mail.idnotif}}.png" width="25px" height="25px"></td>
<td>{{mail.nom}} {{mail.prenom}}</td> <td>{{mail.idathlete}}</td>
<td><a href="#">{{mail.message}}</a></td> <td><a href="#">{{mail.message}}</a></td>
<td>{{ mail.date }}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>

@ -12,7 +12,7 @@
<div class="container-fluid px-4"> <div class="container-fluid px-4">
<h1 class="mt-4">Profile</h1> <h1 class="mt-4">Profile</h1>
<ol class="breadcrumb mb-4"> <ol class="breadcrumb mb-4">
<li class="breadcrumb-item"><a href="/">Accueil</a></li> <li class="breadcrumb-item"><a href="/home">Accueil</a></li>
<li class="breadcrumb-item active">Profile</li> <li class="breadcrumb-item active">Profile</li>
</ol> </ol>
<div class="row"> <div class="row">

@ -12,7 +12,7 @@
<div class="container-fluid px-4"> <div class="container-fluid px-4">
<h1 class="mt-4">Paramètres</h1> <h1 class="mt-4">Paramètres</h1>
<ol class="breadcrumb mb-4"> <ol class="breadcrumb mb-4">
<li class="breadcrumb-item"><a href="/">Accueil</a></li> <li class="breadcrumb-item"><a href="/home">Accueil</a></li>
<li class="breadcrumb-item active">Paramètres</li> <li class="breadcrumb-item active">Paramètres</li>
</ol> </ol>
<div class="row"> <div class="row">

@ -1,127 +0,0 @@
<?php
namespace Database;
class ActiviteEntity {
private $idActivite;
private $type;
private $date;
private $heureDebut;
private $heureFin;
private $effortRessenti;
private $variabilite;
private $variance;
private $ecartType;
private $moyenne;
private $maximum;
private $minimum;
private $temperatureMoyenne;
// Getters
public function getIdActivite() {
return $this->idActivity;
}
public function getType() {
return $this->type;
}
public function getDate() {
return $this->date;
}
public function getHeureDebut() {
return $this->heureDebut;
}
public function getHeureFin() {
return $this->heureFin;
}
public function getEffortRessenti() {
return $this->effortRessenti;
}
public function getVariabilite() {
return $this->variabilite;
}
public function getVariance() {
return $this->variance;
}
public function getEcartType() {
return $this->ecartType;
}
public function getMoyenne() {
return $this->moyenne;
}
public function getMaximum() {
return $this->maximum;
}
public function getMinimum() {
return $this->minimum;
}
public function getTemperatureMoyenne() {
return $this->temperatureMoyenne;
}
// Setters
public function setIdActivite($idActivite) {
$this->idActivity = $idActivity;
}
public function setType($type) {
$this->type = $type;
}
public function setDate($date) {
$this->date = $date;
}
public function setHeureDebut($heureDebut) {
$this->heureDebut = $heureDebut;
}
public function setHeureFin($heureFin) {
$this->heureFin = $heureFin;
}
public function setEffortRessenti($effortRessenti) {
$this->effortRessenti = $effortRessenti;
}
public function setVariabilite($variabilite) {
$this->variabilite = $variabilite;
}
public function setVariance($variance) {
$this->variance = $variance;
}
public function setEcartType($ecartType) {
$this->ecartType = $ecartType;
}
public function setMoyenne($moyenne) {
$this->moyenne = $moyenne;
}
public function setMaximum($maximum) {
$this->maximum = $maximum;
}
public function setMinimum($minimum) {
$this->minimum = $minimum;
}
public function setTemperatureMoyenne($temperatureMoyenne) {
$this->temperatureMoyenne = $temperatureMoyenne;
}
}
?>

@ -1,120 +0,0 @@
<?php
namespace Database;
class ActiviteGateway {
private $connection;
public function __construct(Connection $connection) {
$this->connection = $connection;
}
public function getActivite() {
$query = "SELECT * FROM Activite";
return $this->connection->executeWithErrorHandling($query);
}
public function getActiviteById(int $activiteId) {
$query = "SELECT * FROM Activite WHERE idActivite = :id";
$params = [':id' => [$activiteId, PDO::PARAM_INT]];
return $this->connection->executeWithErrorHandling($query, $params);
}
public function getActiviteByType(string $type) {
$query = "SELECT * FROM Activite WHERE type = :type";
$params = [':type' => [$type, PDO::PARAM_STR]];
return $this->connection->executeWithErrorHandling($query, $params);
}
public function getActiviteByDate(string $date) {
$query = "SELECT * FROM Activite WHERE date = :date";
$params = [':date' => [$date, PDO::PARAM_STR]];
return $this->connection->executeWithErrorHandling($query, $params);
}
public function getActiviteByTimeRange(string $startTime, string $endTime) {
$query = "SELECT * FROM Activite WHERE heureDebut >= :startTime AND heureFin <= :endTime";
$params = [
':startTime' => [$startTime, PDO::PARAM_STR],
':endTime' => [$endTime, PDO::PARAM_STR]
];
return $this->connection->executeWithErrorHandling($query, $params);
}
public function getActiviteByEffort(int $effortRessenti) {
$query = "SELECT * FROM Activite WHERE effortRessenti = :effort";
$params = [':effort' => [$effortRessenti, PDO::PARAM_INT]];
return $this->connection->executeWithErrorHandling($query, $params);
}
public function getActiviteByVariability(int $variabilite) {
$query = "SELECT * FROM Activite WHERE variabilite = :variability";
$params = [':variability' => [$variabilite, PDO::PARAM_INT]];
return $this->connection->executeWithErrorHandling($query, $params);
}
public function getActiviteByTemperature(int $temperatureMoyenne) {
$query = "SELECT * FROM Activite WHERE temperatureMoyenne = :temperature";
$params = [':temperature' => [$temperatureMoyenne, PDO::PARAM_INT]];
return $this->connection->executeWithErrorHandling($query, $params);
}
public function addActivite(ActiviteEntity $activite) {
$query = "INSERT INTO Activite (type, date, heureDebut, heureDeFin, effortRessenti, variabilite, variance, ecartType, moyenne, maximum, minimum, temperatureMoyenne)
VALUES (:type, :date, :heureDebut, :heureDeFin, :effortRessenti, :variabilite, :variance, :ecartType, :moyenne, :maximum, :minimum, :temperatureMoyenne)";
$params = [
':type' => $activite->getType(),
':date' => $activite->getDate()->format('Y-m-d'), // Format date pour SQL
':heureDebut' => $activite->getHeureDebut()->format('H:i:s'), // Format heure pour SQL
':heureDeFin' => $activite->getHeureFin()->format('H:i:s'), // Format heure pour SQL
':effortRessenti' => $activite->getEffortRessenti(),
':variabilite' => $activite->getVariabilite(),
':variance' => $activite->getVariance(),
':ecartType' => $activite->getEcartType(),
':moyenne' => $activite->getMoyenne(),
':maximum' => $activite->getMaximum(),
':minimum' => $activite->getMinimum(),
':temperatureMoyenne' => $activite->getTemperatureMoyenne(),
];
return $this->connection->executeWithErrorHandling($query, $params);
}
public function updateActivite(ActiviteEntity $oldActivite, ActiviteEntity $newActivite) {
$query = "UPDATE Activite
SET type = :type, date = :date, heureDebut = :heureDebut, heureDeFin = :heureDeFin,
effortRessenti = :effortRessenti, variabilite = :variabilite, variance = :variance, ecartType = :ecartType, moyenne = :moyenne, maximum = :maximum, minimum = :minimum, temperatureMoyenne = :temperatureMoyenne
WHERE idActivite = :idActivite";
$params = [
':idActivite' => $oldActivite->getIdActivite(),
':type' => $newActivite->getType(),
':date' => $newActivite->getDate()->format('Y-m-d'), // Format date pour SQL
':heureDebut' => $newActivite->getHeureDebut()->format('H:i:s'), // Format heure pour SQL
':heureDeFin' => $newActivite->getHeureFin()->format('H:i:s'), // Format heure pour SQL
':effortRessenti' => $newActivite->getEffortRessenti(),
':variabilite' => $newActivite->getVariabilite(),
':variance' => $newActivite->getVariance(),
':ecartType' => $newActivite->getEcartType(),
':moyenne' => $newActivite->getMoyenne(),
':maximum' => $newActivite->getMaximum(),
':minimum' => $newActivite->getMinimum(),
':temperatureMoyenne' => $newActivite->getTemperatureMoyenne(),
];
return $this->connection->executeWithErrorHandling($query, $params);
}
public function deleteActivite(int $idActivite) {
$query = "DELETE FROM Activite WHERE idActivite = :idActivite";
$params = [
':idActivite' => $idActivite,
];
return $this->connection->executeWithErrorHandling($query, $params);
}
}
?>

@ -1,51 +0,0 @@
<?php
namespace Database;
use Model\Activite;
class ActiviteMapper {
public function map(array $data):ActiviteEntity {
$activite = new ActiviteEntity();
$activite->setIdActivite($data['idActivite']);
$activite->setType($data['type']);
$activite->setDate($data['date']);
$activite->setHeureDebut($data['heureDebut']);
$activite->setHeureFin($data['heureFin']);
$activite->setEffortRessenti($data['effortRessenti']);
$activite->setVariabilite($data['variabilite']);
$activite->setVariance($data['variance']);
$activite->setEcartType($data['ecartType']);
$activite->setMoyenne($data['moyenne']);
$activite->setMaximum($data['maximum']);
$activite->setMinimum($data['minimum']);
$activite->setTemperatureMoyenne($data['temperatureMoyenne']);
return $activite;
}
//public function ActiviteEntityToModel(ActiviteEntity entity): Activite;
public function ActiviteEntityToModel(ActiviteEntity $activiteEntity):Activite{
$act = new Activite(
$activiteEntity->getIdActivite(),
$activiteEntity->getType(),
$activiteEntity->getDate(),
$activiteEntity->getHeureDebut(),
$activiteEntity->getHeureFin(),
$activiteEntity->getEffortRessenti(),
$activiteEntity->getVariabilite(),
$activiteEntity->getVariance(),
$activiteEntity->getEcartType(),
$activiteEntity->getMoyenne(),
$activiteEntity->getMaximum(),
$activiteEntity->getMinimum(),
$activiteEntity->getTemperatureMoyenne()
);
return $act;
}
//public function ActiviteToEntity(Activite model): ActiviteEntity;
}
?>

@ -21,9 +21,9 @@ class ActivityMapper {
$activity->setType($activityData['type']); $activity->setType($activityData['type']);
} }
// if (isset($activityData['date'])) { if (isset($activityData['date'])) {
// $activity->setDate(DateTime::createFromFormat('yyyy-mm--dd',$activityData['date'])); $activity->setDate(new DateTime($activityData['date']));
// } }
if (isset($activityData['heureDeDebut'])) { if (isset($activityData['heureDeDebut'])) {
$activity->setHeureDebut(new DateTime($activityData['heureDeDebut'])); $activity->setHeureDebut(new DateTime($activityData['heureDeDebut']));
@ -67,7 +67,7 @@ class ActivityMapper {
$activityEntities[] = $activity; $activityEntities[] = $activity;
} }
Log::dd($activityEntities);
return $activityEntities; return $activityEntities;
} }
@ -75,9 +75,9 @@ class ActivityMapper {
* @throws \Exception * @throws \Exception
*/ */
public function ActivityEntityToModel(ActivityEntity $activiteEntity):Activity { public function ActivityEntityToModel(ActivityEntity $activiteEntity):Activity {
$date = new DateTime($activiteEntity->getDate()); $date = new DateTime($activiteEntity->getDate()->format('Y-m-d'));
$heureDebut = new \DateTime($activiteEntity->getHeureDebut()); $heureDebut = new \DateTime($activiteEntity->getHeureDebut()->format('H:i:s'));
$heureFin = new \DateTime($activiteEntity->getHeureFin()); $heureFin = new \DateTime($activiteEntity->getHeureFin()->format('H:i:s'));
$effortRessenti = intval($activiteEntity->getEffortRessenti()); $effortRessenti = intval($activiteEntity->getEffortRessenti());
$variability = floatval($activiteEntity->getVariability()); $variability = floatval($activiteEntity->getVariability());
$variance = floatval($activiteEntity->getVariance()); $variance = floatval($activiteEntity->getVariance());

@ -31,6 +31,7 @@ class AthleteEntity {
public function getUsername(){ public function getUsername(){
return $this->username; return $this->username;
} }
public function getEmail() { public function getEmail() {
return $this->email; return $this->email;
} }
@ -104,5 +105,3 @@ class AthleteEntity {
$this->isCoach = $isCoach; $this->isCoach = $isCoach;
} }
} }
?>

@ -3,6 +3,7 @@
namespace Database; namespace Database;
use \PDO; use \PDO;
use Shared\Log;
class AthleteGateway { class AthleteGateway {
private Connexion $connection; private Connexion $connection;

@ -0,0 +1,20 @@
<?php
namespace Json;
use Shared\Log;
class JsonSerializer
{
public static function serialize($data): string
{
try {
return json_encode($data);
} catch (\JsonException $e) {
// Gérer l'erreur ici, par exemple, journaliser l'exception
error_log('Erreur de sérialisation JSON : ' . $e->getMessage());
return ''; // Ou retournez une valeur par défaut, selon vos besoins
}
}
}

@ -29,7 +29,9 @@ class AuthService implements IAuthService
return false; return false;
} }
$this->currentUser = $user; $this->currentUser = $user;
Session::getInstance()->__set(USER, $this->currentUser->getId()); $id = $this->currentUser->getId();
Session::getInstance()->__set(USER, $id);
Session::getInstance()->__get(USER);
return true; return true;
} }

@ -2,8 +2,46 @@
namespace Model; namespace Model;
use DateTime;
class Notification class Notification
{ {
private static $lastId = 0;
private int $idNotif;
private string $message;
private \DateTime $date;
private bool $statut;
private string $urgence;
private int $toUserId;
/**
* @param string $type
* @param string $message
*/
public function __construct(
string $message,
DateTime $date,
string $statut,
string $urgence,
int $toUserId
)
{
$this->idNotif = self::generateId();
$this->message = $message;
$this->date = $date;
$this->statut = $statut;
$this->urgence = $urgence;
$this->toUserId =$toUserId;
}
private static function generateId(): int
{
self::$lastId++;
return self::$lastId;
}
public function getId(){ return $this->idNotif;}
public function getDate(){ return $this->date;}
public function getStatut(){ return $this->statut;}
public function getUrgence(){ return $this->urgence;}
/** /**
* @return string * @return string
*/ */
@ -36,10 +74,6 @@ class Notification
$this->message = $message; $this->message = $message;
} }
private string $type;
private string $message;
private int $toUserId;
/** /**
* @return int * @return int
*/ */
@ -55,16 +89,6 @@ class Notification
{ {
$this->toUserId = $toUserId; $this->toUserId = $toUserId;
} }
/**
* @param string $type
* @param string $message
*/
public function __construct(int $toUserId,string $type, string $message)
{
$this->type = $type;
$this->toUserId =$toUserId;
$this->message = $message;
}
public function __toString(): string public function __toString(): string
{ {
return var_export($this, true); return var_export($this, true);

@ -18,7 +18,6 @@ use Stub\TrainingRepository;
* @brief Classe abstraite représentant le rôle d'un utilisateur. * @brief Classe abstraite représentant le rôle d'un utilisateur.
*/ */
abstract class Role { abstract class Role {
protected int $id;
protected array $usersList = []; protected array $usersList = [];
protected array $usersRequests = []; protected array $usersRequests = [];
protected array $trainingList = []; protected array $trainingList = [];

@ -4,6 +4,7 @@ namespace Model;
class Training class Training
{ {
private static $lastId = 0;
private int $idTraining; private int $idTraining;
private \DateTime $date; private \DateTime $date;
private float $latitude; private float $latitude;
@ -12,20 +13,25 @@ class Training
private ?String $feedback; private ?String $feedback;
public function __construct( public function __construct(
int $idTraining, int $id,
\DateTime $date, \DateTime $date,
float $latitude, float $latitude,
float $longitude, float $longitude,
?String $description = null, ?String $description = null,
?String $feedback = null ?String $feedback = null
) { ) {
$this->idTraining = $idTraining; $this->idTraining = $id;
$this->date = $date; $this->date = $date;
$this->latitude = $latitude; $this->latitude = $latitude;
$this->longitude = $longitude; $this->longitude = $longitude;
$this->description = $description; $this->description = $description;
$this->feedback = $feedback; $this->feedback = $feedback;
} }
private static function generateId(): int
{
self::$lastId++;
return self::$lastId;
}
public function getId():int { public function getId():int {
return $this->idTraining; return $this->idTraining;
} }
@ -33,7 +39,13 @@ class Training
return $this->date; return $this->date;
} }
public function getLocation(): String { public function getLocation(): String {
return $this->longitude . $this->latitude; return $this->longitude . ", " . $this->latitude;
}
public function getLatitude(): float {
return $this->latitude;
}
public function getLongitude(): float {
return $this->longitude;
} }
public function getDescription(): String public function getDescription(): String
{ {

@ -16,6 +16,7 @@ namespace Model;
* @brief Classe représentant un utilisateur. * @brief Classe représentant un utilisateur.
*/ */
class User { class User {
// private static $lastId = 0;
private int $id; private int $id;
private String $username; private String $username;
private string $nom; private string $nom;
@ -28,14 +29,7 @@ class User {
private \DateTime $dateNaissance; private \DateTime $dateNaissance;
private Role $role; private Role $role;
protected array $notifications = []; protected array $notifications = [];
private array $listFriend = [];
/**
* @return array
*/
public function getNotifications(): array
{
return $this->notifications;
}
/** /**
* @param int $id * @param int $id
@ -50,7 +44,9 @@ class User {
* @param \DateTime $dateNaissance * @param \DateTime $dateNaissance
* @param \Model\Role $role * @param \Model\Role $role
*/ */
public function __construct(int $id, string $nom, string $prenom, string $username, string $email, string $motDePasse, string $sexe, float $taille, float $poids, \DateTime $dateNaissance, Role $role) public function __construct(int $id, string $nom, string $prenom, string $username, string $email,
string $motDePasse, string $sexe, float $taille, float $poids, \DateTime $dateNaissance,
Role $role)
{ {
$this->id = $id; $this->id = $id;
$this->nom = $nom; $this->nom = $nom;
@ -64,6 +60,18 @@ class User {
$this->dateNaissance = $dateNaissance; $this->dateNaissance = $dateNaissance;
$this->role = $role; $this->role = $role;
} }
private static function generateId(): int
{
self::$lastId++;
return self::$lastId;
}
/**
* @return array
*/
public function getNotifications(): array
{
return $this->notifications;
}
public function addNotification($notification): void { public function addNotification($notification): void {
$this->notifications[] = $notification; $this->notifications[] = $notification;
@ -289,5 +297,23 @@ class User {
public function __toString() { public function __toString() {
return "Athlete [ID: {$this->id}, Username : $this->username, Nom: {$this->nom}, Prénom: {$this->prenom}, Email: {$this->email}]"; return "Athlete [ID: {$this->id}, Username : $this->username, Nom: {$this->nom}, Prénom: {$this->prenom}, Email: {$this->email}]";
} }
/**
* Donne la liste des amis de l'utilisateur
*
* @return string Les amis de l'utilisateur.
*/
public function getListFriend(): array {
return $this->listFriend;
}
/**
* Ajoute un utilisateur a sa liste d'amis.
*
* @param User L'utilisateur a ajouter.
*/
public function addFriend(User $user){
array_push($this->listFriend, $user);
}
} }
?> ?>

@ -12,8 +12,16 @@
namespace Manager; namespace Manager;
use adriangibbons\phpFITFileAnalysis; use adriangibbons\phpFITFileAnalysis;
use Database\ActivityGateway;
use Database\ActivityMapper;
use Database\Connexion;
use Database\NotificationGateway;
use Database\NotificationMapper;
use DateTime;
use Exception; use Exception;
use Model\Activity; use Model\Activity;
use Model\Athlete;
use Model\Notification;
use Network\IAuthService; use Network\IAuthService;
use Shared\Log; use Shared\Log;
use Stub\AuthService; use Stub\AuthService;
@ -118,17 +126,25 @@ class ActivityManager
$lastTimestamp = end($monFichierFit->data_mesgs['record']['timestamp']); $lastTimestamp = end($monFichierFit->data_mesgs['record']['timestamp']);
// Conversion des timestamps en objets DateTime // Conversion des timestamps en objets DateTime
$startDate = \DateTime::createFromFormat('Y-m-d', date('Y-m-d', $firstTimestamp)); $startDate = new DateTime();
$startTime = \DateTime::createFromFormat('H:i:s', date('H:i:s', $firstTimestamp)); // $startDate = $startDate->format('Y-m-d');
$endTime = ($lastTimestamp) ? \DateTime::createFromFormat('H:i:s', date('H:i:s', $lastTimestamp)) : null; $startTime = new DateTime();//\DateTime::createFromFormat('H:i:s', date('H:i:s', $firstTimestamp));
// $startTime = $startTime->format('H:i:s');
$endTime = ($lastTimestamp) ? new DateTime() : null;
// if(!empty($endTime)) {
// $endTime = $endTime->format('H:i:s');
// }
// Vérification des conversions en DateTime // Vérification des conversions en DateTime
if (!$startDate || !$startTime || ($lastTimestamp && !$endTime)) { if (!$startDate || !$startTime || ($lastTimestamp && !$endTime)) {
throw new \Exception("La conversion en DateTime a échoué."); throw new \Exception("La conversion en DateTime a échoué.");
return false;
} }
// Extraction des autres données nécessaires // Extraction des autres données nécessaires
$heartRateList = $monFichierFit->data_mesgs['record']['heart_rate']; if(!($heartRateList = $monFichierFit->data_mesgs['record']['heart_rate'])) {
throw new \InvalidArgumentException("Fichier .fit ne comportant pas de fréquences cardiaques.\n Fichier Invalide !");
}
$variability = max($heartRateList) - min($heartRateList); $variability = max($heartRateList) - min($heartRateList);
$average = number_format(array_sum($heartRateList) / count($heartRateList), 2); $average = number_format(array_sum($heartRateList) / count($heartRateList), 2);
$varianceV = array_sum(array_map(fn($x) => pow($x - $average, 2), $heartRateList)) / count($heartRateList); $varianceV = array_sum(array_map(fn($x) => pow($x - $average, 2), $heartRateList)) / count($heartRateList);
@ -136,7 +152,8 @@ class ActivityManager
$standardDeviation = number_format(sqrt($variance), 2); $standardDeviation = number_format(sqrt($variance), 2);
$maximum = max($heartRateList); $maximum = max($heartRateList);
$minimum = min($heartRateList); $minimum = min($heartRateList);
if(isset($monFichierFit->data_mesgs['record']['temperature'])){
if(!empty($monFichierFit->data_mesgs['record']['temperature']) && isset($monFichierFit->data_mesgs['record']['temperature'])){
// Extraction de la température moyenne (si disponible // Extraction de la température moyenne (si disponible
$temperatureList = $monFichierFit->data_mesgs['record']['temperature']; $temperatureList = $monFichierFit->data_mesgs['record']['temperature'];
$averageTemperature = (!empty($temperatureList)) ? number_format(array_sum($temperatureList) / count($temperatureList), 1) : -200; $averageTemperature = (!empty($temperatureList)) ? number_format(array_sum($temperatureList) / count($temperatureList), 1) : -200;
@ -145,10 +162,15 @@ class ActivityManager
$averageTemperature = -200; $averageTemperature = -200;
} }
if($monFichierFit->data_mesgs['record']['speed']) {
$isPaused = count($monFichierFit->isPaused()) > 0; $isPaused = count($monFichierFit->isPaused()) > 0;
} else {
$isPaused = false;
}
// Création de l'objet Activity // Création de l'objet Activity
$newActivity = new Activity( $newActivity = new Activity(
15,
$type, $type,
$startDate, $startDate,
$startTime, $startTime,
@ -166,9 +188,16 @@ class ActivityManager
// $this->dataManager->activityRepository->add($newActivity); // $this->dataManager->activityRepository->add($newActivity);
// if ($this->saveFitFileToJSON($monFichierFit)) { // if ($this->saveFitFileToJSON($monFichierFit)) {
// Ajout de l'activité et enregistrement du fichier FIT en JSON // Ajout de l'activité et enregistrement du fichier FIT en JSON
if ($this->authService->getCurrentUser()->getRole()->addActivity($newActivity)) { $activityGateway = new ActivityGateway(new Connexion(DSN, DB_USER, DB_PASSWORD));
$map = new ActivityMapper();
$activityEntity = $map->activityToEntity($newActivity);
if($activityGateway->addActivity($activityEntity)) {
return true; return true;
} }
// TODO : add the activity
// if ($this->authService->getCurrentUser()->getRole()->addActivity($newActivity)) {
// return true;
// }
// } // }

@ -134,21 +134,7 @@ class UserManager
} }
public function getFriends(): array{ public function getFriends(): array{
return [ return $this->currentUser->getRole()->getUsersList();
[
'nom' => 'John',
'prenom' => 'Doe',
'img' => 'test',
'username' => 'johndoe',
],
[
'nom' => 'Alice',
'prenom' => 'Smith',
'img' => 'test2',
'username' => 'alicesmith',
],
];
//return $this->currentUser->getRole()->getUsersList();
} }
// NEED TO PERSIST // NEED TO PERSIST

@ -1,9 +1,10 @@
<?php <?php
namespace Repository; namespace Repository;
use Model\User;
interface IUserRepository extends IGenericRepository { interface IUserRepository extends IGenericRepository {
public function addFriend(int $user1,int $user2); public function addFriend(User $user1,user $user2);
public function deleteFriend(int $user1,int $user2); public function deleteFriend(User $user1,User $user2);
public function getItemByEmail(string $email); public function getItemByEmail(string $email);

@ -2,6 +2,7 @@
namespace Stub; namespace Stub;
use DateTime;
use Repository\INotificationRepository; use Repository\INotificationRepository;
use Model\Notification; use Model\Notification;
@ -10,13 +11,13 @@ class NotificationRepository implements INotificationRepository
private $notifications = []; // Array to store notifications private $notifications = []; // Array to store notifications
public function __construct() public function __construct()
{ {
$date = DateTime::createFromFormat('d/m/Y', date('d/m/Y'));
// Initialize with some sample notifications for user IDs 1, 2, and 3 // Initialize with some sample notifications for user IDs 1, 2, and 3
$this->notifications[] = new Notification(1, 'info', 'Welcome to our service!'); $this->notifications[] = new Notification('info', $date,'Welcome to our service!', '1', 1);
$this->notifications[] = new Notification(2, 'alert', 'Your subscription is about to expire.'); $this->notifications[] = new Notification('info', $date,'Welcome to our service!', '1', 1);
$this->notifications[] = new Notification(3, 'info', 'New features available.'); $this->notifications[] = new Notification('info', $date,'Welcome to our service!', '1', 1);
$this->notifications[] = new Notification(1, 'reminder', 'Dont forget your upcoming appointment.'); $this->notifications[] = new Notification('info', $date,'Welcome to our service!', '1', 1);
$this->notifications[] = new Notification(2, 'update', 'Service update completed.'); $this->notifications[] = new Notification('info', $date,'Welcome to our service!', '1', 1);
// Add more notifications as needed
} }
public function getItemById(int $id) public function getItemById(int $id)
{ {

@ -0,0 +1,215 @@
<?php
use PHPUnit\Framework\TestCase;
//use Database\{Connexion, AthleteGateway,AthleteEntity};
use Database\AthleteEntity;
use Database\AthleteGateway;
use Database\Connexion;
use Database\AthleteMapper;
use Database\CoachGateway;
use Database\CoachEntity;
use Database\CoachMapper;
class GatewayTest extends TestCase {
//Partie concernant les Athlètes
public function testGetAthlete() {
//$dsn = "pgsql:host=londres;port=8888;dbname=dbkemonteiro2;user=kemonteiro2;password=Mdp";
require "loginDatabase.php";
$connexion = new Connexion($dsn,$username,$password);
$athleteGateway = new AthleteGateway($connexion);
$result = $athleteGateway->getAthlete();
//var_dump($result);
}
/* Fonctionne mais en commentaire pour pas add et del a chaque fois
public function testAddAthlete(){
$dsn = "mysql:host=londres;dbname=dbkemonteiro2;";
$username = "kemonteiro2";
$password = "#Phpmyadmin63";
$connexion = new Connexion($dsn,$username,$password);
$athleteGateway = new AthleteGateway($connexion);
$dateSpecifique = "2023-11-26";
$timestamp = strtotime($dateSpecifique);
$dateSQL = date("Y-m-d", $timestamp);
$athleteEntity = new AthleteEntity();
$athleteEntity->setNom('John');
$athleteEntity->setPrenom('Doe');
$athleteEntity->setIdAthlete(1234);
$athleteEntity->setEmail('kevin.monteiro@gmail.fr');
$athleteEntity->setSexe('H');
$athleteEntity->setTaille(169);
$athleteEntity->setPoids(69);
$athleteEntity->setMotDePasse('motdepasse');
$athleteEntity->setDateNaissance($dateSQL);
$result2 = $athleteGateway->addAthlete($athleteEntity);
}
public function testDeleteAthlete(){
$dsn = "mysql:host=londres;dbname=dbkemonteiro2;";
$username = "kemonteiro2";
$password = "#Phpmyadmin63";
$connexion = new Connexion($dsn,$username,$password);
$athleteGateway = new AthleteGateway($connexion);
$result = $athleteGateway->deleteAthlete( //idAthlete );
var_dump($result);
}*/
public function testUpdateAthlete(){
$dsn = "mysql:host=londres;dbname=dbkemonteiro2;";
$username = "kemonteiro2";
$password = "#Phpmyadmin63";
$connexion = new Connexion($dsn,$username,$password);
$athleteGateway = new AthleteGateway($connexion);
$dateSpecifique = "2004-08-26";
$timestamp = strtotime($dateSpecifique);
$dateSQL = date("Y-m-d", $timestamp);
$athleteEntity = new AthleteEntity();
$athleteEntity->setNom('John');
$athleteEntity->setPrenom('Doe');
$athleteEntity->setIdAthlete(13);
$athleteEntity->setEmail('kevin.monteiro@gmail.fr');
$athleteEntity->setSexe('H');
$athleteEntity->setTaille(169);
$athleteEntity->setPoids(69);
$athleteEntity->setMotDePasse('motdepasse');
$athleteEntity->setDateNaissance($dateSQL);
$athleteEntity->setIsCoach(FALSE);
$athleteEntity->setCoachId(NULL);
$athleteEntity2 = new AthleteEntity();
$athleteEntity2->setNom('Monteiro');
$athleteEntity2->setPrenom('Kevin');
$athleteEntity2->setIdAthlete(13);
$athleteEntity2->setEmail('kevin.monteiro@gmail.fr');
$athleteEntity2->setSexe('H');
$athleteEntity2->setTaille(169);
$athleteEntity2->setPoids(69);
$athleteEntity2->setMotDePasse('motdepasse');
$athleteEntity2->setDateNaissance($dateSQL);
$athleteEntity2->setIsCoach(TRUE);
$athleteEntity2->setCoachId(1);
$result = $athleteGateway->updateAthlete($athleteEntity, $athleteEntity2);
}
//Partie concernant les Coachs
public function testGetCoach() {
//$dsn = "pgsql:host=londres;port=8888;dbname=dbkemonteiro2;user=kemonteiro2;password=Mdp";
$dsn = "mysql:host=londres;dbname=dbkemonteiro2;";
$username = "kemonteiro2";
$password = "#Phpmyadmin63";
$connexion = new Connexion($dsn,$username,$password);
$coachGateway = new CoachGateway($connexion);
$result = $coachGateway->getCoach();
var_dump($result);
}
/*
//Fonctionne PAS A PARTIR DE LA
public function testAddCoach(){
$dsn = "mysql:host=londres;dbname=dbkemonteiro2;";
$username = "kemonteiro2";
$password = "#Phpmyadmin63";
$connexion = new Connexion($dsn,$username,$password);
$coachGateway = new CoachGateway($connexion);
$dateSpecifique = "2023-11-26";
$timestamp = strtotime($dateSpecifique);
$dateSQL = date("Y-m-d", $timestamp);
$coachEntity = new CoachEntity();
$coachEntity->setNom('John');
$coachEntity->setPrenom('Doe');
$coachEntity->setIdCoach(1234);
$coachEntity->setEmail('kevin.monteiro@gmail.fr');
$coachEntity->setSexe('H');
$coachEntity->setTaille(169);
$coachEntity->setPoids(69);
$coachEntity->setMotDePasse('motdepasse');
$coachEntity->setDateNaissance($dateSQL);
$result2 = $coachGateway->addCoach($coachEntity);
}
public function testDeleteAthlete(){
$dsn = "mysql:host=londres;dbname=dbkemonteiro2;";
$username = "kemonteiro2";
$password = "#Phpmyadmin63";
$connexion = new Connexion($dsn,$username,$password);
$athleteGateway = new AthleteGateway($connexion);
$result = $athleteGateway->deleteAthlete( //idAthlete );
var_dump($result);
}*/
/*
public function testUpdateAthlete(){
$dsn = "mysql:host=londres;dbname=dbkemonteiro2;";
$username = "kemonteiro2";
$password = "#Phpmyadmin63";
$connexion = new Connexion($dsn,$username,$password);
$athleteGateway = new AthleteGateway($connexion);
$dateSpecifique = "2004-08-26";
$timestamp = strtotime($dateSpecifique);
$dateSQL = date("Y-m-d", $timestamp);
$athleteEntity = new AthleteEntity();
$athleteEntity->setNom('John');
$athleteEntity->setPrenom('Doe');
$athleteEntity->setIdAthlete(13);
$athleteEntity->setEmail('kevin.monteiro@gmail.fr');
$athleteEntity->setSexe('H');
$athleteEntity->setTaille(169);
$athleteEntity->setPoids(69);
$athleteEntity->setMotDePasse('motdepasse');
$athleteEntity->setDateNaissance($dateSQL);
$athleteEntity2 = new AthleteEntity();
$athleteEntity2->setNom('Monteiro');
$athleteEntity2->setPrenom('Kevin');
$athleteEntity2->setIdAthlete(13);
$athleteEntity2->setEmail('kevin.monteiro@gmail.fr');
$athleteEntity2->setSexe('H');
$athleteEntity2->setTaille(169);
$athleteEntity2->setPoids(69);
$athleteEntity2->setMotDePasse('motdepasse');
$athleteEntity2->setDateNaissance($dateSQL);
$result = $athleteGateway->updateAthlete($athleteEntity, $athleteEntity2);
}*/
}

@ -0,0 +1,43 @@
<?php
use PHPUnit\Framework\TestCase;
use Model\User;
use Database\AthleteEntity;
use Database\AthleteGateway;
use Database\Connexion;
use Database\AthleteMapper;
class MapperTest extends TestCase {
public function testMapperAthlete() {
//$dsn = "pgsql:host=londres;port=8888;dbname=dbkemonteiro2;user=kemonteiro2;password=Mdp";
$dsn = "mysql:host=londres;dbname=dbkemonteiro2;";
$username = "kemonteiro2";
$password = "#Phpmyadmin63";
$connexion = new Connexion($dsn,$username,$password);
$athleteGateway = new AthleteGateway($connexion);
$result = $athleteGateway->getAthlete();
$map = new AthleteMapper ();
//SQL To AthleteEntity
$athleteEntity = $map->athleteSqlToEntity($result);
foreach($athleteEntity as $ath){
$result = $ath->getNom();
var_dump($result);
//Pour chaque AthleteEntity : Athlete Entity To User avec Role Athlete(Model)
$user = $map->athleteEntityToModel($ath);
var_dump($user->getId());
//Pour chaque Athlete du Model -> Athlete Entity
$res = $map->athleteToEntity($user);
var_dump($res->getIdAthlete());
}
}
}

@ -0,0 +1,7 @@
<?php
$dsn = "psql:host=localhost;dbname=sae_3;";
$username = "Perederii";
$password = "";
?>

@ -255,6 +255,7 @@ return array(
'PHPUnit\\Framework\\Attributes\\ExcludeStaticPropertyFromBackup' => $vendorDir . '/phpunit/phpunit/src/Framework/Attributes/ExcludeStaticPropertyFromBackup.php', 'PHPUnit\\Framework\\Attributes\\ExcludeStaticPropertyFromBackup' => $vendorDir . '/phpunit/phpunit/src/Framework/Attributes/ExcludeStaticPropertyFromBackup.php',
'PHPUnit\\Framework\\Attributes\\Group' => $vendorDir . '/phpunit/phpunit/src/Framework/Attributes/Group.php', 'PHPUnit\\Framework\\Attributes\\Group' => $vendorDir . '/phpunit/phpunit/src/Framework/Attributes/Group.php',
'PHPUnit\\Framework\\Attributes\\IgnoreClassForCodeCoverage' => $vendorDir . '/phpunit/phpunit/src/Framework/Attributes/IgnoreClassForCodeCoverage.php', 'PHPUnit\\Framework\\Attributes\\IgnoreClassForCodeCoverage' => $vendorDir . '/phpunit/phpunit/src/Framework/Attributes/IgnoreClassForCodeCoverage.php',
'PHPUnit\\Framework\\Attributes\\IgnoreDeprecations' => $vendorDir . '/phpunit/phpunit/src/Framework/Attributes/IgnoreDeprecations.php',
'PHPUnit\\Framework\\Attributes\\IgnoreFunctionForCodeCoverage' => $vendorDir . '/phpunit/phpunit/src/Framework/Attributes/IgnoreFunctionForCodeCoverage.php', 'PHPUnit\\Framework\\Attributes\\IgnoreFunctionForCodeCoverage' => $vendorDir . '/phpunit/phpunit/src/Framework/Attributes/IgnoreFunctionForCodeCoverage.php',
'PHPUnit\\Framework\\Attributes\\IgnoreMethodForCodeCoverage' => $vendorDir . '/phpunit/phpunit/src/Framework/Attributes/IgnoreMethodForCodeCoverage.php', 'PHPUnit\\Framework\\Attributes\\IgnoreMethodForCodeCoverage' => $vendorDir . '/phpunit/phpunit/src/Framework/Attributes/IgnoreMethodForCodeCoverage.php',
'PHPUnit\\Framework\\Attributes\\Large' => $vendorDir . '/phpunit/phpunit/src/Framework/Attributes/Large.php', 'PHPUnit\\Framework\\Attributes\\Large' => $vendorDir . '/phpunit/phpunit/src/Framework/Attributes/Large.php',
@ -471,6 +472,8 @@ return array(
'PHPUnit\\Logging\\JUnit\\TestFailedSubscriber' => $vendorDir . '/phpunit/phpunit/src/Logging/JUnit/Subscriber/TestFailedSubscriber.php', 'PHPUnit\\Logging\\JUnit\\TestFailedSubscriber' => $vendorDir . '/phpunit/phpunit/src/Logging/JUnit/Subscriber/TestFailedSubscriber.php',
'PHPUnit\\Logging\\JUnit\\TestFinishedSubscriber' => $vendorDir . '/phpunit/phpunit/src/Logging/JUnit/Subscriber/TestFinishedSubscriber.php', 'PHPUnit\\Logging\\JUnit\\TestFinishedSubscriber' => $vendorDir . '/phpunit/phpunit/src/Logging/JUnit/Subscriber/TestFinishedSubscriber.php',
'PHPUnit\\Logging\\JUnit\\TestMarkedIncompleteSubscriber' => $vendorDir . '/phpunit/phpunit/src/Logging/JUnit/Subscriber/TestMarkedIncompleteSubscriber.php', 'PHPUnit\\Logging\\JUnit\\TestMarkedIncompleteSubscriber' => $vendorDir . '/phpunit/phpunit/src/Logging/JUnit/Subscriber/TestMarkedIncompleteSubscriber.php',
'PHPUnit\\Logging\\JUnit\\TestPreparationFailedSubscriber' => $vendorDir . '/phpunit/phpunit/src/Logging/JUnit/Subscriber/TestPreparationFailedSubscriber.php',
'PHPUnit\\Logging\\JUnit\\TestPreparationStartedSubscriber' => $vendorDir . '/phpunit/phpunit/src/Logging/JUnit/Subscriber/TestPreparationStartedSubscriber.php',
'PHPUnit\\Logging\\JUnit\\TestPreparedSubscriber' => $vendorDir . '/phpunit/phpunit/src/Logging/JUnit/Subscriber/TestPreparedSubscriber.php', 'PHPUnit\\Logging\\JUnit\\TestPreparedSubscriber' => $vendorDir . '/phpunit/phpunit/src/Logging/JUnit/Subscriber/TestPreparedSubscriber.php',
'PHPUnit\\Logging\\JUnit\\TestRunnerExecutionFinishedSubscriber' => $vendorDir . '/phpunit/phpunit/src/Logging/JUnit/Subscriber/TestRunnerExecutionFinishedSubscriber.php', 'PHPUnit\\Logging\\JUnit\\TestRunnerExecutionFinishedSubscriber' => $vendorDir . '/phpunit/phpunit/src/Logging/JUnit/Subscriber/TestRunnerExecutionFinishedSubscriber.php',
'PHPUnit\\Logging\\JUnit\\TestSkippedSubscriber' => $vendorDir . '/phpunit/phpunit/src/Logging/JUnit/Subscriber/TestSkippedSubscriber.php', 'PHPUnit\\Logging\\JUnit\\TestSkippedSubscriber' => $vendorDir . '/phpunit/phpunit/src/Logging/JUnit/Subscriber/TestSkippedSubscriber.php',
@ -491,26 +494,19 @@ return array(
'PHPUnit\\Logging\\TestDox\\HtmlRenderer' => $vendorDir . '/phpunit/phpunit/src/Logging/TestDox/HtmlRenderer.php', 'PHPUnit\\Logging\\TestDox\\HtmlRenderer' => $vendorDir . '/phpunit/phpunit/src/Logging/TestDox/HtmlRenderer.php',
'PHPUnit\\Logging\\TestDox\\NamePrettifier' => $vendorDir . '/phpunit/phpunit/src/Logging/TestDox/NamePrettifier.php', 'PHPUnit\\Logging\\TestDox\\NamePrettifier' => $vendorDir . '/phpunit/phpunit/src/Logging/TestDox/NamePrettifier.php',
'PHPUnit\\Logging\\TestDox\\PlainTextRenderer' => $vendorDir . '/phpunit/phpunit/src/Logging/TestDox/PlainTextRenderer.php', 'PHPUnit\\Logging\\TestDox\\PlainTextRenderer' => $vendorDir . '/phpunit/phpunit/src/Logging/TestDox/PlainTextRenderer.php',
'PHPUnit\\Logging\\TestDox\\Subscriber' => $vendorDir . '/phpunit/phpunit/src/Logging/TestDox/TestMethod/Subscriber/Subscriber.php', 'PHPUnit\\Logging\\TestDox\\Subscriber' => $vendorDir . '/phpunit/phpunit/src/Logging/TestDox/TestResult/Subscriber/Subscriber.php',
'PHPUnit\\Logging\\TestDox\\TestConsideredRiskySubscriber' => $vendorDir . '/phpunit/phpunit/src/Logging/TestDox/TestMethod/Subscriber/TestConsideredRiskySubscriber.php', 'PHPUnit\\Logging\\TestDox\\TestConsideredRiskySubscriber' => $vendorDir . '/phpunit/phpunit/src/Logging/TestDox/TestResult/Subscriber/TestConsideredRiskySubscriber.php',
'PHPUnit\\Logging\\TestDox\\TestCreatedMockObjectForAbstractClassSubscriber' => $vendorDir . '/phpunit/phpunit/src/Logging/TestDox/TestMethod/Subscriber/TestCreatedMockObjectForAbstractClassSubscriber.php', 'PHPUnit\\Logging\\TestDox\\TestErroredSubscriber' => $vendorDir . '/phpunit/phpunit/src/Logging/TestDox/TestResult/Subscriber/TestErroredSubscriber.php',
'PHPUnit\\Logging\\TestDox\\TestCreatedMockObjectForTraitSubscriber' => $vendorDir . '/phpunit/phpunit/src/Logging/TestDox/TestMethod/Subscriber/TestCreatedMockObjectForTraitSubscriber.php', 'PHPUnit\\Logging\\TestDox\\TestFailedSubscriber' => $vendorDir . '/phpunit/phpunit/src/Logging/TestDox/TestResult/Subscriber/TestFailedSubscriber.php',
'PHPUnit\\Logging\\TestDox\\TestCreatedMockObjectFromWsdlSubscriber' => $vendorDir . '/phpunit/phpunit/src/Logging/TestDox/TestMethod/Subscriber/TestCreatedMockObjectFromWsdlSubscriber.php', 'PHPUnit\\Logging\\TestDox\\TestFinishedSubscriber' => $vendorDir . '/phpunit/phpunit/src/Logging/TestDox/TestResult/Subscriber/TestFinishedSubscriber.php',
'PHPUnit\\Logging\\TestDox\\TestCreatedMockObjectSubscriber' => $vendorDir . '/phpunit/phpunit/src/Logging/TestDox/TestMethod/Subscriber/TestCreatedMockObjectSubscriber.php', 'PHPUnit\\Logging\\TestDox\\TestMarkedIncompleteSubscriber' => $vendorDir . '/phpunit/phpunit/src/Logging/TestDox/TestResult/Subscriber/TestMarkedIncompleteSubscriber.php',
'PHPUnit\\Logging\\TestDox\\TestCreatedPartialMockObjectSubscriber' => $vendorDir . '/phpunit/phpunit/src/Logging/TestDox/TestMethod/Subscriber/TestCreatedPartialMockObjectSubscriber.php', 'PHPUnit\\Logging\\TestDox\\TestPassedSubscriber' => $vendorDir . '/phpunit/phpunit/src/Logging/TestDox/TestResult/Subscriber/TestPassedSubscriber.php',
'PHPUnit\\Logging\\TestDox\\TestCreatedTestProxySubscriber' => $vendorDir . '/phpunit/phpunit/src/Logging/TestDox/TestMethod/Subscriber/TestCreatedTestProxySubscriber.php', 'PHPUnit\\Logging\\TestDox\\TestPreparedSubscriber' => $vendorDir . '/phpunit/phpunit/src/Logging/TestDox/TestResult/Subscriber/TestPreparedSubscriber.php',
'PHPUnit\\Logging\\TestDox\\TestCreatedTestStubSubscriber' => $vendorDir . '/phpunit/phpunit/src/Logging/TestDox/TestMethod/Subscriber/TestCreatedTestStubSubscriber.php', 'PHPUnit\\Logging\\TestDox\\TestResult' => $vendorDir . '/phpunit/phpunit/src/Logging/TestDox/TestResult/TestResult.php',
'PHPUnit\\Logging\\TestDox\\TestErroredSubscriber' => $vendorDir . '/phpunit/phpunit/src/Logging/TestDox/TestMethod/Subscriber/TestErroredSubscriber.php', 'PHPUnit\\Logging\\TestDox\\TestResultCollection' => $vendorDir . '/phpunit/phpunit/src/Logging/TestDox/TestResult/TestResultCollection.php',
'PHPUnit\\Logging\\TestDox\\TestFailedSubscriber' => $vendorDir . '/phpunit/phpunit/src/Logging/TestDox/TestMethod/Subscriber/TestFailedSubscriber.php', 'PHPUnit\\Logging\\TestDox\\TestResultCollectionIterator' => $vendorDir . '/phpunit/phpunit/src/Logging/TestDox/TestResult/TestResultCollectionIterator.php',
'PHPUnit\\Logging\\TestDox\\TestFinishedSubscriber' => $vendorDir . '/phpunit/phpunit/src/Logging/TestDox/TestMethod/Subscriber/TestFinishedSubscriber.php', 'PHPUnit\\Logging\\TestDox\\TestResultCollector' => $vendorDir . '/phpunit/phpunit/src/Logging/TestDox/TestResult/TestResultCollector.php',
'PHPUnit\\Logging\\TestDox\\TestMarkedIncompleteSubscriber' => $vendorDir . '/phpunit/phpunit/src/Logging/TestDox/TestMethod/Subscriber/TestMarkedIncompleteSubscriber.php', 'PHPUnit\\Logging\\TestDox\\TestSkippedSubscriber' => $vendorDir . '/phpunit/phpunit/src/Logging/TestDox/TestResult/Subscriber/TestSkippedSubscriber.php',
'PHPUnit\\Logging\\TestDox\\TestPassedSubscriber' => $vendorDir . '/phpunit/phpunit/src/Logging/TestDox/TestMethod/Subscriber/TestPassedSubscriber.php',
'PHPUnit\\Logging\\TestDox\\TestPreparedSubscriber' => $vendorDir . '/phpunit/phpunit/src/Logging/TestDox/TestMethod/Subscriber/TestPreparedSubscriber.php',
'PHPUnit\\Logging\\TestDox\\TestResult' => $vendorDir . '/phpunit/phpunit/src/Logging/TestDox/TestMethod/TestResult.php',
'PHPUnit\\Logging\\TestDox\\TestResultCollection' => $vendorDir . '/phpunit/phpunit/src/Logging/TestDox/TestMethod/TestResultCollection.php',
'PHPUnit\\Logging\\TestDox\\TestResultCollectionIterator' => $vendorDir . '/phpunit/phpunit/src/Logging/TestDox/TestMethod/TestResultCollectionIterator.php',
'PHPUnit\\Logging\\TestDox\\TestResultCollector' => $vendorDir . '/phpunit/phpunit/src/Logging/TestDox/TestMethod/TestResultCollector.php',
'PHPUnit\\Logging\\TestDox\\TestSkippedSubscriber' => $vendorDir . '/phpunit/phpunit/src/Logging/TestDox/TestMethod/Subscriber/TestSkippedSubscriber.php',
'PHPUnit\\Metadata\\After' => $vendorDir . '/phpunit/phpunit/src/Metadata/After.php', 'PHPUnit\\Metadata\\After' => $vendorDir . '/phpunit/phpunit/src/Metadata/After.php',
'PHPUnit\\Metadata\\AfterClass' => $vendorDir . '/phpunit/phpunit/src/Metadata/AfterClass.php', 'PHPUnit\\Metadata\\AfterClass' => $vendorDir . '/phpunit/phpunit/src/Metadata/AfterClass.php',
'PHPUnit\\Metadata\\Annotation\\Parser\\DocBlock' => $vendorDir . '/phpunit/phpunit/src/Metadata/Parser/Annotation/DocBlock.php', 'PHPUnit\\Metadata\\Annotation\\Parser\\DocBlock' => $vendorDir . '/phpunit/phpunit/src/Metadata/Parser/Annotation/DocBlock.php',
@ -540,6 +536,7 @@ return array(
'PHPUnit\\Metadata\\ExcludeStaticPropertyFromBackup' => $vendorDir . '/phpunit/phpunit/src/Metadata/ExcludeStaticPropertyFromBackup.php', 'PHPUnit\\Metadata\\ExcludeStaticPropertyFromBackup' => $vendorDir . '/phpunit/phpunit/src/Metadata/ExcludeStaticPropertyFromBackup.php',
'PHPUnit\\Metadata\\Group' => $vendorDir . '/phpunit/phpunit/src/Metadata/Group.php', 'PHPUnit\\Metadata\\Group' => $vendorDir . '/phpunit/phpunit/src/Metadata/Group.php',
'PHPUnit\\Metadata\\IgnoreClassForCodeCoverage' => $vendorDir . '/phpunit/phpunit/src/Metadata/IgnoreClassForCodeCoverage.php', 'PHPUnit\\Metadata\\IgnoreClassForCodeCoverage' => $vendorDir . '/phpunit/phpunit/src/Metadata/IgnoreClassForCodeCoverage.php',
'PHPUnit\\Metadata\\IgnoreDeprecations' => $vendorDir . '/phpunit/phpunit/src/Metadata/IgnoreDeprecations.php',
'PHPUnit\\Metadata\\IgnoreFunctionForCodeCoverage' => $vendorDir . '/phpunit/phpunit/src/Metadata/IgnoreFunctionForCodeCoverage.php', 'PHPUnit\\Metadata\\IgnoreFunctionForCodeCoverage' => $vendorDir . '/phpunit/phpunit/src/Metadata/IgnoreFunctionForCodeCoverage.php',
'PHPUnit\\Metadata\\IgnoreMethodForCodeCoverage' => $vendorDir . '/phpunit/phpunit/src/Metadata/IgnoreMethodForCodeCoverage.php', 'PHPUnit\\Metadata\\IgnoreMethodForCodeCoverage' => $vendorDir . '/phpunit/phpunit/src/Metadata/IgnoreMethodForCodeCoverage.php',
'PHPUnit\\Metadata\\InvalidVersionRequirementException' => $vendorDir . '/phpunit/phpunit/src/Metadata/Exception/InvalidVersionRequirementException.php', 'PHPUnit\\Metadata\\InvalidVersionRequirementException' => $vendorDir . '/phpunit/phpunit/src/Metadata/Exception/InvalidVersionRequirementException.php',
@ -599,6 +596,7 @@ return array(
'PHPUnit\\Runner\\ClassIsAbstractException' => $vendorDir . '/phpunit/phpunit/src/Runner/Exception/ClassIsAbstractException.php', 'PHPUnit\\Runner\\ClassIsAbstractException' => $vendorDir . '/phpunit/phpunit/src/Runner/Exception/ClassIsAbstractException.php',
'PHPUnit\\Runner\\CodeCoverage' => $vendorDir . '/phpunit/phpunit/src/Runner/CodeCoverage.php', 'PHPUnit\\Runner\\CodeCoverage' => $vendorDir . '/phpunit/phpunit/src/Runner/CodeCoverage.php',
'PHPUnit\\Runner\\DirectoryCannotBeCreatedException' => $vendorDir . '/phpunit/phpunit/src/Runner/Exception/DirectoryCannotBeCreatedException.php', 'PHPUnit\\Runner\\DirectoryCannotBeCreatedException' => $vendorDir . '/phpunit/phpunit/src/Runner/Exception/DirectoryCannotBeCreatedException.php',
'PHPUnit\\Runner\\ErrorException' => $vendorDir . '/phpunit/phpunit/src/Runner/Exception/ErrorException.php',
'PHPUnit\\Runner\\ErrorHandler' => $vendorDir . '/phpunit/phpunit/src/Runner/ErrorHandler.php', 'PHPUnit\\Runner\\ErrorHandler' => $vendorDir . '/phpunit/phpunit/src/Runner/ErrorHandler.php',
'PHPUnit\\Runner\\Exception' => $vendorDir . '/phpunit/phpunit/src/Runner/Exception/Exception.php', 'PHPUnit\\Runner\\Exception' => $vendorDir . '/phpunit/phpunit/src/Runner/Exception/Exception.php',
'PHPUnit\\Runner\\Extension\\Extension' => $vendorDir . '/phpunit/phpunit/src/Runner/Extension/Extension.php', 'PHPUnit\\Runner\\Extension\\Extension' => $vendorDir . '/phpunit/phpunit/src/Runner/Extension/Extension.php',

@ -12,24 +12,22 @@ return array(
'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'), 'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'),
'Symfony\\Polyfill\\Ctype\\' => array($vendorDir . '/symfony/polyfill-ctype'), 'Symfony\\Polyfill\\Ctype\\' => array($vendorDir . '/symfony/polyfill-ctype'),
'Stub\\' => array($baseDir . '/src/data/stub', $baseDir . '/src/data/stub/service', $baseDir . '/src/data/stub/repository'), 'Stub\\' => array($baseDir . '/src/data/stub', $baseDir . '/src/data/stub/service', $baseDir . '/src/data/stub/repository'),
'Repository\\' => array($baseDir . '/src/data/model/repository'),
'Network\\' => array($baseDir . '/src/data/core/network'),
'Model\\' => array($baseDir . '/src/data/model'),
'Manager\\' => array($baseDir . '/src/data/model/manager'),
'Dotenv\\' => array($vendorDir . '/vlucas/phpdotenv/src'),
'Data\\' => array($baseDir . '/src/data'),
'Shared\\Exception\\' => array($baseDir . '/src/shared/exception'), 'Shared\\Exception\\' => array($baseDir . '/src/shared/exception'),
'Shared\\Attributes\\' => array($baseDir . '/src/shared/attributes'), 'Shared\\Attributes\\' => array($baseDir . '/src/shared/attributes'),
'Shared\\' => array($baseDir . '/src/shared'), 'Shared\\' => array($baseDir . '/src/shared'),
'Repository\\' => array($baseDir . '/src/data/model/repository'),
'Psr\\Container\\' => array($vendorDir . '/psr/container/src'), 'Psr\\Container\\' => array($vendorDir . '/psr/container/src'),
'PhpParser\\' => array($vendorDir . '/nikic/php-parser/lib/PhpParser'), 'PhpParser\\' => array($vendorDir . '/nikic/php-parser/lib/PhpParser'),
'PhpOption\\' => array($vendorDir . '/phpoption/phpoption/src/PhpOption'), 'PhpOption\\' => array($vendorDir . '/phpoption/phpoption/src/PhpOption'),
'Repository\\' => array($baseDir . '/src/data/model/repository'),
'Network\\' => array($baseDir . '/src/data/core/network'), 'Network\\' => array($baseDir . '/src/data/core/network'),
'Model\\' => array($baseDir . '/src/data/model'),
'Manager\\' => array($baseDir . '/src/data/model/manager'),
'Json\\' => array($baseDir . '/src/data/core/json'),
'Hearttrack\\' => array($baseDir . '/src'), 'Hearttrack\\' => array($baseDir . '/src'),
'GrahamCampbell\\ResultType\\' => array($vendorDir . '/graham-campbell/result-type/src'), 'GrahamCampbell\\ResultType\\' => array($vendorDir . '/graham-campbell/result-type/src'),
'Dotenv\\' => array($vendorDir . '/vlucas/phpdotenv/src'), 'Dotenv\\' => array($vendorDir . '/vlucas/phpdotenv/src'),
'DeepCopy\\' => array($vendorDir . '/myclabs/deep-copy/src/DeepCopy'), 'DeepCopy\\' => array($vendorDir . '/myclabs/deep-copy/src/DeepCopy'),
'Database\\' => array($baseDir . '/src/data/core/database'),
'Data\\Core\\' => array($baseDir . '/src/data/core'), 'Data\\Core\\' => array($baseDir . '/src/data/core'),
'Data\\' => array($baseDir . '/src/data'), 'Data\\' => array($baseDir . '/src/data'),
'Console\\' => array($baseDir . '/src/console'), 'Console\\' => array($baseDir . '/src/console'),

@ -52,6 +52,10 @@ class ComposerStaticInit1887e85fc3cfddacf8d7e17588dae6f1
'Model\\' => 6, 'Model\\' => 6,
'Manager\\' => 8, 'Manager\\' => 8,
), ),
'J' =>
array (
'Json\\' => 5,
),
'H' => 'H' =>
array ( array (
'Hearttrack\\' => 11, 'Hearttrack\\' => 11,
@ -63,10 +67,10 @@ class ComposerStaticInit1887e85fc3cfddacf8d7e17588dae6f1
'D' => 'D' =>
array ( array (
'Dotenv\\' => 7, 'Dotenv\\' => 7,
'Data\\' => 5,
'Doctrine\\Instantiator\\' => 22,
'DeepCopy\\' => 9, 'DeepCopy\\' => 9,
'Database\\' => 9,
'Data\\Core\\' => 10, 'Data\\Core\\' => 10,
'Data\\' => 5,
), ),
'C' => 'C' =>
array ( array (
@ -151,6 +155,10 @@ class ComposerStaticInit1887e85fc3cfddacf8d7e17588dae6f1
array ( array (
0 => __DIR__ . '/../..' . '/src/data/model/manager', 0 => __DIR__ . '/../..' . '/src/data/model/manager',
), ),
'Json\\' =>
array (
0 => __DIR__ . '/../..' . '/src/data/core/json',
),
'Hearttrack\\' => 'Hearttrack\\' =>
array ( array (
0 => __DIR__ . '/../..' . '/src', 0 => __DIR__ . '/../..' . '/src',
@ -163,18 +171,14 @@ class ComposerStaticInit1887e85fc3cfddacf8d7e17588dae6f1
array ( array (
0 => __DIR__ . '/..' . '/vlucas/phpdotenv/src', 0 => __DIR__ . '/..' . '/vlucas/phpdotenv/src',
), ),
'Doctrine\\Instantiator\\' =>
array (
0 => __DIR__ . '/..' . '/graham-campbell/result-type/src',
),
'Dotenv\\' =>
array (
0 => __DIR__ . '/..' . '/vlucas/phpdotenv/src',
),
'DeepCopy\\' => 'DeepCopy\\' =>
array ( array (
0 => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy', 0 => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy',
), ),
'Database\\' =>
array (
0 => __DIR__ . '/../..' . '/src/data/core/database',
),
'Data\\Core\\' => 'Data\\Core\\' =>
array ( array (
0 => __DIR__ . '/../..' . '/src/data/core', 0 => __DIR__ . '/../..' . '/src/data/core',
@ -467,6 +471,7 @@ class ComposerStaticInit1887e85fc3cfddacf8d7e17588dae6f1
'PHPUnit\\Framework\\Attributes\\ExcludeStaticPropertyFromBackup' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Attributes/ExcludeStaticPropertyFromBackup.php', 'PHPUnit\\Framework\\Attributes\\ExcludeStaticPropertyFromBackup' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Attributes/ExcludeStaticPropertyFromBackup.php',
'PHPUnit\\Framework\\Attributes\\Group' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Attributes/Group.php', 'PHPUnit\\Framework\\Attributes\\Group' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Attributes/Group.php',
'PHPUnit\\Framework\\Attributes\\IgnoreClassForCodeCoverage' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Attributes/IgnoreClassForCodeCoverage.php', 'PHPUnit\\Framework\\Attributes\\IgnoreClassForCodeCoverage' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Attributes/IgnoreClassForCodeCoverage.php',
'PHPUnit\\Framework\\Attributes\\IgnoreDeprecations' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Attributes/IgnoreDeprecations.php',
'PHPUnit\\Framework\\Attributes\\IgnoreFunctionForCodeCoverage' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Attributes/IgnoreFunctionForCodeCoverage.php', 'PHPUnit\\Framework\\Attributes\\IgnoreFunctionForCodeCoverage' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Attributes/IgnoreFunctionForCodeCoverage.php',
'PHPUnit\\Framework\\Attributes\\IgnoreMethodForCodeCoverage' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Attributes/IgnoreMethodForCodeCoverage.php', 'PHPUnit\\Framework\\Attributes\\IgnoreMethodForCodeCoverage' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Attributes/IgnoreMethodForCodeCoverage.php',
'PHPUnit\\Framework\\Attributes\\Large' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Attributes/Large.php', 'PHPUnit\\Framework\\Attributes\\Large' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Attributes/Large.php',
@ -683,6 +688,8 @@ class ComposerStaticInit1887e85fc3cfddacf8d7e17588dae6f1
'PHPUnit\\Logging\\JUnit\\TestFailedSubscriber' => __DIR__ . '/..' . '/phpunit/phpunit/src/Logging/JUnit/Subscriber/TestFailedSubscriber.php', 'PHPUnit\\Logging\\JUnit\\TestFailedSubscriber' => __DIR__ . '/..' . '/phpunit/phpunit/src/Logging/JUnit/Subscriber/TestFailedSubscriber.php',
'PHPUnit\\Logging\\JUnit\\TestFinishedSubscriber' => __DIR__ . '/..' . '/phpunit/phpunit/src/Logging/JUnit/Subscriber/TestFinishedSubscriber.php', 'PHPUnit\\Logging\\JUnit\\TestFinishedSubscriber' => __DIR__ . '/..' . '/phpunit/phpunit/src/Logging/JUnit/Subscriber/TestFinishedSubscriber.php',
'PHPUnit\\Logging\\JUnit\\TestMarkedIncompleteSubscriber' => __DIR__ . '/..' . '/phpunit/phpunit/src/Logging/JUnit/Subscriber/TestMarkedIncompleteSubscriber.php', 'PHPUnit\\Logging\\JUnit\\TestMarkedIncompleteSubscriber' => __DIR__ . '/..' . '/phpunit/phpunit/src/Logging/JUnit/Subscriber/TestMarkedIncompleteSubscriber.php',
'PHPUnit\\Logging\\JUnit\\TestPreparationFailedSubscriber' => __DIR__ . '/..' . '/phpunit/phpunit/src/Logging/JUnit/Subscriber/TestPreparationFailedSubscriber.php',
'PHPUnit\\Logging\\JUnit\\TestPreparationStartedSubscriber' => __DIR__ . '/..' . '/phpunit/phpunit/src/Logging/JUnit/Subscriber/TestPreparationStartedSubscriber.php',
'PHPUnit\\Logging\\JUnit\\TestPreparedSubscriber' => __DIR__ . '/..' . '/phpunit/phpunit/src/Logging/JUnit/Subscriber/TestPreparedSubscriber.php', 'PHPUnit\\Logging\\JUnit\\TestPreparedSubscriber' => __DIR__ . '/..' . '/phpunit/phpunit/src/Logging/JUnit/Subscriber/TestPreparedSubscriber.php',
'PHPUnit\\Logging\\JUnit\\TestRunnerExecutionFinishedSubscriber' => __DIR__ . '/..' . '/phpunit/phpunit/src/Logging/JUnit/Subscriber/TestRunnerExecutionFinishedSubscriber.php', 'PHPUnit\\Logging\\JUnit\\TestRunnerExecutionFinishedSubscriber' => __DIR__ . '/..' . '/phpunit/phpunit/src/Logging/JUnit/Subscriber/TestRunnerExecutionFinishedSubscriber.php',
'PHPUnit\\Logging\\JUnit\\TestSkippedSubscriber' => __DIR__ . '/..' . '/phpunit/phpunit/src/Logging/JUnit/Subscriber/TestSkippedSubscriber.php', 'PHPUnit\\Logging\\JUnit\\TestSkippedSubscriber' => __DIR__ . '/..' . '/phpunit/phpunit/src/Logging/JUnit/Subscriber/TestSkippedSubscriber.php',
@ -703,26 +710,19 @@ class ComposerStaticInit1887e85fc3cfddacf8d7e17588dae6f1
'PHPUnit\\Logging\\TestDox\\HtmlRenderer' => __DIR__ . '/..' . '/phpunit/phpunit/src/Logging/TestDox/HtmlRenderer.php', 'PHPUnit\\Logging\\TestDox\\HtmlRenderer' => __DIR__ . '/..' . '/phpunit/phpunit/src/Logging/TestDox/HtmlRenderer.php',
'PHPUnit\\Logging\\TestDox\\NamePrettifier' => __DIR__ . '/..' . '/phpunit/phpunit/src/Logging/TestDox/NamePrettifier.php', 'PHPUnit\\Logging\\TestDox\\NamePrettifier' => __DIR__ . '/..' . '/phpunit/phpunit/src/Logging/TestDox/NamePrettifier.php',
'PHPUnit\\Logging\\TestDox\\PlainTextRenderer' => __DIR__ . '/..' . '/phpunit/phpunit/src/Logging/TestDox/PlainTextRenderer.php', 'PHPUnit\\Logging\\TestDox\\PlainTextRenderer' => __DIR__ . '/..' . '/phpunit/phpunit/src/Logging/TestDox/PlainTextRenderer.php',
'PHPUnit\\Logging\\TestDox\\Subscriber' => __DIR__ . '/..' . '/phpunit/phpunit/src/Logging/TestDox/TestMethod/Subscriber/Subscriber.php', 'PHPUnit\\Logging\\TestDox\\Subscriber' => __DIR__ . '/..' . '/phpunit/phpunit/src/Logging/TestDox/TestResult/Subscriber/Subscriber.php',
'PHPUnit\\Logging\\TestDox\\TestConsideredRiskySubscriber' => __DIR__ . '/..' . '/phpunit/phpunit/src/Logging/TestDox/TestMethod/Subscriber/TestConsideredRiskySubscriber.php', 'PHPUnit\\Logging\\TestDox\\TestConsideredRiskySubscriber' => __DIR__ . '/..' . '/phpunit/phpunit/src/Logging/TestDox/TestResult/Subscriber/TestConsideredRiskySubscriber.php',
'PHPUnit\\Logging\\TestDox\\TestCreatedMockObjectForAbstractClassSubscriber' => __DIR__ . '/..' . '/phpunit/phpunit/src/Logging/TestDox/TestMethod/Subscriber/TestCreatedMockObjectForAbstractClassSubscriber.php', 'PHPUnit\\Logging\\TestDox\\TestErroredSubscriber' => __DIR__ . '/..' . '/phpunit/phpunit/src/Logging/TestDox/TestResult/Subscriber/TestErroredSubscriber.php',
'PHPUnit\\Logging\\TestDox\\TestCreatedMockObjectForTraitSubscriber' => __DIR__ . '/..' . '/phpunit/phpunit/src/Logging/TestDox/TestMethod/Subscriber/TestCreatedMockObjectForTraitSubscriber.php', 'PHPUnit\\Logging\\TestDox\\TestFailedSubscriber' => __DIR__ . '/..' . '/phpunit/phpunit/src/Logging/TestDox/TestResult/Subscriber/TestFailedSubscriber.php',
'PHPUnit\\Logging\\TestDox\\TestCreatedMockObjectFromWsdlSubscriber' => __DIR__ . '/..' . '/phpunit/phpunit/src/Logging/TestDox/TestMethod/Subscriber/TestCreatedMockObjectFromWsdlSubscriber.php', 'PHPUnit\\Logging\\TestDox\\TestFinishedSubscriber' => __DIR__ . '/..' . '/phpunit/phpunit/src/Logging/TestDox/TestResult/Subscriber/TestFinishedSubscriber.php',
'PHPUnit\\Logging\\TestDox\\TestCreatedMockObjectSubscriber' => __DIR__ . '/..' . '/phpunit/phpunit/src/Logging/TestDox/TestMethod/Subscriber/TestCreatedMockObjectSubscriber.php', 'PHPUnit\\Logging\\TestDox\\TestMarkedIncompleteSubscriber' => __DIR__ . '/..' . '/phpunit/phpunit/src/Logging/TestDox/TestResult/Subscriber/TestMarkedIncompleteSubscriber.php',
'PHPUnit\\Logging\\TestDox\\TestCreatedPartialMockObjectSubscriber' => __DIR__ . '/..' . '/phpunit/phpunit/src/Logging/TestDox/TestMethod/Subscriber/TestCreatedPartialMockObjectSubscriber.php', 'PHPUnit\\Logging\\TestDox\\TestPassedSubscriber' => __DIR__ . '/..' . '/phpunit/phpunit/src/Logging/TestDox/TestResult/Subscriber/TestPassedSubscriber.php',
'PHPUnit\\Logging\\TestDox\\TestCreatedTestProxySubscriber' => __DIR__ . '/..' . '/phpunit/phpunit/src/Logging/TestDox/TestMethod/Subscriber/TestCreatedTestProxySubscriber.php', 'PHPUnit\\Logging\\TestDox\\TestPreparedSubscriber' => __DIR__ . '/..' . '/phpunit/phpunit/src/Logging/TestDox/TestResult/Subscriber/TestPreparedSubscriber.php',
'PHPUnit\\Logging\\TestDox\\TestCreatedTestStubSubscriber' => __DIR__ . '/..' . '/phpunit/phpunit/src/Logging/TestDox/TestMethod/Subscriber/TestCreatedTestStubSubscriber.php', 'PHPUnit\\Logging\\TestDox\\TestResult' => __DIR__ . '/..' . '/phpunit/phpunit/src/Logging/TestDox/TestResult/TestResult.php',
'PHPUnit\\Logging\\TestDox\\TestErroredSubscriber' => __DIR__ . '/..' . '/phpunit/phpunit/src/Logging/TestDox/TestMethod/Subscriber/TestErroredSubscriber.php', 'PHPUnit\\Logging\\TestDox\\TestResultCollection' => __DIR__ . '/..' . '/phpunit/phpunit/src/Logging/TestDox/TestResult/TestResultCollection.php',
'PHPUnit\\Logging\\TestDox\\TestFailedSubscriber' => __DIR__ . '/..' . '/phpunit/phpunit/src/Logging/TestDox/TestMethod/Subscriber/TestFailedSubscriber.php', 'PHPUnit\\Logging\\TestDox\\TestResultCollectionIterator' => __DIR__ . '/..' . '/phpunit/phpunit/src/Logging/TestDox/TestResult/TestResultCollectionIterator.php',
'PHPUnit\\Logging\\TestDox\\TestFinishedSubscriber' => __DIR__ . '/..' . '/phpunit/phpunit/src/Logging/TestDox/TestMethod/Subscriber/TestFinishedSubscriber.php', 'PHPUnit\\Logging\\TestDox\\TestResultCollector' => __DIR__ . '/..' . '/phpunit/phpunit/src/Logging/TestDox/TestResult/TestResultCollector.php',
'PHPUnit\\Logging\\TestDox\\TestMarkedIncompleteSubscriber' => __DIR__ . '/..' . '/phpunit/phpunit/src/Logging/TestDox/TestMethod/Subscriber/TestMarkedIncompleteSubscriber.php', 'PHPUnit\\Logging\\TestDox\\TestSkippedSubscriber' => __DIR__ . '/..' . '/phpunit/phpunit/src/Logging/TestDox/TestResult/Subscriber/TestSkippedSubscriber.php',
'PHPUnit\\Logging\\TestDox\\TestPassedSubscriber' => __DIR__ . '/..' . '/phpunit/phpunit/src/Logging/TestDox/TestMethod/Subscriber/TestPassedSubscriber.php',
'PHPUnit\\Logging\\TestDox\\TestPreparedSubscriber' => __DIR__ . '/..' . '/phpunit/phpunit/src/Logging/TestDox/TestMethod/Subscriber/TestPreparedSubscriber.php',
'PHPUnit\\Logging\\TestDox\\TestResult' => __DIR__ . '/..' . '/phpunit/phpunit/src/Logging/TestDox/TestMethod/TestResult.php',
'PHPUnit\\Logging\\TestDox\\TestResultCollection' => __DIR__ . '/..' . '/phpunit/phpunit/src/Logging/TestDox/TestMethod/TestResultCollection.php',
'PHPUnit\\Logging\\TestDox\\TestResultCollectionIterator' => __DIR__ . '/..' . '/phpunit/phpunit/src/Logging/TestDox/TestMethod/TestResultCollectionIterator.php',
'PHPUnit\\Logging\\TestDox\\TestResultCollector' => __DIR__ . '/..' . '/phpunit/phpunit/src/Logging/TestDox/TestMethod/TestResultCollector.php',
'PHPUnit\\Logging\\TestDox\\TestSkippedSubscriber' => __DIR__ . '/..' . '/phpunit/phpunit/src/Logging/TestDox/TestMethod/Subscriber/TestSkippedSubscriber.php',
'PHPUnit\\Metadata\\After' => __DIR__ . '/..' . '/phpunit/phpunit/src/Metadata/After.php', 'PHPUnit\\Metadata\\After' => __DIR__ . '/..' . '/phpunit/phpunit/src/Metadata/After.php',
'PHPUnit\\Metadata\\AfterClass' => __DIR__ . '/..' . '/phpunit/phpunit/src/Metadata/AfterClass.php', 'PHPUnit\\Metadata\\AfterClass' => __DIR__ . '/..' . '/phpunit/phpunit/src/Metadata/AfterClass.php',
'PHPUnit\\Metadata\\Annotation\\Parser\\DocBlock' => __DIR__ . '/..' . '/phpunit/phpunit/src/Metadata/Parser/Annotation/DocBlock.php', 'PHPUnit\\Metadata\\Annotation\\Parser\\DocBlock' => __DIR__ . '/..' . '/phpunit/phpunit/src/Metadata/Parser/Annotation/DocBlock.php',
@ -752,6 +752,7 @@ class ComposerStaticInit1887e85fc3cfddacf8d7e17588dae6f1
'PHPUnit\\Metadata\\ExcludeStaticPropertyFromBackup' => __DIR__ . '/..' . '/phpunit/phpunit/src/Metadata/ExcludeStaticPropertyFromBackup.php', 'PHPUnit\\Metadata\\ExcludeStaticPropertyFromBackup' => __DIR__ . '/..' . '/phpunit/phpunit/src/Metadata/ExcludeStaticPropertyFromBackup.php',
'PHPUnit\\Metadata\\Group' => __DIR__ . '/..' . '/phpunit/phpunit/src/Metadata/Group.php', 'PHPUnit\\Metadata\\Group' => __DIR__ . '/..' . '/phpunit/phpunit/src/Metadata/Group.php',
'PHPUnit\\Metadata\\IgnoreClassForCodeCoverage' => __DIR__ . '/..' . '/phpunit/phpunit/src/Metadata/IgnoreClassForCodeCoverage.php', 'PHPUnit\\Metadata\\IgnoreClassForCodeCoverage' => __DIR__ . '/..' . '/phpunit/phpunit/src/Metadata/IgnoreClassForCodeCoverage.php',
'PHPUnit\\Metadata\\IgnoreDeprecations' => __DIR__ . '/..' . '/phpunit/phpunit/src/Metadata/IgnoreDeprecations.php',
'PHPUnit\\Metadata\\IgnoreFunctionForCodeCoverage' => __DIR__ . '/..' . '/phpunit/phpunit/src/Metadata/IgnoreFunctionForCodeCoverage.php', 'PHPUnit\\Metadata\\IgnoreFunctionForCodeCoverage' => __DIR__ . '/..' . '/phpunit/phpunit/src/Metadata/IgnoreFunctionForCodeCoverage.php',
'PHPUnit\\Metadata\\IgnoreMethodForCodeCoverage' => __DIR__ . '/..' . '/phpunit/phpunit/src/Metadata/IgnoreMethodForCodeCoverage.php', 'PHPUnit\\Metadata\\IgnoreMethodForCodeCoverage' => __DIR__ . '/..' . '/phpunit/phpunit/src/Metadata/IgnoreMethodForCodeCoverage.php',
'PHPUnit\\Metadata\\InvalidVersionRequirementException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Metadata/Exception/InvalidVersionRequirementException.php', 'PHPUnit\\Metadata\\InvalidVersionRequirementException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Metadata/Exception/InvalidVersionRequirementException.php',
@ -811,6 +812,7 @@ class ComposerStaticInit1887e85fc3cfddacf8d7e17588dae6f1
'PHPUnit\\Runner\\ClassIsAbstractException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Exception/ClassIsAbstractException.php', 'PHPUnit\\Runner\\ClassIsAbstractException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Exception/ClassIsAbstractException.php',
'PHPUnit\\Runner\\CodeCoverage' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/CodeCoverage.php', 'PHPUnit\\Runner\\CodeCoverage' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/CodeCoverage.php',
'PHPUnit\\Runner\\DirectoryCannotBeCreatedException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Exception/DirectoryCannotBeCreatedException.php', 'PHPUnit\\Runner\\DirectoryCannotBeCreatedException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Exception/DirectoryCannotBeCreatedException.php',
'PHPUnit\\Runner\\ErrorException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Exception/ErrorException.php',
'PHPUnit\\Runner\\ErrorHandler' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/ErrorHandler.php', 'PHPUnit\\Runner\\ErrorHandler' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/ErrorHandler.php',
'PHPUnit\\Runner\\Exception' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Exception/Exception.php', 'PHPUnit\\Runner\\Exception' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Exception/Exception.php',
'PHPUnit\\Runner\\Extension\\Extension' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Extension/Extension.php', 'PHPUnit\\Runner\\Extension\\Extension' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Extension/Extension.php',

@ -99,177 +99,6 @@
}, },
"install-path": "../altorouter/altorouter" "install-path": "../altorouter/altorouter"
}, },
{
"name": "doctrine/instantiator",
"version": "1.5.0",
"version_normalized": "1.5.0.0",
"source": {
"type": "git",
"url": "https://github.com/adriangibbons/php-fit-file-analysis.git",
"reference": "8efd36b1b963f01c42dc5329626519c040dec664"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/adriangibbons/php-fit-file-analysis/zipball/8efd36b1b963f01c42dc5329626519c040dec664",
"reference": "8efd36b1b963f01c42dc5329626519c040dec664",
"shasum": ""
},
"require-dev": {
"phpunit/phpunit": "4.8.*",
"satooshi/php-coveralls": "^2.0",
"squizlabs/php_codesniffer": "2.*"
},
"time": "2019-11-20T06:58:56+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"adriangibbons\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"description": "A PHP class for analysing FIT files created by Garmin GPS devices",
"homepage": "https://github.com/adriangibbons/php-fit-file-analysis",
"keywords": [
"Fit",
"garmin"
],
"support": {
"issues": "https://github.com/adriangibbons/php-fit-file-analysis/issues",
"source": "https://github.com/adriangibbons/php-fit-file-analysis/tree/master"
},
"install-path": "../adriangibbons/php-fit-file-analysis"
},
{
"name": "graham-campbell/result-type",
"version": "v1.1.2",
"version_normalized": "1.1.2.0",
"source": {
"type": "git",
"url": "https://github.com/GrahamCampbell/Result-Type.git",
"reference": "fbd48bce38f73f8a4ec8583362e732e4095e5862"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/fbd48bce38f73f8a4ec8583362e732e4095e5862",
"reference": "fbd48bce38f73f8a4ec8583362e732e4095e5862",
"shasum": ""
},
"require": {
"php": "^7.2.5 || ^8.0",
"phpoption/phpoption": "^1.9.2"
},
"require-dev": {
"phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2"
},
"time": "2023-11-12T22:16:48+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"GrahamCampbell\\ResultType\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Graham Campbell",
"email": "hello@gjcampbell.co.uk",
"homepage": "https://github.com/GrahamCampbell"
}
],
"description": "An Implementation Of The Result Type",
"keywords": [
"Graham Campbell",
"GrahamCampbell",
"Result Type",
"Result-Type",
"result"
],
"support": {
"issues": "https://github.com/GrahamCampbell/Result-Type/issues",
"source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.2"
},
"funding": [
{
"url": "https://github.com/GrahamCampbell",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type",
"type": "tidelift"
}
],
"install-path": "../graham-campbell/result-type"
},
{
"name": "graham-campbell/result-type",
"version": "v1.1.2",
"version_normalized": "1.1.2.0",
"source": {
"type": "git",
"url": "https://github.com/GrahamCampbell/Result-Type.git",
"reference": "fbd48bce38f73f8a4ec8583362e732e4095e5862"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/fbd48bce38f73f8a4ec8583362e732e4095e5862",
"reference": "fbd48bce38f73f8a4ec8583362e732e4095e5862",
"shasum": ""
},
"require": {
"php": "^7.2.5 || ^8.0",
"phpoption/phpoption": "^1.9.2"
},
"require-dev": {
"phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2"
},
"time": "2023-11-12T22:16:48+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"GrahamCampbell\\ResultType\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Graham Campbell",
"email": "hello@gjcampbell.co.uk",
"homepage": "https://github.com/GrahamCampbell"
}
],
"description": "An Implementation Of The Result Type",
"keywords": [
"Graham Campbell",
"GrahamCampbell",
"Result Type",
"Result-Type",
"result"
],
"support": {
"issues": "https://github.com/GrahamCampbell/Result-Type/issues",
"source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.2"
},
"funding": [
{
"url": "https://github.com/GrahamCampbell",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type",
"type": "tidelift"
}
],
"install-path": "../graham-campbell/result-type"
},
{ {
"name": "graham-campbell/result-type", "name": "graham-campbell/result-type",
"version": "v1.1.2", "version": "v1.1.2",
@ -399,35 +228,37 @@
}, },
{ {
"name": "nikic/php-parser", "name": "nikic/php-parser",
"version": "v4.17.1", "version": "v5.0.0",
"version_normalized": "4.17.1.0", "version_normalized": "5.0.0.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/nikic/PHP-Parser.git", "url": "https://github.com/nikic/PHP-Parser.git",
"reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d" "reference": "4a21235f7e56e713259a6f76bf4b5ea08502b9dc"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/4a21235f7e56e713259a6f76bf4b5ea08502b9dc",
"reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", "reference": "4a21235f7e56e713259a6f76bf4b5ea08502b9dc",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"ext-ctype": "*",
"ext-json": "*",
"ext-tokenizer": "*", "ext-tokenizer": "*",
"php": ">=7.0" "php": ">=7.4"
}, },
"require-dev": { "require-dev": {
"ircmaxell/php-yacc": "^0.0.7", "ircmaxell/php-yacc": "^0.0.7",
"phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0"
}, },
"time": "2023-08-13T19:53:39+00:00", "time": "2024-01-07T17:17:35+00:00",
"bin": [ "bin": [
"bin/php-parse" "bin/php-parse"
], ],
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "4.9-dev" "dev-master": "5.0-dev"
} }
}, },
"installation-source": "dist", "installation-source": "dist",
@ -452,7 +283,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/nikic/PHP-Parser/issues", "issues": "https://github.com/nikic/PHP-Parser/issues",
"source": "https://github.com/nikic/PHP-Parser/tree/v4.17.1" "source": "https://github.com/nikic/PHP-Parser/tree/v5.0.0"
}, },
"install-path": "../nikic/php-parser" "install-path": "../nikic/php-parser"
}, },
@ -653,102 +484,24 @@
}, },
{ {
"name": "phpunit/php-code-coverage", "name": "phpunit/php-code-coverage",
"version": "10.1.9", "version": "10.1.11",
"version_normalized": "10.1.9.0", "version_normalized": "10.1.11.0",
"source": {
"type": "git",
"url": "https://github.com/schmittjoh/php-option.git",
"reference": "80735db690fe4fc5c76dfa7f9b770634285fa820"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/schmittjoh/php-option/zipball/80735db690fe4fc5c76dfa7f9b770634285fa820",
"reference": "80735db690fe4fc5c76dfa7f9b770634285fa820",
"shasum": ""
},
"require": {
"php": "^7.2.5 || ^8.0"
},
"require-dev": {
"bamarni/composer-bin-plugin": "^1.8.2",
"phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2"
},
"time": "2023-11-12T21:59:55+00:00",
"type": "library",
"extra": {
"bamarni-bin": {
"bin-links": true,
"forward-command": true
},
"branch-alias": {
"dev-master": "1.9-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"PhpOption\\": "src/PhpOption/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache-2.0"
],
"authors": [
{
"name": "Johannes M. Schmitt",
"email": "schmittjoh@gmail.com",
"homepage": "https://github.com/schmittjoh"
},
{
"name": "Graham Campbell",
"email": "hello@gjcampbell.co.uk",
"homepage": "https://github.com/GrahamCampbell"
}
],
"description": "Option Type for PHP",
"keywords": [
"language",
"option",
"php",
"type"
],
"support": {
"issues": "https://github.com/schmittjoh/php-option/issues",
"source": "https://github.com/schmittjoh/php-option/tree/1.9.2"
},
"funding": [
{
"url": "https://github.com/GrahamCampbell",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption",
"type": "tidelift"
}
],
"install-path": "../phpoption/phpoption"
},
{
"name": "phpunit/php-code-coverage",
"version": "10.1.9",
"version_normalized": "10.1.9.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "a56a9ab2f680246adcf3db43f38ddf1765774735" "reference": "78c3b7625965c2513ee96569a4dbb62601784145"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/a56a9ab2f680246adcf3db43f38ddf1765774735", "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/78c3b7625965c2513ee96569a4dbb62601784145",
"reference": "a56a9ab2f680246adcf3db43f38ddf1765774735", "reference": "78c3b7625965c2513ee96569a4dbb62601784145",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"ext-dom": "*", "ext-dom": "*",
"ext-libxml": "*", "ext-libxml": "*",
"ext-xmlwriter": "*", "ext-xmlwriter": "*",
"nikic/php-parser": "^4.15", "nikic/php-parser": "^4.18 || ^5.0",
"php": ">=8.1", "php": ">=8.1",
"phpunit/php-file-iterator": "^4.0", "phpunit/php-file-iterator": "^4.0",
"phpunit/php-text-template": "^3.0", "phpunit/php-text-template": "^3.0",
@ -766,7 +519,7 @@
"ext-pcov": "PHP extension that provides line coverage", "ext-pcov": "PHP extension that provides line coverage",
"ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage"
}, },
"time": "2023-11-23T12:23:20+00:00", "time": "2023-12-21T15:38:30+00:00",
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
@ -800,7 +553,7 @@
"support": { "support": {
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
"security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.9" "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.11"
}, },
"funding": [ "funding": [
{ {
@ -1067,17 +820,17 @@
}, },
{ {
"name": "phpunit/phpunit", "name": "phpunit/phpunit",
"version": "10.4.2", "version": "10.5.5",
"version_normalized": "10.4.2.0", "version_normalized": "10.5.5.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git", "url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "cacd8b9dd224efa8eb28beb69004126c7ca1a1a1" "reference": "ed21115d505b4b4f7dc7b5651464e19a2c7f7856"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/cacd8b9dd224efa8eb28beb69004126c7ca1a1a1", "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/ed21115d505b4b4f7dc7b5651464e19a2c7f7856",
"reference": "cacd8b9dd224efa8eb28beb69004126c7ca1a1a1", "reference": "ed21115d505b4b4f7dc7b5651464e19a2c7f7856",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1111,14 +864,14 @@
"suggest": { "suggest": {
"ext-soap": "To be able to generate mocks based on WSDL files" "ext-soap": "To be able to generate mocks based on WSDL files"
}, },
"time": "2023-10-26T07:21:45+00:00", "time": "2023-12-27T15:13:52+00:00",
"bin": [ "bin": [
"phpunit" "phpunit"
], ],
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "10.4-dev" "dev-main": "10.5-dev"
} }
}, },
"installation-source": "dist", "installation-source": "dist",
@ -1151,7 +904,7 @@
"support": { "support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues", "issues": "https://github.com/sebastianbergmann/phpunit/issues",
"security": "https://github.com/sebastianbergmann/phpunit/security/policy", "security": "https://github.com/sebastianbergmann/phpunit/security/policy",
"source": "https://github.com/sebastianbergmann/phpunit/tree/10.4.2" "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.5"
}, },
"funding": [ "funding": [
{ {
@ -1225,62 +978,6 @@
}, },
"install-path": "../psr/container" "install-path": "../psr/container"
}, },
{
"name": "sebastian/cli-parser",
"version": "2.0.0",
"version_normalized": "2.0.0.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/container.git",
"reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963",
"reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963",
"shasum": ""
},
"require": {
"php": ">=7.4.0"
},
"time": "2021-11-05T16:47:00+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.0.x-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"Psr\\Container\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "https://www.php-fig.org/"
}
],
"description": "Common Container Interface (PHP FIG PSR-11)",
"homepage": "https://github.com/php-fig/container",
"keywords": [
"PSR-11",
"container",
"container-interface",
"container-interop",
"psr"
],
"support": {
"issues": "https://github.com/php-fig/container/issues",
"source": "https://github.com/php-fig/container/tree/2.0.2"
},
"install-path": "../psr/container"
},
{ {
"name": "sebastian/cli-parser", "name": "sebastian/cli-parser",
"version": "2.0.0", "version": "2.0.0",
@ -1539,31 +1236,31 @@
}, },
{ {
"name": "sebastian/complexity", "name": "sebastian/complexity",
"version": "3.1.0", "version": "3.2.0",
"version_normalized": "3.1.0.0", "version_normalized": "3.2.0.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/complexity.git", "url": "https://github.com/sebastianbergmann/complexity.git",
"reference": "68cfb347a44871f01e33ab0ef8215966432f6957" "reference": "68ff824baeae169ec9f2137158ee529584553799"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68cfb347a44871f01e33ab0ef8215966432f6957", "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68ff824baeae169ec9f2137158ee529584553799",
"reference": "68cfb347a44871f01e33ab0ef8215966432f6957", "reference": "68ff824baeae169ec9f2137158ee529584553799",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"nikic/php-parser": "^4.10", "nikic/php-parser": "^4.18 || ^5.0",
"php": ">=8.1" "php": ">=8.1"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "^10.0" "phpunit/phpunit": "^10.0"
}, },
"time": "2023-09-28T11:50:59+00:00", "time": "2023-12-21T08:37:17+00:00",
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "3.1-dev" "dev-main": "3.2-dev"
} }
}, },
"installation-source": "dist", "installation-source": "dist",
@ -1588,7 +1285,7 @@
"support": { "support": {
"issues": "https://github.com/sebastianbergmann/complexity/issues", "issues": "https://github.com/sebastianbergmann/complexity/issues",
"security": "https://github.com/sebastianbergmann/complexity/security/policy", "security": "https://github.com/sebastianbergmann/complexity/security/policy",
"source": "https://github.com/sebastianbergmann/complexity/tree/3.1.0" "source": "https://github.com/sebastianbergmann/complexity/tree/3.2.0"
}, },
"funding": [ "funding": [
{ {
@ -1600,17 +1297,17 @@
}, },
{ {
"name": "sebastian/diff", "name": "sebastian/diff",
"version": "5.0.3", "version": "5.1.0",
"version_normalized": "5.0.3.0", "version_normalized": "5.1.0.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/diff.git", "url": "https://github.com/sebastianbergmann/diff.git",
"reference": "912dc2fbe3e3c1e7873313cc801b100b6c68c87b" "reference": "fbf413a49e54f6b9b17e12d900ac7f6101591b7f"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/912dc2fbe3e3c1e7873313cc801b100b6c68c87b", "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/fbf413a49e54f6b9b17e12d900ac7f6101591b7f",
"reference": "912dc2fbe3e3c1e7873313cc801b100b6c68c87b", "reference": "fbf413a49e54f6b9b17e12d900ac7f6101591b7f",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1620,11 +1317,11 @@
"phpunit/phpunit": "^10.0", "phpunit/phpunit": "^10.0",
"symfony/process": "^4.2 || ^5" "symfony/process": "^4.2 || ^5"
}, },
"time": "2023-05-01T07:48:21+00:00", "time": "2023-12-22T10:55:06+00:00",
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "5.0-dev" "dev-main": "5.1-dev"
} }
}, },
"installation-source": "dist", "installation-source": "dist",
@ -1658,7 +1355,7 @@
"support": { "support": {
"issues": "https://github.com/sebastianbergmann/diff/issues", "issues": "https://github.com/sebastianbergmann/diff/issues",
"security": "https://github.com/sebastianbergmann/diff/security/policy", "security": "https://github.com/sebastianbergmann/diff/security/policy",
"source": "https://github.com/sebastianbergmann/diff/tree/5.0.3" "source": "https://github.com/sebastianbergmann/diff/tree/5.1.0"
}, },
"funding": [ "funding": [
{ {
@ -1883,27 +1580,27 @@
}, },
{ {
"name": "sebastian/lines-of-code", "name": "sebastian/lines-of-code",
"version": "2.0.1", "version": "2.0.2",
"version_normalized": "2.0.1.0", "version_normalized": "2.0.2.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/lines-of-code.git", "url": "https://github.com/sebastianbergmann/lines-of-code.git",
"reference": "649e40d279e243d985aa8fb6e74dd5bb28dc185d" "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/649e40d279e243d985aa8fb6e74dd5bb28dc185d", "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/856e7f6a75a84e339195d48c556f23be2ebf75d0",
"reference": "649e40d279e243d985aa8fb6e74dd5bb28dc185d", "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"nikic/php-parser": "^4.10", "nikic/php-parser": "^4.18 || ^5.0",
"php": ">=8.1" "php": ">=8.1"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "^10.0" "phpunit/phpunit": "^10.0"
}, },
"time": "2023-08-31T09:25:50+00:00", "time": "2023-12-21T08:38:20+00:00",
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
@ -1932,7 +1629,7 @@
"support": { "support": {
"issues": "https://github.com/sebastianbergmann/lines-of-code/issues", "issues": "https://github.com/sebastianbergmann/lines-of-code/issues",
"security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy",
"source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.1" "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.2"
}, },
"funding": [ "funding": [
{ {

@ -3,7 +3,7 @@
'name' => 'hearttrack/package', 'name' => 'hearttrack/package',
'pretty_version' => 'dev-master', 'pretty_version' => 'dev-master',
'version' => 'dev-master', 'version' => 'dev-master',
'reference' => 'eb625309ce4a814abafb1d0ded3d0d5937ddc905', 'reference' => 'e86b3f7b002e2852b5084d5448b98251eec6e846',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../../', 'install_path' => __DIR__ . '/../../',
'aliases' => array(), 'aliases' => array(),
@ -28,33 +28,6 @@
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'doctrine/instantiator' => array(
'pretty_version' => '1.5.0',
'version' => '1.5.0.0',
'reference' => '0a0fa9780f5d4e507415a065172d26a98d02047b',
'type' => 'library',
'install_path' => __DIR__ . '/../adriangibbons/php-fit-file-analysis',
'aliases' => array(),
'dev_requirement' => false,
),
'graham-campbell/result-type' => array(
'pretty_version' => 'v1.1.2',
'version' => '1.1.2.0',
'reference' => 'fbd48bce38f73f8a4ec8583362e732e4095e5862',
'type' => 'library',
'install_path' => __DIR__ . '/../graham-campbell/result-type',
'aliases' => array(),
'dev_requirement' => false,
),
'graham-campbell/result-type' => array(
'pretty_version' => 'v1.1.2',
'version' => '1.1.2.0',
'reference' => 'fbd48bce38f73f8a4ec8583362e732e4095e5862',
'type' => 'library',
'install_path' => __DIR__ . '/../graham-campbell/result-type',
'aliases' => array(),
'dev_requirement' => false,
),
'graham-campbell/result-type' => array( 'graham-campbell/result-type' => array(
'pretty_version' => 'v1.1.2', 'pretty_version' => 'v1.1.2',
'version' => '1.1.2.0', 'version' => '1.1.2.0',
@ -67,7 +40,7 @@
'hearttrack/package' => array( 'hearttrack/package' => array(
'pretty_version' => 'dev-master', 'pretty_version' => 'dev-master',
'version' => 'dev-master', 'version' => 'dev-master',
'reference' => 'eb625309ce4a814abafb1d0ded3d0d5937ddc905', 'reference' => 'e86b3f7b002e2852b5084d5448b98251eec6e846',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../../', 'install_path' => __DIR__ . '/../../',
'aliases' => array(), 'aliases' => array(),
@ -83,9 +56,9 @@
'dev_requirement' => true, 'dev_requirement' => true,
), ),
'nikic/php-parser' => array( 'nikic/php-parser' => array(
'pretty_version' => 'v4.17.1', 'pretty_version' => 'v5.0.0',
'version' => '4.17.1.0', 'version' => '5.0.0.0',
'reference' => 'a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d', 'reference' => '4a21235f7e56e713259a6f76bf4b5ea08502b9dc',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../nikic/php-parser', 'install_path' => __DIR__ . '/../nikic/php-parser',
'aliases' => array(), 'aliases' => array(),
@ -119,9 +92,9 @@
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'phpunit/php-code-coverage' => array( 'phpunit/php-code-coverage' => array(
'pretty_version' => '10.1.9', 'pretty_version' => '10.1.11',
'version' => '10.1.9.0', 'version' => '10.1.11.0',
'reference' => 'a56a9ab2f680246adcf3db43f38ddf1765774735', 'reference' => '78c3b7625965c2513ee96569a4dbb62601784145',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../phpunit/php-code-coverage', 'install_path' => __DIR__ . '/../phpunit/php-code-coverage',
'aliases' => array(), 'aliases' => array(),
@ -164,9 +137,9 @@
'dev_requirement' => true, 'dev_requirement' => true,
), ),
'phpunit/phpunit' => array( 'phpunit/phpunit' => array(
'pretty_version' => '10.4.2', 'pretty_version' => '10.5.5',
'version' => '10.4.2.0', 'version' => '10.5.5.0',
'reference' => 'cacd8b9dd224efa8eb28beb69004126c7ca1a1a1', 'reference' => 'ed21115d505b4b4f7dc7b5651464e19a2c7f7856',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../phpunit/phpunit', 'install_path' => __DIR__ . '/../phpunit/phpunit',
'aliases' => array(), 'aliases' => array(),
@ -218,18 +191,18 @@
'dev_requirement' => true, 'dev_requirement' => true,
), ),
'sebastian/complexity' => array( 'sebastian/complexity' => array(
'pretty_version' => '3.1.0', 'pretty_version' => '3.2.0',
'version' => '3.1.0.0', 'version' => '3.2.0.0',
'reference' => '68cfb347a44871f01e33ab0ef8215966432f6957', 'reference' => '68ff824baeae169ec9f2137158ee529584553799',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../sebastian/complexity', 'install_path' => __DIR__ . '/../sebastian/complexity',
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => true, 'dev_requirement' => true,
), ),
'sebastian/diff' => array( 'sebastian/diff' => array(
'pretty_version' => '5.0.3', 'pretty_version' => '5.1.0',
'version' => '5.0.3.0', 'version' => '5.1.0.0',
'reference' => '912dc2fbe3e3c1e7873313cc801b100b6c68c87b', 'reference' => 'fbf413a49e54f6b9b17e12d900ac7f6101591b7f',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../sebastian/diff', 'install_path' => __DIR__ . '/../sebastian/diff',
'aliases' => array(), 'aliases' => array(),
@ -263,9 +236,9 @@
'dev_requirement' => true, 'dev_requirement' => true,
), ),
'sebastian/lines-of-code' => array( 'sebastian/lines-of-code' => array(
'pretty_version' => '2.0.1', 'pretty_version' => '2.0.2',
'version' => '2.0.1.0', 'version' => '2.0.2.0',
'reference' => '649e40d279e243d985aa8fb6e74dd5bb28dc185d', 'reference' => '856e7f6a75a84e339195d48c556f23be2ebf75d0',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../sebastian/lines-of-code', 'install_path' => __DIR__ . '/../sebastian/lines-of-code',
'aliases' => array(), 'aliases' => array(),

@ -0,0 +1,31 @@
<?php
$finder = PhpCsFixer\Finder::create()
->exclude('PhpParser/Parser')
->in(__DIR__ . '/lib')
->in(__DIR__ . '/test')
->in(__DIR__ . '/grammar')
;
$config = new PhpCsFixer\Config();
return $config->setRiskyAllowed(true)
->setRules([
'@PSR12' => true,
// We use PSR12 with consistent brace placement.
'curly_braces_position' => [
'functions_opening_brace' => 'same_line',
'classes_opening_brace' => 'same_line',
],
// declare(strict_types=1) on the same line as <?php.
'blank_line_after_opening_tag' => false,
'declare_strict_types' => true,
// Keep argument formatting for now.
'method_argument_space' => ['on_multiline' => 'ignore'],
'phpdoc_align' => ['align' => 'left'],
'phpdoc_trim' => true,
'no_empty_phpdoc' => true,
'no_superfluous_phpdoc_tags' => ['allow_mixed' => true],
'no_extra_blank_lines' => true,
])
->setFinder($finder)
;

@ -0,0 +1,10 @@
.PHONY: phpstan php-cs-fixer
tools/vendor:
composer install -d tools
phpstan: tools/vendor
tools/vendor/bin/phpstan
php-cs-fixer: tools/vendor
tools/vendor/bin/php-cs-fixer fix

@ -3,24 +3,24 @@ PHP Parser
[![Coverage Status](https://coveralls.io/repos/github/nikic/PHP-Parser/badge.svg?branch=master)](https://coveralls.io/github/nikic/PHP-Parser?branch=master) [![Coverage Status](https://coveralls.io/repos/github/nikic/PHP-Parser/badge.svg?branch=master)](https://coveralls.io/github/nikic/PHP-Parser?branch=master)
This is a PHP 5.2 to PHP 8.2 parser written in PHP. Its purpose is to simplify static code analysis and This is a PHP parser written in PHP. Its purpose is to simplify static code analysis and
manipulation. manipulation.
[**Documentation for version 4.x**][doc_4_x] (stable; for running on PHP >= 7.0; for parsing PHP 5.2 to PHP 8.2). [**Documentation for version 5.x**][doc_master] (current; for running on PHP >= 7.4; for parsing PHP 7.0 to PHP 8.3, with limited support for parsing PHP 5.x).
[Documentation for version 3.x][doc_3_x] (unsupported; for running on PHP >= 5.5; for parsing PHP 5.2 to PHP 7.2). [Documentation for version 4.x][doc_4_x] (supported; for running on PHP >= 7.0; for parsing PHP 5.2 to PHP 8.3).
Features Features
-------- --------
The main features provided by this library are: The main features provided by this library are:
* Parsing PHP 5, PHP 7, and PHP 8 code into an abstract syntax tree (AST). * Parsing PHP 7, and PHP 8 code into an abstract syntax tree (AST).
* Invalid code can be parsed into a partial AST. * Invalid code can be parsed into a partial AST.
* The AST contains accurate location information. * The AST contains accurate location information.
* Dumping the AST in human-readable form. * Dumping the AST in human-readable form.
* Converting an AST back to PHP code. * Converting an AST back to PHP code.
* Experimental: Formatting can be preserved for partially changed ASTs. * Formatting can be preserved for partially changed ASTs.
* Infrastructure to traverse and modify ASTs. * Infrastructure to traverse and modify ASTs.
* Resolution of namespaced names. * Resolution of namespaced names.
* Evaluation of constant expressions. * Evaluation of constant expressions.
@ -51,7 +51,7 @@ function test($foo)
} }
CODE; CODE;
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7); $parser = (new ParserFactory())->createForNewestSupportedVersion();
try { try {
$ast = $parser->parse($code); $ast = $parser->parse($code);
} catch (Error $error) { } catch (Error $error) {
@ -68,12 +68,17 @@ This dumps an AST looking something like this:
``` ```
array( array(
0: Stmt_Function( 0: Stmt_Function(
attrGroups: array(
)
byRef: false byRef: false
name: Identifier( name: Identifier(
name: test name: test
) )
params: array( params: array(
0: Param( 0: Param(
attrGroups: array(
)
flags: 0
type: null type: null
byRef: false byRef: false
variadic: false variadic: false
@ -88,12 +93,11 @@ array(
0: Stmt_Expression( 0: Stmt_Expression(
expr: Expr_FuncCall( expr: Expr_FuncCall(
name: Name( name: Name(
parts: array( name: var_dump
0: var_dump
)
) )
args: array( args: array(
0: Arg( 0: Arg(
name: null
value: Expr_Variable( value: Expr_Variable(
name: foo name: foo
) )
@ -135,12 +139,16 @@ This gives us an AST where the `Function_::$stmts` are empty:
``` ```
array( array(
0: Stmt_Function( 0: Stmt_Function(
attrGroups: array(
)
byRef: false byRef: false
name: Identifier( name: Identifier(
name: test name: test
) )
params: array( params: array(
0: Param( 0: Param(
attrGroups: array(
)
type: null type: null
byRef: false byRef: false
variadic: false variadic: false
@ -203,9 +211,8 @@ Component documentation:
* [AST builders](doc/component/AST_builders.markdown) * [AST builders](doc/component/AST_builders.markdown)
* Fluent builders for AST nodes * Fluent builders for AST nodes
* [Lexer](doc/component/Lexer.markdown) * [Lexer](doc/component/Lexer.markdown)
* Lexer options * Emulation
* Token and file positions for nodes * Tokens, positions and attributes
* Custom attributes
* [Error handling](doc/component/Error_handling.markdown) * [Error handling](doc/component/Error_handling.markdown)
* Column information for errors * Column information for errors
* Error recovery (parsing of syntactically incorrect code) * Error recovery (parsing of syntactically incorrect code)
@ -223,3 +230,4 @@ Component documentation:
[doc_3_x]: https://github.com/nikic/PHP-Parser/tree/3.x/doc [doc_3_x]: https://github.com/nikic/PHP-Parser/tree/3.x/doc
[doc_4_x]: https://github.com/nikic/PHP-Parser/tree/4.x/doc [doc_4_x]: https://github.com/nikic/PHP-Parser/tree/4.x/doc
[doc_master]: https://github.com/nikic/PHP-Parser/tree/master/doc

@ -26,13 +26,7 @@ if (empty($files)) {
showHelp("Must specify at least one file."); showHelp("Must specify at least one file.");
} }
$lexer = new PhpParser\Lexer\Emulative(['usedAttributes' => [ $parser = (new PhpParser\ParserFactory())->createForVersion($attributes['version']);
'startLine', 'endLine', 'startFilePos', 'endFilePos', 'comments'
]]);
$parser = (new PhpParser\ParserFactory)->create(
PhpParser\ParserFactory::PREFER_PHP7,
$lexer
);
$dumper = new PhpParser\NodeDumper([ $dumper = new PhpParser\NodeDumper([
'dumpComments' => true, 'dumpComments' => true,
'dumpPositions' => $attributes['with-positions'], 'dumpPositions' => $attributes['with-positions'],
@ -43,7 +37,10 @@ $traverser = new PhpParser\NodeTraverser();
$traverser->addVisitor(new PhpParser\NodeVisitor\NameResolver); $traverser->addVisitor(new PhpParser\NodeVisitor\NameResolver);
foreach ($files as $file) { foreach ($files as $file) {
if (strpos($file, '<?php') === 0) { if ($file === '-') {
$code = file_get_contents('php://stdin');
fwrite(STDERR, "====> Stdin:\n");
} else if (strpos($file, '<?php') === 0) {
$code = $file; $code = $file;
fwrite(STDERR, "====> Code $code\n"); fwrite(STDERR, "====> Code $code\n");
} else { } else {
@ -108,7 +105,7 @@ function showHelp($error = '') {
if ($error) { if ($error) {
fwrite(STDERR, $error . "\n\n"); fwrite(STDERR, $error . "\n\n");
} }
fwrite($error ? STDERR : STDOUT, <<<OUTPUT fwrite($error ? STDERR : STDOUT, <<<'OUTPUT'
Usage: php-parse [operations] file1.php [file2.php ...] Usage: php-parse [operations] file1.php [file2.php ...]
or: php-parse [operations] "<?php code" or: php-parse [operations] "<?php code"
Turn PHP source code into an abstract syntax tree. Turn PHP source code into an abstract syntax tree.
@ -123,6 +120,7 @@ Operations is a list of the following options (--dump by default):
-c, --with-column-info Show column-numbers for errors (if available) -c, --with-column-info Show column-numbers for errors (if available)
-P, --with-positions Show positions in node dumps -P, --with-positions Show positions in node dumps
-r, --with-recovery Use parsing with error recovery -r, --with-recovery Use parsing with error recovery
--version=VERSION Target specific PHP version (default: newest)
-h, --help Display this page -h, --help Display this page
Example: Example:
@ -143,6 +141,7 @@ function parseArgs($args) {
'with-column-info' => false, 'with-column-info' => false,
'with-positions' => false, 'with-positions' => false,
'with-recovery' => false, 'with-recovery' => false,
'version' => PhpParser\PhpVersion::getNewestSupported(),
]; ];
array_shift($args); array_shift($args);
@ -193,7 +192,9 @@ function parseArgs($args) {
$parseOptions = false; $parseOptions = false;
break; break;
default: default:
if ($arg[0] === '-') { if (preg_match('/^--version=(.*)$/', $arg, $matches)) {
$attributes['version'] = PhpParser\PhpVersion::fromString($matches[1]);
} elseif ($arg[0] === '-' && \strlen($arg[0]) > 1) {
showHelp("Invalid operation $arg."); showHelp("Invalid operation $arg.");
} else { } else {
$files[] = $arg; $files[] = $arg;

@ -13,16 +13,18 @@
} }
], ],
"require": { "require": {
"php": ">=7.0", "php": ">=7.4",
"ext-tokenizer": "*" "ext-tokenizer": "*",
"ext-json": "*",
"ext-ctype": "*"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0", "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0",
"ircmaxell/php-yacc": "^0.0.7" "ircmaxell/php-yacc": "^0.0.7"
}, },
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "4.9-dev" "dev-master": "5.0-dev"
} }
}, },
"autoload": { "autoload": {

@ -1,30 +0,0 @@
What do all those files mean?
=============================
* `php5.y`: PHP 5 grammar written in a pseudo language
* `php7.y`: PHP 7 grammar written in a pseudo language
* `tokens.y`: Tokens definition shared between PHP 5 and PHP 7 grammars
* `parser.template`: A `kmyacc` parser prototype file for PHP
* `tokens.template`: A `kmyacc` prototype file for the `Tokens` class
* `rebuildParsers.php`: Preprocesses the grammar and builds the parser using `kmyacc`
.phpy pseudo language
=====================
The `.y` file is a normal grammar in `kmyacc` (`yacc`) style, with some transformations
applied to it:
* Nodes are created using the syntax `Name[..., ...]`. This is transformed into
`new Name(..., ..., attributes())`
* Some function-like constructs are resolved (see `rebuildParsers.php` for a list)
Building the parser
===================
Run `php grammar/rebuildParsers.php` to rebuild the parsers. Additional options:
* The `KMYACC` environment variable can be used to specify an alternative `kmyacc` binary.
By default the `phpyacc` dev dependency will be used. To use the original `kmyacc`, you
need to compile [moriyoshi's fork](https://github.com/moriyoshi/kmyacc-forked).
* The `--debug` option enables emission of debug symbols and creates the `y.output` file.
* The `--keep-tmp-grammar` option preserves the preprocessed grammar file.

@ -1,106 +0,0 @@
<?php
$meta #
#semval($) $this->semValue
#semval($,%t) $this->semValue
#semval(%n) $stackPos-(%l-%n)
#semval(%n,%t) $stackPos-(%l-%n)
namespace PhpParser\Parser;
use PhpParser\Error;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Name;
use PhpParser\Node\Scalar;
use PhpParser\Node\Stmt;
#include;
/* This is an automatically GENERATED file, which should not be manually edited.
* Instead edit one of the following:
* * the grammar files grammar/php5.y or grammar/php7.y
* * the skeleton file grammar/parser.template
* * the preprocessing script grammar/rebuildParsers.php
*/
class #(-p) extends \PhpParser\ParserAbstract
{
protected $tokenToSymbolMapSize = #(YYMAXLEX);
protected $actionTableSize = #(YYLAST);
protected $gotoTableSize = #(YYGLAST);
protected $invalidSymbol = #(YYBADCH);
protected $errorSymbol = #(YYINTERRTOK);
protected $defaultAction = #(YYDEFAULT);
protected $unexpectedTokenRule = #(YYUNEXPECTED);
protected $YY2TBLSTATE = #(YY2TBLSTATE);
protected $numNonLeafStates = #(YYNLSTATES);
protected $symbolToName = array(
#listvar terminals
);
protected $tokenToSymbol = array(
#listvar yytranslate
);
protected $action = array(
#listvar yyaction
);
protected $actionCheck = array(
#listvar yycheck
);
protected $actionBase = array(
#listvar yybase
);
protected $actionDefault = array(
#listvar yydefault
);
protected $goto = array(
#listvar yygoto
);
protected $gotoCheck = array(
#listvar yygcheck
);
protected $gotoBase = array(
#listvar yygbase
);
protected $gotoDefault = array(
#listvar yygdefault
);
protected $ruleToNonTerminal = array(
#listvar yylhs
);
protected $ruleToLength = array(
#listvar yylen
);
#if -t
protected $productions = array(
#production-strings;
);
#endif
protected function initReduceCallbacks() {
$this->reduceCallbacks = [
#reduce
%n => function ($stackPos) {
%b
},
#noact
%n => function ($stackPos) {
$this->semValue = $this->semStack[$stackPos];
},
#endreduce
];
}
}
#tailcode;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -1,184 +0,0 @@
<?php
///////////////////////////////
/// Utility regex constants ///
///////////////////////////////
const LIB = '(?(DEFINE)
(?<singleQuotedString>\'[^\\\\\']*+(?:\\\\.[^\\\\\']*+)*+\')
(?<doubleQuotedString>"[^\\\\"]*+(?:\\\\.[^\\\\"]*+)*+")
(?<string>(?&singleQuotedString)|(?&doubleQuotedString))
(?<comment>/\*[^*]*+(?:\*(?!/)[^*]*+)*+\*/)
(?<code>\{[^\'"/{}]*+(?:(?:(?&string)|(?&comment)|(?&code)|/)[^\'"/{}]*+)*+})
)';
const PARAMS = '\[(?<params>[^[\]]*+(?:\[(?&params)\][^[\]]*+)*+)\]';
const ARGS = '\((?<args>[^()]*+(?:\((?&args)\)[^()]*+)*+)\)';
///////////////////////////////
/// Preprocessing functions ///
///////////////////////////////
function preprocessGrammar($code) {
$code = resolveNodes($code);
$code = resolveMacros($code);
$code = resolveStackAccess($code);
return $code;
}
function resolveNodes($code) {
return preg_replace_callback(
'~\b(?<name>[A-Z][a-zA-Z_\\\\]++)\s*' . PARAMS . '~',
function($matches) {
// recurse
$matches['params'] = resolveNodes($matches['params']);
$params = magicSplit(
'(?:' . PARAMS . '|' . ARGS . ')(*SKIP)(*FAIL)|,',
$matches['params']
);
$paramCode = '';
foreach ($params as $param) {
$paramCode .= $param . ', ';
}
return 'new ' . $matches['name'] . '(' . $paramCode . 'attributes())';
},
$code
);
}
function resolveMacros($code) {
return preg_replace_callback(
'~\b(?<!::|->)(?!array\()(?<name>[a-z][A-Za-z]++)' . ARGS . '~',
function($matches) {
// recurse
$matches['args'] = resolveMacros($matches['args']);
$name = $matches['name'];
$args = magicSplit(
'(?:' . PARAMS . '|' . ARGS . ')(*SKIP)(*FAIL)|,',
$matches['args']
);
if ('attributes' === $name) {
assertArgs(0, $args, $name);
return '$this->startAttributeStack[#1] + $this->endAttributes';
}
if ('stackAttributes' === $name) {
assertArgs(1, $args, $name);
return '$this->startAttributeStack[' . $args[0] . ']'
. ' + $this->endAttributeStack[' . $args[0] . ']';
}
if ('init' === $name) {
return '$$ = array(' . implode(', ', $args) . ')';
}
if ('push' === $name) {
assertArgs(2, $args, $name);
return $args[0] . '[] = ' . $args[1] . '; $$ = ' . $args[0];
}
if ('pushNormalizing' === $name) {
assertArgs(2, $args, $name);
return 'if (is_array(' . $args[1] . ')) { $$ = array_merge(' . $args[0] . ', ' . $args[1] . '); }'
. ' else { ' . $args[0] . '[] = ' . $args[1] . '; $$ = ' . $args[0] . '; }';
}
if ('toArray' == $name) {
assertArgs(1, $args, $name);
return 'is_array(' . $args[0] . ') ? ' . $args[0] . ' : array(' . $args[0] . ')';
}
if ('parseVar' === $name) {
assertArgs(1, $args, $name);
return 'substr(' . $args[0] . ', 1)';
}
if ('parseEncapsed' === $name) {
assertArgs(3, $args, $name);
return 'foreach (' . $args[0] . ' as $s) { if ($s instanceof Node\Scalar\EncapsedStringPart) {'
. ' $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, ' . $args[1] . ', ' . $args[2] . '); } }';
}
if ('makeNop' === $name) {
assertArgs(3, $args, $name);
return '$startAttributes = ' . $args[1] . ';'
. ' if (isset($startAttributes[\'comments\']))'
. ' { ' . $args[0] . ' = new Stmt\Nop($startAttributes + ' . $args[2] . '); }'
. ' else { ' . $args[0] . ' = null; }';
}
if ('makeZeroLengthNop' == $name) {
assertArgs(2, $args, $name);
return '$startAttributes = ' . $args[1] . ';'
. ' if (isset($startAttributes[\'comments\']))'
. ' { ' . $args[0] . ' = new Stmt\Nop($this->createCommentNopAttributes($startAttributes[\'comments\'])); }'
. ' else { ' . $args[0] . ' = null; }';
}
if ('prependLeadingComments' === $name) {
assertArgs(1, $args, $name);
return '$attrs = $this->startAttributeStack[#1]; $stmts = ' . $args[0] . '; '
. 'if (!empty($attrs[\'comments\'])) {'
. '$stmts[0]->setAttribute(\'comments\', '
. 'array_merge($attrs[\'comments\'], $stmts[0]->getAttribute(\'comments\', []))); }';
}
return $matches[0];
},
$code
);
}
function assertArgs($num, $args, $name) {
if ($num != count($args)) {
die('Wrong argument count for ' . $name . '().');
}
}
function resolveStackAccess($code) {
$code = preg_replace('/\$\d+/', '$this->semStack[$0]', $code);
$code = preg_replace('/#(\d+)/', '$$1', $code);
return $code;
}
function removeTrailingWhitespace($code) {
$lines = explode("\n", $code);
$lines = array_map('rtrim', $lines);
return implode("\n", $lines);
}
//////////////////////////////
/// Regex helper functions ///
//////////////////////////////
function regex($regex) {
return '~' . LIB . '(?:' . str_replace('~', '\~', $regex) . ')~';
}
function magicSplit($regex, $string) {
$pieces = preg_split(regex('(?:(?&string)|(?&comment)|(?&code))(*SKIP)(*FAIL)|' . $regex), $string);
foreach ($pieces as &$piece) {
$piece = trim($piece);
}
if ($pieces === ['']) {
return [];
}
return $pieces;
}

@ -1,81 +0,0 @@
<?php
require __DIR__ . '/phpyLang.php';
$grammarFileToName = [
__DIR__ . '/php5.y' => 'Php5',
__DIR__ . '/php7.y' => 'Php7',
];
$tokensFile = __DIR__ . '/tokens.y';
$tokensTemplate = __DIR__ . '/tokens.template';
$skeletonFile = __DIR__ . '/parser.template';
$tmpGrammarFile = __DIR__ . '/tmp_parser.phpy';
$tmpResultFile = __DIR__ . '/tmp_parser.php';
$resultDir = __DIR__ . '/../lib/PhpParser/Parser';
$tokensResultsFile = $resultDir . '/Tokens.php';
$kmyacc = getenv('KMYACC');
if (!$kmyacc) {
// Use phpyacc from dev dependencies by default.
$kmyacc = __DIR__ . '/../vendor/bin/phpyacc';
}
$options = array_flip($argv);
$optionDebug = isset($options['--debug']);
$optionKeepTmpGrammar = isset($options['--keep-tmp-grammar']);
///////////////////
/// Main script ///
///////////////////
$tokens = file_get_contents($tokensFile);
foreach ($grammarFileToName as $grammarFile => $name) {
echo "Building temporary $name grammar file.\n";
$grammarCode = file_get_contents($grammarFile);
$grammarCode = str_replace('%tokens', $tokens, $grammarCode);
$grammarCode = preprocessGrammar($grammarCode);
file_put_contents($tmpGrammarFile, $grammarCode);
$additionalArgs = $optionDebug ? '-t -v' : '';
echo "Building $name parser.\n";
$output = execCmd("$kmyacc $additionalArgs -m $skeletonFile -p $name $tmpGrammarFile");
$resultCode = file_get_contents($tmpResultFile);
$resultCode = removeTrailingWhitespace($resultCode);
ensureDirExists($resultDir);
file_put_contents("$resultDir/$name.php", $resultCode);
unlink($tmpResultFile);
echo "Building token definition.\n";
$output = execCmd("$kmyacc -m $tokensTemplate $tmpGrammarFile");
rename($tmpResultFile, $tokensResultsFile);
if (!$optionKeepTmpGrammar) {
unlink($tmpGrammarFile);
}
}
////////////////////////////////
/// Utility helper functions ///
////////////////////////////////
function ensureDirExists($dir) {
if (!is_dir($dir)) {
mkdir($dir, 0777, true);
}
}
function execCmd($cmd) {
$output = trim(shell_exec("$cmd 2>&1"));
if ($output !== "") {
echo "> " . $cmd . "\n";
echo $output;
}
return $output;
}

@ -1,17 +0,0 @@
<?php
$meta #
#semval($) $this->semValue
#semval($,%t) $this->semValue
#semval(%n) $this->stackPos-(%l-%n)
#semval(%n,%t) $this->stackPos-(%l-%n)
namespace PhpParser\Parser;
#include;
/* GENERATED file based on grammar/tokens.y */
final class Tokens
{
#tokenval
const %s = %n;
#endtokenval
}

@ -1,115 +0,0 @@
/* We currently rely on the token ID mapping to be the same between PHP 5 and PHP 7 - so the same lexer can be used for
* both. This is enforced by sharing this token file. */
%right T_THROW
%left T_INCLUDE T_INCLUDE_ONCE T_EVAL T_REQUIRE T_REQUIRE_ONCE
%left ','
%left T_LOGICAL_OR
%left T_LOGICAL_XOR
%left T_LOGICAL_AND
%right T_PRINT
%right T_YIELD
%right T_DOUBLE_ARROW
%right T_YIELD_FROM
%left '=' T_PLUS_EQUAL T_MINUS_EQUAL T_MUL_EQUAL T_DIV_EQUAL T_CONCAT_EQUAL T_MOD_EQUAL T_AND_EQUAL T_OR_EQUAL T_XOR_EQUAL T_SL_EQUAL T_SR_EQUAL T_POW_EQUAL T_COALESCE_EQUAL
%left '?' ':'
%right T_COALESCE
%left T_BOOLEAN_OR
%left T_BOOLEAN_AND
%left '|'
%left '^'
%left T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG
%nonassoc T_IS_EQUAL T_IS_NOT_EQUAL T_IS_IDENTICAL T_IS_NOT_IDENTICAL T_SPACESHIP
%nonassoc '<' T_IS_SMALLER_OR_EQUAL '>' T_IS_GREATER_OR_EQUAL
%left T_SL T_SR
%left '+' '-' '.'
%left '*' '/' '%'
%right '!'
%nonassoc T_INSTANCEOF
%right '~' T_INC T_DEC T_INT_CAST T_DOUBLE_CAST T_STRING_CAST T_ARRAY_CAST T_OBJECT_CAST T_BOOL_CAST T_UNSET_CAST '@'
%right T_POW
%right '['
%nonassoc T_NEW T_CLONE
%token T_EXIT
%token T_IF
%left T_ELSEIF
%left T_ELSE
%left T_ENDIF
%token T_LNUMBER
%token T_DNUMBER
%token T_STRING
%token T_STRING_VARNAME
%token T_VARIABLE
%token T_NUM_STRING
%token T_INLINE_HTML
%token T_ENCAPSED_AND_WHITESPACE
%token T_CONSTANT_ENCAPSED_STRING
%token T_ECHO
%token T_DO
%token T_WHILE
%token T_ENDWHILE
%token T_FOR
%token T_ENDFOR
%token T_FOREACH
%token T_ENDFOREACH
%token T_DECLARE
%token T_ENDDECLARE
%token T_AS
%token T_SWITCH
%token T_MATCH
%token T_ENDSWITCH
%token T_CASE
%token T_DEFAULT
%token T_BREAK
%token T_CONTINUE
%token T_GOTO
%token T_FUNCTION
%token T_FN
%token T_CONST
%token T_RETURN
%token T_TRY
%token T_CATCH
%token T_FINALLY
%token T_THROW
%token T_USE
%token T_INSTEADOF
%token T_GLOBAL
%right T_STATIC T_ABSTRACT T_FINAL T_PRIVATE T_PROTECTED T_PUBLIC T_READONLY
%token T_VAR
%token T_UNSET
%token T_ISSET
%token T_EMPTY
%token T_HALT_COMPILER
%token T_CLASS
%token T_TRAIT
%token T_INTERFACE
%token T_ENUM
%token T_EXTENDS
%token T_IMPLEMENTS
%token T_OBJECT_OPERATOR
%token T_NULLSAFE_OBJECT_OPERATOR
%token T_DOUBLE_ARROW
%token T_LIST
%token T_ARRAY
%token T_CALLABLE
%token T_CLASS_C
%token T_TRAIT_C
%token T_METHOD_C
%token T_FUNC_C
%token T_LINE
%token T_FILE
%token T_START_HEREDOC
%token T_END_HEREDOC
%token T_DOLLAR_OPEN_CURLY_BRACES
%token T_CURLY_OPEN
%token T_PAAMAYIM_NEKUDOTAYIM
%token T_NAMESPACE
%token T_NS_C
%token T_DIR
%token T_NS_SEPARATOR
%token T_ELLIPSIS
%token T_NAME_FULLY_QUALIFIED
%token T_NAME_QUALIFIED
%token T_NAME_RELATIVE
%token T_ATTRIBUTE
%token T_ENUM

@ -2,8 +2,7 @@
namespace PhpParser; namespace PhpParser;
interface Builder interface Builder {
{
/** /**
* Returns the built node. * Returns the built node.
* *

@ -6,21 +6,23 @@ namespace PhpParser\Builder;
use PhpParser; use PhpParser;
use PhpParser\BuilderHelpers; use PhpParser\BuilderHelpers;
use PhpParser\Modifiers;
use PhpParser\Node; use PhpParser\Node;
use PhpParser\Node\Const_; use PhpParser\Node\Const_;
use PhpParser\Node\Identifier; use PhpParser\Node\Identifier;
use PhpParser\Node\Stmt; use PhpParser\Node\Stmt;
class ClassConst implements PhpParser\Builder class ClassConst implements PhpParser\Builder {
{ protected int $flags = 0;
protected $flags = 0; /** @var array<string, mixed> */
protected $attributes = []; protected array $attributes = [];
protected $constants = []; /** @var list<Const_> */
protected array $constants = [];
/** @var Node\AttributeGroup[] */ /** @var list<Node\AttributeGroup> */
protected $attributeGroups = []; protected array $attributeGroups = [];
/** @var Identifier|Node\Name|Node\ComplexType */ /** @var Identifier|Node\Name|Node\ComplexType|null */
protected $type; protected ?Node $type = null;
/** /**
* Creates a class constant builder * Creates a class constant builder
@ -52,7 +54,7 @@ class ClassConst implements PhpParser\Builder
* @return $this The builder instance (for fluid interface) * @return $this The builder instance (for fluid interface)
*/ */
public function makePublic() { public function makePublic() {
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PUBLIC); $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PUBLIC);
return $this; return $this;
} }
@ -63,7 +65,7 @@ class ClassConst implements PhpParser\Builder
* @return $this The builder instance (for fluid interface) * @return $this The builder instance (for fluid interface)
*/ */
public function makeProtected() { public function makeProtected() {
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PROTECTED); $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PROTECTED);
return $this; return $this;
} }
@ -74,7 +76,7 @@ class ClassConst implements PhpParser\Builder
* @return $this The builder instance (for fluid interface) * @return $this The builder instance (for fluid interface)
*/ */
public function makePrivate() { public function makePrivate() {
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PRIVATE); $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PRIVATE);
return $this; return $this;
} }
@ -85,7 +87,7 @@ class ClassConst implements PhpParser\Builder
* @return $this The builder instance (for fluid interface) * @return $this The builder instance (for fluid interface)
*/ */
public function makeFinal() { public function makeFinal() {
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_FINAL); $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::FINAL);
return $this; return $this;
} }

@ -4,25 +4,27 @@ namespace PhpParser\Builder;
use PhpParser; use PhpParser;
use PhpParser\BuilderHelpers; use PhpParser\BuilderHelpers;
use PhpParser\Modifiers;
use PhpParser\Node; use PhpParser\Node;
use PhpParser\Node\Name; use PhpParser\Node\Name;
use PhpParser\Node\Stmt; use PhpParser\Node\Stmt;
class Class_ extends Declaration class Class_ extends Declaration {
{ protected string $name;
protected $name; protected ?Name $extends = null;
/** @var list<Name> */
protected $extends = null; protected array $implements = [];
protected $implements = []; protected int $flags = 0;
protected $flags = 0; /** @var list<Stmt\TraitUse> */
protected array $uses = [];
protected $uses = []; /** @var list<Stmt\ClassConst> */
protected $constants = []; protected array $constants = [];
protected $properties = []; /** @var list<Stmt\Property> */
protected $methods = []; protected array $properties = [];
/** @var list<Stmt\ClassMethod> */
/** @var Node\AttributeGroup[] */ protected array $methods = [];
protected $attributeGroups = []; /** @var list<Node\AttributeGroup> */
protected array $attributeGroups = [];
/** /**
* Creates a class builder. * Creates a class builder.
@ -67,7 +69,7 @@ class Class_ extends Declaration
* @return $this The builder instance (for fluid interface) * @return $this The builder instance (for fluid interface)
*/ */
public function makeAbstract() { public function makeAbstract() {
$this->flags = BuilderHelpers::addClassModifier($this->flags, Stmt\Class_::MODIFIER_ABSTRACT); $this->flags = BuilderHelpers::addClassModifier($this->flags, Modifiers::ABSTRACT);
return $this; return $this;
} }
@ -78,13 +80,18 @@ class Class_ extends Declaration
* @return $this The builder instance (for fluid interface) * @return $this The builder instance (for fluid interface)
*/ */
public function makeFinal() { public function makeFinal() {
$this->flags = BuilderHelpers::addClassModifier($this->flags, Stmt\Class_::MODIFIER_FINAL); $this->flags = BuilderHelpers::addClassModifier($this->flags, Modifiers::FINAL);
return $this; return $this;
} }
/**
* Makes the class readonly.
*
* @return $this The builder instance (for fluid interface)
*/
public function makeReadonly() { public function makeReadonly() {
$this->flags = BuilderHelpers::addClassModifier($this->flags, Stmt\Class_::MODIFIER_READONLY); $this->flags = BuilderHelpers::addClassModifier($this->flags, Modifiers::READONLY);
return $this; return $this;
} }
@ -99,20 +106,18 @@ class Class_ extends Declaration
public function addStmt($stmt) { public function addStmt($stmt) {
$stmt = BuilderHelpers::normalizeNode($stmt); $stmt = BuilderHelpers::normalizeNode($stmt);
$targets = [ if ($stmt instanceof Stmt\Property) {
Stmt\TraitUse::class => &$this->uses, $this->properties[] = $stmt;
Stmt\ClassConst::class => &$this->constants, } elseif ($stmt instanceof Stmt\ClassMethod) {
Stmt\Property::class => &$this->properties, $this->methods[] = $stmt;
Stmt\ClassMethod::class => &$this->methods, } elseif ($stmt instanceof Stmt\TraitUse) {
]; $this->uses[] = $stmt;
} elseif ($stmt instanceof Stmt\ClassConst) {
$class = \get_class($stmt); $this->constants[] = $stmt;
if (!isset($targets[$class])) { } else {
throw new \LogicException(sprintf('Unexpected node of type "%s"', $stmt->getType())); throw new \LogicException(sprintf('Unexpected node of type "%s"', $stmt->getType()));
} }
$targets[$class][] = $stmt;
return $this; return $this;
} }

@ -5,16 +5,23 @@ namespace PhpParser\Builder;
use PhpParser; use PhpParser;
use PhpParser\BuilderHelpers; use PhpParser\BuilderHelpers;
abstract class Declaration implements PhpParser\Builder abstract class Declaration implements PhpParser\Builder {
{ /** @var array<string, mixed> */
protected $attributes = []; protected array $attributes = [];
/**
* Adds a statement.
*
* @param PhpParser\Node\Stmt|PhpParser\Builder $stmt The statement to add
*
* @return $this The builder instance (for fluid interface)
*/
abstract public function addStmt($stmt); abstract public function addStmt($stmt);
/** /**
* Adds multiple statements. * Adds multiple statements.
* *
* @param array $stmts The statements to add * @param (PhpParser\Node\Stmt|PhpParser\Builder)[] $stmts The statements to add
* *
* @return $this The builder instance (for fluid interface) * @return $this The builder instance (for fluid interface)
*/ */

@ -10,14 +10,16 @@ use PhpParser\Node;
use PhpParser\Node\Identifier; use PhpParser\Node\Identifier;
use PhpParser\Node\Stmt; use PhpParser\Node\Stmt;
class EnumCase implements PhpParser\Builder class EnumCase implements PhpParser\Builder {
{ /** @var Identifier|string */
protected $name; protected $name;
protected $value = null; /** @var ?Node\Expr */
protected $attributes = []; protected ?Node\Expr $value = null;
/** @var array<string, mixed> */
protected array $attributes = [];
/** @var Node\AttributeGroup[] */ /** @var list<Node\AttributeGroup> */
protected $attributeGroups = []; protected array $attributeGroups = [];
/** /**
* Creates an enum case builder. * Creates an enum case builder.

@ -9,20 +9,21 @@ use PhpParser\Node\Identifier;
use PhpParser\Node\Name; use PhpParser\Node\Name;
use PhpParser\Node\Stmt; use PhpParser\Node\Stmt;
class Enum_ extends Declaration class Enum_ extends Declaration {
{ protected string $name;
protected $name; protected ?Identifier $scalarType = null;
protected $scalarType = null; /** @var list<Name> */
protected array $implements = [];
protected $implements = []; /** @var list<Stmt\TraitUse> */
protected array $uses = [];
protected $uses = []; /** @var list<Stmt\EnumCase> */
protected $enumCases = []; protected array $enumCases = [];
protected $constants = []; /** @var list<Stmt\ClassConst> */
protected $methods = []; protected array $constants = [];
/** @var list<Stmt\ClassMethod> */
/** @var Node\AttributeGroup[] */ protected array $methods = [];
protected $attributeGroups = []; /** @var list<Node\AttributeGroup> */
protected array $attributeGroups = [];
/** /**
* Creates an enum builder. * Creates an enum builder.
@ -36,7 +37,7 @@ class Enum_ extends Declaration
/** /**
* Sets the scalar type. * Sets the scalar type.
* *
* @param string|Identifier $type * @param string|Identifier $scalarType
* *
* @return $this * @return $this
*/ */
@ -71,20 +72,18 @@ class Enum_ extends Declaration
public function addStmt($stmt) { public function addStmt($stmt) {
$stmt = BuilderHelpers::normalizeNode($stmt); $stmt = BuilderHelpers::normalizeNode($stmt);
$targets = [ if ($stmt instanceof Stmt\EnumCase) {
Stmt\TraitUse::class => &$this->uses, $this->enumCases[] = $stmt;
Stmt\EnumCase::class => &$this->enumCases, } elseif ($stmt instanceof Stmt\ClassMethod) {
Stmt\ClassConst::class => &$this->constants, $this->methods[] = $stmt;
Stmt\ClassMethod::class => &$this->methods, } elseif ($stmt instanceof Stmt\TraitUse) {
]; $this->uses[] = $stmt;
} elseif ($stmt instanceof Stmt\ClassConst) {
$class = \get_class($stmt); $this->constants[] = $stmt;
if (!isset($targets[$class])) { } else {
throw new \LogicException(sprintf('Unexpected node of type "%s"', $stmt->getType())); throw new \LogicException(sprintf('Unexpected node of type "%s"', $stmt->getType()));
} }
$targets[$class][] = $stmt;
return $this; return $this;
} }

@ -5,13 +5,13 @@ namespace PhpParser\Builder;
use PhpParser\BuilderHelpers; use PhpParser\BuilderHelpers;
use PhpParser\Node; use PhpParser\Node;
abstract class FunctionLike extends Declaration abstract class FunctionLike extends Declaration {
{ protected bool $returnByRef = false;
protected $returnByRef = false; /** @var Node\Param[] */
protected $params = []; protected array $params = [];
/** @var string|Node\Name|Node\NullableType|null */ /** @var Node\Identifier|Node\Name|Node\ComplexType|null */
protected $returnType = null; protected ?Node $returnType = null;
/** /**
* Make the function return by reference. * Make the function return by reference.
@ -46,7 +46,7 @@ abstract class FunctionLike extends Declaration
/** /**
* Adds multiple parameters. * Adds multiple parameters.
* *
* @param array $params The parameters to add * @param (Node\Param|Param)[] $params The parameters to add
* *
* @return $this The builder instance (for fluid interface) * @return $this The builder instance (for fluid interface)
*/ */

@ -7,13 +7,13 @@ use PhpParser\BuilderHelpers;
use PhpParser\Node; use PhpParser\Node;
use PhpParser\Node\Stmt; use PhpParser\Node\Stmt;
class Function_ extends FunctionLike class Function_ extends FunctionLike {
{ protected string $name;
protected $name; /** @var list<Stmt> */
protected $stmts = []; protected array $stmts = [];
/** @var Node\AttributeGroup[] */ /** @var list<Node\AttributeGroup> */
protected $attributeGroups = []; protected array $attributeGroups = [];
/** /**
* Creates a function builder. * Creates a function builder.

@ -8,15 +8,16 @@ use PhpParser\Node;
use PhpParser\Node\Name; use PhpParser\Node\Name;
use PhpParser\Node\Stmt; use PhpParser\Node\Stmt;
class Interface_ extends Declaration class Interface_ extends Declaration {
{ protected string $name;
protected $name; /** @var list<Name> */
protected $extends = []; protected array $extends = [];
protected $constants = []; /** @var list<Stmt\ClassConst> */
protected $methods = []; protected array $constants = [];
/** @var list<Stmt\ClassMethod> */
/** @var Node\AttributeGroup[] */ protected array $methods = [];
protected $attributeGroups = []; /** @var list<Node\AttributeGroup> */
protected array $attributeGroups = [];
/** /**
* Creates an interface builder. * Creates an interface builder.

@ -4,19 +4,20 @@ namespace PhpParser\Builder;
use PhpParser; use PhpParser;
use PhpParser\BuilderHelpers; use PhpParser\BuilderHelpers;
use PhpParser\Modifiers;
use PhpParser\Node; use PhpParser\Node;
use PhpParser\Node\Stmt; use PhpParser\Node\Stmt;
class Method extends FunctionLike class Method extends FunctionLike {
{ protected string $name;
protected $name;
protected $flags = 0;
/** @var array|null */ protected int $flags = 0;
protected $stmts = [];
/** @var Node\AttributeGroup[] */ /** @var list<Stmt>|null */
protected $attributeGroups = []; protected ?array $stmts = [];
/** @var list<Node\AttributeGroup> */
protected array $attributeGroups = [];
/** /**
* Creates a method builder. * Creates a method builder.
@ -33,7 +34,7 @@ class Method extends FunctionLike
* @return $this The builder instance (for fluid interface) * @return $this The builder instance (for fluid interface)
*/ */
public function makePublic() { public function makePublic() {
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PUBLIC); $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PUBLIC);
return $this; return $this;
} }
@ -44,7 +45,7 @@ class Method extends FunctionLike
* @return $this The builder instance (for fluid interface) * @return $this The builder instance (for fluid interface)
*/ */
public function makeProtected() { public function makeProtected() {
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PROTECTED); $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PROTECTED);
return $this; return $this;
} }
@ -55,7 +56,7 @@ class Method extends FunctionLike
* @return $this The builder instance (for fluid interface) * @return $this The builder instance (for fluid interface)
*/ */
public function makePrivate() { public function makePrivate() {
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PRIVATE); $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PRIVATE);
return $this; return $this;
} }
@ -66,7 +67,7 @@ class Method extends FunctionLike
* @return $this The builder instance (for fluid interface) * @return $this The builder instance (for fluid interface)
*/ */
public function makeStatic() { public function makeStatic() {
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_STATIC); $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::STATIC);
return $this; return $this;
} }
@ -81,7 +82,7 @@ class Method extends FunctionLike
throw new \LogicException('Cannot make method with statements abstract'); throw new \LogicException('Cannot make method with statements abstract');
} }
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_ABSTRACT); $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::ABSTRACT);
$this->stmts = null; // abstract methods don't have statements $this->stmts = null; // abstract methods don't have statements
return $this; return $this;
@ -93,7 +94,7 @@ class Method extends FunctionLike
* @return $this The builder instance (for fluid interface) * @return $this The builder instance (for fluid interface)
*/ */
public function makeFinal() { public function makeFinal() {
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_FINAL); $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::FINAL);
return $this; return $this;
} }

@ -7,10 +7,10 @@ use PhpParser\BuilderHelpers;
use PhpParser\Node; use PhpParser\Node;
use PhpParser\Node\Stmt; use PhpParser\Node\Stmt;
class Namespace_ extends Declaration class Namespace_ extends Declaration {
{ private ?Node\Name $name;
private $name; /** @var Stmt[] */
private $stmts = []; private array $stmts = [];
/** /**
* Creates a namespace builder. * Creates a namespace builder.

@ -4,25 +4,19 @@ namespace PhpParser\Builder;
use PhpParser; use PhpParser;
use PhpParser\BuilderHelpers; use PhpParser\BuilderHelpers;
use PhpParser\Modifiers;
use PhpParser\Node; use PhpParser\Node;
class Param implements PhpParser\Builder class Param implements PhpParser\Builder {
{ protected string $name;
protected $name; protected ?Node\Expr $default = null;
/** @var Node\Identifier|Node\Name|Node\ComplexType|null */
protected $default = null; protected ?Node $type = null;
protected bool $byRef = false;
/** @var Node\Identifier|Node\Name|Node\NullableType|null */ protected int $flags = 0;
protected $type = null; protected bool $variadic = false;
/** @var list<Node\AttributeGroup> */
protected $byRef = false; protected array $attributeGroups = [];
protected $variadic = false;
protected $flags = 0;
/** @var Node\AttributeGroup[] */
protected $attributeGroups = [];
/** /**
* Creates a parameter builder. * Creates a parameter builder.
@ -62,19 +56,6 @@ class Param implements PhpParser\Builder
return $this; return $this;
} }
/**
* Sets type for the parameter.
*
* @param string|Node\Name|Node\Identifier|Node\ComplexType $type Parameter type
*
* @return $this The builder instance (for fluid interface)
*
* @deprecated Use setType() instead
*/
public function setTypeHint($type) {
return $this->setType($type);
}
/** /**
* Make the parameter accept the value by reference. * Make the parameter accept the value by reference.
* *
@ -103,7 +84,7 @@ class Param implements PhpParser\Builder
* @return $this The builder instance (for fluid interface) * @return $this The builder instance (for fluid interface)
*/ */
public function makePublic() { public function makePublic() {
$this->flags = BuilderHelpers::addModifier($this->flags, Node\Stmt\Class_::MODIFIER_PUBLIC); $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PUBLIC);
return $this; return $this;
} }
@ -114,7 +95,7 @@ class Param implements PhpParser\Builder
* @return $this The builder instance (for fluid interface) * @return $this The builder instance (for fluid interface)
*/ */
public function makeProtected() { public function makeProtected() {
$this->flags = BuilderHelpers::addModifier($this->flags, Node\Stmt\Class_::MODIFIER_PROTECTED); $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PROTECTED);
return $this; return $this;
} }
@ -125,7 +106,7 @@ class Param implements PhpParser\Builder
* @return $this The builder instance (for fluid interface) * @return $this The builder instance (for fluid interface)
*/ */
public function makePrivate() { public function makePrivate() {
$this->flags = BuilderHelpers::addModifier($this->flags, Node\Stmt\Class_::MODIFIER_PRIVATE); $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PRIVATE);
return $this; return $this;
} }
@ -136,7 +117,7 @@ class Param implements PhpParser\Builder
* @return $this The builder instance (for fluid interface) * @return $this The builder instance (for fluid interface)
*/ */
public function makeReadonly() { public function makeReadonly() {
$this->flags = BuilderHelpers::addModifier($this->flags, Node\Stmt\Class_::MODIFIER_READONLY); $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::READONLY);
return $this; return $this;
} }

@ -4,25 +4,25 @@ namespace PhpParser\Builder;
use PhpParser; use PhpParser;
use PhpParser\BuilderHelpers; use PhpParser\BuilderHelpers;
use PhpParser\Modifiers;
use PhpParser\Node; use PhpParser\Node;
use PhpParser\Node\Identifier; use PhpParser\Node\Identifier;
use PhpParser\Node\Name; use PhpParser\Node\Name;
use PhpParser\Node\Stmt; use PhpParser\Node\Stmt;
use PhpParser\Node\ComplexType; use PhpParser\Node\ComplexType;
class Property implements PhpParser\Builder class Property implements PhpParser\Builder {
{ protected string $name;
protected $name;
protected $flags = 0; protected int $flags = 0;
protected $default = null;
protected $attributes = [];
/** @var null|Identifier|Name|NullableType */ protected ?Node\Expr $default = null;
protected $type; /** @var array<string, mixed> */
protected array $attributes = [];
/** @var Node\AttributeGroup[] */ /** @var null|Identifier|Name|ComplexType */
protected $attributeGroups = []; protected ?Node $type = null;
/** @var list<Node\AttributeGroup> */
protected array $attributeGroups = [];
/** /**
* Creates a property builder. * Creates a property builder.
@ -39,7 +39,7 @@ class Property implements PhpParser\Builder
* @return $this The builder instance (for fluid interface) * @return $this The builder instance (for fluid interface)
*/ */
public function makePublic() { public function makePublic() {
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PUBLIC); $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PUBLIC);
return $this; return $this;
} }
@ -50,7 +50,7 @@ class Property implements PhpParser\Builder
* @return $this The builder instance (for fluid interface) * @return $this The builder instance (for fluid interface)
*/ */
public function makeProtected() { public function makeProtected() {
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PROTECTED); $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PROTECTED);
return $this; return $this;
} }
@ -61,7 +61,7 @@ class Property implements PhpParser\Builder
* @return $this The builder instance (for fluid interface) * @return $this The builder instance (for fluid interface)
*/ */
public function makePrivate() { public function makePrivate() {
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PRIVATE); $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PRIVATE);
return $this; return $this;
} }
@ -72,7 +72,7 @@ class Property implements PhpParser\Builder
* @return $this The builder instance (for fluid interface) * @return $this The builder instance (for fluid interface)
*/ */
public function makeStatic() { public function makeStatic() {
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_STATIC); $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::STATIC);
return $this; return $this;
} }
@ -83,7 +83,7 @@ class Property implements PhpParser\Builder
* @return $this The builder instance (for fluid interface) * @return $this The builder instance (for fluid interface)
*/ */
public function makeReadonly() { public function makeReadonly() {
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_READONLY); $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::READONLY);
return $this; return $this;
} }
@ -149,9 +149,9 @@ class Property implements PhpParser\Builder
*/ */
public function getNode(): PhpParser\Node { public function getNode(): PhpParser\Node {
return new Stmt\Property( return new Stmt\Property(
$this->flags !== 0 ? $this->flags : Stmt\Class_::MODIFIER_PUBLIC, $this->flags !== 0 ? $this->flags : Modifiers::PUBLIC,
[ [
new Stmt\PropertyProperty($this->name, $this->default) new Node\PropertyItem($this->name, $this->default)
], ],
$this->attributes, $this->attributes,
$this->type, $this->type,

@ -7,10 +7,11 @@ use PhpParser\BuilderHelpers;
use PhpParser\Node; use PhpParser\Node;
use PhpParser\Node\Stmt; use PhpParser\Node\Stmt;
class TraitUse implements Builder class TraitUse implements Builder {
{ /** @var Node\Name[] */
protected $traits = []; protected array $traits = [];
protected $adaptations = []; /** @var Stmt\TraitUseAdaptation[] */
protected array $adaptations = [];
/** /**
* Creates a trait use builder. * Creates a trait use builder.

@ -4,31 +4,28 @@ namespace PhpParser\Builder;
use PhpParser\Builder; use PhpParser\Builder;
use PhpParser\BuilderHelpers; use PhpParser\BuilderHelpers;
use PhpParser\Modifiers;
use PhpParser\Node; use PhpParser\Node;
use PhpParser\Node\Stmt; use PhpParser\Node\Stmt;
class TraitUseAdaptation implements Builder class TraitUseAdaptation implements Builder {
{ private const TYPE_UNDEFINED = 0;
const TYPE_UNDEFINED = 0; private const TYPE_ALIAS = 1;
const TYPE_ALIAS = 1; private const TYPE_PRECEDENCE = 2;
const TYPE_PRECEDENCE = 2;
/** @var int Type of building adaptation */ protected int $type;
protected $type; protected ?Node\Name $trait;
protected Node\Identifier $method;
protected $trait; protected ?int $modifier = null;
protected $method; protected ?Node\Identifier $alias = null;
/** @var Node\Name[] */
protected $modifier = null; protected array $insteadof = [];
protected $alias = null;
protected $insteadof = [];
/** /**
* Creates a trait use adaptation builder. * Creates a trait use adaptation builder.
* *
* @param Node\Name|string|null $trait Name of adaptated trait * @param Node\Name|string|null $trait Name of adapted trait
* @param Node\Identifier|string $method Name of adaptated method * @param Node\Identifier|string $method Name of adapted method
*/ */
public function __construct($trait, $method) { public function __construct($trait, $method) {
$this->type = self::TYPE_UNDEFINED; $this->type = self::TYPE_UNDEFINED;
@ -40,7 +37,7 @@ class TraitUseAdaptation implements Builder
/** /**
* Sets alias of method. * Sets alias of method.
* *
* @param Node\Identifier|string $alias Alias for adaptated method * @param Node\Identifier|string $alias Alias for adapted method
* *
* @return $this The builder instance (for fluid interface) * @return $this The builder instance (for fluid interface)
*/ */
@ -53,37 +50,37 @@ class TraitUseAdaptation implements Builder
throw new \LogicException('Cannot set alias for not alias adaptation buider'); throw new \LogicException('Cannot set alias for not alias adaptation buider');
} }
$this->alias = $alias; $this->alias = BuilderHelpers::normalizeIdentifier($alias);
return $this; return $this;
} }
/** /**
* Sets adaptated method public. * Sets adapted method public.
* *
* @return $this The builder instance (for fluid interface) * @return $this The builder instance (for fluid interface)
*/ */
public function makePublic() { public function makePublic() {
$this->setModifier(Stmt\Class_::MODIFIER_PUBLIC); $this->setModifier(Modifiers::PUBLIC);
return $this; return $this;
} }
/** /**
* Sets adaptated method protected. * Sets adapted method protected.
* *
* @return $this The builder instance (for fluid interface) * @return $this The builder instance (for fluid interface)
*/ */
public function makeProtected() { public function makeProtected() {
$this->setModifier(Stmt\Class_::MODIFIER_PROTECTED); $this->setModifier(Modifiers::PROTECTED);
return $this; return $this;
} }
/** /**
* Sets adaptated method private. * Sets adapted method private.
* *
* @return $this The builder instance (for fluid interface) * @return $this The builder instance (for fluid interface)
*/ */
public function makePrivate() { public function makePrivate() {
$this->setModifier(Stmt\Class_::MODIFIER_PRIVATE); $this->setModifier(Modifiers::PRIVATE);
return $this; return $this;
} }
@ -114,7 +111,7 @@ class TraitUseAdaptation implements Builder
return $this; return $this;
} }
protected function setModifier(int $modifier) { protected function setModifier(int $modifier): void {
if ($this->type === self::TYPE_UNDEFINED) { if ($this->type === self::TYPE_UNDEFINED) {
$this->type = self::TYPE_ALIAS; $this->type = self::TYPE_ALIAS;
} }

@ -7,15 +7,18 @@ use PhpParser\BuilderHelpers;
use PhpParser\Node; use PhpParser\Node;
use PhpParser\Node\Stmt; use PhpParser\Node\Stmt;
class Trait_ extends Declaration class Trait_ extends Declaration {
{ protected string $name;
protected $name; /** @var list<Stmt\TraitUse> */
protected $uses = []; protected array $uses = [];
protected $properties = []; /** @var list<Stmt\ClassConst> */
protected $methods = []; protected array $constants = [];
/** @var list<Stmt\Property> */
/** @var Node\AttributeGroup[] */ protected array $properties = [];
protected $attributeGroups = []; /** @var list<Stmt\ClassMethod> */
protected array $methods = [];
/** @var list<Node\AttributeGroup> */
protected array $attributeGroups = [];
/** /**
* Creates an interface builder. * Creates an interface builder.
@ -42,6 +45,8 @@ class Trait_ extends Declaration
$this->methods[] = $stmt; $this->methods[] = $stmt;
} elseif ($stmt instanceof Stmt\TraitUse) { } elseif ($stmt instanceof Stmt\TraitUse) {
$this->uses[] = $stmt; $this->uses[] = $stmt;
} elseif ($stmt instanceof Stmt\ClassConst) {
$this->constants[] = $stmt;
} else { } else {
throw new \LogicException(sprintf('Unexpected node of type "%s"', $stmt->getType())); throw new \LogicException(sprintf('Unexpected node of type "%s"', $stmt->getType()));
} }
@ -70,7 +75,7 @@ class Trait_ extends Declaration
public function getNode(): PhpParser\Node { public function getNode(): PhpParser\Node {
return new Stmt\Trait_( return new Stmt\Trait_(
$this->name, [ $this->name, [
'stmts' => array_merge($this->uses, $this->properties, $this->methods), 'stmts' => array_merge($this->uses, $this->constants, $this->properties, $this->methods),
'attrGroups' => $this->attributeGroups, 'attrGroups' => $this->attributeGroups,
], $this->attributes ], $this->attributes
); );

@ -7,17 +7,17 @@ use PhpParser\BuilderHelpers;
use PhpParser\Node; use PhpParser\Node;
use PhpParser\Node\Stmt; use PhpParser\Node\Stmt;
class Use_ implements Builder class Use_ implements Builder {
{ protected Node\Name $name;
protected $name; /** @var Stmt\Use_::TYPE_* */
protected $type; protected int $type;
protected $alias = null; protected ?string $alias = null;
/** /**
* Creates a name use (alias) builder. * Creates a name use (alias) builder.
* *
* @param Node\Name|string $name Name of the entity (namespace, class, function, constant) to alias * @param Node\Name|string $name Name of the entity (namespace, class, function, constant) to alias
* @param int $type One of the Stmt\Use_::TYPE_* constants * @param Stmt\Use_::TYPE_* $type One of the Stmt\Use_::TYPE_* constants
*/ */
public function __construct($name, int $type) { public function __construct($name, int $type) {
$this->name = BuilderHelpers::normalizeName($name); $this->name = BuilderHelpers::normalizeName($name);
@ -43,7 +43,7 @@ class Use_ implements Builder
*/ */
public function getNode(): Node { public function getNode(): Node {
return new Stmt\Use_([ return new Stmt\Use_([
new Stmt\UseUse($this->name, $this->alias) new Node\UseItem($this->name, $this->alias)
], $this->type); ], $this->type);
} }
} }

@ -10,15 +10,12 @@ use PhpParser\Node\Name;
use PhpParser\Node\Scalar\String_; use PhpParser\Node\Scalar\String_;
use PhpParser\Node\Stmt\Use_; use PhpParser\Node\Stmt\Use_;
class BuilderFactory class BuilderFactory {
{
/** /**
* Creates an attribute node. * Creates an attribute node.
* *
* @param string|Name $name Name of the attribute * @param string|Name $name Name of the attribute
* @param array $args Attribute named arguments * @param array $args Attribute named arguments
*
* @return Node\Attribute
*/ */
public function attribute($name, array $args = []): Node\Attribute { public function attribute($name, array $args = []): Node\Attribute {
return new Node\Attribute( return new Node\Attribute(
@ -87,7 +84,7 @@ class BuilderFactory
* *
* @param Node\Name|string ...$traits Trait names * @param Node\Name|string ...$traits Trait names
* *
* @return Builder\TraitUse The create trait use builder * @return Builder\TraitUse The created trait use builder
*/ */
public function useTrait(...$traits): Builder\TraitUse { public function useTrait(...$traits): Builder\TraitUse {
return new Builder\TraitUse(...$traits); return new Builder\TraitUse(...$traits);
@ -99,7 +96,7 @@ class BuilderFactory
* @param Node\Name|string|null $trait Trait name * @param Node\Name|string|null $trait Trait name
* @param Node\Identifier|string $method Method name * @param Node\Identifier|string $method Method name
* *
* @return Builder\TraitUseAdaptation The create trait use adaptation builder * @return Builder\TraitUseAdaptation The created trait use adaptation builder
*/ */
public function traitUseAdaptation($trait, $method = null): Builder\TraitUseAdaptation { public function traitUseAdaptation($trait, $method = null): Builder\TraitUseAdaptation {
if ($method === null) { if ($method === null) {
@ -214,8 +211,6 @@ class BuilderFactory
* Creates node a for a literal value. * Creates node a for a literal value.
* *
* @param Expr|bool|null|int|float|string|array $value $value * @param Expr|bool|null|int|float|string|array $value $value
*
* @return Expr
*/ */
public function val($value): Expr { public function val($value): Expr {
return BuilderHelpers::normalizeValue($value); return BuilderHelpers::normalizeValue($value);
@ -225,8 +220,6 @@ class BuilderFactory
* Creates variable node. * Creates variable node.
* *
* @param string|Expr $name Name * @param string|Expr $name Name
*
* @return Expr\Variable
*/ */
public function var($name): Expr\Variable { public function var($name): Expr\Variable {
if (!\is_string($name) && !$name instanceof Expr) { if (!\is_string($name) && !$name instanceof Expr) {
@ -243,7 +236,7 @@ class BuilderFactory
* *
* @param array $args List of arguments to normalize * @param array $args List of arguments to normalize
* *
* @return Arg[] * @return list<Arg>
*/ */
public function args(array $args): array { public function args(array $args): array {
$normalizedArgs = []; $normalizedArgs = [];
@ -264,8 +257,6 @@ class BuilderFactory
* *
* @param string|Name|Expr $name Function name * @param string|Name|Expr $name Function name
* @param array $args Function arguments * @param array $args Function arguments
*
* @return Expr\FuncCall
*/ */
public function funcCall($name, array $args = []): Expr\FuncCall { public function funcCall($name, array $args = []): Expr\FuncCall {
return new Expr\FuncCall( return new Expr\FuncCall(
@ -280,8 +271,6 @@ class BuilderFactory
* @param Expr $var Variable the method is called on * @param Expr $var Variable the method is called on
* @param string|Identifier|Expr $name Method name * @param string|Identifier|Expr $name Method name
* @param array $args Method arguments * @param array $args Method arguments
*
* @return Expr\MethodCall
*/ */
public function methodCall(Expr $var, $name, array $args = []): Expr\MethodCall { public function methodCall(Expr $var, $name, array $args = []): Expr\MethodCall {
return new Expr\MethodCall( return new Expr\MethodCall(
@ -297,8 +286,6 @@ class BuilderFactory
* @param string|Name|Expr $class Class name * @param string|Name|Expr $class Class name
* @param string|Identifier|Expr $name Method name * @param string|Identifier|Expr $name Method name
* @param array $args Method arguments * @param array $args Method arguments
*
* @return Expr\StaticCall
*/ */
public function staticCall($class, $name, array $args = []): Expr\StaticCall { public function staticCall($class, $name, array $args = []): Expr\StaticCall {
return new Expr\StaticCall( return new Expr\StaticCall(
@ -313,8 +300,6 @@ class BuilderFactory
* *
* @param string|Name|Expr $class Class name * @param string|Name|Expr $class Class name
* @param array $args Constructor arguments * @param array $args Constructor arguments
*
* @return Expr\New_
*/ */
public function new($class, array $args = []): Expr\New_ { public function new($class, array $args = []): Expr\New_ {
return new Expr\New_( return new Expr\New_(
@ -327,8 +312,6 @@ class BuilderFactory
* Creates a constant fetch node. * Creates a constant fetch node.
* *
* @param string|Name $name Constant name * @param string|Name $name Constant name
*
* @return Expr\ConstFetch
*/ */
public function constFetch($name): Expr\ConstFetch { public function constFetch($name): Expr\ConstFetch {
return new Expr\ConstFetch(BuilderHelpers::normalizeName($name)); return new Expr\ConstFetch(BuilderHelpers::normalizeName($name));
@ -339,8 +322,6 @@ class BuilderFactory
* *
* @param Expr $var Variable holding object * @param Expr $var Variable holding object
* @param string|Identifier|Expr $name Property name * @param string|Identifier|Expr $name Property name
*
* @return Expr\PropertyFetch
*/ */
public function propertyFetch(Expr $var, $name): Expr\PropertyFetch { public function propertyFetch(Expr $var, $name): Expr\PropertyFetch {
return new Expr\PropertyFetch($var, BuilderHelpers::normalizeIdentifierOrExpr($name)); return new Expr\PropertyFetch($var, BuilderHelpers::normalizeIdentifierOrExpr($name));
@ -351,8 +332,6 @@ class BuilderFactory
* *
* @param string|Name|Expr $class Class name * @param string|Name|Expr $class Class name
* @param string|Identifier|Expr $name Constant name * @param string|Identifier|Expr $name Constant name
*
* @return Expr\ClassConstFetch
*/ */
public function classConstFetch($class, $name): Expr\ClassConstFetch { public function classConstFetch($class, $name): Expr\ClassConstFetch {
return new Expr\ClassConstFetch( return new Expr\ClassConstFetch(
@ -365,8 +344,6 @@ class BuilderFactory
* Creates nested Concat nodes from a list of expressions. * Creates nested Concat nodes from a list of expressions.
* *
* @param Expr|string ...$exprs Expressions or literal strings * @param Expr|string ...$exprs Expressions or literal strings
*
* @return Concat
*/ */
public function concat(...$exprs): Concat { public function concat(...$exprs): Concat {
$numExprs = count($exprs); $numExprs = count($exprs);
@ -383,7 +360,6 @@ class BuilderFactory
/** /**
* @param string|Expr $expr * @param string|Expr $expr
* @return Expr
*/ */
private function normalizeStringExpr($expr): Expr { private function normalizeStringExpr($expr): Expr {
if ($expr instanceof Expr) { if ($expr instanceof Expr) {

@ -15,8 +15,7 @@ use PhpParser\Node\Stmt;
* *
* @internal * @internal
*/ */
final class BuilderHelpers final class BuilderHelpers {
{
/** /**
* Normalizes a node: Converts builder objects to nodes. * Normalizes a node: Converts builder objects to nodes.
* *
@ -237,11 +236,11 @@ final class BuilderHelpers
} }
if (is_int($value)) { if (is_int($value)) {
return new Scalar\LNumber($value); return new Scalar\Int_($value);
} }
if (is_float($value)) { if (is_float($value)) {
return new Scalar\DNumber($value); return new Scalar\Float_($value);
} }
if (is_string($value)) { if (is_string($value)) {
@ -254,12 +253,12 @@ final class BuilderHelpers
foreach ($value as $itemKey => $itemValue) { foreach ($value as $itemKey => $itemValue) {
// for consecutive, numeric keys don't generate keys // for consecutive, numeric keys don't generate keys
if (null !== $lastKey && ++$lastKey === $itemKey) { if (null !== $lastKey && ++$lastKey === $itemKey) {
$items[] = new Expr\ArrayItem( $items[] = new Node\ArrayItem(
self::normalizeValue($itemValue) self::normalizeValue($itemValue)
); );
} else { } else {
$lastKey = null; $lastKey = null;
$items[] = new Expr\ArrayItem( $items[] = new Node\ArrayItem(
self::normalizeValue($itemValue), self::normalizeValue($itemValue),
self::normalizeValue($itemKey) self::normalizeValue($itemKey)
); );
@ -298,8 +297,7 @@ final class BuilderHelpers
* *
* @return Node\AttributeGroup The Attribute Group * @return Node\AttributeGroup The Attribute Group
*/ */
public static function normalizeAttribute($attribute) : Node\AttributeGroup public static function normalizeAttribute($attribute): Node\AttributeGroup {
{
if ($attribute instanceof Node\AttributeGroup) { if ($attribute instanceof Node\AttributeGroup) {
return $attribute; return $attribute;
} }
@ -320,7 +318,7 @@ final class BuilderHelpers
* @return int New modifiers * @return int New modifiers
*/ */
public static function addModifier(int $modifiers, int $modifier): int { public static function addModifier(int $modifiers, int $modifier): int {
Stmt\Class_::verifyModifier($modifiers, $modifier); Modifiers::verifyModifier($modifiers, $modifier);
return $modifiers | $modifier; return $modifiers | $modifier;
} }
@ -329,7 +327,7 @@ final class BuilderHelpers
* @return int New modifiers * @return int New modifiers
*/ */
public static function addClassModifier(int $existingModifiers, int $modifierToSet): int { public static function addClassModifier(int $existingModifiers, int $modifierToSet): int {
Stmt\Class_::verifyClassModifier($existingModifiers, $modifierToSet); Modifiers::verifyClassModifier($existingModifiers, $modifierToSet);
return $existingModifiers | $modifierToSet; return $existingModifiers | $modifierToSet;
} }
} }

@ -2,15 +2,14 @@
namespace PhpParser; namespace PhpParser;
class Comment implements \JsonSerializable class Comment implements \JsonSerializable {
{ protected string $text;
protected $text; protected int $startLine;
protected $startLine; protected int $startFilePos;
protected $startFilePos; protected int $startTokenPos;
protected $startTokenPos; protected int $endLine;
protected $endLine; protected int $endFilePos;
protected $endFilePos; protected int $endTokenPos;
protected $endTokenPos;
/** /**
* Constructs a comment node. * Constructs a comment node.
@ -97,39 +96,6 @@ class Comment implements \JsonSerializable
return $this->endTokenPos; return $this->endTokenPos;
} }
/**
* Gets the line number the comment started on.
*
* @deprecated Use getStartLine() instead
*
* @return int Line number
*/
public function getLine() : int {
return $this->startLine;
}
/**
* Gets the file offset the comment started on.
*
* @deprecated Use getStartFilePos() instead
*
* @return int File offset
*/
public function getFilePos() : int {
return $this->startFilePos;
}
/**
* Gets the token offset the comment started on.
*
* @deprecated Use getStartTokenPos() instead
*
* @return int Token offset
*/
public function getTokenPos() : int {
return $this->startTokenPos;
}
/** /**
* Gets the comment text. * Gets the comment text.
* *
@ -144,18 +110,19 @@ class Comment implements \JsonSerializable
* *
* "Reformatted" here means that we try to clean up the whitespace at the * "Reformatted" here means that we try to clean up the whitespace at the
* starts of the lines. This is necessary because we receive the comments * starts of the lines. This is necessary because we receive the comments
* without trailing whitespace on the first line, but with trailing whitespace * without leading whitespace on the first line, but with leading whitespace
* on all subsequent lines. * on all subsequent lines.
* *
* @return mixed|string * Additionally, this normalizes CRLF newlines to LF newlines.
*/ */
public function getReformattedText() { public function getReformattedText(): string {
$text = trim($this->text); $text = str_replace("\r\n", "\n", $this->text);
$newlinePos = strpos($text, "\n"); $newlinePos = strpos($text, "\n");
if (false === $newlinePos) { if (false === $newlinePos) {
// Single line comments don't need further processing // Single line comments don't need further processing
return $text; return $text;
} elseif (preg_match('((*BSR_ANYCRLF)(*ANYCRLF)^.*(?:\R\s+\*.*)+$)', $text)) { }
if (preg_match('(^.*(?:\n\s+\*.*)+$)', $text)) {
// Multi line comment of the type // Multi line comment of the type
// //
// /* // /*
@ -164,8 +131,9 @@ class Comment implements \JsonSerializable
// */ // */
// //
// is handled by replacing the whitespace sequences before the * by a single space // is handled by replacing the whitespace sequences before the * by a single space
return preg_replace('(^\s+\*)m', ' *', $this->text); return preg_replace('(^\s+\*)m', ' *', $text);
} elseif (preg_match('(^/\*\*?\s*[\r\n])', $text) && preg_match('(\n(\s*)\*/$)', $text, $matches)) { }
if (preg_match('(^/\*\*?\s*\n)', $text) && preg_match('(\n(\s*)\*/$)', $text, $matches)) {
// Multi line comment of the type // Multi line comment of the type
// //
// /* // /*
@ -177,7 +145,8 @@ class Comment implements \JsonSerializable
// */ on all lines. So if the last line is " */", then " " is removed at the // */ on all lines. So if the last line is " */", then " " is removed at the
// start of all lines. // start of all lines.
return preg_replace('(^' . preg_quote($matches[1]) . ')m', '', $text); return preg_replace('(^' . preg_quote($matches[1]) . ')m', '', $text);
} elseif (preg_match('(^/\*\*?\s*(?!\s))', $text, $matches)) { }
if (preg_match('(^/\*\*?\s*(?!\s))', $text, $matches)) {
// Multi line comment of the type // Multi line comment of the type
// //
// /* Some text. // /* Some text.
@ -206,7 +175,7 @@ class Comment implements \JsonSerializable
*/ */
private function getShortestWhitespacePrefixLen(string $str): int { private function getShortestWhitespacePrefixLen(string $str): int {
$lines = explode("\n", $str); $lines = explode("\n", $str);
$shortestPrefixLen = \INF; $shortestPrefixLen = \PHP_INT_MAX;
foreach ($lines as $line) { foreach ($lines as $line) {
preg_match('(^\s*)', $line, $matches); preg_match('(^\s*)', $line, $matches);
$prefixLen = strlen($matches[0]); $prefixLen = strlen($matches[0]);
@ -218,8 +187,7 @@ class Comment implements \JsonSerializable
} }
/** /**
* @return array * @return array{nodeType:string, text:mixed, line:mixed, filePos:mixed}
* @psalm-return array{nodeType:string, text:mixed, line:mixed, filePos:mixed}
*/ */
public function jsonSerialize(): array { public function jsonSerialize(): array {
// Technically not a node, but we make it look like one anyway // Technically not a node, but we make it look like one anyway

@ -2,6 +2,5 @@
namespace PhpParser\Comment; namespace PhpParser\Comment;
class Doc extends \PhpParser\Comment class Doc extends \PhpParser\Comment {
{
} }

@ -1,6 +1,6 @@
<?php <?php declare(strict_types=1);
namespace PhpParser; namespace PhpParser;
class ConstExprEvaluationException extends \Exception class ConstExprEvaluationException extends \Exception {
{} }

@ -1,11 +1,12 @@
<?php <?php declare(strict_types=1);
namespace PhpParser; namespace PhpParser;
use function array_merge;
use PhpParser\Node\Expr; use PhpParser\Node\Expr;
use PhpParser\Node\Scalar; use PhpParser\Node\Scalar;
use function array_merge;
/** /**
* Evaluates constant expressions. * Evaluates constant expressions.
* *
@ -25,8 +26,8 @@ use PhpParser\Node\Scalar;
* point to string conversions are affected by the precision ini setting. Secondly, they are also * point to string conversions are affected by the precision ini setting. Secondly, they are also
* affected by the LC_NUMERIC locale. * affected by the LC_NUMERIC locale.
*/ */
class ConstExprEvaluator class ConstExprEvaluator {
{ /** @var callable|null */
private $fallbackEvaluator; private $fallbackEvaluator;
/** /**
@ -37,7 +38,7 @@ class ConstExprEvaluator
* *
* @param callable|null $fallbackEvaluator To call if subexpression cannot be evaluated * @param callable|null $fallbackEvaluator To call if subexpression cannot be evaluated
*/ */
public function __construct(callable $fallbackEvaluator = null) { public function __construct(?callable $fallbackEvaluator = null) {
$this->fallbackEvaluator = $fallbackEvaluator ?? function (Expr $expr) { $this->fallbackEvaluator = $fallbackEvaluator ?? function (Expr $expr) {
throw new ConstExprEvaluationException( throw new ConstExprEvaluationException(
"Expression of type {$expr->getType()} cannot be evaluated" "Expression of type {$expr->getType()} cannot be evaluated"
@ -101,9 +102,10 @@ class ConstExprEvaluator
return $this->evaluate($expr); return $this->evaluate($expr);
} }
/** @return mixed */
private function evaluate(Expr $expr) { private function evaluate(Expr $expr) {
if ($expr instanceof Scalar\LNumber if ($expr instanceof Scalar\Int_
|| $expr instanceof Scalar\DNumber || $expr instanceof Scalar\Float_
|| $expr instanceof Scalar\String_ || $expr instanceof Scalar\String_
) { ) {
return $expr->value; return $expr->value;
@ -146,7 +148,7 @@ class ConstExprEvaluator
return ($this->fallbackEvaluator)($expr); return ($this->fallbackEvaluator)($expr);
} }
private function evaluateArray(Expr\Array_ $expr) { private function evaluateArray(Expr\Array_ $expr): array {
$array = []; $array = [];
foreach ($expr->items as $item) { foreach ($expr->items as $item) {
if (null !== $item->key) { if (null !== $item->key) {
@ -160,6 +162,7 @@ class ConstExprEvaluator
return $array; return $array;
} }
/** @return mixed */
private function evaluateTernary(Expr\Ternary $expr) { private function evaluateTernary(Expr\Ternary $expr) {
if (null === $expr->if) { if (null === $expr->if) {
return $this->evaluate($expr->cond) ?: $this->evaluate($expr->else); return $this->evaluate($expr->cond) ?: $this->evaluate($expr->else);
@ -170,6 +173,7 @@ class ConstExprEvaluator
: $this->evaluate($expr->else); : $this->evaluate($expr->else);
} }
/** @return mixed */
private function evaluateBinaryOp(Expr\BinaryOp $expr) { private function evaluateBinaryOp(Expr\BinaryOp $expr) {
if ($expr instanceof Expr\BinaryOp\Coalesce if ($expr instanceof Expr\BinaryOp\Coalesce
&& $expr->left instanceof Expr\ArrayDimFetch && $expr->left instanceof Expr\ArrayDimFetch
@ -216,6 +220,7 @@ class ConstExprEvaluator
throw new \Exception('Should not happen'); throw new \Exception('Should not happen');
} }
/** @return mixed */
private function evaluateConstFetch(Expr\ConstFetch $expr) { private function evaluateConstFetch(Expr\ConstFetch $expr) {
$name = $expr->name->toLowerString(); $name = $expr->name->toLowerString();
switch ($name) { switch ($name) {

@ -2,25 +2,20 @@
namespace PhpParser; namespace PhpParser;
class Error extends \RuntimeException class Error extends \RuntimeException {
{ protected string $rawMessage;
protected $rawMessage; /** @var array<string, mixed> */
protected $attributes; protected array $attributes;
/** /**
* Creates an Exception signifying a parse error. * Creates an Exception signifying a parse error.
* *
* @param string $message Error message * @param string $message Error message
* @param array|int $attributes Attributes of node/token where error occurred * @param array<string, mixed> $attributes Attributes of node/token where error occurred
* (or start line of error -- deprecated)
*/ */
public function __construct(string $message, $attributes = []) { public function __construct(string $message, array $attributes = []) {
$this->rawMessage = $message; $this->rawMessage = $message;
if (is_array($attributes)) {
$this->attributes = $attributes; $this->attributes = $attributes;
} else {
$this->attributes = ['startLine' => $attributes];
}
$this->updateMessage(); $this->updateMessage();
} }
@ -54,7 +49,7 @@ class Error extends \RuntimeException
/** /**
* Gets the attributes of the node/token the error occurred at. * Gets the attributes of the node/token the error occurred at.
* *
* @return array * @return array<string, mixed>
*/ */
public function getAttributes(): array { public function getAttributes(): array {
return $this->attributes; return $this->attributes;
@ -63,9 +58,9 @@ class Error extends \RuntimeException
/** /**
* Sets the attributes of the node/token the error occurred at. * Sets the attributes of the node/token the error occurred at.
* *
* @param array $attributes * @param array<string, mixed> $attributes
*/ */
public function setAttributes(array $attributes) { public function setAttributes(array $attributes): void {
$this->attributes = $attributes; $this->attributes = $attributes;
$this->updateMessage(); $this->updateMessage();
} }
@ -75,7 +70,7 @@ class Error extends \RuntimeException
* *
* @param string $message Error message * @param string $message Error message
*/ */
public function setRawMessage(string $message) { public function setRawMessage(string $message): void {
$this->rawMessage = $message; $this->rawMessage = $message;
$this->updateMessage(); $this->updateMessage();
} }
@ -85,7 +80,7 @@ class Error extends \RuntimeException
* *
* @param int $line Error start line * @param int $line Error start line
*/ */
public function setStartLine(int $line) { public function setStartLine(int $line): void {
$this->attributes['startLine'] = $line; $this->attributes['startLine'] = $line;
$this->updateMessage(); $this->updateMessage();
} }
@ -94,8 +89,6 @@ class Error extends \RuntimeException
* Returns whether the error has start and end column information. * Returns whether the error has start and end column information.
* *
* For column information enable the startFilePos and endFilePos in the lexer options. * For column information enable the startFilePos and endFilePos in the lexer options.
*
* @return bool
*/ */
public function hasColumnInfo(): bool { public function hasColumnInfo(): bool {
return isset($this->attributes['startFilePos'], $this->attributes['endFilePos']); return isset($this->attributes['startFilePos'], $this->attributes['endFilePos']);
@ -105,7 +98,6 @@ class Error extends \RuntimeException
* Gets the start column (1-based) into the line where the error started. * Gets the start column (1-based) into the line where the error started.
* *
* @param string $code Source code of the file * @param string $code Source code of the file
* @return int
*/ */
public function getStartColumn(string $code): int { public function getStartColumn(string $code): int {
if (!$this->hasColumnInfo()) { if (!$this->hasColumnInfo()) {
@ -119,7 +111,6 @@ class Error extends \RuntimeException
* Gets the end column (1-based) into the line where the error ended. * Gets the end column (1-based) into the line where the error ended.
* *
* @param string $code Source code of the file * @param string $code Source code of the file
* @return int
*/ */
public function getEndColumn(string $code): int { public function getEndColumn(string $code): int {
if (!$this->hasColumnInfo()) { if (!$this->hasColumnInfo()) {
@ -168,7 +159,7 @@ class Error extends \RuntimeException
/** /**
* Updates the exception message after a change to rawMessage or rawLine. * Updates the exception message after a change to rawMessage or rawLine.
*/ */
protected function updateMessage() { protected function updateMessage(): void {
$this->message = $this->rawMessage; $this->message = $this->rawMessage;
if (-1 === $this->getStartLine()) { if (-1 === $this->getStartLine()) {

@ -2,12 +2,11 @@
namespace PhpParser; namespace PhpParser;
interface ErrorHandler interface ErrorHandler {
{
/** /**
* Handle an error generated during lexing, parsing or some other operation. * Handle an error generated during lexing, parsing or some other operation.
* *
* @param Error $error The error that needs to be handled * @param Error $error The error that needs to be handled
*/ */
public function handleError(Error $error); public function handleError(Error $error): void;
} }

@ -10,12 +10,11 @@ use PhpParser\ErrorHandler;
* *
* This allows graceful handling of errors. * This allows graceful handling of errors.
*/ */
class Collecting implements ErrorHandler class Collecting implements ErrorHandler {
{
/** @var Error[] Collected errors */ /** @var Error[] Collected errors */
private $errors = []; private array $errors = [];
public function handleError(Error $error) { public function handleError(Error $error): void {
$this->errors[] = $error; $this->errors[] = $error;
} }
@ -30,8 +29,6 @@ class Collecting implements ErrorHandler
/** /**
* Check whether there are any errors. * Check whether there are any errors.
*
* @return bool
*/ */
public function hasErrors(): bool { public function hasErrors(): bool {
return !empty($this->errors); return !empty($this->errors);
@ -40,7 +37,7 @@ class Collecting implements ErrorHandler
/** /**
* Reset/clear collected errors. * Reset/clear collected errors.
*/ */
public function clearErrors() { public function clearErrors(): void {
$this->errors = []; $this->errors = [];
} }
} }

@ -10,9 +10,8 @@ use PhpParser\ErrorHandler;
* *
* This is the default strategy used by all components. * This is the default strategy used by all components.
*/ */
class Throwing implements ErrorHandler class Throwing implements ErrorHandler {
{ public function handleError(Error $error): void {
public function handleError(Error $error) {
throw $error; throw $error;
} }
} }

@ -5,20 +5,24 @@ namespace PhpParser\Internal;
/** /**
* @internal * @internal
*/ */
class DiffElem class DiffElem {
{ public const TYPE_KEEP = 0;
const TYPE_KEEP = 0; public const TYPE_REMOVE = 1;
const TYPE_REMOVE = 1; public const TYPE_ADD = 2;
const TYPE_ADD = 2; public const TYPE_REPLACE = 3;
const TYPE_REPLACE = 3;
/** @var int One of the TYPE_* constants */ /** @var int One of the TYPE_* constants */
public $type; public int $type;
/** @var mixed Is null for add operations */ /** @var mixed Is null for add operations */
public $old; public $old;
/** @var mixed Is null for remove operations */ /** @var mixed Is null for remove operations */
public $new; public $new;
/**
* @param int $type One of the TYPE_* constants
* @param mixed $old Is null for add operations
* @param mixed $new Is null for remove operations
*/
public function __construct(int $type, $old, $new) { public function __construct(int $type, $old, $new) {
$this->type = $type; $this->type = $type;
$this->old = $old; $this->old = $old;

@ -8,16 +8,17 @@ namespace PhpParser\Internal;
* Myers, Eugene W. "An O (ND) difference algorithm and its variations." * Myers, Eugene W. "An O (ND) difference algorithm and its variations."
* Algorithmica 1.1 (1986): 251-266. * Algorithmica 1.1 (1986): 251-266.
* *
* @template T
* @internal * @internal
*/ */
class Differ class Differ {
{ /** @var callable(T, T): bool */
private $isEqual; private $isEqual;
/** /**
* Create differ over the given equality relation. * Create differ over the given equality relation.
* *
* @param callable $isEqual Equality relation with signature function($a, $b) : bool * @param callable(T, T): bool $isEqual Equality relation
*/ */
public function __construct(callable $isEqual) { public function __construct(callable $isEqual) {
$this->isEqual = $isEqual; $this->isEqual = $isEqual;
@ -26,12 +27,14 @@ class Differ
/** /**
* Calculate diff (edit script) from $old to $new. * Calculate diff (edit script) from $old to $new.
* *
* @param array $old Original array * @param T[] $old Original array
* @param array $new New array * @param T[] $new New array
* *
* @return DiffElem[] Diff (edit script) * @return DiffElem[] Diff (edit script)
*/ */
public function diff(array $old, array $new) { public function diff(array $old, array $new): array {
$old = \array_values($old);
$new = \array_values($new);
list($trace, $x, $y) = $this->calculateTrace($old, $new); list($trace, $x, $y) = $this->calculateTrace($old, $new);
return $this->extractDiff($trace, $x, $y, $old, $new); return $this->extractDiff($trace, $x, $y, $old, $new);
} }
@ -42,18 +45,23 @@ class Differ
* If a sequence of remove operations is followed by the same number of add operations, these * If a sequence of remove operations is followed by the same number of add operations, these
* will be coalesced into replace operations. * will be coalesced into replace operations.
* *
* @param array $old Original array * @param T[] $old Original array
* @param array $new New array * @param T[] $new New array
* *
* @return DiffElem[] Diff (edit script), including replace operations * @return DiffElem[] Diff (edit script), including replace operations
*/ */
public function diffWithReplacements(array $old, array $new) { public function diffWithReplacements(array $old, array $new): array {
return $this->coalesceReplacements($this->diff($old, $new)); return $this->coalesceReplacements($this->diff($old, $new));
} }
private function calculateTrace(array $a, array $b) { /**
$n = \count($a); * @param T[] $old
$m = \count($b); * @param T[] $new
* @return array{array<int, array<int, int>>, int, int}
*/
private function calculateTrace(array $old, array $new): array {
$n = \count($old);
$m = \count($new);
$max = $n + $m; $max = $n + $m;
$v = [1 => 0]; $v = [1 => 0];
$trace = []; $trace = [];
@ -67,7 +75,7 @@ class Differ
} }
$y = $x - $k; $y = $x - $k;
while ($x < $n && $y < $m && ($this->isEqual)($a[$x], $b[$y])) { while ($x < $n && $y < $m && ($this->isEqual)($old[$x], $new[$y])) {
$x++; $x++;
$y++; $y++;
} }
@ -81,7 +89,13 @@ class Differ
throw new \Exception('Should not happen'); throw new \Exception('Should not happen');
} }
private function extractDiff(array $trace, int $x, int $y, array $a, array $b) { /**
* @param array<int, array<int, int>> $trace
* @param T[] $old
* @param T[] $new
* @return DiffElem[]
*/
private function extractDiff(array $trace, int $x, int $y, array $old, array $new): array {
$result = []; $result = [];
for ($d = \count($trace) - 1; $d >= 0; $d--) { for ($d = \count($trace) - 1; $d >= 0; $d--) {
$v = $trace[$d]; $v = $trace[$d];
@ -97,7 +111,7 @@ class Differ
$prevY = $prevX - $prevK; $prevY = $prevX - $prevK;
while ($x > $prevX && $y > $prevY) { while ($x > $prevX && $y > $prevY) {
$result[] = new DiffElem(DiffElem::TYPE_KEEP, $a[$x-1], $b[$y-1]); $result[] = new DiffElem(DiffElem::TYPE_KEEP, $old[$x - 1], $new[$y - 1]);
$x--; $x--;
$y--; $y--;
} }
@ -107,12 +121,12 @@ class Differ
} }
while ($x > $prevX) { while ($x > $prevX) {
$result[] = new DiffElem(DiffElem::TYPE_REMOVE, $a[$x-1], null); $result[] = new DiffElem(DiffElem::TYPE_REMOVE, $old[$x - 1], null);
$x--; $x--;
} }
while ($y > $prevY) { while ($y > $prevY) {
$result[] = new DiffElem(DiffElem::TYPE_ADD, null, $b[$y-1]); $result[] = new DiffElem(DiffElem::TYPE_ADD, null, $new[$y - 1]);
$y--; $y--;
} }
} }
@ -125,7 +139,7 @@ class Differ
* @param DiffElem[] $diff * @param DiffElem[] $diff
* @return DiffElem[] * @return DiffElem[]
*/ */
private function coalesceReplacements(array $diff) { private function coalesceReplacements(array $diff): array {
$newDiff = []; $newDiff = [];
$c = \count($diff); $c = \count($diff);
for ($i = 0; $i < $c; $i++) { for ($i = 0; $i < $c; $i++) {

@ -15,23 +15,30 @@ use PhpParser\Node\Expr;
* *
* @internal * @internal
*/ */
class PrintableNewAnonClassNode extends Expr class PrintableNewAnonClassNode extends Expr {
{
/** @var Node\AttributeGroup[] PHP attribute groups */ /** @var Node\AttributeGroup[] PHP attribute groups */
public $attrGroups; public array $attrGroups;
/** @var int Modifiers */ /** @var int Modifiers */
public $flags; public int $flags;
/** @var Node\Arg[] Arguments */ /** @var (Node\Arg|Node\VariadicPlaceholder)[] Arguments */
public $args; public array $args;
/** @var null|Node\Name Name of extended class */ /** @var null|Node\Name Name of extended class */
public $extends; public ?Node\Name $extends;
/** @var Node\Name[] Names of implemented interfaces */ /** @var Node\Name[] Names of implemented interfaces */
public $implements; public array $implements;
/** @var Node\Stmt[] Statements */ /** @var Node\Stmt[] Statements */
public $stmts; public array $stmts;
/**
* @param Node\AttributeGroup[] $attrGroups PHP attribute groups
* @param (Node\Arg|Node\VariadicPlaceholder)[] $args Arguments
* @param Node\Name|null $extends Name of extended class
* @param Node\Name[] $implements Names of implemented interfaces
* @param Node\Stmt[] $stmts Statements
* @param array<string, mixed> $attributes Attributes
*/
public function __construct( public function __construct(
array $attrGroups, int $flags, array $args, Node\Name $extends = null, array $implements, array $attrGroups, int $flags, array $args, ?Node\Name $extends, array $implements,
array $stmts, array $attributes array $stmts, array $attributes
) { ) {
parent::__construct($attributes); parent::__construct($attributes);
@ -43,7 +50,7 @@ class PrintableNewAnonClassNode extends Expr
$this->stmts = $stmts; $this->stmts = $stmts;
} }
public static function fromNewNode(Expr\New_ $newNode) { public static function fromNewNode(Expr\New_ $newNode): self {
$class = $newNode->class; $class = $newNode->class;
assert($class instanceof Node\Stmt\Class_); assert($class instanceof Node\Stmt\Class_);
// We don't assert that $class->name is null here, to allow consumers to assign unique names // We don't assert that $class->name is null here, to allow consumers to assign unique names

@ -0,0 +1,237 @@
<?php declare(strict_types=1);
namespace PhpParser\Internal;
if (\PHP_VERSION_ID >= 80000) {
class TokenPolyfill extends \PhpToken {
}
return;
}
/**
* This is a polyfill for the PhpToken class introduced in PHP 8.0. We do not actually polyfill
* PhpToken, because composer might end up picking a different polyfill implementation, which does
* not meet our requirements.
*
* @internal
*/
class TokenPolyfill {
/** @var int The ID of the token. Either a T_* constant of a character code < 256. */
public int $id;
/** @var string The textual content of the token. */
public string $text;
/** @var int The 1-based starting line of the token (or -1 if unknown). */
public int $line;
/** @var int The 0-based starting position of the token (or -1 if unknown). */
public int $pos;
/** @var array<int, bool> Tokens ignored by the PHP parser. */
private const IGNORABLE_TOKENS = [
\T_WHITESPACE => true,
\T_COMMENT => true,
\T_DOC_COMMENT => true,
\T_OPEN_TAG => true,
];
/** @var array<int, bool> Tokens that may be part of a T_NAME_* identifier. */
private static array $identifierTokens;
/**
* Create a Token with the given ID and text, as well optional line and position information.
*/
final public function __construct(int $id, string $text, int $line = -1, int $pos = -1) {
$this->id = $id;
$this->text = $text;
$this->line = $line;
$this->pos = $pos;
}
/**
* Get the name of the token. For single-char tokens this will be the token character.
* Otherwise it will be a T_* style name, or null if the token ID is unknown.
*/
public function getTokenName(): ?string {
if ($this->id < 256) {
return \chr($this->id);
}
$name = token_name($this->id);
return $name === 'UNKNOWN' ? null : $name;
}
/**
* Check whether the token is of the given kind. The kind may be either an integer that matches
* the token ID, a string that matches the token text, or an array of integers/strings. In the
* latter case, the function returns true if any of the kinds in the array match.
*
* @param int|string|(int|string)[] $kind
*/
public function is($kind): bool {
if (\is_int($kind)) {
return $this->id === $kind;
}
if (\is_string($kind)) {
return $this->text === $kind;
}
if (\is_array($kind)) {
foreach ($kind as $entry) {
if (\is_int($entry)) {
if ($this->id === $entry) {
return true;
}
} elseif (\is_string($entry)) {
if ($this->text === $entry) {
return true;
}
} else {
throw new \TypeError(
'Argument #1 ($kind) must only have elements of type string|int, ' .
gettype($entry) . ' given');
}
}
return false;
}
throw new \TypeError(
'Argument #1 ($kind) must be of type string|int|array, ' .gettype($kind) . ' given');
}
/**
* Check whether this token would be ignored by the PHP parser. Returns true for T_WHITESPACE,
* T_COMMENT, T_DOC_COMMENT and T_OPEN_TAG, and false for everything else.
*/
public function isIgnorable(): bool {
return isset(self::IGNORABLE_TOKENS[$this->id]);
}
/**
* Return the textual content of the token.
*/
public function __toString(): string {
return $this->text;
}
/**
* Tokenize the given source code and return an array of tokens.
*
* This performs certain canonicalizations to match the PHP 8.0 token format:
* * Bad characters are represented using T_BAD_CHARACTER rather than omitted.
* * T_COMMENT does not include trailing newlines, instead the newline is part of a following
* T_WHITESPACE token.
* * Namespaced names are represented using T_NAME_* tokens.
*
* @return static[]
*/
public static function tokenize(string $code, int $flags = 0): array {
self::init();
$tokens = [];
$line = 1;
$pos = 0;
$origTokens = \token_get_all($code, $flags);
$numTokens = \count($origTokens);
for ($i = 0; $i < $numTokens; $i++) {
$token = $origTokens[$i];
if (\is_string($token)) {
if (\strlen($token) === 2) {
// b" and B" are tokenized as single-char tokens, even though they aren't.
$tokens[] = new static(\ord('"'), $token, $line, $pos);
$pos += 2;
} else {
$tokens[] = new static(\ord($token), $token, $line, $pos);
$pos++;
}
} else {
$id = $token[0];
$text = $token[1];
// Emulate PHP 8.0 comment format, which does not include trailing whitespace anymore.
if ($id === \T_COMMENT && \substr($text, 0, 2) !== '/*' &&
\preg_match('/(\r\n|\n|\r)$/D', $text, $matches)
) {
$trailingNewline = $matches[0];
$text = \substr($text, 0, -\strlen($trailingNewline));
$tokens[] = new static($id, $text, $line, $pos);
$pos += \strlen($text);
if ($i + 1 < $numTokens && $origTokens[$i + 1][0] === \T_WHITESPACE) {
// Move trailing newline into following T_WHITESPACE token, if it already exists.
$origTokens[$i + 1][1] = $trailingNewline . $origTokens[$i + 1][1];
$origTokens[$i + 1][2]--;
} else {
// Otherwise, we need to create a new T_WHITESPACE token.
$tokens[] = new static(\T_WHITESPACE, $trailingNewline, $line, $pos);
$line++;
$pos += \strlen($trailingNewline);
}
continue;
}
// Emulate PHP 8.0 T_NAME_* tokens, by combining sequences of T_NS_SEPARATOR and
// T_STRING into a single token.
if (($id === \T_NS_SEPARATOR || isset(self::$identifierTokens[$id]))) {
$newText = $text;
$lastWasSeparator = $id === \T_NS_SEPARATOR;
for ($j = $i + 1; $j < $numTokens; $j++) {
if ($lastWasSeparator) {
if (!isset(self::$identifierTokens[$origTokens[$j][0]])) {
break;
}
$lastWasSeparator = false;
} else {
if ($origTokens[$j][0] !== \T_NS_SEPARATOR) {
break;
}
$lastWasSeparator = true;
}
$newText .= $origTokens[$j][1];
}
if ($lastWasSeparator) {
// Trailing separator is not part of the name.
$j--;
$newText = \substr($newText, 0, -1);
}
if ($j > $i + 1) {
if ($id === \T_NS_SEPARATOR) {
$id = \T_NAME_FULLY_QUALIFIED;
} elseif ($id === \T_NAMESPACE) {
$id = \T_NAME_RELATIVE;
} else {
$id = \T_NAME_QUALIFIED;
}
$tokens[] = new static($id, $newText, $line, $pos);
$pos += \strlen($newText);
$i = $j - 1;
continue;
}
}
$tokens[] = new static($id, $text, $line, $pos);
$line += \substr_count($text, "\n");
$pos += \strlen($text);
}
}
return $tokens;
}
/** Initialize private static state needed by tokenize(). */
private static function init(): void {
if (isset(self::$identifierTokens)) {
return;
}
// Based on semi_reserved production.
self::$identifierTokens = \array_fill_keys([
\T_STRING,
\T_STATIC, \T_ABSTRACT, \T_FINAL, \T_PRIVATE, \T_PROTECTED, \T_PUBLIC, \T_READONLY,
\T_INCLUDE, \T_INCLUDE_ONCE, \T_EVAL, \T_REQUIRE, \T_REQUIRE_ONCE, \T_LOGICAL_OR, \T_LOGICAL_XOR, \T_LOGICAL_AND,
\T_INSTANCEOF, \T_NEW, \T_CLONE, \T_EXIT, \T_IF, \T_ELSEIF, \T_ELSE, \T_ENDIF, \T_ECHO, \T_DO, \T_WHILE,
\T_ENDWHILE, \T_FOR, \T_ENDFOR, \T_FOREACH, \T_ENDFOREACH, \T_DECLARE, \T_ENDDECLARE, \T_AS, \T_TRY, \T_CATCH,
\T_FINALLY, \T_THROW, \T_USE, \T_INSTEADOF, \T_GLOBAL, \T_VAR, \T_UNSET, \T_ISSET, \T_EMPTY, \T_CONTINUE, \T_GOTO,
\T_FUNCTION, \T_CONST, \T_RETURN, \T_PRINT, \T_YIELD, \T_LIST, \T_SWITCH, \T_ENDSWITCH, \T_CASE, \T_DEFAULT,
\T_BREAK, \T_ARRAY, \T_CALLABLE, \T_EXTENDS, \T_IMPLEMENTS, \T_NAMESPACE, \T_TRAIT, \T_INTERFACE, \T_CLASS,
\T_CLASS_C, \T_TRAIT_C, \T_FUNC_C, \T_METHOD_C, \T_LINE, \T_FILE, \T_DIR, \T_NS_C, \T_HALT_COMPILER, \T_FN,
\T_MATCH,
], true);
}
}

@ -2,22 +2,23 @@
namespace PhpParser\Internal; namespace PhpParser\Internal;
use PhpParser\Token;
/** /**
* Provides operations on token streams, for use by pretty printer. * Provides operations on token streams, for use by pretty printer.
* *
* @internal * @internal
*/ */
class TokenStream class TokenStream {
{ /** @var Token[] Tokens (in PhpToken::tokenize() format) */
/** @var array Tokens (in token_get_all format) */ private array $tokens;
private $tokens;
/** @var int[] Map from position to indentation */ /** @var int[] Map from position to indentation */
private $indentMap; private array $indentMap;
/** /**
* Create token stream instance. * Create token stream instance.
* *
* @param array $tokens Tokens in token_get_all() format * @param Token[] $tokens Tokens in PhpToken::tokenize() format
*/ */
public function __construct(array $tokens) { public function __construct(array $tokens) {
$this->tokens = $tokens; $this->tokens = $tokens;
@ -29,8 +30,6 @@ class TokenStream
* *
* @param int $startPos Start position * @param int $startPos Start position
* @param int $endPos End position * @param int $endPos End position
*
* @return bool
*/ */
public function haveParens(int $startPos, int $endPos): bool { public function haveParens(int $startPos, int $endPos): bool {
return $this->haveTokenImmediatelyBefore($startPos, '(') return $this->haveTokenImmediatelyBefore($startPos, '(')
@ -42,8 +41,6 @@ class TokenStream
* *
* @param int $startPos Start position * @param int $startPos Start position
* @param int $endPos End position * @param int $endPos End position
*
* @return bool
*/ */
public function haveBraces(int $startPos, int $endPos): bool { public function haveBraces(int $startPos, int $endPos): bool {
return ($this->haveTokenImmediatelyBefore($startPos, '{') return ($this->haveTokenImmediatelyBefore($startPos, '{')
@ -65,12 +62,11 @@ class TokenStream
$tokens = $this->tokens; $tokens = $this->tokens;
$pos--; $pos--;
for (; $pos >= 0; $pos--) { for (; $pos >= 0; $pos--) {
$tokenType = $tokens[$pos][0]; $token = $tokens[$pos];
if ($tokenType === $expectedTokenType) { if ($token->is($expectedTokenType)) {
return true; return true;
} }
if ($tokenType !== \T_WHITESPACE if (!$token->isIgnorable()) {
&& $tokenType !== \T_COMMENT && $tokenType !== \T_DOC_COMMENT) {
break; break;
} }
} }
@ -90,20 +86,20 @@ class TokenStream
public function haveTokenImmediatelyAfter(int $pos, $expectedTokenType): bool { public function haveTokenImmediatelyAfter(int $pos, $expectedTokenType): bool {
$tokens = $this->tokens; $tokens = $this->tokens;
$pos++; $pos++;
for (; $pos < \count($tokens); $pos++) { for ($c = \count($tokens); $pos < $c; $pos++) {
$tokenType = $tokens[$pos][0]; $token = $tokens[$pos];
if ($tokenType === $expectedTokenType) { if ($token->is($expectedTokenType)) {
return true; return true;
} }
if ($tokenType !== \T_WHITESPACE if (!$token->isIgnorable()) {
&& $tokenType !== \T_COMMENT && $tokenType !== \T_DOC_COMMENT) {
break; break;
} }
} }
return false; return false;
} }
public function skipLeft(int $pos, $skipTokenType) { /** @param int|string|(int|string)[] $skipTokenType */
public function skipLeft(int $pos, $skipTokenType): int {
$tokens = $this->tokens; $tokens = $this->tokens;
$pos = $this->skipLeftWhitespace($pos); $pos = $this->skipLeftWhitespace($pos);
@ -111,7 +107,7 @@ class TokenStream
return $pos; return $pos;
} }
if ($tokens[$pos][0] !== $skipTokenType) { if (!$tokens[$pos]->is($skipTokenType)) {
// Shouldn't happen. The skip token MUST be there // Shouldn't happen. The skip token MUST be there
throw new \Exception('Encountered unexpected token'); throw new \Exception('Encountered unexpected token');
} }
@ -120,7 +116,8 @@ class TokenStream
return $this->skipLeftWhitespace($pos); return $this->skipLeftWhitespace($pos);
} }
public function skipRight(int $pos, $skipTokenType) { /** @param int|string|(int|string)[] $skipTokenType */
public function skipRight(int $pos, $skipTokenType): int {
$tokens = $this->tokens; $tokens = $this->tokens;
$pos = $this->skipRightWhitespace($pos); $pos = $this->skipRightWhitespace($pos);
@ -128,7 +125,7 @@ class TokenStream
return $pos; return $pos;
} }
if ($tokens[$pos][0] !== $skipTokenType) { if (!$tokens[$pos]->is($skipTokenType)) {
// Shouldn't happen. The skip token MUST be there // Shouldn't happen. The skip token MUST be there
throw new \Exception('Encountered unexpected token'); throw new \Exception('Encountered unexpected token');
} }
@ -143,11 +140,10 @@ class TokenStream
* @param int $pos Token position * @param int $pos Token position
* @return int Non-whitespace token position * @return int Non-whitespace token position
*/ */
public function skipLeftWhitespace(int $pos) { public function skipLeftWhitespace(int $pos): int {
$tokens = $this->tokens; $tokens = $this->tokens;
for (; $pos >= 0; $pos--) { for (; $pos >= 0; $pos--) {
$type = $tokens[$pos][0]; if (!$tokens[$pos]->isIgnorable()) {
if ($type !== \T_WHITESPACE && $type !== \T_COMMENT && $type !== \T_DOC_COMMENT) {
break; break;
} }
} }
@ -160,22 +156,21 @@ class TokenStream
* @param int $pos Token position * @param int $pos Token position
* @return int Non-whitespace token position * @return int Non-whitespace token position
*/ */
public function skipRightWhitespace(int $pos) { public function skipRightWhitespace(int $pos): int {
$tokens = $this->tokens; $tokens = $this->tokens;
for ($count = \count($tokens); $pos < $count; $pos++) { for ($count = \count($tokens); $pos < $count; $pos++) {
$type = $tokens[$pos][0]; if (!$tokens[$pos]->isIgnorable()) {
if ($type !== \T_WHITESPACE && $type !== \T_COMMENT && $type !== \T_DOC_COMMENT) {
break; break;
} }
} }
return $pos; return $pos;
} }
public function findRight(int $pos, $findTokenType) { /** @param int|string|(int|string)[] $findTokenType */
public function findRight(int $pos, $findTokenType): int {
$tokens = $this->tokens; $tokens = $this->tokens;
for ($count = \count($tokens); $pos < $count; $pos++) { for ($count = \count($tokens); $pos < $count; $pos++) {
$type = $tokens[$pos][0]; if ($tokens[$pos]->is($findTokenType)) {
if ($type === $findTokenType) {
return $pos; return $pos;
} }
} }
@ -190,22 +185,16 @@ class TokenStream
* @param int|string $tokenType Token type to look for * @param int|string $tokenType Token type to look for
* @return bool Whether the token occurs in the given range * @return bool Whether the token occurs in the given range
*/ */
public function haveTokenInRange(int $startPos, int $endPos, $tokenType) { public function haveTokenInRange(int $startPos, int $endPos, $tokenType): bool {
$tokens = $this->tokens; $tokens = $this->tokens;
for ($pos = $startPos; $pos < $endPos; $pos++) { for ($pos = $startPos; $pos < $endPos; $pos++) {
if ($tokens[$pos][0] === $tokenType) { if ($tokens[$pos]->is($tokenType)) {
return true; return true;
} }
} }
return false; return false;
} }
public function haveBracesInRange(int $startPos, int $endPos) {
return $this->haveTokenInRange($startPos, $endPos, '{')
|| $this->haveTokenInRange($startPos, $endPos, T_CURLY_OPEN)
|| $this->haveTokenInRange($startPos, $endPos, '}');
}
public function haveTagInRange(int $startPos, int $endPos): bool { public function haveTagInRange(int $startPos, int $endPos): bool {
return $this->haveTokenInRange($startPos, $endPos, \T_OPEN_TAG) return $this->haveTokenInRange($startPos, $endPos, \T_OPEN_TAG)
|| $this->haveTokenInRange($startPos, $endPos, \T_CLOSE_TAG); || $this->haveTokenInRange($startPos, $endPos, \T_CLOSE_TAG);
@ -236,24 +225,20 @@ class TokenStream
$result = ''; $result = '';
for ($pos = $from; $pos < $to; $pos++) { for ($pos = $from; $pos < $to; $pos++) {
$token = $tokens[$pos]; $token = $tokens[$pos];
if (\is_array($token)) { $id = $token->id;
$type = $token[0]; $text = $token->text;
$content = $token[1]; if ($id === \T_CONSTANT_ENCAPSED_STRING || $id === \T_ENCAPSED_AND_WHITESPACE) {
if ($type === \T_CONSTANT_ENCAPSED_STRING || $type === \T_ENCAPSED_AND_WHITESPACE) { $result .= $text;
$result .= $content;
} else { } else {
// TODO Handle non-space indentation // TODO Handle non-space indentation
if ($indent < 0) { if ($indent < 0) {
$result .= str_replace("\n" . str_repeat(" ", -$indent), "\n", $content); $result .= str_replace("\n" . str_repeat(" ", -$indent), "\n", $text);
} elseif ($indent > 0) { } elseif ($indent > 0) {
$result .= str_replace("\n", "\n" . str_repeat(" ", $indent), $content); $result .= str_replace("\n", "\n" . str_repeat(" ", $indent), $text);
} else { } else {
$result .= $content; $result .= $text;
} }
} }
} else {
$result .= $token;
}
} }
return $result; return $result;
} }
@ -263,14 +248,14 @@ class TokenStream
* *
* @return int[] Token position to indentation map * @return int[] Token position to indentation map
*/ */
private function calcIndentMap() { private function calcIndentMap(): array {
$indentMap = []; $indentMap = [];
$indent = 0; $indent = 0;
foreach ($this->tokens as $token) { foreach ($this->tokens as $token) {
$indentMap[] = $indent; $indentMap[] = $indent;
if ($token[0] === \T_WHITESPACE) { if ($token->id === \T_WHITESPACE) {
$content = $token[1]; $content = $token->text;
$newlinePos = \strrpos($content, "\n"); $newlinePos = \strrpos($content, "\n");
if (false !== $newlinePos) { if (false !== $newlinePos) {
$indent = \strlen($content) - $newlinePos - 1; $indent = \strlen($content) - $newlinePos - 1;

@ -2,11 +2,11 @@
namespace PhpParser; namespace PhpParser;
class JsonDecoder class JsonDecoder {
{ /** @var \ReflectionClass<Node>[] Node type to reflection class map */
/** @var \ReflectionClass[] Node type to reflection class map */ private array $reflectionClassCache;
private $reflectionClassCache;
/** @return mixed */
public function decode(string $json) { public function decode(string $json) {
$value = json_decode($json, true); $value = json_decode($json, true);
if (json_last_error()) { if (json_last_error()) {
@ -16,6 +16,10 @@ class JsonDecoder
return $this->decodeRecursive($value); return $this->decodeRecursive($value);
} }
/**
* @param mixed $value
* @return mixed
*/
private function decodeRecursive($value) { private function decodeRecursive($value) {
if (\is_array($value)) { if (\is_array($value)) {
if (isset($value['nodeType'])) { if (isset($value['nodeType'])) {
@ -44,7 +48,6 @@ class JsonDecoder
} }
$reflectionClass = $this->reflectionClassFromNodeType($nodeType); $reflectionClass = $this->reflectionClassFromNodeType($nodeType);
/** @var Node $node */
$node = $reflectionClass->newInstanceWithoutConstructor(); $node = $reflectionClass->newInstanceWithoutConstructor();
if (isset($value['attributes'])) { if (isset($value['attributes'])) {
@ -79,6 +82,7 @@ class JsonDecoder
); );
} }
/** @return \ReflectionClass<Node> */
private function reflectionClassFromNodeType(string $nodeType): \ReflectionClass { private function reflectionClassFromNodeType(string $nodeType): \ReflectionClass {
if (!isset($this->reflectionClassCache[$nodeType])) { if (!isset($this->reflectionClassCache[$nodeType])) {
$className = $this->classNameFromNodeType($nodeType); $className = $this->classNameFromNodeType($nodeType);
@ -87,6 +91,7 @@ class JsonDecoder
return $this->reflectionClassCache[$nodeType]; return $this->reflectionClassCache[$nodeType];
} }
/** @return class-string<Node> */
private function classNameFromNodeType(string $nodeType): string { private function classNameFromNodeType(string $nodeType): string {
$className = 'PhpParser\\Node\\' . strtr($nodeType, '_', '\\'); $className = 'PhpParser\\Node\\' . strtr($nodeType, '_', '\\');
if (class_exists($className)) { if (class_exists($className)) {

@ -2,101 +2,44 @@
namespace PhpParser; namespace PhpParser;
use PhpParser\Parser\Tokens; require __DIR__ . '/compatibility_tokens.php';
class Lexer
{
protected $code;
protected $tokens;
protected $pos;
protected $line;
protected $filePos;
protected $prevCloseTagHasNewline;
protected $tokenMap;
protected $dropTokens;
protected $identifierTokens;
private $attributeStartLineUsed;
private $attributeEndLineUsed;
private $attributeStartTokenPosUsed;
private $attributeEndTokenPosUsed;
private $attributeStartFilePosUsed;
private $attributeEndFilePosUsed;
private $attributeCommentsUsed;
class Lexer {
/** /**
* Creates a Lexer. * Tokenize the provided source code.
* *
* @param array $options Options array. Currently only the 'usedAttributes' option is supported, * The token array is in the same format as provided by the PhpToken::tokenize() method in
* which is an array of attributes to add to the AST nodes. Possible * PHP 8.0. The tokens are instances of PhpParser\Token, to abstract over a polyfill
* attributes are: 'comments', 'startLine', 'endLine', 'startTokenPos', * implementation in earlier PHP version.
* 'endTokenPos', 'startFilePos', 'endFilePos'. The option defaults to the
* first three. For more info see getNextToken() docs.
*/
public function __construct(array $options = []) {
// Create Map from internal tokens to PhpParser tokens.
$this->defineCompatibilityTokens();
$this->tokenMap = $this->createTokenMap();
$this->identifierTokens = $this->createIdentifierTokenMap();
// map of tokens to drop while lexing (the map is only used for isset lookup,
// that's why the value is simply set to 1; the value is never actually used.)
$this->dropTokens = array_fill_keys(
[\T_WHITESPACE, \T_OPEN_TAG, \T_COMMENT, \T_DOC_COMMENT, \T_BAD_CHARACTER], 1
);
$defaultAttributes = ['comments', 'startLine', 'endLine'];
$usedAttributes = array_fill_keys($options['usedAttributes'] ?? $defaultAttributes, true);
// Create individual boolean properties to make these checks faster.
$this->attributeStartLineUsed = isset($usedAttributes['startLine']);
$this->attributeEndLineUsed = isset($usedAttributes['endLine']);
$this->attributeStartTokenPosUsed = isset($usedAttributes['startTokenPos']);
$this->attributeEndTokenPosUsed = isset($usedAttributes['endTokenPos']);
$this->attributeStartFilePosUsed = isset($usedAttributes['startFilePos']);
$this->attributeEndFilePosUsed = isset($usedAttributes['endFilePos']);
$this->attributeCommentsUsed = isset($usedAttributes['comments']);
}
/**
* Initializes the lexer for lexing the provided source code.
* *
* This function does not throw if lexing errors occur. Instead, errors may be retrieved using * The token array is terminated by a sentinel token with token ID 0.
* the getErrors() method. * The token array does not discard any tokens (i.e. whitespace and comments are included).
* The token position attributes are against this token array.
* *
* @param string $code The source code to lex * @param string $code The source code to tokenize.
* @param ErrorHandler|null $errorHandler Error handler to use for lexing errors. Defaults to * @param ErrorHandler|null $errorHandler Error handler to use for lexing errors. Defaults to
* ErrorHandler\Throwing * ErrorHandler\Throwing.
* @return Token[] Tokens
*/ */
public function startLexing(string $code, ErrorHandler $errorHandler = null) { public function tokenize(string $code, ?ErrorHandler $errorHandler = null): array {
if (null === $errorHandler) { if (null === $errorHandler) {
$errorHandler = new ErrorHandler\Throwing(); $errorHandler = new ErrorHandler\Throwing();
} }
$this->code = $code; // keep the code around for __halt_compiler() handling
$this->pos = -1;
$this->line = 1;
$this->filePos = 0;
// If inline HTML occurs without preceding code, treat it as if it had a leading newline.
// This ensures proper composability, because having a newline is the "safe" assumption.
$this->prevCloseTagHasNewline = true;
$scream = ini_set('xdebug.scream', '0'); $scream = ini_set('xdebug.scream', '0');
$this->tokens = @token_get_all($code); $tokens = @Token::tokenize($code);
$this->postprocessTokens($errorHandler); $this->postprocessTokens($tokens, $errorHandler);
if (false !== $scream) { if (false !== $scream) {
ini_set('xdebug.scream', $scream); ini_set('xdebug.scream', $scream);
} }
return $tokens;
} }
private function handleInvalidCharacterRange($start, $end, $line, ErrorHandler $errorHandler) { private function handleInvalidCharacter(Token $token, ErrorHandler $errorHandler): void {
$tokens = []; $chr = $token->text;
for ($i = $start; $i < $end; $i++) {
$chr = $this->code[$i];
if ($chr === "\0") { if ($chr === "\0") {
// PHP cuts error message after null byte, so need special case // PHP cuts error message after null byte, so need special case
$errorMsg = 'Unexpected null byte'; $errorMsg = 'Unexpected null byte';
@ -106,455 +49,68 @@ class Lexer
); );
} }
$tokens[] = [\T_BAD_CHARACTER, $chr, $line];
$errorHandler->handleError(new Error($errorMsg, [ $errorHandler->handleError(new Error($errorMsg, [
'startLine' => $line, 'startLine' => $token->line,
'endLine' => $line, 'endLine' => $token->line,
'startFilePos' => $i, 'startFilePos' => $token->pos,
'endFilePos' => $i, 'endFilePos' => $token->pos,
])); ]));
} }
return $tokens;
private function isUnterminatedComment(Token $token): bool {
return $token->is([\T_COMMENT, \T_DOC_COMMENT])
&& substr($token->text, 0, 2) === '/*'
&& substr($token->text, -2) !== '*/';
} }
/** /**
* Check whether comment token is unterminated. * @param list<Token> $tokens
*
* @return bool
*/ */
private function isUnterminatedComment($token) : bool { protected function postprocessTokens(array &$tokens, ErrorHandler $errorHandler): void {
return ($token[0] === \T_COMMENT || $token[0] === \T_DOC_COMMENT) // This function reports errors (bad characters and unterminated comments) in the token
&& substr($token[1], 0, 2) === '/*' // array, and performs certain canonicalizations:
&& substr($token[1], -2) !== '*/';
}
protected function postprocessTokens(ErrorHandler $errorHandler) {
// PHP's error handling for token_get_all() is rather bad, so if we want detailed
// error information we need to compute it ourselves. Invalid character errors are
// detected by finding "gaps" in the token array. Unterminated comments are detected
// by checking if a trailing comment has a "*/" at the end.
//
// Additionally, we perform a number of canonicalizations here:
// * Use the PHP 8.0 comment format, which does not include trailing whitespace anymore.
// * Use PHP 8.0 T_NAME_* tokens.
// * Use PHP 8.1 T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG and // * Use PHP 8.1 T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG and
// T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG tokens used to disambiguate intersection types. // T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG tokens used to disambiguate intersection types.
// * Add a sentinel token with ID 0.
$filePos = 0; $numTokens = \count($tokens);
$line = 1; if ($numTokens === 0) {
$numTokens = \count($this->tokens); // Empty input edge case: Just add the sentinel token.
for ($i = 0; $i < $numTokens; $i++) { $tokens[] = new Token(0, "\0", 1, 0);
$token = $this->tokens[$i]; return;
// Since PHP 7.4 invalid characters are represented by a T_BAD_CHARACTER token.
// In this case we only need to emit an error.
if ($token[0] === \T_BAD_CHARACTER) {
$this->handleInvalidCharacterRange($filePos, $filePos + 1, $line, $errorHandler);
}
if ($token[0] === \T_COMMENT && substr($token[1], 0, 2) !== '/*'
&& preg_match('/(\r\n|\n|\r)$/D', $token[1], $matches)) {
$trailingNewline = $matches[0];
$token[1] = substr($token[1], 0, -strlen($trailingNewline));
$this->tokens[$i] = $token;
if (isset($this->tokens[$i + 1]) && $this->tokens[$i + 1][0] === \T_WHITESPACE) {
// Move trailing newline into following T_WHITESPACE token, if it already exists.
$this->tokens[$i + 1][1] = $trailingNewline . $this->tokens[$i + 1][1];
$this->tokens[$i + 1][2]--;
} else {
// Otherwise, we need to create a new T_WHITESPACE token.
array_splice($this->tokens, $i + 1, 0, [
[\T_WHITESPACE, $trailingNewline, $line],
]);
$numTokens++;
}
} }
// Emulate PHP 8 T_NAME_* tokens, by combining sequences of T_NS_SEPARATOR and T_STRING for ($i = 0; $i < $numTokens; $i++) {
// into a single token. $token = $tokens[$i];
if (\is_array($token) if ($token->id === \T_BAD_CHARACTER) {
&& ($token[0] === \T_NS_SEPARATOR || isset($this->identifierTokens[$token[0]]))) { $this->handleInvalidCharacter($token, $errorHandler);
$lastWasSeparator = $token[0] === \T_NS_SEPARATOR;
$text = $token[1];
for ($j = $i + 1; isset($this->tokens[$j]); $j++) {
if ($lastWasSeparator) {
if (!isset($this->identifierTokens[$this->tokens[$j][0]])) {
break;
}
$lastWasSeparator = false;
} else {
if ($this->tokens[$j][0] !== \T_NS_SEPARATOR) {
break;
}
$lastWasSeparator = true;
}
$text .= $this->tokens[$j][1];
}
if ($lastWasSeparator) {
// Trailing separator is not part of the name.
$j--;
$text = substr($text, 0, -1);
}
if ($j > $i + 1) {
if ($token[0] === \T_NS_SEPARATOR) {
$type = \T_NAME_FULLY_QUALIFIED;
} else if ($token[0] === \T_NAMESPACE) {
$type = \T_NAME_RELATIVE;
} else {
$type = \T_NAME_QUALIFIED;
}
$token = [$type, $text, $line];
array_splice($this->tokens, $i, $j - $i, [$token]);
$numTokens -= $j - $i - 1;
}
} }
if ($token === '&') { if ($token->id === \ord('&')) {
$next = $i + 1; $next = $i + 1;
while (isset($this->tokens[$next]) && $this->tokens[$next][0] === \T_WHITESPACE) { while (isset($tokens[$next]) && $tokens[$next]->id === \T_WHITESPACE) {
$next++; $next++;
} }
$followedByVarOrVarArg = isset($this->tokens[$next]) && $followedByVarOrVarArg = isset($tokens[$next]) &&
($this->tokens[$next][0] === \T_VARIABLE || $this->tokens[$next][0] === \T_ELLIPSIS); $tokens[$next]->is([\T_VARIABLE, \T_ELLIPSIS]);
$this->tokens[$i] = $token = [ $token->id = $followedByVarOrVarArg
$followedByVarOrVarArg
? \T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG ? \T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG
: \T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG, : \T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG;
'&',
$line,
];
}
$tokenValue = \is_string($token) ? $token : $token[1];
$tokenLen = \strlen($tokenValue);
if (substr($this->code, $filePos, $tokenLen) !== $tokenValue) {
// Something is missing, must be an invalid character
$nextFilePos = strpos($this->code, $tokenValue, $filePos);
$badCharTokens = $this->handleInvalidCharacterRange(
$filePos, $nextFilePos, $line, $errorHandler);
$filePos = (int) $nextFilePos;
array_splice($this->tokens, $i, 0, $badCharTokens);
$numTokens += \count($badCharTokens);
$i += \count($badCharTokens);
}
$filePos += $tokenLen;
$line += substr_count($tokenValue, "\n");
}
if ($filePos !== \strlen($this->code)) {
if (substr($this->code, $filePos, 2) === '/*') {
// Unlike PHP, HHVM will drop unterminated comments entirely
$comment = substr($this->code, $filePos);
$errorHandler->handleError(new Error('Unterminated comment', [
'startLine' => $line,
'endLine' => $line + substr_count($comment, "\n"),
'startFilePos' => $filePos,
'endFilePos' => $filePos + \strlen($comment),
]));
// Emulate the PHP behavior
$isDocComment = isset($comment[3]) && $comment[3] === '*';
$this->tokens[] = [$isDocComment ? \T_DOC_COMMENT : \T_COMMENT, $comment, $line];
} else {
// Invalid characters at the end of the input
$badCharTokens = $this->handleInvalidCharacterRange(
$filePos, \strlen($this->code), $line, $errorHandler);
$this->tokens = array_merge($this->tokens, $badCharTokens);
} }
return;
} }
if (count($this->tokens) > 0) {
// Check for unterminated comment // Check for unterminated comment
$lastToken = $this->tokens[count($this->tokens) - 1]; $lastToken = $tokens[$numTokens - 1];
if ($this->isUnterminatedComment($lastToken)) { if ($this->isUnterminatedComment($lastToken)) {
$errorHandler->handleError(new Error('Unterminated comment', [ $errorHandler->handleError(new Error('Unterminated comment', [
'startLine' => $line - substr_count($lastToken[1], "\n"), 'startLine' => $lastToken->line,
'endLine' => $line, 'endLine' => $lastToken->getEndLine(),
'startFilePos' => $filePos - \strlen($lastToken[1]), 'startFilePos' => $lastToken->pos,
'endFilePos' => $filePos, 'endFilePos' => $lastToken->getEndPos(),
])); ]));
} }
}
}
/**
* Fetches the next token.
*
* The available attributes are determined by the 'usedAttributes' option, which can
* be specified in the constructor. The following attributes are supported:
*
* * 'comments' => Array of PhpParser\Comment or PhpParser\Comment\Doc instances,
* representing all comments that occurred between the previous
* non-discarded token and the current one.
* * 'startLine' => Line in which the node starts.
* * 'endLine' => Line in which the node ends.
* * 'startTokenPos' => Offset into the token array of the first token in the node.
* * 'endTokenPos' => Offset into the token array of the last token in the node.
* * 'startFilePos' => Offset into the code string of the first character that is part of the node.
* * 'endFilePos' => Offset into the code string of the last character that is part of the node.
*
* @param mixed $value Variable to store token content in
* @param mixed $startAttributes Variable to store start attributes in
* @param mixed $endAttributes Variable to store end attributes in
*
* @return int Token id
*/
public function getNextToken(&$value = null, &$startAttributes = null, &$endAttributes = null) : int {
$startAttributes = [];
$endAttributes = [];
while (1) {
if (isset($this->tokens[++$this->pos])) {
$token = $this->tokens[$this->pos];
} else {
// EOF token with ID 0
$token = "\0";
}
if ($this->attributeStartLineUsed) {
$startAttributes['startLine'] = $this->line;
}
if ($this->attributeStartTokenPosUsed) {
$startAttributes['startTokenPos'] = $this->pos;
}
if ($this->attributeStartFilePosUsed) {
$startAttributes['startFilePos'] = $this->filePos;
}
if (\is_string($token)) {
$value = $token;
if (isset($token[1])) {
// bug in token_get_all
$this->filePos += 2;
$id = ord('"');
} else {
$this->filePos += 1;
$id = ord($token);
}
} elseif (!isset($this->dropTokens[$token[0]])) {
$value = $token[1];
$id = $this->tokenMap[$token[0]];
if (\T_CLOSE_TAG === $token[0]) {
$this->prevCloseTagHasNewline = false !== strpos($token[1], "\n")
|| false !== strpos($token[1], "\r");
} elseif (\T_INLINE_HTML === $token[0]) {
$startAttributes['hasLeadingNewline'] = $this->prevCloseTagHasNewline;
}
$this->line += substr_count($value, "\n");
$this->filePos += \strlen($value);
} else {
$origLine = $this->line;
$origFilePos = $this->filePos;
$this->line += substr_count($token[1], "\n");
$this->filePos += \strlen($token[1]);
if (\T_COMMENT === $token[0] || \T_DOC_COMMENT === $token[0]) {
if ($this->attributeCommentsUsed) {
$comment = \T_DOC_COMMENT === $token[0]
? new Comment\Doc($token[1],
$origLine, $origFilePos, $this->pos,
$this->line, $this->filePos - 1, $this->pos)
: new Comment($token[1],
$origLine, $origFilePos, $this->pos,
$this->line, $this->filePos - 1, $this->pos);
$startAttributes['comments'][] = $comment;
}
}
continue;
}
if ($this->attributeEndLineUsed) {
$endAttributes['endLine'] = $this->line;
}
if ($this->attributeEndTokenPosUsed) {
$endAttributes['endTokenPos'] = $this->pos;
}
if ($this->attributeEndFilePosUsed) {
$endAttributes['endFilePos'] = $this->filePos - 1;
}
return $id;
}
throw new \RuntimeException('Reached end of lexer loop');
}
/**
* Returns the token array for current code.
*
* The token array is in the same format as provided by the
* token_get_all() function and does not discard tokens (i.e.
* whitespace and comments are included). The token position
* attributes are against this token array.
*
* @return array Array of tokens in token_get_all() format
*/
public function getTokens() : array {
return $this->tokens;
}
/**
* Handles __halt_compiler() by returning the text after it.
*
* @return string Remaining text
*/
public function handleHaltCompiler() : string {
// text after T_HALT_COMPILER, still including ();
$textAfter = substr($this->code, $this->filePos);
// ensure that it is followed by ();
// this simplifies the situation, by not allowing any comments
// in between of the tokens.
if (!preg_match('~^\s*\(\s*\)\s*(?:;|\?>\r?\n?)~', $textAfter, $matches)) {
throw new Error('__HALT_COMPILER must be followed by "();"');
}
// prevent the lexer from returning any further tokens
$this->pos = count($this->tokens);
// return with (); removed
return substr($textAfter, strlen($matches[0]));
}
private function defineCompatibilityTokens() {
static $compatTokensDefined = false;
if ($compatTokensDefined) {
return;
}
$compatTokens = [
// PHP 7.4
'T_BAD_CHARACTER',
'T_FN',
'T_COALESCE_EQUAL',
// PHP 8.0
'T_NAME_QUALIFIED',
'T_NAME_FULLY_QUALIFIED',
'T_NAME_RELATIVE',
'T_MATCH',
'T_NULLSAFE_OBJECT_OPERATOR',
'T_ATTRIBUTE',
// PHP 8.1
'T_ENUM',
'T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG',
'T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG',
'T_READONLY',
];
// PHP-Parser might be used together with another library that also emulates some or all
// of these tokens. Perform a sanity-check that all already defined tokens have been
// assigned a unique ID.
$usedTokenIds = [];
foreach ($compatTokens as $token) {
if (\defined($token)) {
$tokenId = \constant($token);
$clashingToken = $usedTokenIds[$tokenId] ?? null;
if ($clashingToken !== null) {
throw new \Error(sprintf(
'Token %s has same ID as token %s, ' .
'you may be using a library with broken token emulation',
$token, $clashingToken
));
}
$usedTokenIds[$tokenId] = $token;
}
}
// Now define any tokens that have not yet been emulated. Try to assign IDs from -1
// downwards, but skip any IDs that may already be in use.
$newTokenId = -1;
foreach ($compatTokens as $token) {
if (!\defined($token)) {
while (isset($usedTokenIds[$newTokenId])) {
$newTokenId--;
}
\define($token, $newTokenId);
$newTokenId--;
}
}
$compatTokensDefined = true;
}
/**
* Creates the token map.
*
* The token map maps the PHP internal token identifiers
* to the identifiers used by the Parser. Additionally it
* maps T_OPEN_TAG_WITH_ECHO to T_ECHO and T_CLOSE_TAG to ';'.
*
* @return array The token map
*/
protected function createTokenMap() : array {
$tokenMap = [];
// 256 is the minimum possible token number, as everything below
// it is an ASCII value
for ($i = 256; $i < 1000; ++$i) {
if (\T_DOUBLE_COLON === $i) {
// T_DOUBLE_COLON is equivalent to T_PAAMAYIM_NEKUDOTAYIM
$tokenMap[$i] = Tokens::T_PAAMAYIM_NEKUDOTAYIM;
} elseif(\T_OPEN_TAG_WITH_ECHO === $i) {
// T_OPEN_TAG_WITH_ECHO with dropped T_OPEN_TAG results in T_ECHO
$tokenMap[$i] = Tokens::T_ECHO;
} elseif(\T_CLOSE_TAG === $i) {
// T_CLOSE_TAG is equivalent to ';'
$tokenMap[$i] = ord(';');
} elseif ('UNKNOWN' !== $name = token_name($i)) {
if ('T_HASHBANG' === $name) {
// HHVM uses a special token for #! hashbang lines
$tokenMap[$i] = Tokens::T_INLINE_HTML;
} elseif (defined($name = Tokens::class . '::' . $name)) {
// Other tokens can be mapped directly
$tokenMap[$i] = constant($name);
}
}
}
// HHVM uses a special token for numbers that overflow to double
if (defined('T_ONUMBER')) {
$tokenMap[\T_ONUMBER] = Tokens::T_DNUMBER;
}
// HHVM also has a separate token for the __COMPILER_HALT_OFFSET__ constant
if (defined('T_COMPILER_HALT_OFFSET')) {
$tokenMap[\T_COMPILER_HALT_OFFSET] = Tokens::T_STRING;
}
// Assign tokens for which we define compatibility constants, as token_name() does not know them.
$tokenMap[\T_FN] = Tokens::T_FN;
$tokenMap[\T_COALESCE_EQUAL] = Tokens::T_COALESCE_EQUAL;
$tokenMap[\T_NAME_QUALIFIED] = Tokens::T_NAME_QUALIFIED;
$tokenMap[\T_NAME_FULLY_QUALIFIED] = Tokens::T_NAME_FULLY_QUALIFIED;
$tokenMap[\T_NAME_RELATIVE] = Tokens::T_NAME_RELATIVE;
$tokenMap[\T_MATCH] = Tokens::T_MATCH;
$tokenMap[\T_NULLSAFE_OBJECT_OPERATOR] = Tokens::T_NULLSAFE_OBJECT_OPERATOR;
$tokenMap[\T_ATTRIBUTE] = Tokens::T_ATTRIBUTE;
$tokenMap[\T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG] = Tokens::T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG;
$tokenMap[\T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG] = Tokens::T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG;
$tokenMap[\T_ENUM] = Tokens::T_ENUM;
$tokenMap[\T_READONLY] = Tokens::T_READONLY;
return $tokenMap;
}
private function createIdentifierTokenMap(): array { // Add sentinel token.
// Based on semi_reserved production. $tokens[] = new Token(0, "\0", $lastToken->getEndLine(), $lastToken->getEndPos());
return array_fill_keys([
\T_STRING,
\T_STATIC, \T_ABSTRACT, \T_FINAL, \T_PRIVATE, \T_PROTECTED, \T_PUBLIC, \T_READONLY,
\T_INCLUDE, \T_INCLUDE_ONCE, \T_EVAL, \T_REQUIRE, \T_REQUIRE_ONCE, \T_LOGICAL_OR, \T_LOGICAL_XOR, \T_LOGICAL_AND,
\T_INSTANCEOF, \T_NEW, \T_CLONE, \T_EXIT, \T_IF, \T_ELSEIF, \T_ELSE, \T_ENDIF, \T_ECHO, \T_DO, \T_WHILE,
\T_ENDWHILE, \T_FOR, \T_ENDFOR, \T_FOREACH, \T_ENDFOREACH, \T_DECLARE, \T_ENDDECLARE, \T_AS, \T_TRY, \T_CATCH,
\T_FINALLY, \T_THROW, \T_USE, \T_INSTEADOF, \T_GLOBAL, \T_VAR, \T_UNSET, \T_ISSET, \T_EMPTY, \T_CONTINUE, \T_GOTO,
\T_FUNCTION, \T_CONST, \T_RETURN, \T_PRINT, \T_YIELD, \T_LIST, \T_SWITCH, \T_ENDSWITCH, \T_CASE, \T_DEFAULT,
\T_BREAK, \T_ARRAY, \T_CALLABLE, \T_EXTENDS, \T_IMPLEMENTS, \T_NAMESPACE, \T_TRAIT, \T_INTERFACE, \T_CLASS,
\T_CLASS_C, \T_TRAIT_C, \T_FUNC_C, \T_METHOD_C, \T_LINE, \T_FILE, \T_DIR, \T_NS_C, \T_HALT_COMPILER, \T_FN,
\T_MATCH,
], true);
} }
} }

@ -18,42 +18,29 @@ use PhpParser\Lexer\TokenEmulator\ReadonlyFunctionTokenEmulator;
use PhpParser\Lexer\TokenEmulator\ReadonlyTokenEmulator; use PhpParser\Lexer\TokenEmulator\ReadonlyTokenEmulator;
use PhpParser\Lexer\TokenEmulator\ReverseEmulator; use PhpParser\Lexer\TokenEmulator\ReverseEmulator;
use PhpParser\Lexer\TokenEmulator\TokenEmulator; use PhpParser\Lexer\TokenEmulator\TokenEmulator;
use PhpParser\PhpVersion;
use PhpParser\Token;
class Emulative extends Lexer class Emulative extends Lexer {
{ /** @var array{int, string, string}[] Patches used to reverse changes introduced in the code */
const PHP_7_3 = '7.3dev'; private array $patches = [];
const PHP_7_4 = '7.4dev';
const PHP_8_0 = '8.0dev';
const PHP_8_1 = '8.1dev';
const PHP_8_2 = '8.2dev';
/** @var mixed[] Patches used to reverse changes introduced in the code */ /** @var list<TokenEmulator> */
private $patches = []; private array $emulators = [];
/** @var TokenEmulator[] */ private PhpVersion $targetPhpVersion;
private $emulators = [];
/** @var string */ private PhpVersion $hostPhpVersion;
private $targetPhpVersion;
/** /**
* @param mixed[] $options Lexer options. In addition to the usual options, * @param PhpVersion|null $phpVersion PHP version to emulate. Defaults to newest supported.
* accepts a 'phpVersion' string that specifies the
* version to emulate. Defaults to newest supported.
*/ */
public function __construct(array $options = []) public function __construct(?PhpVersion $phpVersion = null) {
{ $this->targetPhpVersion = $phpVersion ?? PhpVersion::getNewestSupported();
$this->targetPhpVersion = $options['phpVersion'] ?? Emulative::PHP_8_2; $this->hostPhpVersion = PhpVersion::getHostVersion();
unset($options['phpVersion']);
parent::__construct($options);
$emulators = [ $emulators = [
new FlexibleDocStringEmulator(),
new FnTokenEmulator(),
new MatchTokenEmulator(), new MatchTokenEmulator(),
new CoaleseEqualTokenEmulator(),
new NumericLiteralSeparatorEmulator(),
new NullsafeTokenEmulator(), new NullsafeTokenEmulator(),
new AttributeEmulator(), new AttributeEmulator(),
new EnumTokenEmulator(), new EnumTokenEmulator(),
@ -74,15 +61,18 @@ class Emulative extends Lexer
} }
} }
public function startLexing(string $code, ErrorHandler $errorHandler = null) { public function tokenize(string $code, ?ErrorHandler $errorHandler = null): array {
$emulators = array_filter($this->emulators, function ($emulator) use ($code) { $emulators = array_filter($this->emulators, function ($emulator) use ($code) {
return $emulator->isEmulationNeeded($code); return $emulator->isEmulationNeeded($code);
}); });
if (empty($emulators)) { if (empty($emulators)) {
// Nothing to emulate, yay // Nothing to emulate, yay
parent::startLexing($code, $errorHandler); return parent::tokenize($code, $errorHandler);
return; }
if ($errorHandler === null) {
$errorHandler = new ErrorHandler\Throwing();
} }
$this->patches = []; $this->patches = [];
@ -91,9 +81,9 @@ class Emulative extends Lexer
} }
$collector = new ErrorHandler\Collecting(); $collector = new ErrorHandler\Collecting();
parent::startLexing($code, $collector); $tokens = parent::tokenize($code, $collector);
$this->sortPatches(); $this->sortPatches();
$this->fixupTokens(); $tokens = $this->fixupTokens($tokens);
$errors = $collector->getErrors(); $errors = $collector->getErrors();
if (!empty($errors)) { if (!empty($errors)) {
@ -104,22 +94,23 @@ class Emulative extends Lexer
} }
foreach ($emulators as $emulator) { foreach ($emulators as $emulator) {
$this->tokens = $emulator->emulate($code, $this->tokens); $tokens = $emulator->emulate($code, $tokens);
} }
return $tokens;
} }
private function isForwardEmulationNeeded(string $emulatorPhpVersion): bool { private function isForwardEmulationNeeded(PhpVersion $emulatorPhpVersion): bool {
return version_compare(\PHP_VERSION, $emulatorPhpVersion, '<') return $this->hostPhpVersion->older($emulatorPhpVersion)
&& version_compare($this->targetPhpVersion, $emulatorPhpVersion, '>='); && $this->targetPhpVersion->newerOrEqual($emulatorPhpVersion);
} }
private function isReverseEmulationNeeded(string $emulatorPhpVersion): bool { private function isReverseEmulationNeeded(PhpVersion $emulatorPhpVersion): bool {
return version_compare(\PHP_VERSION, $emulatorPhpVersion, '>=') return $this->hostPhpVersion->newerOrEqual($emulatorPhpVersion)
&& version_compare($this->targetPhpVersion, $emulatorPhpVersion, '<'); && $this->targetPhpVersion->older($emulatorPhpVersion);
} }
private function sortPatches() private function sortPatches(): void {
{
// Patches may be contributed by different emulators. // Patches may be contributed by different emulators.
// Make sure they are sorted by increasing patch position. // Make sure they are sorted by increasing patch position.
usort($this->patches, function ($p1, $p2) { usort($this->patches, function ($p1, $p2) {
@ -127,67 +118,56 @@ class Emulative extends Lexer
}); });
} }
private function fixupTokens() /**
{ * @param list<Token> $tokens
* @return list<Token>
*/
private function fixupTokens(array $tokens): array {
if (\count($this->patches) === 0) { if (\count($this->patches) === 0) {
return; return $tokens;
} }
// Load first patch // Load first patch
$patchIdx = 0; $patchIdx = 0;
list($patchPos, $patchType, $patchText) = $this->patches[$patchIdx]; list($patchPos, $patchType, $patchText) = $this->patches[$patchIdx];
// We use a manual loop over the tokens, because we modify the array on the fly // We use a manual loop over the tokens, because we modify the array on the fly
$pos = 0;
for ($i = 0, $c = \count($this->tokens); $i < $c; $i++) {
$token = $this->tokens[$i];
if (\is_string($token)) {
if ($patchPos === $pos) {
// Only support replacement for string tokens.
assert($patchType === 'replace');
$this->tokens[$i] = $patchText;
// Fetch the next patch
$patchIdx++;
if ($patchIdx >= \count($this->patches)) {
// No more patches, we're done
return;
}
list($patchPos, $patchType, $patchText) = $this->patches[$patchIdx];
}
$pos += \strlen($token);
continue;
}
$len = \strlen($token[1]);
$posDelta = 0; $posDelta = 0;
$lineDelta = 0;
for ($i = 0, $c = \count($tokens); $i < $c; $i++) {
$token = $tokens[$i];
$pos = $token->pos;
$token->pos += $posDelta;
$token->line += $lineDelta;
$localPosDelta = 0;
$len = \strlen($token->text);
while ($patchPos >= $pos && $patchPos < $pos + $len) { while ($patchPos >= $pos && $patchPos < $pos + $len) {
$patchTextLen = \strlen($patchText); $patchTextLen = \strlen($patchText);
if ($patchType === 'remove') { if ($patchType === 'remove') {
if ($patchPos === $pos && $patchTextLen === $len) { if ($patchPos === $pos && $patchTextLen === $len) {
// Remove token entirely // Remove token entirely
array_splice($this->tokens, $i, 1, []); array_splice($tokens, $i, 1, []);
$i--; $i--;
$c--; $c--;
} else { } else {
// Remove from token string // Remove from token string
$this->tokens[$i][1] = substr_replace( $token->text = substr_replace(
$token[1], '', $patchPos - $pos + $posDelta, $patchTextLen $token->text, '', $patchPos - $pos + $localPosDelta, $patchTextLen
); );
$posDelta -= $patchTextLen; $localPosDelta -= $patchTextLen;
} }
$lineDelta -= \substr_count($patchText, "\n");
} elseif ($patchType === 'add') { } elseif ($patchType === 'add') {
// Insert into the token string // Insert into the token string
$this->tokens[$i][1] = substr_replace( $token->text = substr_replace(
$token[1], $patchText, $patchPos - $pos + $posDelta, 0 $token->text, $patchText, $patchPos - $pos + $localPosDelta, 0
); );
$posDelta += $patchTextLen; $localPosDelta += $patchTextLen;
$lineDelta += \substr_count($patchText, "\n");
} elseif ($patchType === 'replace') { } elseif ($patchType === 'replace') {
// Replace inside the token string // Replace inside the token string
$this->tokens[$i][1] = substr_replace( $token->text = substr_replace(
$token[1], $patchText, $patchPos - $pos + $posDelta, $patchTextLen $token->text, $patchText, $patchPos - $pos + $localPosDelta, $patchTextLen
); );
} else { } else {
assert(false); assert(false);
@ -196,22 +176,17 @@ class Emulative extends Lexer
// Fetch the next patch // Fetch the next patch
$patchIdx++; $patchIdx++;
if ($patchIdx >= \count($this->patches)) { if ($patchIdx >= \count($this->patches)) {
// No more patches, we're done // No more patches. However, we still need to adjust position.
return; $patchPos = \PHP_INT_MAX;
break;
} }
list($patchPos, $patchType, $patchText) = $this->patches[$patchIdx]; list($patchPos, $patchType, $patchText) = $this->patches[$patchIdx];
// Multiple patches may apply to the same token. Reload the current one to check
// If the new patch applies
$token = $this->tokens[$i];
} }
$pos += $len; $posDelta += $localPosDelta;
} }
return $tokens;
// A patch did not apply
assert(false);
} }
/** /**
@ -219,7 +194,7 @@ class Emulative extends Lexer
* *
* @param Error[] $errors * @param Error[] $errors
*/ */
private function fixupErrors(array $errors) { private function fixupErrors(array $errors): void {
foreach ($errors as $error) { foreach ($errors as $error) {
$attrs = $error->getAttributes(); $attrs = $error->getAttributes();

@ -2,43 +2,36 @@
namespace PhpParser\Lexer\TokenEmulator; namespace PhpParser\Lexer\TokenEmulator;
use PhpParser\Lexer\Emulative; use PhpParser\PhpVersion;
use PhpParser\Token;
final class AttributeEmulator extends TokenEmulator final class AttributeEmulator extends TokenEmulator {
{ public function getPhpVersion(): PhpVersion {
public function getPhpVersion(): string return PhpVersion::fromComponents(8, 0);
{
return Emulative::PHP_8_0;
} }
public function isEmulationNeeded(string $code) : bool public function isEmulationNeeded(string $code): bool {
{
return strpos($code, '#[') !== false; return strpos($code, '#[') !== false;
} }
public function emulate(string $code, array $tokens): array public function emulate(string $code, array $tokens): array {
{
// We need to manually iterate and manage a count because we'll change // We need to manually iterate and manage a count because we'll change
// the tokens array on the way. // the tokens array on the way.
$line = 1;
for ($i = 0, $c = count($tokens); $i < $c; ++$i) { for ($i = 0, $c = count($tokens); $i < $c; ++$i) {
if ($tokens[$i] === '#' && isset($tokens[$i + 1]) && $tokens[$i + 1] === '[') { $token = $tokens[$i];
if ($token->text === '#' && isset($tokens[$i + 1]) && $tokens[$i + 1]->text === '[') {
array_splice($tokens, $i, 2, [ array_splice($tokens, $i, 2, [
[\T_ATTRIBUTE, '#[', $line] new Token(\T_ATTRIBUTE, '#[', $token->line, $token->pos),
]); ]);
$c--; $c--;
continue; continue;
} }
if (\is_array($tokens[$i])) {
$line += substr_count($tokens[$i][1], "\n");
}
} }
return $tokens; return $tokens;
} }
public function reverseEmulate(string $code, array $tokens): array public function reverseEmulate(string $code, array $tokens): array {
{
// TODO // TODO
return $tokens; return $tokens;
} }

@ -1,47 +0,0 @@
<?php declare(strict_types=1);
namespace PhpParser\Lexer\TokenEmulator;
use PhpParser\Lexer\Emulative;
final class CoaleseEqualTokenEmulator extends TokenEmulator
{
public function getPhpVersion(): string
{
return Emulative::PHP_7_4;
}
public function isEmulationNeeded(string $code): bool
{
return strpos($code, '??=') !== false;
}
public function emulate(string $code, array $tokens): array
{
// We need to manually iterate and manage a count because we'll change
// the tokens array on the way
$line = 1;
for ($i = 0, $c = count($tokens); $i < $c; ++$i) {
if (isset($tokens[$i + 1])) {
if ($tokens[$i][0] === T_COALESCE && $tokens[$i + 1] === '=') {
array_splice($tokens, $i, 2, [
[\T_COALESCE_EQUAL, '??=', $line]
]);
$c--;
continue;
}
}
if (\is_array($tokens[$i])) {
$line += substr_count($tokens[$i][1], "\n");
}
}
return $tokens;
}
public function reverseEmulate(string $code, array $tokens): array
{
// ??= was not valid code previously, don't bother.
return $tokens;
}
}

@ -2,30 +2,25 @@
namespace PhpParser\Lexer\TokenEmulator; namespace PhpParser\Lexer\TokenEmulator;
use PhpParser\Lexer\Emulative; use PhpParser\PhpVersion;
final class EnumTokenEmulator extends KeywordEmulator final class EnumTokenEmulator extends KeywordEmulator {
{ public function getPhpVersion(): PhpVersion {
public function getPhpVersion(): string return PhpVersion::fromComponents(8, 1);
{
return Emulative::PHP_8_1;
} }
public function getKeywordString(): string public function getKeywordString(): string {
{
return 'enum'; return 'enum';
} }
public function getKeywordToken(): int public function getKeywordToken(): int {
{
return \T_ENUM; return \T_ENUM;
} }
protected function isKeywordContext(array $tokens, int $pos): bool protected function isKeywordContext(array $tokens, int $pos): bool {
{
return parent::isKeywordContext($tokens, $pos) return parent::isKeywordContext($tokens, $pos)
&& isset($tokens[$pos + 2]) && isset($tokens[$pos + 2])
&& $tokens[$pos + 1][0] === \T_WHITESPACE && $tokens[$pos + 1]->id === \T_WHITESPACE
&& $tokens[$pos + 2][0] === \T_STRING; && $tokens[$pos + 2]->id === \T_STRING;
} }
} }

@ -2,11 +2,12 @@
namespace PhpParser\Lexer\TokenEmulator; namespace PhpParser\Lexer\TokenEmulator;
use PhpParser\Lexer\Emulative; use PhpParser\PhpVersion;
use PhpParser\Token;
class ExplicitOctalEmulator extends TokenEmulator { class ExplicitOctalEmulator extends TokenEmulator {
public function getPhpVersion(): string { public function getPhpVersion(): PhpVersion {
return Emulative::PHP_8_1; return PhpVersion::fromComponents(8, 1);
} }
public function isEmulationNeeded(string $code): bool { public function isEmulationNeeded(string $code): bool {
@ -15,13 +16,14 @@ class ExplicitOctalEmulator extends TokenEmulator {
public function emulate(string $code, array $tokens): array { public function emulate(string $code, array $tokens): array {
for ($i = 0, $c = count($tokens); $i < $c; ++$i) { for ($i = 0, $c = count($tokens); $i < $c; ++$i) {
if ($tokens[$i][0] == \T_LNUMBER && $tokens[$i][1] === '0' && $token = $tokens[$i];
isset($tokens[$i + 1]) && $tokens[$i + 1][0] == \T_STRING && if ($token->id == \T_LNUMBER && $token->text === '0' &&
preg_match('/[oO][0-7]+(?:_[0-7]+)*/', $tokens[$i + 1][1]) isset($tokens[$i + 1]) && $tokens[$i + 1]->id == \T_STRING &&
preg_match('/[oO][0-7]+(?:_[0-7]+)*/', $tokens[$i + 1]->text)
) { ) {
$tokenKind = $this->resolveIntegerOrFloatToken($tokens[$i + 1][1]); $tokenKind = $this->resolveIntegerOrFloatToken($tokens[$i + 1]->text);
array_splice($tokens, $i, 2, [ array_splice($tokens, $i, 2, [
[$tokenKind, '0' . $tokens[$i + 1][1], $tokens[$i][2]], new Token($tokenKind, '0' . $tokens[$i + 1]->text, $token->line, $token->pos),
]); ]);
$c--; $c--;
} }
@ -29,8 +31,7 @@ class ExplicitOctalEmulator extends TokenEmulator {
return $tokens; return $tokens;
} }
private function resolveIntegerOrFloatToken(string $str): int private function resolveIntegerOrFloatToken(string $str): int {
{
$str = substr($str, 1); $str = substr($str, 1);
$str = str_replace('_', '', $str); $str = str_replace('_', '', $str);
$num = octdec($str); $num = octdec($str);

@ -1,76 +0,0 @@
<?php declare(strict_types=1);
namespace PhpParser\Lexer\TokenEmulator;
use PhpParser\Lexer\Emulative;
final class FlexibleDocStringEmulator extends TokenEmulator
{
const FLEXIBLE_DOC_STRING_REGEX = <<<'REGEX'
/<<<[ \t]*(['"]?)([a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*)\1\r?\n
(?:.*\r?\n)*?
(?<indentation>\h*)\2(?![a-zA-Z0-9_\x80-\xff])(?<separator>(?:;?[\r\n])?)/x
REGEX;
public function getPhpVersion(): string
{
return Emulative::PHP_7_3;
}
public function isEmulationNeeded(string $code) : bool
{
return strpos($code, '<<<') !== false;
}
public function emulate(string $code, array $tokens): array
{
// Handled by preprocessing + fixup.
return $tokens;
}
public function reverseEmulate(string $code, array $tokens): array
{
// Not supported.
return $tokens;
}
public function preprocessCode(string $code, array &$patches): string {
if (!preg_match_all(self::FLEXIBLE_DOC_STRING_REGEX, $code, $matches, PREG_SET_ORDER|PREG_OFFSET_CAPTURE)) {
// No heredoc/nowdoc found
return $code;
}
// Keep track of how much we need to adjust string offsets due to the modifications we
// already made
$posDelta = 0;
foreach ($matches as $match) {
$indentation = $match['indentation'][0];
$indentationStart = $match['indentation'][1];
$separator = $match['separator'][0];
$separatorStart = $match['separator'][1];
if ($indentation === '' && $separator !== '') {
// Ordinary heredoc/nowdoc
continue;
}
if ($indentation !== '') {
// Remove indentation
$indentationLen = strlen($indentation);
$code = substr_replace($code, '', $indentationStart + $posDelta, $indentationLen);
$patches[] = [$indentationStart + $posDelta, 'add', $indentation];
$posDelta -= $indentationLen;
}
if ($separator === '') {
// Insert newline as separator
$code = substr_replace($code, "\n", $separatorStart + $posDelta, 0);
$patches[] = [$separatorStart + $posDelta, 'remove', "\n"];
$posDelta += 1;
}
}
return $code;
}
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save