├── .env ├── .gitignore ├── README.md ├── bin └── console ├── bootstrap └── bootstrap.php ├── composer.json ├── composer.lock ├── config └── services.php ├── docker-compose.yaml ├── framework ├── .gitignore ├── composer.json ├── composer.lock ├── src │ ├── Authentication │ │ ├── AuthRepositoryInterface.php │ │ ├── AuthUserInterface.php │ │ ├── SessionAuthInterface.php │ │ └── SessionAuthentication.php │ ├── Console │ │ ├── Application.php │ │ ├── Command │ │ │ ├── CommandInterface.php │ │ │ └── MigrateDatabase.php │ │ ├── ConsoleException.php │ │ └── Kernel.php │ ├── Container │ │ ├── Container.php │ │ └── ContainerException.php │ ├── Controller │ │ └── AbstractController.php │ ├── Dbal │ │ ├── ConnectionFactory.php │ │ ├── DataMapper.php │ │ ├── Entity.php │ │ └── Event │ │ │ └── PostPersist.php │ ├── EventDispatcher │ │ ├── Event.php │ │ └── EventDispatcher.php │ ├── Http │ │ ├── Event │ │ │ └── ResponseEvent.php │ │ ├── HttpException.php │ │ ├── HttpRequestMethodException.php │ │ ├── Kernel.php │ │ ├── Middleware │ │ │ ├── Authenticate.php │ │ │ ├── Dummy.php │ │ │ ├── ExtractRouteInfo.php │ │ │ ├── Guest.php │ │ │ ├── MiddlewareInterface.php │ │ │ ├── RequestHandler.php │ │ │ ├── RequestHandlerInterface.php │ │ │ ├── RouterDispatch.php │ │ │ ├── StartSession.php │ │ │ └── Success.php │ │ ├── NotFoundException.php │ │ ├── RedirectResponse.php │ │ ├── Request.php │ │ └── Response.php │ ├── Routing │ │ ├── Router.php │ │ └── RouterInterface.php │ ├── ServiceProvider │ │ └── ServiceProviderInterface.php │ ├── Session │ │ ├── Session.php │ │ └── SessionInterface.php │ └── Template │ │ └── TwigFactory.php └── tests │ ├── ContainerTest.php │ ├── DependantClass.php │ ├── DependencyClass.php │ ├── SessionTest.php │ └── SubDependencyClass.php ├── migrations ├── 20230304203000.php ├── 20230305153000.php ├── 20230306123000.php └── 20230407133000.php ├── public └── index.php ├── routes └── web.php ├── src ├── Controller │ ├── DashboardController.php │ ├── HomeController.php │ ├── LoginController.php │ ├── PostsController.php │ └── RegistrationController.php ├── Entity │ ├── Post.php │ └── User.php ├── EventListener │ ├── ContentLengthListener.php │ └── InternalErrorListener.php ├── Form │ └── User │ │ └── RegistrationForm.php ├── Provider │ └── EventServiceProvider.php ├── Repository │ ├── PostMapper.php │ ├── PostRepository.php │ ├── UserMapper.php │ └── UserRepository.php └── Widget.php ├── templates ├── base.html.twig ├── create-post.html.twig ├── dashboard.html.twig ├── home.html.twig ├── login.html.twig ├── post.html.twig └── register.html.twig └── var └── .gitignore /.env: -------------------------------------------------------------------------------- 1 | APP_ENV=dev -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | /vendor/ 3 | notes.txt -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

PHP FRAMEWORK PRO

2 | 3 |

Created by Gary Clarke

4 | 5 |
6 | 7 | A comprehensive step-by-step video course created to help PHP developers grasp how frameworks function behind the scenes by actually building one. 8 | 9 | 10 | To get started, all you need to do is: 11 | 12 | Purchase the course here and get 20% discount using this coupon code: GCTREPO20 13 | 14 | All the code examples used in the lessons are stored in individual branches within this repository. Each lesson has its own branch, and within that branch, you can find all the relevant code samples used in that specific lesson. 15 | 16 | To set up your development environment, you will need to install Composer on your local machine. 17 | 18 | All these are for your information. The most exciting part is that every single step is covered in the course. 19 | 20 | Happy Coding. 21 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | get(\GaryClarke\Framework\Console\Kernel::class); 16 | 17 | // Call a handle method on the Kernel, return the console application status code 18 | $status = $kernel->handle(); 19 | 20 | // exit with that status 21 | exit($status); 22 | 23 | -------------------------------------------------------------------------------- /bootstrap/bootstrap.php: -------------------------------------------------------------------------------- 1 | get($providerClass); 9 | $provider->register(); 10 | } -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "autoload": { 3 | "psr-4": { 4 | "App\\": "src/" 5 | } 6 | }, 7 | "minimum-stability": "dev", 8 | "repositories": [ 9 | { 10 | "type": "path", 11 | "url": "framework" 12 | } 13 | ], 14 | "require-dev": { 15 | "symfony/var-dumper": "6.3.x-dev" 16 | }, 17 | "require": { 18 | "garyclarketech/php-framework-pro": "dev-main" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "45db679556ef570bcba0da51c149f1b1", 8 | "packages": [ 9 | { 10 | "name": "doctrine/cache", 11 | "version": "2.2.x-dev", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/doctrine/cache.git", 15 | "reference": "5d55089cbe7339f72405560a254153e21c24e9c5" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/doctrine/cache/zipball/5d55089cbe7339f72405560a254153e21c24e9c5", 20 | "reference": "5d55089cbe7339f72405560a254153e21c24e9c5", 21 | "shasum": "" 22 | }, 23 | "require": { 24 | "php": "~7.1 || ^8.0" 25 | }, 26 | "conflict": { 27 | "doctrine/common": ">2.2,<2.4" 28 | }, 29 | "require-dev": { 30 | "cache/integration-tests": "dev-master", 31 | "doctrine/coding-standard": "^9", 32 | "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", 33 | "psr/cache": "^1.0 || ^2.0 || ^3.0", 34 | "symfony/cache": "^4.4 || ^5.4 || ^6", 35 | "symfony/var-exporter": "^4.4 || ^5.4 || ^6" 36 | }, 37 | "default-branch": true, 38 | "type": "library", 39 | "autoload": { 40 | "psr-4": { 41 | "Doctrine\\Common\\Cache\\": "lib/Doctrine/Common/Cache" 42 | } 43 | }, 44 | "notification-url": "https://packagist.org/downloads/", 45 | "license": [ 46 | "MIT" 47 | ], 48 | "authors": [ 49 | { 50 | "name": "Guilherme Blanco", 51 | "email": "guilhermeblanco@gmail.com" 52 | }, 53 | { 54 | "name": "Roman Borschel", 55 | "email": "roman@code-factory.org" 56 | }, 57 | { 58 | "name": "Benjamin Eberlei", 59 | "email": "kontakt@beberlei.de" 60 | }, 61 | { 62 | "name": "Jonathan Wage", 63 | "email": "jonwage@gmail.com" 64 | }, 65 | { 66 | "name": "Johannes Schmitt", 67 | "email": "schmittjoh@gmail.com" 68 | } 69 | ], 70 | "description": "PHP Doctrine Cache library is a popular cache implementation that supports many different drivers such as redis, memcache, apc, mongodb and others.", 71 | "homepage": "https://www.doctrine-project.org/projects/cache.html", 72 | "keywords": [ 73 | "abstraction", 74 | "apcu", 75 | "cache", 76 | "caching", 77 | "couchdb", 78 | "memcached", 79 | "php", 80 | "redis", 81 | "xcache" 82 | ], 83 | "support": { 84 | "issues": "https://github.com/doctrine/cache/issues", 85 | "source": "https://github.com/doctrine/cache/tree/2.2.x" 86 | }, 87 | "funding": [ 88 | { 89 | "url": "https://www.doctrine-project.org/sponsorship.html", 90 | "type": "custom" 91 | }, 92 | { 93 | "url": "https://www.patreon.com/phpdoctrine", 94 | "type": "patreon" 95 | }, 96 | { 97 | "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fcache", 98 | "type": "tidelift" 99 | } 100 | ], 101 | "time": "2023-01-18T18:44:28+00:00" 102 | }, 103 | { 104 | "name": "doctrine/dbal", 105 | "version": "3.7.x-dev", 106 | "source": { 107 | "type": "git", 108 | "url": "https://github.com/doctrine/dbal.git", 109 | "reference": "3db3f61496c07e98f29e4a73bef72b3eae1380d6" 110 | }, 111 | "dist": { 112 | "type": "zip", 113 | "url": "https://api.github.com/repos/doctrine/dbal/zipball/3db3f61496c07e98f29e4a73bef72b3eae1380d6", 114 | "reference": "3db3f61496c07e98f29e4a73bef72b3eae1380d6", 115 | "shasum": "" 116 | }, 117 | "require": { 118 | "composer-runtime-api": "^2", 119 | "doctrine/cache": "^1.11|^2.0", 120 | "doctrine/deprecations": "^0.5.3|^1", 121 | "doctrine/event-manager": "^1|^2", 122 | "php": "^7.4 || ^8.0", 123 | "psr/cache": "^1|^2|^3", 124 | "psr/log": "^1|^2|^3" 125 | }, 126 | "require-dev": { 127 | "doctrine/coding-standard": "11.1.0", 128 | "fig/log-test": "^1", 129 | "jetbrains/phpstorm-stubs": "2022.3", 130 | "phpstan/phpstan": "1.10.9", 131 | "phpstan/phpstan-strict-rules": "^1.5", 132 | "phpunit/phpunit": "9.6.6", 133 | "psalm/plugin-phpunit": "0.18.4", 134 | "squizlabs/php_codesniffer": "3.7.2", 135 | "symfony/cache": "^5.4|^6.0", 136 | "symfony/console": "^4.4|^5.4|^6.0", 137 | "vimeo/psalm": "4.30.0" 138 | }, 139 | "suggest": { 140 | "symfony/console": "For helpful console commands such as SQL execution and import of files." 141 | }, 142 | "bin": [ 143 | "bin/doctrine-dbal" 144 | ], 145 | "type": "library", 146 | "autoload": { 147 | "psr-4": { 148 | "Doctrine\\DBAL\\": "src" 149 | } 150 | }, 151 | "notification-url": "https://packagist.org/downloads/", 152 | "license": [ 153 | "MIT" 154 | ], 155 | "authors": [ 156 | { 157 | "name": "Guilherme Blanco", 158 | "email": "guilhermeblanco@gmail.com" 159 | }, 160 | { 161 | "name": "Roman Borschel", 162 | "email": "roman@code-factory.org" 163 | }, 164 | { 165 | "name": "Benjamin Eberlei", 166 | "email": "kontakt@beberlei.de" 167 | }, 168 | { 169 | "name": "Jonathan Wage", 170 | "email": "jonwage@gmail.com" 171 | } 172 | ], 173 | "description": "Powerful PHP database abstraction layer (DBAL) with many features for database schema introspection and management.", 174 | "homepage": "https://www.doctrine-project.org/projects/dbal.html", 175 | "keywords": [ 176 | "abstraction", 177 | "database", 178 | "db2", 179 | "dbal", 180 | "mariadb", 181 | "mssql", 182 | "mysql", 183 | "oci8", 184 | "oracle", 185 | "pdo", 186 | "pgsql", 187 | "postgresql", 188 | "queryobject", 189 | "sasql", 190 | "sql", 191 | "sqlite", 192 | "sqlserver", 193 | "sqlsrv" 194 | ], 195 | "support": { 196 | "issues": "https://github.com/doctrine/dbal/issues", 197 | "source": "https://github.com/doctrine/dbal/tree/3.7.x" 198 | }, 199 | "funding": [ 200 | { 201 | "url": "https://www.doctrine-project.org/sponsorship.html", 202 | "type": "custom" 203 | }, 204 | { 205 | "url": "https://www.patreon.com/phpdoctrine", 206 | "type": "patreon" 207 | }, 208 | { 209 | "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fdbal", 210 | "type": "tidelift" 211 | } 212 | ], 213 | "time": "2023-04-13T22:06:37+00:00" 214 | }, 215 | { 216 | "name": "doctrine/deprecations", 217 | "version": "v1.0.0", 218 | "source": { 219 | "type": "git", 220 | "url": "https://github.com/doctrine/deprecations.git", 221 | "reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de" 222 | }, 223 | "dist": { 224 | "type": "zip", 225 | "url": "https://api.github.com/repos/doctrine/deprecations/zipball/0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de", 226 | "reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de", 227 | "shasum": "" 228 | }, 229 | "require": { 230 | "php": "^7.1|^8.0" 231 | }, 232 | "require-dev": { 233 | "doctrine/coding-standard": "^9", 234 | "phpunit/phpunit": "^7.5|^8.5|^9.5", 235 | "psr/log": "^1|^2|^3" 236 | }, 237 | "suggest": { 238 | "psr/log": "Allows logging deprecations via PSR-3 logger implementation" 239 | }, 240 | "type": "library", 241 | "autoload": { 242 | "psr-4": { 243 | "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" 244 | } 245 | }, 246 | "notification-url": "https://packagist.org/downloads/", 247 | "license": [ 248 | "MIT" 249 | ], 250 | "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", 251 | "homepage": "https://www.doctrine-project.org/", 252 | "support": { 253 | "issues": "https://github.com/doctrine/deprecations/issues", 254 | "source": "https://github.com/doctrine/deprecations/tree/v1.0.0" 255 | }, 256 | "time": "2022-05-02T15:47:09+00:00" 257 | }, 258 | { 259 | "name": "doctrine/event-manager", 260 | "version": "2.0.x-dev", 261 | "source": { 262 | "type": "git", 263 | "url": "https://github.com/doctrine/event-manager.git", 264 | "reference": "fe71bc032e5de972b8d18939abfa87d23f43ebd3" 265 | }, 266 | "dist": { 267 | "type": "zip", 268 | "url": "https://api.github.com/repos/doctrine/event-manager/zipball/fe71bc032e5de972b8d18939abfa87d23f43ebd3", 269 | "reference": "fe71bc032e5de972b8d18939abfa87d23f43ebd3", 270 | "shasum": "" 271 | }, 272 | "require": { 273 | "php": "^8.1" 274 | }, 275 | "conflict": { 276 | "doctrine/common": "<2.9" 277 | }, 278 | "require-dev": { 279 | "doctrine/coding-standard": "^10", 280 | "phpstan/phpstan": "^1.8.8", 281 | "phpunit/phpunit": "^9.5", 282 | "vimeo/psalm": "^4.28" 283 | }, 284 | "default-branch": true, 285 | "type": "library", 286 | "autoload": { 287 | "psr-4": { 288 | "Doctrine\\Common\\": "src" 289 | } 290 | }, 291 | "notification-url": "https://packagist.org/downloads/", 292 | "license": [ 293 | "MIT" 294 | ], 295 | "authors": [ 296 | { 297 | "name": "Guilherme Blanco", 298 | "email": "guilhermeblanco@gmail.com" 299 | }, 300 | { 301 | "name": "Roman Borschel", 302 | "email": "roman@code-factory.org" 303 | }, 304 | { 305 | "name": "Benjamin Eberlei", 306 | "email": "kontakt@beberlei.de" 307 | }, 308 | { 309 | "name": "Jonathan Wage", 310 | "email": "jonwage@gmail.com" 311 | }, 312 | { 313 | "name": "Johannes Schmitt", 314 | "email": "schmittjoh@gmail.com" 315 | }, 316 | { 317 | "name": "Marco Pivetta", 318 | "email": "ocramius@gmail.com" 319 | } 320 | ], 321 | "description": "The Doctrine Event Manager is a simple PHP event system that was built to be used with the various Doctrine projects.", 322 | "homepage": "https://www.doctrine-project.org/projects/event-manager.html", 323 | "keywords": [ 324 | "event", 325 | "event dispatcher", 326 | "event manager", 327 | "event system", 328 | "events" 329 | ], 330 | "support": { 331 | "issues": "https://github.com/doctrine/event-manager/issues", 332 | "source": "https://github.com/doctrine/event-manager/tree/2.0.x" 333 | }, 334 | "funding": [ 335 | { 336 | "url": "https://www.doctrine-project.org/sponsorship.html", 337 | "type": "custom" 338 | }, 339 | { 340 | "url": "https://www.patreon.com/phpdoctrine", 341 | "type": "patreon" 342 | }, 343 | { 344 | "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fevent-manager", 345 | "type": "tidelift" 346 | } 347 | ], 348 | "time": "2022-10-13T11:16:49+00:00" 349 | }, 350 | { 351 | "name": "garyclarketech/php-framework-pro", 352 | "version": "dev-main", 353 | "dist": { 354 | "type": "path", 355 | "url": "framework", 356 | "reference": "71f30e6b4ace462188a572bca5c4f5248c819aa6" 357 | }, 358 | "require": { 359 | "doctrine/dbal": "^3.6", 360 | "league/container": "^4.0.0", 361 | "nikic/fast-route": "dev-master", 362 | "psr/container": "2.0.x-dev", 363 | "psr/event-dispatcher": "^1.0", 364 | "symfony/dotenv": "^6.0", 365 | "twig/twig": "^3.0" 366 | }, 367 | "require-dev": { 368 | "phpunit/phpunit": "10.1.x-dev", 369 | "symfony/var-dumper": "6.3.x-dev" 370 | }, 371 | "type": "library", 372 | "autoload": { 373 | "psr-4": { 374 | "GaryClarke\\Framework\\": "src/" 375 | } 376 | }, 377 | "autoload-dev": { 378 | "psr-4": { 379 | "GaryClarke\\Framework\\Tests\\": "tests" 380 | } 381 | }, 382 | "license": [ 383 | "MIT" 384 | ], 385 | "authors": [ 386 | { 387 | "name": "Gary Clarke", 388 | "email": "info@garyclarke.tech" 389 | } 390 | ], 391 | "description": "A PHP framework tutorial project", 392 | "transport-options": { 393 | "relative": true 394 | } 395 | }, 396 | { 397 | "name": "league/container", 398 | "version": "4.x-dev", 399 | "source": { 400 | "type": "git", 401 | "url": "https://github.com/thephpleague/container.git", 402 | "reference": "388e992884ab75632d93dbaf4d29ec024d64b964" 403 | }, 404 | "dist": { 405 | "type": "zip", 406 | "url": "https://api.github.com/repos/thephpleague/container/zipball/388e992884ab75632d93dbaf4d29ec024d64b964", 407 | "reference": "388e992884ab75632d93dbaf4d29ec024d64b964", 408 | "shasum": "" 409 | }, 410 | "require": { 411 | "php": "^7.2 || ^8.0", 412 | "psr/container": "^1.1 || ^2.0" 413 | }, 414 | "provide": { 415 | "psr/container-implementation": "^1.0" 416 | }, 417 | "replace": { 418 | "orno/di": "~2.0" 419 | }, 420 | "require-dev": { 421 | "nette/php-generator": "^3.4", 422 | "nikic/php-parser": "^4.10", 423 | "phpstan/phpstan": "^0.12.47", 424 | "phpunit/phpunit": "^8.5.17", 425 | "roave/security-advisories": "dev-latest", 426 | "scrutinizer/ocular": "^1.8", 427 | "squizlabs/php_codesniffer": "^3.6" 428 | }, 429 | "default-branch": true, 430 | "type": "library", 431 | "extra": { 432 | "branch-alias": { 433 | "dev-master": "4.x-dev", 434 | "dev-4.x": "4.x-dev", 435 | "dev-3.x": "3.x-dev", 436 | "dev-2.x": "2.x-dev", 437 | "dev-1.x": "1.x-dev" 438 | } 439 | }, 440 | "autoload": { 441 | "psr-4": { 442 | "League\\Container\\": "src" 443 | } 444 | }, 445 | "notification-url": "https://packagist.org/downloads/", 446 | "license": [ 447 | "MIT" 448 | ], 449 | "authors": [ 450 | { 451 | "name": "Phil Bennett", 452 | "email": "mail@philbennett.co.uk", 453 | "role": "Developer" 454 | } 455 | ], 456 | "description": "A fast and intuitive dependency injection container.", 457 | "homepage": "https://github.com/thephpleague/container", 458 | "keywords": [ 459 | "container", 460 | "dependency", 461 | "di", 462 | "injection", 463 | "league", 464 | "provider", 465 | "service" 466 | ], 467 | "support": { 468 | "issues": "https://github.com/thephpleague/container/issues", 469 | "source": "https://github.com/thephpleague/container/tree/4.x" 470 | }, 471 | "funding": [ 472 | { 473 | "url": "https://github.com/philipobenito", 474 | "type": "github" 475 | } 476 | ], 477 | "time": "2022-02-02T09:18:15+00:00" 478 | }, 479 | { 480 | "name": "nikic/fast-route", 481 | "version": "dev-master", 482 | "source": { 483 | "type": "git", 484 | "url": "https://github.com/nikic/FastRoute.git", 485 | "reference": "34128a32009e1b41c4615bcd7d4c39968931e9ea" 486 | }, 487 | "dist": { 488 | "type": "zip", 489 | "url": "https://api.github.com/repos/nikic/FastRoute/zipball/34128a32009e1b41c4615bcd7d4c39968931e9ea", 490 | "reference": "34128a32009e1b41c4615bcd7d4c39968931e9ea", 491 | "shasum": "" 492 | }, 493 | "require": { 494 | "php": ">=7.4.0" 495 | }, 496 | "require-dev": { 497 | "doctrine/coding-standard": "^9.0", 498 | "phpbench/phpbench": "^1.0", 499 | "phpstan/extension-installer": "^1.0", 500 | "phpstan/phpstan": "^0.12", 501 | "phpstan/phpstan-deprecation-rules": "^0.12", 502 | "phpstan/phpstan-phpunit": "^0.12", 503 | "phpstan/phpstan-strict-rules": "^0.12", 504 | "phpunit/phpunit": "^9.5" 505 | }, 506 | "suggest": { 507 | "ext-apcu": "To be able to use APCu cache driver" 508 | }, 509 | "default-branch": true, 510 | "type": "library", 511 | "extra": { 512 | "branch-alias": { 513 | "dev-master": "2.0-dev" 514 | } 515 | }, 516 | "autoload": { 517 | "files": [ 518 | "src/functions.php" 519 | ], 520 | "psr-4": { 521 | "FastRoute\\": "src/" 522 | } 523 | }, 524 | "notification-url": "https://packagist.org/downloads/", 525 | "license": [ 526 | "BSD-3-Clause" 527 | ], 528 | "authors": [ 529 | { 530 | "name": "Nikita Popov", 531 | "email": "nikic@php.net" 532 | } 533 | ], 534 | "description": "Fast request router for PHP", 535 | "keywords": [ 536 | "router", 537 | "routing" 538 | ], 539 | "support": { 540 | "issues": "https://github.com/nikic/FastRoute/issues", 541 | "source": "https://github.com/nikic/FastRoute/tree/master" 542 | }, 543 | "time": "2023-03-16T08:06:25+00:00" 544 | }, 545 | { 546 | "name": "psr/cache", 547 | "version": "dev-master", 548 | "source": { 549 | "type": "git", 550 | "url": "https://github.com/php-fig/cache.git", 551 | "reference": "0a7c67d0d1c8167b342eb74339d6f961663826ce" 552 | }, 553 | "dist": { 554 | "type": "zip", 555 | "url": "https://api.github.com/repos/php-fig/cache/zipball/0a7c67d0d1c8167b342eb74339d6f961663826ce", 556 | "reference": "0a7c67d0d1c8167b342eb74339d6f961663826ce", 557 | "shasum": "" 558 | }, 559 | "require": { 560 | "php": ">=8.0.0" 561 | }, 562 | "suggest": { 563 | "fig/cache-util": "Provides some useful PSR-6 utilities" 564 | }, 565 | "default-branch": true, 566 | "type": "library", 567 | "extra": { 568 | "branch-alias": { 569 | "dev-master": "3.0.x-dev" 570 | } 571 | }, 572 | "autoload": { 573 | "psr-4": { 574 | "Psr\\Cache\\": "src/" 575 | } 576 | }, 577 | "notification-url": "https://packagist.org/downloads/", 578 | "license": [ 579 | "MIT" 580 | ], 581 | "authors": [ 582 | { 583 | "name": "PHP-FIG", 584 | "homepage": "https://www.php-fig.org/" 585 | } 586 | ], 587 | "description": "Common interface for caching libraries", 588 | "keywords": [ 589 | "cache", 590 | "psr", 591 | "psr-6" 592 | ], 593 | "support": { 594 | "source": "https://github.com/php-fig/cache/tree/master" 595 | }, 596 | "time": "2021-02-24T03:25:37+00:00" 597 | }, 598 | { 599 | "name": "psr/container", 600 | "version": "dev-master", 601 | "source": { 602 | "type": "git", 603 | "url": "https://github.com/php-fig/container.git", 604 | "reference": "90db7b9ac2a2c5b849fcb69dde58f3ae182c68f5" 605 | }, 606 | "dist": { 607 | "type": "zip", 608 | "url": "https://api.github.com/repos/php-fig/container/zipball/90db7b9ac2a2c5b849fcb69dde58f3ae182c68f5", 609 | "reference": "90db7b9ac2a2c5b849fcb69dde58f3ae182c68f5", 610 | "shasum": "" 611 | }, 612 | "require": { 613 | "php": ">=7.4.0" 614 | }, 615 | "default-branch": true, 616 | "type": "library", 617 | "extra": { 618 | "branch-alias": { 619 | "dev-master": "2.0.x-dev" 620 | } 621 | }, 622 | "autoload": { 623 | "psr-4": { 624 | "Psr\\Container\\": "src/" 625 | } 626 | }, 627 | "notification-url": "https://packagist.org/downloads/", 628 | "license": [ 629 | "MIT" 630 | ], 631 | "authors": [ 632 | { 633 | "name": "PHP-FIG", 634 | "homepage": "https://www.php-fig.org/" 635 | } 636 | ], 637 | "description": "Common Container Interface (PHP FIG PSR-11)", 638 | "homepage": "https://github.com/php-fig/container", 639 | "keywords": [ 640 | "PSR-11", 641 | "container", 642 | "container-interface", 643 | "container-interop", 644 | "psr" 645 | ], 646 | "support": { 647 | "issues": "https://github.com/php-fig/container/issues", 648 | "source": "https://github.com/php-fig/container/tree/master" 649 | }, 650 | "time": "2022-07-19T17:36:59+00:00" 651 | }, 652 | { 653 | "name": "psr/event-dispatcher", 654 | "version": "dev-master", 655 | "source": { 656 | "type": "git", 657 | "url": "https://github.com/php-fig/event-dispatcher.git", 658 | "reference": "e275e2d67d53964a3f13e056886ecd769edee021" 659 | }, 660 | "dist": { 661 | "type": "zip", 662 | "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/e275e2d67d53964a3f13e056886ecd769edee021", 663 | "reference": "e275e2d67d53964a3f13e056886ecd769edee021", 664 | "shasum": "" 665 | }, 666 | "require": { 667 | "php": ">=7.2.0" 668 | }, 669 | "suggest": { 670 | "fig/event-dispatcher-util": "Provides some useful PSR-14 utilities" 671 | }, 672 | "default-branch": true, 673 | "type": "library", 674 | "extra": { 675 | "branch-alias": { 676 | "dev-master": "1.0.x-dev" 677 | } 678 | }, 679 | "autoload": { 680 | "psr-4": { 681 | "Psr\\EventDispatcher\\": "src/" 682 | } 683 | }, 684 | "notification-url": "https://packagist.org/downloads/", 685 | "license": [ 686 | "MIT" 687 | ], 688 | "authors": [ 689 | { 690 | "name": "PHP-FIG", 691 | "homepage": "https://www.php-fig.org/" 692 | } 693 | ], 694 | "description": "Standard interfaces for event handling.", 695 | "keywords": [ 696 | "events", 697 | "psr", 698 | "psr-14" 699 | ], 700 | "support": { 701 | "source": "https://github.com/php-fig/event-dispatcher/tree/master" 702 | }, 703 | "time": "2022-06-29T17:22:39+00:00" 704 | }, 705 | { 706 | "name": "psr/log", 707 | "version": "dev-master", 708 | "source": { 709 | "type": "git", 710 | "url": "https://github.com/php-fig/log.git", 711 | "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" 712 | }, 713 | "dist": { 714 | "type": "zip", 715 | "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", 716 | "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", 717 | "shasum": "" 718 | }, 719 | "require": { 720 | "php": ">=8.0.0" 721 | }, 722 | "default-branch": true, 723 | "type": "library", 724 | "extra": { 725 | "branch-alias": { 726 | "dev-master": "3.x-dev" 727 | } 728 | }, 729 | "autoload": { 730 | "psr-4": { 731 | "Psr\\Log\\": "src" 732 | } 733 | }, 734 | "notification-url": "https://packagist.org/downloads/", 735 | "license": [ 736 | "MIT" 737 | ], 738 | "authors": [ 739 | { 740 | "name": "PHP-FIG", 741 | "homepage": "https://www.php-fig.org/" 742 | } 743 | ], 744 | "description": "Common interface for logging libraries", 745 | "homepage": "https://github.com/php-fig/log", 746 | "keywords": [ 747 | "log", 748 | "psr", 749 | "psr-3" 750 | ], 751 | "support": { 752 | "source": "https://github.com/php-fig/log/tree/3.0.0" 753 | }, 754 | "time": "2021-07-14T16:46:02+00:00" 755 | }, 756 | { 757 | "name": "symfony/dotenv", 758 | "version": "6.3.x-dev", 759 | "source": { 760 | "type": "git", 761 | "url": "https://github.com/symfony/dotenv.git", 762 | "reference": "070cdb03ac12b8f3b2db7e6b89145ccebfd54543" 763 | }, 764 | "dist": { 765 | "type": "zip", 766 | "url": "https://api.github.com/repos/symfony/dotenv/zipball/070cdb03ac12b8f3b2db7e6b89145ccebfd54543", 767 | "reference": "070cdb03ac12b8f3b2db7e6b89145ccebfd54543", 768 | "shasum": "" 769 | }, 770 | "require": { 771 | "php": ">=8.1" 772 | }, 773 | "conflict": { 774 | "symfony/console": "<5.4", 775 | "symfony/process": "<5.4" 776 | }, 777 | "require-dev": { 778 | "symfony/console": "^5.4|^6.0", 779 | "symfony/process": "^5.4|^6.0" 780 | }, 781 | "type": "library", 782 | "autoload": { 783 | "psr-4": { 784 | "Symfony\\Component\\Dotenv\\": "" 785 | }, 786 | "exclude-from-classmap": [ 787 | "/Tests/" 788 | ] 789 | }, 790 | "notification-url": "https://packagist.org/downloads/", 791 | "license": [ 792 | "MIT" 793 | ], 794 | "authors": [ 795 | { 796 | "name": "Fabien Potencier", 797 | "email": "fabien@symfony.com" 798 | }, 799 | { 800 | "name": "Symfony Community", 801 | "homepage": "https://symfony.com/contributors" 802 | } 803 | ], 804 | "description": "Registers environment variables from a .env file", 805 | "homepage": "https://symfony.com", 806 | "keywords": [ 807 | "dotenv", 808 | "env", 809 | "environment" 810 | ], 811 | "support": { 812 | "source": "https://github.com/symfony/dotenv/tree/6.3" 813 | }, 814 | "funding": [ 815 | { 816 | "url": "https://symfony.com/sponsor", 817 | "type": "custom" 818 | }, 819 | { 820 | "url": "https://github.com/fabpot", 821 | "type": "github" 822 | }, 823 | { 824 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 825 | "type": "tidelift" 826 | } 827 | ], 828 | "time": "2023-03-10T10:08:00+00:00" 829 | }, 830 | { 831 | "name": "symfony/polyfill-ctype", 832 | "version": "dev-main", 833 | "source": { 834 | "type": "git", 835 | "url": "https://github.com/symfony/polyfill-ctype.git", 836 | "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb" 837 | }, 838 | "dist": { 839 | "type": "zip", 840 | "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", 841 | "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", 842 | "shasum": "" 843 | }, 844 | "require": { 845 | "php": ">=7.1" 846 | }, 847 | "provide": { 848 | "ext-ctype": "*" 849 | }, 850 | "suggest": { 851 | "ext-ctype": "For best performance" 852 | }, 853 | "default-branch": true, 854 | "type": "library", 855 | "extra": { 856 | "branch-alias": { 857 | "dev-main": "1.28-dev" 858 | }, 859 | "thanks": { 860 | "name": "symfony/polyfill", 861 | "url": "https://github.com/symfony/polyfill" 862 | } 863 | }, 864 | "autoload": { 865 | "files": [ 866 | "bootstrap.php" 867 | ], 868 | "psr-4": { 869 | "Symfony\\Polyfill\\Ctype\\": "" 870 | } 871 | }, 872 | "notification-url": "https://packagist.org/downloads/", 873 | "license": [ 874 | "MIT" 875 | ], 876 | "authors": [ 877 | { 878 | "name": "Gert de Pagter", 879 | "email": "BackEndTea@gmail.com" 880 | }, 881 | { 882 | "name": "Symfony Community", 883 | "homepage": "https://symfony.com/contributors" 884 | } 885 | ], 886 | "description": "Symfony polyfill for ctype functions", 887 | "homepage": "https://symfony.com", 888 | "keywords": [ 889 | "compatibility", 890 | "ctype", 891 | "polyfill", 892 | "portable" 893 | ], 894 | "support": { 895 | "source": "https://github.com/symfony/polyfill-ctype/tree/main" 896 | }, 897 | "funding": [ 898 | { 899 | "url": "https://symfony.com/sponsor", 900 | "type": "custom" 901 | }, 902 | { 903 | "url": "https://github.com/fabpot", 904 | "type": "github" 905 | }, 906 | { 907 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 908 | "type": "tidelift" 909 | } 910 | ], 911 | "time": "2023-01-26T09:26:14+00:00" 912 | }, 913 | { 914 | "name": "symfony/polyfill-mbstring", 915 | "version": "dev-main", 916 | "source": { 917 | "type": "git", 918 | "url": "https://github.com/symfony/polyfill-mbstring.git", 919 | "reference": "f9c7affe77a00ae32ca127ca6833d034e6d33f25" 920 | }, 921 | "dist": { 922 | "type": "zip", 923 | "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/f9c7affe77a00ae32ca127ca6833d034e6d33f25", 924 | "reference": "f9c7affe77a00ae32ca127ca6833d034e6d33f25", 925 | "shasum": "" 926 | }, 927 | "require": { 928 | "php": ">=7.1" 929 | }, 930 | "provide": { 931 | "ext-mbstring": "*" 932 | }, 933 | "suggest": { 934 | "ext-mbstring": "For best performance" 935 | }, 936 | "default-branch": true, 937 | "type": "library", 938 | "extra": { 939 | "branch-alias": { 940 | "dev-main": "1.28-dev" 941 | }, 942 | "thanks": { 943 | "name": "symfony/polyfill", 944 | "url": "https://github.com/symfony/polyfill" 945 | } 946 | }, 947 | "autoload": { 948 | "files": [ 949 | "bootstrap.php" 950 | ], 951 | "psr-4": { 952 | "Symfony\\Polyfill\\Mbstring\\": "" 953 | } 954 | }, 955 | "notification-url": "https://packagist.org/downloads/", 956 | "license": [ 957 | "MIT" 958 | ], 959 | "authors": [ 960 | { 961 | "name": "Nicolas Grekas", 962 | "email": "p@tchwork.com" 963 | }, 964 | { 965 | "name": "Symfony Community", 966 | "homepage": "https://symfony.com/contributors" 967 | } 968 | ], 969 | "description": "Symfony polyfill for the Mbstring extension", 970 | "homepage": "https://symfony.com", 971 | "keywords": [ 972 | "compatibility", 973 | "mbstring", 974 | "polyfill", 975 | "portable", 976 | "shim" 977 | ], 978 | "support": { 979 | "source": "https://github.com/symfony/polyfill-mbstring/tree/main" 980 | }, 981 | "funding": [ 982 | { 983 | "url": "https://symfony.com/sponsor", 984 | "type": "custom" 985 | }, 986 | { 987 | "url": "https://github.com/fabpot", 988 | "type": "github" 989 | }, 990 | { 991 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 992 | "type": "tidelift" 993 | } 994 | ], 995 | "time": "2023-01-30T17:25:47+00:00" 996 | }, 997 | { 998 | "name": "twig/twig", 999 | "version": "3.x-dev", 1000 | "source": { 1001 | "type": "git", 1002 | "url": "https://github.com/twigphp/Twig.git", 1003 | "reference": "1812ca1ade2006c9238e7fa0562d438f401246a6" 1004 | }, 1005 | "dist": { 1006 | "type": "zip", 1007 | "url": "https://api.github.com/repos/twigphp/Twig/zipball/1812ca1ade2006c9238e7fa0562d438f401246a6", 1008 | "reference": "1812ca1ade2006c9238e7fa0562d438f401246a6", 1009 | "shasum": "" 1010 | }, 1011 | "require": { 1012 | "php": ">=7.2.5", 1013 | "symfony/polyfill-ctype": "^1.8", 1014 | "symfony/polyfill-mbstring": "^1.3" 1015 | }, 1016 | "require-dev": { 1017 | "psr/container": "^1.0|^2.0", 1018 | "symfony/phpunit-bridge": "^4.4.9|^5.0.9|^6.0" 1019 | }, 1020 | "default-branch": true, 1021 | "type": "library", 1022 | "autoload": { 1023 | "psr-4": { 1024 | "Twig\\": "src/" 1025 | } 1026 | }, 1027 | "notification-url": "https://packagist.org/downloads/", 1028 | "license": [ 1029 | "BSD-3-Clause" 1030 | ], 1031 | "authors": [ 1032 | { 1033 | "name": "Fabien Potencier", 1034 | "email": "fabien@symfony.com", 1035 | "homepage": "http://fabien.potencier.org", 1036 | "role": "Lead Developer" 1037 | }, 1038 | { 1039 | "name": "Twig Team", 1040 | "role": "Contributors" 1041 | }, 1042 | { 1043 | "name": "Armin Ronacher", 1044 | "email": "armin.ronacher@active-4.com", 1045 | "role": "Project Founder" 1046 | } 1047 | ], 1048 | "description": "Twig, the flexible, fast, and secure template language for PHP", 1049 | "homepage": "https://twig.symfony.com", 1050 | "keywords": [ 1051 | "templating" 1052 | ], 1053 | "support": { 1054 | "issues": "https://github.com/twigphp/Twig/issues", 1055 | "source": "https://github.com/twigphp/Twig/tree/3.x" 1056 | }, 1057 | "funding": [ 1058 | { 1059 | "url": "https://github.com/fabpot", 1060 | "type": "github" 1061 | }, 1062 | { 1063 | "url": "https://tidelift.com/funding/github/packagist/twig/twig", 1064 | "type": "tidelift" 1065 | } 1066 | ], 1067 | "time": "2023-04-14T11:18:53+00:00" 1068 | } 1069 | ], 1070 | "packages-dev": [ 1071 | { 1072 | "name": "symfony/var-dumper", 1073 | "version": "6.3.x-dev", 1074 | "source": { 1075 | "type": "git", 1076 | "url": "https://github.com/symfony/var-dumper.git", 1077 | "reference": "3f026afbd79aa419a889126333c25767ef354e09" 1078 | }, 1079 | "dist": { 1080 | "type": "zip", 1081 | "url": "https://api.github.com/repos/symfony/var-dumper/zipball/3f026afbd79aa419a889126333c25767ef354e09", 1082 | "reference": "3f026afbd79aa419a889126333c25767ef354e09", 1083 | "shasum": "" 1084 | }, 1085 | "require": { 1086 | "php": ">=8.1", 1087 | "symfony/polyfill-mbstring": "~1.0" 1088 | }, 1089 | "conflict": { 1090 | "phpunit/phpunit": "<5.4.3", 1091 | "symfony/console": "<5.4" 1092 | }, 1093 | "require-dev": { 1094 | "ext-iconv": "*", 1095 | "symfony/console": "^5.4|^6.0", 1096 | "symfony/process": "^5.4|^6.0", 1097 | "symfony/uid": "^5.4|^6.0", 1098 | "twig/twig": "^2.13|^3.0.4" 1099 | }, 1100 | "suggest": { 1101 | "ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).", 1102 | "ext-intl": "To show region name in time zone dump", 1103 | "symfony/console": "To use the ServerDumpCommand and/or the bin/var-dump-server script" 1104 | }, 1105 | "bin": [ 1106 | "Resources/bin/var-dump-server" 1107 | ], 1108 | "type": "library", 1109 | "autoload": { 1110 | "files": [ 1111 | "Resources/functions/dump.php" 1112 | ], 1113 | "psr-4": { 1114 | "Symfony\\Component\\VarDumper\\": "" 1115 | }, 1116 | "exclude-from-classmap": [ 1117 | "/Tests/" 1118 | ] 1119 | }, 1120 | "notification-url": "https://packagist.org/downloads/", 1121 | "license": [ 1122 | "MIT" 1123 | ], 1124 | "authors": [ 1125 | { 1126 | "name": "Nicolas Grekas", 1127 | "email": "p@tchwork.com" 1128 | }, 1129 | { 1130 | "name": "Symfony Community", 1131 | "homepage": "https://symfony.com/contributors" 1132 | } 1133 | ], 1134 | "description": "Provides mechanisms for walking through any arbitrary PHP variable", 1135 | "homepage": "https://symfony.com", 1136 | "keywords": [ 1137 | "debug", 1138 | "dump" 1139 | ], 1140 | "support": { 1141 | "source": "https://github.com/symfony/var-dumper/tree/6.3" 1142 | }, 1143 | "funding": [ 1144 | { 1145 | "url": "https://symfony.com/sponsor", 1146 | "type": "custom" 1147 | }, 1148 | { 1149 | "url": "https://github.com/fabpot", 1150 | "type": "github" 1151 | }, 1152 | { 1153 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 1154 | "type": "tidelift" 1155 | } 1156 | ], 1157 | "time": "2023-04-14T12:13:08+00:00" 1158 | } 1159 | ], 1160 | "aliases": [], 1161 | "minimum-stability": "dev", 1162 | "stability-flags": { 1163 | "garyclarketech/php-framework-pro": 20, 1164 | "symfony/var-dumper": 20 1165 | }, 1166 | "prefer-stable": false, 1167 | "prefer-lowest": false, 1168 | "platform": [], 1169 | "platform-dev": [], 1170 | "plugin-api-version": "2.3.0" 1171 | } 1172 | -------------------------------------------------------------------------------- /config/services.php: -------------------------------------------------------------------------------- 1 | load(dirname(__DIR__) . '/.env'); 5 | 6 | $container = new \League\Container\Container(); 7 | 8 | $container->delegate(new \League\Container\ReflectionContainer(true)); 9 | 10 | # parameters for application config 11 | $basePath = dirname(__DIR__); 12 | $container->add('basePath', new \League\Container\Argument\Literal\StringArgument($basePath)); 13 | $routes = include $basePath . '/routes/web.php'; 14 | $appEnv = $_SERVER['APP_ENV']; 15 | $templatesPath = $basePath . '/templates'; 16 | 17 | $container->add('APP_ENV', new \League\Container\Argument\Literal\StringArgument($appEnv)); 18 | $databaseUrl = 'sqlite:///' . $basePath . '/var/db.sqlite'; 19 | 20 | $container->add( 21 | 'base-commands-namespace', 22 | new \League\Container\Argument\Literal\StringArgument('GaryClarke\\Framework\\Console\\Command\\') 23 | ); 24 | 25 | # services 26 | 27 | $container->add( 28 | GaryClarke\Framework\Routing\RouterInterface::class, 29 | GaryClarke\Framework\Routing\Router::class 30 | ); 31 | 32 | $container->extend(GaryClarke\Framework\Routing\RouterInterface::class) 33 | ->addMethodCall( 34 | 'setRoutes', 35 | [new \League\Container\Argument\Literal\ArrayArgument($routes)] 36 | ); 37 | 38 | $container->add( 39 | \GaryClarke\Framework\Http\Middleware\RequestHandlerInterface::class, 40 | \GaryClarke\Framework\Http\Middleware\RequestHandler::class 41 | )->addArgument($container); 42 | 43 | $container->addShared(\GaryClarke\Framework\EventDispatcher\EventDispatcher::class); 44 | 45 | $container->add(GaryClarke\Framework\Http\Kernel::class) 46 | ->addArguments([ 47 | $container, 48 | \GaryClarke\Framework\Http\Middleware\RequestHandlerInterface::class, 49 | \GaryClarke\Framework\EventDispatcher\EventDispatcher::class 50 | ]); 51 | 52 | $container->add(\GaryClarke\Framework\Console\Application::class) 53 | ->addArgument($container); 54 | 55 | $container->add(\GaryClarke\Framework\Console\Kernel::class) 56 | ->addArguments([$container, \GaryClarke\Framework\Console\Application::class]); 57 | 58 | $container->addShared( 59 | \GaryClarke\Framework\Session\SessionInterface::class, 60 | \GaryClarke\Framework\Session\Session::class 61 | ); 62 | 63 | $container->add('template-renderer-factory', \GaryClarke\Framework\Template\TwigFactory::class) 64 | ->addArguments([ 65 | \GaryClarke\Framework\Session\SessionInterface::class, 66 | new \League\Container\Argument\Literal\StringArgument($templatesPath) 67 | ]); 68 | 69 | $container->addShared('twig', function () use ($container) { 70 | return $container->get('template-renderer-factory')->create(); 71 | }); 72 | 73 | $container->add(\GaryClarke\Framework\Controller\AbstractController::class); 74 | 75 | $container->inflector(\GaryClarke\Framework\Controller\AbstractController::class) 76 | ->invokeMethod('setContainer', [$container]); 77 | 78 | $container->add(\GaryClarke\Framework\Dbal\ConnectionFactory::class) 79 | ->addArguments([ 80 | new \League\Container\Argument\Literal\StringArgument($databaseUrl) 81 | ]); 82 | 83 | $container->addShared(\Doctrine\DBAL\Connection::class, function () use ($container): \Doctrine\DBAL\Connection { 84 | return $container->get(\GaryClarke\Framework\Dbal\ConnectionFactory::class)->create(); 85 | }); 86 | 87 | $container->add( 88 | 'database:migrations:migrate', 89 | \GaryClarke\Framework\Console\Command\MigrateDatabase::class 90 | )->addArguments([ 91 | \Doctrine\DBAL\Connection::class, 92 | new \League\Container\Argument\Literal\StringArgument($basePath . '/migrations') 93 | ]); 94 | 95 | $container->add(\GaryClarke\Framework\Http\Middleware\RouterDispatch::class) 96 | ->addArguments([ 97 | \GaryClarke\Framework\Routing\RouterInterface::class, 98 | $container 99 | ]); 100 | 101 | $container->add(\GaryClarke\Framework\Authentication\SessionAuthentication::class) 102 | ->addArguments([ 103 | \App\Repository\UserRepository::class, 104 | \GaryClarke\Framework\Session\SessionInterface::class 105 | ]); 106 | 107 | $container->add(\GaryClarke\Framework\Http\Middleware\ExtractRouteInfo::class) 108 | ->addArgument(new \League\Container\Argument\Literal\ArrayArgument($routes)); 109 | 110 | return $container; 111 | 112 | -------------------------------------------------------------------------------- /docker-compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | # server 3 | web: 4 | image: garyclarke/php-framework-web 5 | volumes: 6 | - ./public:/var/www/html/public 7 | ports: 8 | - "80:80" 9 | # application 10 | app: 11 | image: garyclarke/php-framework-app 12 | volumes: 13 | - .:/var/www/html 14 | extra_hosts: 15 | - host.docker.internal:host-gateway 16 | -------------------------------------------------------------------------------- /framework/.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ -------------------------------------------------------------------------------- /framework/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "garyclarketech/php-framework-pro", 3 | "description": "A PHP framework tutorial project", 4 | "minimum-stability": "dev", 5 | "license": "MIT", 6 | "authors": [ 7 | { 8 | "name": "Gary Clarke", 9 | "email": "info@garyclarke.tech" 10 | } 11 | ], 12 | "autoload": { 13 | "psr-4": { 14 | "GaryClarke\\Framework\\": "src/" 15 | } 16 | }, 17 | "autoload-dev": { 18 | "psr-4": { 19 | "GaryClarke\\Framework\\Tests\\": "tests" 20 | } 21 | }, 22 | "require-dev": { 23 | "symfony/var-dumper": "6.3.x-dev", 24 | "phpunit/phpunit": "10.1.x-dev" 25 | }, 26 | "require": { 27 | "nikic/fast-route": "dev-master", 28 | "psr/container": "2.0.x-dev", 29 | "league/container": "^4.0.0", 30 | "symfony/dotenv": "^6.0", 31 | "twig/twig": "^3.0", 32 | "doctrine/dbal": "^3.6", 33 | "psr/event-dispatcher": "^1.0" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /framework/composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "62ca7c15188f039d13e48db41cb02d50", 8 | "packages": [ 9 | { 10 | "name": "nikic/fast-route", 11 | "version": "dev-master", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/nikic/FastRoute.git", 15 | "reference": "ea5d3abe8b9fc7d63bb42b0384cb1db60972ff68" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/nikic/FastRoute/zipball/ea5d3abe8b9fc7d63bb42b0384cb1db60972ff68", 20 | "reference": "ea5d3abe8b9fc7d63bb42b0384cb1db60972ff68", 21 | "shasum": "" 22 | }, 23 | "require": { 24 | "php": ">=7.4.0" 25 | }, 26 | "require-dev": { 27 | "doctrine/coding-standard": "^9.0", 28 | "phpbench/phpbench": "^1.0", 29 | "phpstan/extension-installer": "^1.0", 30 | "phpstan/phpstan": "^0.12", 31 | "phpstan/phpstan-deprecation-rules": "^0.12", 32 | "phpstan/phpstan-phpunit": "^0.12", 33 | "phpstan/phpstan-strict-rules": "^0.12", 34 | "phpunit/phpunit": "^9.5" 35 | }, 36 | "suggest": { 37 | "ext-apcu": "To be able to use APCu cache driver" 38 | }, 39 | "default-branch": true, 40 | "type": "library", 41 | "extra": { 42 | "branch-alias": { 43 | "dev-master": "2.0-dev" 44 | } 45 | }, 46 | "autoload": { 47 | "files": [ 48 | "src/functions.php" 49 | ], 50 | "psr-4": { 51 | "FastRoute\\": "src/" 52 | } 53 | }, 54 | "notification-url": "https://packagist.org/downloads/", 55 | "license": [ 56 | "BSD-3-Clause" 57 | ], 58 | "authors": [ 59 | { 60 | "name": "Nikita Popov", 61 | "email": "nikic@php.net" 62 | } 63 | ], 64 | "description": "Fast request router for PHP", 65 | "keywords": [ 66 | "router", 67 | "routing" 68 | ], 69 | "support": { 70 | "issues": "https://github.com/nikic/FastRoute/issues", 71 | "source": "https://github.com/nikic/FastRoute/tree/master" 72 | }, 73 | "time": "2021-06-21T15:01:25+00:00" 74 | }, 75 | { 76 | "name": "psr/container", 77 | "version": "dev-master", 78 | "source": { 79 | "type": "git", 80 | "url": "https://github.com/php-fig/container.git", 81 | "reference": "90db7b9ac2a2c5b849fcb69dde58f3ae182c68f5" 82 | }, 83 | "dist": { 84 | "type": "zip", 85 | "url": "https://api.github.com/repos/php-fig/container/zipball/90db7b9ac2a2c5b849fcb69dde58f3ae182c68f5", 86 | "reference": "90db7b9ac2a2c5b849fcb69dde58f3ae182c68f5", 87 | "shasum": "" 88 | }, 89 | "require": { 90 | "php": ">=7.4.0" 91 | }, 92 | "default-branch": true, 93 | "type": "library", 94 | "extra": { 95 | "branch-alias": { 96 | "dev-master": "2.0.x-dev" 97 | } 98 | }, 99 | "autoload": { 100 | "psr-4": { 101 | "Psr\\Container\\": "src/" 102 | } 103 | }, 104 | "notification-url": "https://packagist.org/downloads/", 105 | "license": [ 106 | "MIT" 107 | ], 108 | "authors": [ 109 | { 110 | "name": "PHP-FIG", 111 | "homepage": "https://www.php-fig.org/" 112 | } 113 | ], 114 | "description": "Common Container Interface (PHP FIG PSR-11)", 115 | "homepage": "https://github.com/php-fig/container", 116 | "keywords": [ 117 | "PSR-11", 118 | "container", 119 | "container-interface", 120 | "container-interop", 121 | "psr" 122 | ], 123 | "support": { 124 | "issues": "https://github.com/php-fig/container/issues", 125 | "source": "https://github.com/php-fig/container/tree/master" 126 | }, 127 | "time": "2022-07-19T17:36:59+00:00" 128 | } 129 | ], 130 | "packages-dev": [ 131 | { 132 | "name": "myclabs/deep-copy", 133 | "version": "1.x-dev", 134 | "source": { 135 | "type": "git", 136 | "url": "https://github.com/myclabs/DeepCopy.git", 137 | "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614" 138 | }, 139 | "dist": { 140 | "type": "zip", 141 | "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/14daed4296fae74d9e3201d2c4925d1acb7aa614", 142 | "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614", 143 | "shasum": "" 144 | }, 145 | "require": { 146 | "php": "^7.1 || ^8.0" 147 | }, 148 | "conflict": { 149 | "doctrine/collections": "<1.6.8", 150 | "doctrine/common": "<2.13.3 || >=3,<3.2.2" 151 | }, 152 | "require-dev": { 153 | "doctrine/collections": "^1.6.8", 154 | "doctrine/common": "^2.13.3 || ^3.2.2", 155 | "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" 156 | }, 157 | "default-branch": true, 158 | "type": "library", 159 | "autoload": { 160 | "files": [ 161 | "src/DeepCopy/deep_copy.php" 162 | ], 163 | "psr-4": { 164 | "DeepCopy\\": "src/DeepCopy/" 165 | } 166 | }, 167 | "notification-url": "https://packagist.org/downloads/", 168 | "license": [ 169 | "MIT" 170 | ], 171 | "description": "Create deep copies (clones) of your objects", 172 | "keywords": [ 173 | "clone", 174 | "copy", 175 | "duplicate", 176 | "object", 177 | "object graph" 178 | ], 179 | "support": { 180 | "issues": "https://github.com/myclabs/DeepCopy/issues", 181 | "source": "https://github.com/myclabs/DeepCopy/tree/1.11.0" 182 | }, 183 | "funding": [ 184 | { 185 | "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", 186 | "type": "tidelift" 187 | } 188 | ], 189 | "time": "2022-03-03T13:19:32+00:00" 190 | }, 191 | { 192 | "name": "nikic/php-parser", 193 | "version": "4.x-dev", 194 | "source": { 195 | "type": "git", 196 | "url": "https://github.com/nikic/PHP-Parser.git", 197 | "reference": "570e980a201d8ed0236b0a62ddf2c9cbb2034039" 198 | }, 199 | "dist": { 200 | "type": "zip", 201 | "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/570e980a201d8ed0236b0a62ddf2c9cbb2034039", 202 | "reference": "570e980a201d8ed0236b0a62ddf2c9cbb2034039", 203 | "shasum": "" 204 | }, 205 | "require": { 206 | "ext-tokenizer": "*", 207 | "php": ">=7.0" 208 | }, 209 | "require-dev": { 210 | "ircmaxell/php-yacc": "^0.0.7", 211 | "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" 212 | }, 213 | "default-branch": true, 214 | "bin": [ 215 | "bin/php-parse" 216 | ], 217 | "type": "library", 218 | "extra": { 219 | "branch-alias": { 220 | "dev-master": "4.9-dev" 221 | } 222 | }, 223 | "autoload": { 224 | "psr-4": { 225 | "PhpParser\\": "lib/PhpParser" 226 | } 227 | }, 228 | "notification-url": "https://packagist.org/downloads/", 229 | "license": [ 230 | "BSD-3-Clause" 231 | ], 232 | "authors": [ 233 | { 234 | "name": "Nikita Popov" 235 | } 236 | ], 237 | "description": "A PHP parser written in PHP", 238 | "keywords": [ 239 | "parser", 240 | "php" 241 | ], 242 | "support": { 243 | "issues": "https://github.com/nikic/PHP-Parser/issues", 244 | "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.3" 245 | }, 246 | "time": "2023-01-16T22:05:37+00:00" 247 | }, 248 | { 249 | "name": "phar-io/manifest", 250 | "version": "dev-master", 251 | "source": { 252 | "type": "git", 253 | "url": "https://github.com/phar-io/manifest.git", 254 | "reference": "36d8a21e851a9512db2b086dc5ac2c61308f0138" 255 | }, 256 | "dist": { 257 | "type": "zip", 258 | "url": "https://api.github.com/repos/phar-io/manifest/zipball/36d8a21e851a9512db2b086dc5ac2c61308f0138", 259 | "reference": "36d8a21e851a9512db2b086dc5ac2c61308f0138", 260 | "shasum": "" 261 | }, 262 | "require": { 263 | "ext-dom": "*", 264 | "ext-libxml": "*", 265 | "ext-phar": "*", 266 | "ext-xmlwriter": "*", 267 | "phar-io/version": "^3.0.1", 268 | "php": "^7.2 || ^8.0" 269 | }, 270 | "default-branch": true, 271 | "type": "library", 272 | "extra": { 273 | "branch-alias": { 274 | "dev-master": "2.0.x-dev" 275 | } 276 | }, 277 | "autoload": { 278 | "classmap": [ 279 | "src/" 280 | ] 281 | }, 282 | "notification-url": "https://packagist.org/downloads/", 283 | "license": [ 284 | "BSD-3-Clause" 285 | ], 286 | "authors": [ 287 | { 288 | "name": "Arne Blankerts", 289 | "email": "arne@blankerts.de", 290 | "role": "Developer" 291 | }, 292 | { 293 | "name": "Sebastian Heuer", 294 | "email": "sebastian@phpeople.de", 295 | "role": "Developer" 296 | }, 297 | { 298 | "name": "Sebastian Bergmann", 299 | "email": "sebastian@phpunit.de", 300 | "role": "Developer" 301 | } 302 | ], 303 | "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", 304 | "support": { 305 | "issues": "https://github.com/phar-io/manifest/issues", 306 | "source": "https://github.com/phar-io/manifest/tree/master" 307 | }, 308 | "funding": [ 309 | { 310 | "url": "https://github.com/theseer", 311 | "type": "github" 312 | } 313 | ], 314 | "time": "2022-02-21T19:55:33+00:00" 315 | }, 316 | { 317 | "name": "phar-io/version", 318 | "version": "3.2.1", 319 | "source": { 320 | "type": "git", 321 | "url": "https://github.com/phar-io/version.git", 322 | "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" 323 | }, 324 | "dist": { 325 | "type": "zip", 326 | "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", 327 | "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", 328 | "shasum": "" 329 | }, 330 | "require": { 331 | "php": "^7.2 || ^8.0" 332 | }, 333 | "type": "library", 334 | "autoload": { 335 | "classmap": [ 336 | "src/" 337 | ] 338 | }, 339 | "notification-url": "https://packagist.org/downloads/", 340 | "license": [ 341 | "BSD-3-Clause" 342 | ], 343 | "authors": [ 344 | { 345 | "name": "Arne Blankerts", 346 | "email": "arne@blankerts.de", 347 | "role": "Developer" 348 | }, 349 | { 350 | "name": "Sebastian Heuer", 351 | "email": "sebastian@phpeople.de", 352 | "role": "Developer" 353 | }, 354 | { 355 | "name": "Sebastian Bergmann", 356 | "email": "sebastian@phpunit.de", 357 | "role": "Developer" 358 | } 359 | ], 360 | "description": "Library for handling version information and constraints", 361 | "support": { 362 | "issues": "https://github.com/phar-io/version/issues", 363 | "source": "https://github.com/phar-io/version/tree/3.2.1" 364 | }, 365 | "time": "2022-02-21T01:04:05+00:00" 366 | }, 367 | { 368 | "name": "phpunit/php-code-coverage", 369 | "version": "dev-main", 370 | "source": { 371 | "type": "git", 372 | "url": "https://github.com/sebastianbergmann/php-code-coverage.git", 373 | "reference": "bf4fbc9c13af7da12b3ea807574fb460f255daba" 374 | }, 375 | "dist": { 376 | "type": "zip", 377 | "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/bf4fbc9c13af7da12b3ea807574fb460f255daba", 378 | "reference": "bf4fbc9c13af7da12b3ea807574fb460f255daba", 379 | "shasum": "" 380 | }, 381 | "require": { 382 | "ext-dom": "*", 383 | "ext-libxml": "*", 384 | "ext-xmlwriter": "*", 385 | "nikic/php-parser": "^4.14", 386 | "php": ">=8.1", 387 | "phpunit/php-file-iterator": "^4.0", 388 | "phpunit/php-text-template": "^3.0", 389 | "sebastian/code-unit-reverse-lookup": "^3.0", 390 | "sebastian/complexity": "^3.0", 391 | "sebastian/environment": "^6.0", 392 | "sebastian/lines-of-code": "^2.0", 393 | "sebastian/version": "^4.0", 394 | "theseer/tokenizer": "^1.2.0" 395 | }, 396 | "require-dev": { 397 | "phpunit/phpunit": "^10.0" 398 | }, 399 | "suggest": { 400 | "ext-pcov": "*", 401 | "ext-xdebug": "*" 402 | }, 403 | "default-branch": true, 404 | "type": "library", 405 | "extra": { 406 | "branch-alias": { 407 | "dev-main": "10.0-dev" 408 | } 409 | }, 410 | "autoload": { 411 | "classmap": [ 412 | "src/" 413 | ] 414 | }, 415 | "notification-url": "https://packagist.org/downloads/", 416 | "license": [ 417 | "BSD-3-Clause" 418 | ], 419 | "authors": [ 420 | { 421 | "name": "Sebastian Bergmann", 422 | "email": "sebastian@phpunit.de", 423 | "role": "lead" 424 | } 425 | ], 426 | "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", 427 | "homepage": "https://github.com/sebastianbergmann/php-code-coverage", 428 | "keywords": [ 429 | "coverage", 430 | "testing", 431 | "xunit" 432 | ], 433 | "support": { 434 | "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", 435 | "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.0.0" 436 | }, 437 | "funding": [ 438 | { 439 | "url": "https://github.com/sebastianbergmann", 440 | "type": "github" 441 | } 442 | ], 443 | "time": "2023-02-03T07:14:34+00:00" 444 | }, 445 | { 446 | "name": "phpunit/php-file-iterator", 447 | "version": "dev-main", 448 | "source": { 449 | "type": "git", 450 | "url": "https://github.com/sebastianbergmann/php-file-iterator.git", 451 | "reference": "7d66d4e816d34e90acec9db9d8d94b5cfbfe926f" 452 | }, 453 | "dist": { 454 | "type": "zip", 455 | "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/7d66d4e816d34e90acec9db9d8d94b5cfbfe926f", 456 | "reference": "7d66d4e816d34e90acec9db9d8d94b5cfbfe926f", 457 | "shasum": "" 458 | }, 459 | "require": { 460 | "php": ">=8.1" 461 | }, 462 | "require-dev": { 463 | "phpunit/phpunit": "^10.0" 464 | }, 465 | "default-branch": true, 466 | "type": "library", 467 | "extra": { 468 | "branch-alias": { 469 | "dev-main": "4.0-dev" 470 | } 471 | }, 472 | "autoload": { 473 | "classmap": [ 474 | "src/" 475 | ] 476 | }, 477 | "notification-url": "https://packagist.org/downloads/", 478 | "license": [ 479 | "BSD-3-Clause" 480 | ], 481 | "authors": [ 482 | { 483 | "name": "Sebastian Bergmann", 484 | "email": "sebastian@phpunit.de", 485 | "role": "lead" 486 | } 487 | ], 488 | "description": "FilterIterator implementation that filters files based on a list of suffixes.", 489 | "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", 490 | "keywords": [ 491 | "filesystem", 492 | "iterator" 493 | ], 494 | "support": { 495 | "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", 496 | "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/4.0.0" 497 | }, 498 | "funding": [ 499 | { 500 | "url": "https://github.com/sebastianbergmann", 501 | "type": "github" 502 | } 503 | ], 504 | "time": "2023-02-03T06:55:11+00:00" 505 | }, 506 | { 507 | "name": "phpunit/php-invoker", 508 | "version": "dev-main", 509 | "source": { 510 | "type": "git", 511 | "url": "https://github.com/sebastianbergmann/php-invoker.git", 512 | "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7" 513 | }, 514 | "dist": { 515 | "type": "zip", 516 | "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", 517 | "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", 518 | "shasum": "" 519 | }, 520 | "require": { 521 | "php": ">=8.1" 522 | }, 523 | "require-dev": { 524 | "ext-pcntl": "*", 525 | "phpunit/phpunit": "^10.0" 526 | }, 527 | "suggest": { 528 | "ext-pcntl": "*" 529 | }, 530 | "default-branch": true, 531 | "type": "library", 532 | "extra": { 533 | "branch-alias": { 534 | "dev-main": "4.0-dev" 535 | } 536 | }, 537 | "autoload": { 538 | "classmap": [ 539 | "src/" 540 | ] 541 | }, 542 | "notification-url": "https://packagist.org/downloads/", 543 | "license": [ 544 | "BSD-3-Clause" 545 | ], 546 | "authors": [ 547 | { 548 | "name": "Sebastian Bergmann", 549 | "email": "sebastian@phpunit.de", 550 | "role": "lead" 551 | } 552 | ], 553 | "description": "Invoke callables with a timeout", 554 | "homepage": "https://github.com/sebastianbergmann/php-invoker/", 555 | "keywords": [ 556 | "process" 557 | ], 558 | "support": { 559 | "issues": "https://github.com/sebastianbergmann/php-invoker/issues", 560 | "source": "https://github.com/sebastianbergmann/php-invoker/tree/4.0.0" 561 | }, 562 | "funding": [ 563 | { 564 | "url": "https://github.com/sebastianbergmann", 565 | "type": "github" 566 | } 567 | ], 568 | "time": "2023-02-03T06:56:09+00:00" 569 | }, 570 | { 571 | "name": "phpunit/php-text-template", 572 | "version": "dev-main", 573 | "source": { 574 | "type": "git", 575 | "url": "https://github.com/sebastianbergmann/php-text-template.git", 576 | "reference": "9f3d3709577a527025f55bcf0f7ab8052c8bb37d" 577 | }, 578 | "dist": { 579 | "type": "zip", 580 | "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/9f3d3709577a527025f55bcf0f7ab8052c8bb37d", 581 | "reference": "9f3d3709577a527025f55bcf0f7ab8052c8bb37d", 582 | "shasum": "" 583 | }, 584 | "require": { 585 | "php": ">=8.1" 586 | }, 587 | "require-dev": { 588 | "phpunit/phpunit": "^10.0" 589 | }, 590 | "default-branch": true, 591 | "type": "library", 592 | "extra": { 593 | "branch-alias": { 594 | "dev-main": "3.0-dev" 595 | } 596 | }, 597 | "autoload": { 598 | "classmap": [ 599 | "src/" 600 | ] 601 | }, 602 | "notification-url": "https://packagist.org/downloads/", 603 | "license": [ 604 | "BSD-3-Clause" 605 | ], 606 | "authors": [ 607 | { 608 | "name": "Sebastian Bergmann", 609 | "email": "sebastian@phpunit.de", 610 | "role": "lead" 611 | } 612 | ], 613 | "description": "Simple template engine.", 614 | "homepage": "https://github.com/sebastianbergmann/php-text-template/", 615 | "keywords": [ 616 | "template" 617 | ], 618 | "support": { 619 | "issues": "https://github.com/sebastianbergmann/php-text-template/issues", 620 | "source": "https://github.com/sebastianbergmann/php-text-template/tree/3.0.0" 621 | }, 622 | "funding": [ 623 | { 624 | "url": "https://github.com/sebastianbergmann", 625 | "type": "github" 626 | } 627 | ], 628 | "time": "2023-02-03T06:56:46+00:00" 629 | }, 630 | { 631 | "name": "phpunit/php-timer", 632 | "version": "dev-main", 633 | "source": { 634 | "type": "git", 635 | "url": "https://github.com/sebastianbergmann/php-timer.git", 636 | "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d" 637 | }, 638 | "dist": { 639 | "type": "zip", 640 | "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/e2a2d67966e740530f4a3343fe2e030ffdc1161d", 641 | "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d", 642 | "shasum": "" 643 | }, 644 | "require": { 645 | "php": ">=8.1" 646 | }, 647 | "require-dev": { 648 | "phpunit/phpunit": "^10.0" 649 | }, 650 | "default-branch": true, 651 | "type": "library", 652 | "extra": { 653 | "branch-alias": { 654 | "dev-main": "6.0-dev" 655 | } 656 | }, 657 | "autoload": { 658 | "classmap": [ 659 | "src/" 660 | ] 661 | }, 662 | "notification-url": "https://packagist.org/downloads/", 663 | "license": [ 664 | "BSD-3-Clause" 665 | ], 666 | "authors": [ 667 | { 668 | "name": "Sebastian Bergmann", 669 | "email": "sebastian@phpunit.de", 670 | "role": "lead" 671 | } 672 | ], 673 | "description": "Utility class for timing", 674 | "homepage": "https://github.com/sebastianbergmann/php-timer/", 675 | "keywords": [ 676 | "timer" 677 | ], 678 | "support": { 679 | "issues": "https://github.com/sebastianbergmann/php-timer/issues", 680 | "source": "https://github.com/sebastianbergmann/php-timer/tree/6.0.0" 681 | }, 682 | "funding": [ 683 | { 684 | "url": "https://github.com/sebastianbergmann", 685 | "type": "github" 686 | } 687 | ], 688 | "time": "2023-02-03T06:57:52+00:00" 689 | }, 690 | { 691 | "name": "phpunit/phpunit", 692 | "version": "dev-main", 693 | "source": { 694 | "type": "git", 695 | "url": "https://github.com/sebastianbergmann/phpunit.git", 696 | "reference": "8b1361ca3050006eb279390813eba842f8762ad7" 697 | }, 698 | "dist": { 699 | "type": "zip", 700 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/8b1361ca3050006eb279390813eba842f8762ad7", 701 | "reference": "8b1361ca3050006eb279390813eba842f8762ad7", 702 | "shasum": "" 703 | }, 704 | "require": { 705 | "ext-dom": "*", 706 | "ext-json": "*", 707 | "ext-libxml": "*", 708 | "ext-mbstring": "*", 709 | "ext-xml": "*", 710 | "ext-xmlwriter": "*", 711 | "myclabs/deep-copy": "^1.10.1", 712 | "phar-io/manifest": "^2.0.3", 713 | "phar-io/version": "^3.0.2", 714 | "php": ">=8.1", 715 | "phpunit/php-code-coverage": "^10.0", 716 | "phpunit/php-file-iterator": "^4.0", 717 | "phpunit/php-invoker": "^4.0", 718 | "phpunit/php-text-template": "^3.0", 719 | "phpunit/php-timer": "^6.0", 720 | "sebastian/cli-parser": "^2.0", 721 | "sebastian/code-unit": "^2.0", 722 | "sebastian/comparator": "^5.0", 723 | "sebastian/diff": "^5.0", 724 | "sebastian/environment": "^6.0", 725 | "sebastian/exporter": "^5.0", 726 | "sebastian/global-state": "^6.0", 727 | "sebastian/object-enumerator": "^5.0", 728 | "sebastian/recursion-context": "^5.0", 729 | "sebastian/type": "^4.0", 730 | "sebastian/version": "^4.0" 731 | }, 732 | "suggest": { 733 | "ext-soap": "*" 734 | }, 735 | "default-branch": true, 736 | "bin": [ 737 | "phpunit" 738 | ], 739 | "type": "library", 740 | "extra": { 741 | "branch-alias": { 742 | "dev-main": "10.1-dev" 743 | } 744 | }, 745 | "autoload": { 746 | "files": [ 747 | "src/Framework/Assert/Functions.php" 748 | ], 749 | "classmap": [ 750 | "src/" 751 | ] 752 | }, 753 | "notification-url": "https://packagist.org/downloads/", 754 | "license": [ 755 | "BSD-3-Clause" 756 | ], 757 | "authors": [ 758 | { 759 | "name": "Sebastian Bergmann", 760 | "email": "sebastian@phpunit.de", 761 | "role": "lead" 762 | } 763 | ], 764 | "description": "The PHP Unit Testing framework.", 765 | "homepage": "https://phpunit.de/", 766 | "keywords": [ 767 | "phpunit", 768 | "testing", 769 | "xunit" 770 | ], 771 | "support": { 772 | "issues": "https://github.com/sebastianbergmann/phpunit/issues", 773 | "source": "https://github.com/sebastianbergmann/phpunit/tree/main" 774 | }, 775 | "funding": [ 776 | { 777 | "url": "https://phpunit.de/sponsors.html", 778 | "type": "custom" 779 | }, 780 | { 781 | "url": "https://github.com/sebastianbergmann", 782 | "type": "github" 783 | }, 784 | { 785 | "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", 786 | "type": "tidelift" 787 | } 788 | ], 789 | "time": "2023-02-03T19:42:15+00:00" 790 | }, 791 | { 792 | "name": "sebastian/cli-parser", 793 | "version": "dev-main", 794 | "source": { 795 | "type": "git", 796 | "url": "https://github.com/sebastianbergmann/cli-parser.git", 797 | "reference": "efdc130dbbbb8ef0b545a994fd811725c5282cae" 798 | }, 799 | "dist": { 800 | "type": "zip", 801 | "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/efdc130dbbbb8ef0b545a994fd811725c5282cae", 802 | "reference": "efdc130dbbbb8ef0b545a994fd811725c5282cae", 803 | "shasum": "" 804 | }, 805 | "require": { 806 | "php": ">=8.1" 807 | }, 808 | "require-dev": { 809 | "phpunit/phpunit": "^10.0" 810 | }, 811 | "default-branch": true, 812 | "type": "library", 813 | "extra": { 814 | "branch-alias": { 815 | "dev-main": "2.0-dev" 816 | } 817 | }, 818 | "autoload": { 819 | "classmap": [ 820 | "src/" 821 | ] 822 | }, 823 | "notification-url": "https://packagist.org/downloads/", 824 | "license": [ 825 | "BSD-3-Clause" 826 | ], 827 | "authors": [ 828 | { 829 | "name": "Sebastian Bergmann", 830 | "email": "sebastian@phpunit.de", 831 | "role": "lead" 832 | } 833 | ], 834 | "description": "Library for parsing CLI options", 835 | "homepage": "https://github.com/sebastianbergmann/cli-parser", 836 | "support": { 837 | "issues": "https://github.com/sebastianbergmann/cli-parser/issues", 838 | "source": "https://github.com/sebastianbergmann/cli-parser/tree/2.0.0" 839 | }, 840 | "funding": [ 841 | { 842 | "url": "https://github.com/sebastianbergmann", 843 | "type": "github" 844 | } 845 | ], 846 | "time": "2023-02-03T06:58:15+00:00" 847 | }, 848 | { 849 | "name": "sebastian/code-unit", 850 | "version": "dev-main", 851 | "source": { 852 | "type": "git", 853 | "url": "https://github.com/sebastianbergmann/code-unit.git", 854 | "reference": "a81fee9eef0b7a76af11d121767abc44c104e503" 855 | }, 856 | "dist": { 857 | "type": "zip", 858 | "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/a81fee9eef0b7a76af11d121767abc44c104e503", 859 | "reference": "a81fee9eef0b7a76af11d121767abc44c104e503", 860 | "shasum": "" 861 | }, 862 | "require": { 863 | "php": ">=8.1" 864 | }, 865 | "require-dev": { 866 | "phpunit/phpunit": "^10.0" 867 | }, 868 | "default-branch": true, 869 | "type": "library", 870 | "extra": { 871 | "branch-alias": { 872 | "dev-main": "2.0-dev" 873 | } 874 | }, 875 | "autoload": { 876 | "classmap": [ 877 | "src/" 878 | ] 879 | }, 880 | "notification-url": "https://packagist.org/downloads/", 881 | "license": [ 882 | "BSD-3-Clause" 883 | ], 884 | "authors": [ 885 | { 886 | "name": "Sebastian Bergmann", 887 | "email": "sebastian@phpunit.de", 888 | "role": "lead" 889 | } 890 | ], 891 | "description": "Collection of value objects that represent the PHP code units", 892 | "homepage": "https://github.com/sebastianbergmann/code-unit", 893 | "support": { 894 | "issues": "https://github.com/sebastianbergmann/code-unit/issues", 895 | "source": "https://github.com/sebastianbergmann/code-unit/tree/2.0.0" 896 | }, 897 | "funding": [ 898 | { 899 | "url": "https://github.com/sebastianbergmann", 900 | "type": "github" 901 | } 902 | ], 903 | "time": "2023-02-03T06:58:43+00:00" 904 | }, 905 | { 906 | "name": "sebastian/code-unit-reverse-lookup", 907 | "version": "dev-main", 908 | "source": { 909 | "type": "git", 910 | "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", 911 | "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d" 912 | }, 913 | "dist": { 914 | "type": "zip", 915 | "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", 916 | "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", 917 | "shasum": "" 918 | }, 919 | "require": { 920 | "php": ">=8.1" 921 | }, 922 | "require-dev": { 923 | "phpunit/phpunit": "^10.0" 924 | }, 925 | "default-branch": true, 926 | "type": "library", 927 | "extra": { 928 | "branch-alias": { 929 | "dev-main": "3.0-dev" 930 | } 931 | }, 932 | "autoload": { 933 | "classmap": [ 934 | "src/" 935 | ] 936 | }, 937 | "notification-url": "https://packagist.org/downloads/", 938 | "license": [ 939 | "BSD-3-Clause" 940 | ], 941 | "authors": [ 942 | { 943 | "name": "Sebastian Bergmann", 944 | "email": "sebastian@phpunit.de" 945 | } 946 | ], 947 | "description": "Looks up which function or method a line of code belongs to", 948 | "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", 949 | "support": { 950 | "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", 951 | "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/3.0.0" 952 | }, 953 | "funding": [ 954 | { 955 | "url": "https://github.com/sebastianbergmann", 956 | "type": "github" 957 | } 958 | ], 959 | "time": "2023-02-03T06:59:15+00:00" 960 | }, 961 | { 962 | "name": "sebastian/comparator", 963 | "version": "dev-main", 964 | "source": { 965 | "type": "git", 966 | "url": "https://github.com/sebastianbergmann/comparator.git", 967 | "reference": "72f01e6586e0caf6af81297897bd112eb7e9627c" 968 | }, 969 | "dist": { 970 | "type": "zip", 971 | "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/72f01e6586e0caf6af81297897bd112eb7e9627c", 972 | "reference": "72f01e6586e0caf6af81297897bd112eb7e9627c", 973 | "shasum": "" 974 | }, 975 | "require": { 976 | "ext-dom": "*", 977 | "ext-mbstring": "*", 978 | "php": ">=8.1", 979 | "sebastian/diff": "^5.0", 980 | "sebastian/exporter": "^5.0" 981 | }, 982 | "require-dev": { 983 | "phpunit/phpunit": "^10.0" 984 | }, 985 | "default-branch": true, 986 | "type": "library", 987 | "extra": { 988 | "branch-alias": { 989 | "dev-main": "5.0-dev" 990 | } 991 | }, 992 | "autoload": { 993 | "classmap": [ 994 | "src/" 995 | ] 996 | }, 997 | "notification-url": "https://packagist.org/downloads/", 998 | "license": [ 999 | "BSD-3-Clause" 1000 | ], 1001 | "authors": [ 1002 | { 1003 | "name": "Sebastian Bergmann", 1004 | "email": "sebastian@phpunit.de" 1005 | }, 1006 | { 1007 | "name": "Jeff Welch", 1008 | "email": "whatthejeff@gmail.com" 1009 | }, 1010 | { 1011 | "name": "Volker Dusch", 1012 | "email": "github@wallbash.com" 1013 | }, 1014 | { 1015 | "name": "Bernhard Schussek", 1016 | "email": "bschussek@2bepublished.at" 1017 | } 1018 | ], 1019 | "description": "Provides the functionality to compare PHP values for equality", 1020 | "homepage": "https://github.com/sebastianbergmann/comparator", 1021 | "keywords": [ 1022 | "comparator", 1023 | "compare", 1024 | "equality" 1025 | ], 1026 | "support": { 1027 | "issues": "https://github.com/sebastianbergmann/comparator/issues", 1028 | "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.0" 1029 | }, 1030 | "funding": [ 1031 | { 1032 | "url": "https://github.com/sebastianbergmann", 1033 | "type": "github" 1034 | } 1035 | ], 1036 | "time": "2023-02-03T07:07:16+00:00" 1037 | }, 1038 | { 1039 | "name": "sebastian/complexity", 1040 | "version": "dev-main", 1041 | "source": { 1042 | "type": "git", 1043 | "url": "https://github.com/sebastianbergmann/complexity.git", 1044 | "reference": "e67d240970c9dc7ea7b2123a6d520e334dd61dc6" 1045 | }, 1046 | "dist": { 1047 | "type": "zip", 1048 | "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/e67d240970c9dc7ea7b2123a6d520e334dd61dc6", 1049 | "reference": "e67d240970c9dc7ea7b2123a6d520e334dd61dc6", 1050 | "shasum": "" 1051 | }, 1052 | "require": { 1053 | "nikic/php-parser": "^4.10", 1054 | "php": ">=8.1" 1055 | }, 1056 | "require-dev": { 1057 | "phpunit/phpunit": "^10.0" 1058 | }, 1059 | "default-branch": true, 1060 | "type": "library", 1061 | "extra": { 1062 | "branch-alias": { 1063 | "dev-main": "3.0-dev" 1064 | } 1065 | }, 1066 | "autoload": { 1067 | "classmap": [ 1068 | "src/" 1069 | ] 1070 | }, 1071 | "notification-url": "https://packagist.org/downloads/", 1072 | "license": [ 1073 | "BSD-3-Clause" 1074 | ], 1075 | "authors": [ 1076 | { 1077 | "name": "Sebastian Bergmann", 1078 | "email": "sebastian@phpunit.de", 1079 | "role": "lead" 1080 | } 1081 | ], 1082 | "description": "Library for calculating the complexity of PHP code units", 1083 | "homepage": "https://github.com/sebastianbergmann/complexity", 1084 | "support": { 1085 | "issues": "https://github.com/sebastianbergmann/complexity/issues", 1086 | "source": "https://github.com/sebastianbergmann/complexity/tree/3.0.0" 1087 | }, 1088 | "funding": [ 1089 | { 1090 | "url": "https://github.com/sebastianbergmann", 1091 | "type": "github" 1092 | } 1093 | ], 1094 | "time": "2023-02-03T06:59:47+00:00" 1095 | }, 1096 | { 1097 | "name": "sebastian/diff", 1098 | "version": "dev-main", 1099 | "source": { 1100 | "type": "git", 1101 | "url": "https://github.com/sebastianbergmann/diff.git", 1102 | "reference": "70dd1b20bc198da394ad542e988381b44e64e39f" 1103 | }, 1104 | "dist": { 1105 | "type": "zip", 1106 | "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/70dd1b20bc198da394ad542e988381b44e64e39f", 1107 | "reference": "70dd1b20bc198da394ad542e988381b44e64e39f", 1108 | "shasum": "" 1109 | }, 1110 | "require": { 1111 | "php": ">=8.1" 1112 | }, 1113 | "require-dev": { 1114 | "phpunit/phpunit": "^10.0", 1115 | "symfony/process": "^4.2 || ^5" 1116 | }, 1117 | "default-branch": true, 1118 | "type": "library", 1119 | "extra": { 1120 | "branch-alias": { 1121 | "dev-main": "5.0-dev" 1122 | } 1123 | }, 1124 | "autoload": { 1125 | "classmap": [ 1126 | "src/" 1127 | ] 1128 | }, 1129 | "notification-url": "https://packagist.org/downloads/", 1130 | "license": [ 1131 | "BSD-3-Clause" 1132 | ], 1133 | "authors": [ 1134 | { 1135 | "name": "Sebastian Bergmann", 1136 | "email": "sebastian@phpunit.de" 1137 | }, 1138 | { 1139 | "name": "Kore Nordmann", 1140 | "email": "mail@kore-nordmann.de" 1141 | } 1142 | ], 1143 | "description": "Diff implementation", 1144 | "homepage": "https://github.com/sebastianbergmann/diff", 1145 | "keywords": [ 1146 | "diff", 1147 | "udiff", 1148 | "unidiff", 1149 | "unified diff" 1150 | ], 1151 | "support": { 1152 | "issues": "https://github.com/sebastianbergmann/diff/issues", 1153 | "source": "https://github.com/sebastianbergmann/diff/tree/5.0.0" 1154 | }, 1155 | "funding": [ 1156 | { 1157 | "url": "https://github.com/sebastianbergmann", 1158 | "type": "github" 1159 | } 1160 | ], 1161 | "time": "2023-02-03T07:00:31+00:00" 1162 | }, 1163 | { 1164 | "name": "sebastian/environment", 1165 | "version": "dev-main", 1166 | "source": { 1167 | "type": "git", 1168 | "url": "https://github.com/sebastianbergmann/environment.git", 1169 | "reference": "b6f3694c6386c7959915a0037652e0c40f6f69cc" 1170 | }, 1171 | "dist": { 1172 | "type": "zip", 1173 | "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/b6f3694c6386c7959915a0037652e0c40f6f69cc", 1174 | "reference": "b6f3694c6386c7959915a0037652e0c40f6f69cc", 1175 | "shasum": "" 1176 | }, 1177 | "require": { 1178 | "php": ">=8.1" 1179 | }, 1180 | "require-dev": { 1181 | "phpunit/phpunit": "^10.0" 1182 | }, 1183 | "suggest": { 1184 | "ext-posix": "*" 1185 | }, 1186 | "default-branch": true, 1187 | "type": "library", 1188 | "extra": { 1189 | "branch-alias": { 1190 | "dev-main": "6.0-dev" 1191 | } 1192 | }, 1193 | "autoload": { 1194 | "classmap": [ 1195 | "src/" 1196 | ] 1197 | }, 1198 | "notification-url": "https://packagist.org/downloads/", 1199 | "license": [ 1200 | "BSD-3-Clause" 1201 | ], 1202 | "authors": [ 1203 | { 1204 | "name": "Sebastian Bergmann", 1205 | "email": "sebastian@phpunit.de" 1206 | } 1207 | ], 1208 | "description": "Provides functionality to handle HHVM/PHP environments", 1209 | "homepage": "https://github.com/sebastianbergmann/environment", 1210 | "keywords": [ 1211 | "Xdebug", 1212 | "environment", 1213 | "hhvm" 1214 | ], 1215 | "support": { 1216 | "issues": "https://github.com/sebastianbergmann/environment/issues", 1217 | "source": "https://github.com/sebastianbergmann/environment/tree/6.0.0" 1218 | }, 1219 | "funding": [ 1220 | { 1221 | "url": "https://github.com/sebastianbergmann", 1222 | "type": "github" 1223 | } 1224 | ], 1225 | "time": "2023-02-03T07:03:04+00:00" 1226 | }, 1227 | { 1228 | "name": "sebastian/exporter", 1229 | "version": "dev-main", 1230 | "source": { 1231 | "type": "git", 1232 | "url": "https://github.com/sebastianbergmann/exporter.git", 1233 | "reference": "f3ec4bf931c0b31e5b413f5b4fc970a7d03338c0" 1234 | }, 1235 | "dist": { 1236 | "type": "zip", 1237 | "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/f3ec4bf931c0b31e5b413f5b4fc970a7d03338c0", 1238 | "reference": "f3ec4bf931c0b31e5b413f5b4fc970a7d03338c0", 1239 | "shasum": "" 1240 | }, 1241 | "require": { 1242 | "ext-mbstring": "*", 1243 | "php": ">=8.1", 1244 | "sebastian/recursion-context": "^5.0" 1245 | }, 1246 | "require-dev": { 1247 | "phpunit/phpunit": "^10.0" 1248 | }, 1249 | "default-branch": true, 1250 | "type": "library", 1251 | "extra": { 1252 | "branch-alias": { 1253 | "dev-main": "5.0-dev" 1254 | } 1255 | }, 1256 | "autoload": { 1257 | "classmap": [ 1258 | "src/" 1259 | ] 1260 | }, 1261 | "notification-url": "https://packagist.org/downloads/", 1262 | "license": [ 1263 | "BSD-3-Clause" 1264 | ], 1265 | "authors": [ 1266 | { 1267 | "name": "Sebastian Bergmann", 1268 | "email": "sebastian@phpunit.de" 1269 | }, 1270 | { 1271 | "name": "Jeff Welch", 1272 | "email": "whatthejeff@gmail.com" 1273 | }, 1274 | { 1275 | "name": "Volker Dusch", 1276 | "email": "github@wallbash.com" 1277 | }, 1278 | { 1279 | "name": "Adam Harvey", 1280 | "email": "aharvey@php.net" 1281 | }, 1282 | { 1283 | "name": "Bernhard Schussek", 1284 | "email": "bschussek@gmail.com" 1285 | } 1286 | ], 1287 | "description": "Provides the functionality to export PHP variables for visualization", 1288 | "homepage": "https://www.github.com/sebastianbergmann/exporter", 1289 | "keywords": [ 1290 | "export", 1291 | "exporter" 1292 | ], 1293 | "support": { 1294 | "issues": "https://github.com/sebastianbergmann/exporter/issues", 1295 | "source": "https://github.com/sebastianbergmann/exporter/tree/5.0.0" 1296 | }, 1297 | "funding": [ 1298 | { 1299 | "url": "https://github.com/sebastianbergmann", 1300 | "type": "github" 1301 | } 1302 | ], 1303 | "time": "2023-02-03T07:06:49+00:00" 1304 | }, 1305 | { 1306 | "name": "sebastian/global-state", 1307 | "version": "dev-main", 1308 | "source": { 1309 | "type": "git", 1310 | "url": "https://github.com/sebastianbergmann/global-state.git", 1311 | "reference": "aab257c712de87b90194febd52e4d184551c2d44" 1312 | }, 1313 | "dist": { 1314 | "type": "zip", 1315 | "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/aab257c712de87b90194febd52e4d184551c2d44", 1316 | "reference": "aab257c712de87b90194febd52e4d184551c2d44", 1317 | "shasum": "" 1318 | }, 1319 | "require": { 1320 | "php": ">=8.1", 1321 | "sebastian/object-reflector": "^3.0", 1322 | "sebastian/recursion-context": "^5.0" 1323 | }, 1324 | "require-dev": { 1325 | "ext-dom": "*", 1326 | "phpunit/phpunit": "^10.0" 1327 | }, 1328 | "default-branch": true, 1329 | "type": "library", 1330 | "extra": { 1331 | "branch-alias": { 1332 | "dev-main": "6.0-dev" 1333 | } 1334 | }, 1335 | "autoload": { 1336 | "classmap": [ 1337 | "src/" 1338 | ] 1339 | }, 1340 | "notification-url": "https://packagist.org/downloads/", 1341 | "license": [ 1342 | "BSD-3-Clause" 1343 | ], 1344 | "authors": [ 1345 | { 1346 | "name": "Sebastian Bergmann", 1347 | "email": "sebastian@phpunit.de" 1348 | } 1349 | ], 1350 | "description": "Snapshotting of global state", 1351 | "homepage": "http://www.github.com/sebastianbergmann/global-state", 1352 | "keywords": [ 1353 | "global state" 1354 | ], 1355 | "support": { 1356 | "issues": "https://github.com/sebastianbergmann/global-state/issues", 1357 | "source": "https://github.com/sebastianbergmann/global-state/tree/6.0.0" 1358 | }, 1359 | "funding": [ 1360 | { 1361 | "url": "https://github.com/sebastianbergmann", 1362 | "type": "github" 1363 | } 1364 | ], 1365 | "time": "2023-02-03T07:07:38+00:00" 1366 | }, 1367 | { 1368 | "name": "sebastian/lines-of-code", 1369 | "version": "dev-main", 1370 | "source": { 1371 | "type": "git", 1372 | "url": "https://github.com/sebastianbergmann/lines-of-code.git", 1373 | "reference": "17c4d940ecafb3d15d2cf916f4108f664e28b130" 1374 | }, 1375 | "dist": { 1376 | "type": "zip", 1377 | "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/17c4d940ecafb3d15d2cf916f4108f664e28b130", 1378 | "reference": "17c4d940ecafb3d15d2cf916f4108f664e28b130", 1379 | "shasum": "" 1380 | }, 1381 | "require": { 1382 | "nikic/php-parser": "^4.10", 1383 | "php": ">=8.1" 1384 | }, 1385 | "require-dev": { 1386 | "phpunit/phpunit": "^10.0" 1387 | }, 1388 | "default-branch": true, 1389 | "type": "library", 1390 | "extra": { 1391 | "branch-alias": { 1392 | "dev-main": "2.0-dev" 1393 | } 1394 | }, 1395 | "autoload": { 1396 | "classmap": [ 1397 | "src/" 1398 | ] 1399 | }, 1400 | "notification-url": "https://packagist.org/downloads/", 1401 | "license": [ 1402 | "BSD-3-Clause" 1403 | ], 1404 | "authors": [ 1405 | { 1406 | "name": "Sebastian Bergmann", 1407 | "email": "sebastian@phpunit.de", 1408 | "role": "lead" 1409 | } 1410 | ], 1411 | "description": "Library for counting the lines of code in PHP source code", 1412 | "homepage": "https://github.com/sebastianbergmann/lines-of-code", 1413 | "support": { 1414 | "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", 1415 | "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.0" 1416 | }, 1417 | "funding": [ 1418 | { 1419 | "url": "https://github.com/sebastianbergmann", 1420 | "type": "github" 1421 | } 1422 | ], 1423 | "time": "2023-02-03T07:08:02+00:00" 1424 | }, 1425 | { 1426 | "name": "sebastian/object-enumerator", 1427 | "version": "dev-main", 1428 | "source": { 1429 | "type": "git", 1430 | "url": "https://github.com/sebastianbergmann/object-enumerator.git", 1431 | "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906" 1432 | }, 1433 | "dist": { 1434 | "type": "zip", 1435 | "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/202d0e344a580d7f7d04b3fafce6933e59dae906", 1436 | "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906", 1437 | "shasum": "" 1438 | }, 1439 | "require": { 1440 | "php": ">=8.1", 1441 | "sebastian/object-reflector": "^3.0", 1442 | "sebastian/recursion-context": "^5.0" 1443 | }, 1444 | "require-dev": { 1445 | "phpunit/phpunit": "^10.0" 1446 | }, 1447 | "default-branch": true, 1448 | "type": "library", 1449 | "extra": { 1450 | "branch-alias": { 1451 | "dev-main": "5.0-dev" 1452 | } 1453 | }, 1454 | "autoload": { 1455 | "classmap": [ 1456 | "src/" 1457 | ] 1458 | }, 1459 | "notification-url": "https://packagist.org/downloads/", 1460 | "license": [ 1461 | "BSD-3-Clause" 1462 | ], 1463 | "authors": [ 1464 | { 1465 | "name": "Sebastian Bergmann", 1466 | "email": "sebastian@phpunit.de" 1467 | } 1468 | ], 1469 | "description": "Traverses array structures and object graphs to enumerate all referenced objects", 1470 | "homepage": "https://github.com/sebastianbergmann/object-enumerator/", 1471 | "support": { 1472 | "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", 1473 | "source": "https://github.com/sebastianbergmann/object-enumerator/tree/5.0.0" 1474 | }, 1475 | "funding": [ 1476 | { 1477 | "url": "https://github.com/sebastianbergmann", 1478 | "type": "github" 1479 | } 1480 | ], 1481 | "time": "2023-02-03T07:08:32+00:00" 1482 | }, 1483 | { 1484 | "name": "sebastian/object-reflector", 1485 | "version": "dev-main", 1486 | "source": { 1487 | "type": "git", 1488 | "url": "https://github.com/sebastianbergmann/object-reflector.git", 1489 | "reference": "24ed13d98130f0e7122df55d06c5c4942a577957" 1490 | }, 1491 | "dist": { 1492 | "type": "zip", 1493 | "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/24ed13d98130f0e7122df55d06c5c4942a577957", 1494 | "reference": "24ed13d98130f0e7122df55d06c5c4942a577957", 1495 | "shasum": "" 1496 | }, 1497 | "require": { 1498 | "php": ">=8.1" 1499 | }, 1500 | "require-dev": { 1501 | "phpunit/phpunit": "^10.0" 1502 | }, 1503 | "default-branch": true, 1504 | "type": "library", 1505 | "extra": { 1506 | "branch-alias": { 1507 | "dev-main": "3.0-dev" 1508 | } 1509 | }, 1510 | "autoload": { 1511 | "classmap": [ 1512 | "src/" 1513 | ] 1514 | }, 1515 | "notification-url": "https://packagist.org/downloads/", 1516 | "license": [ 1517 | "BSD-3-Clause" 1518 | ], 1519 | "authors": [ 1520 | { 1521 | "name": "Sebastian Bergmann", 1522 | "email": "sebastian@phpunit.de" 1523 | } 1524 | ], 1525 | "description": "Allows reflection of object attributes, including inherited and non-public ones", 1526 | "homepage": "https://github.com/sebastianbergmann/object-reflector/", 1527 | "support": { 1528 | "issues": "https://github.com/sebastianbergmann/object-reflector/issues", 1529 | "source": "https://github.com/sebastianbergmann/object-reflector/tree/3.0.0" 1530 | }, 1531 | "funding": [ 1532 | { 1533 | "url": "https://github.com/sebastianbergmann", 1534 | "type": "github" 1535 | } 1536 | ], 1537 | "time": "2023-02-03T07:06:18+00:00" 1538 | }, 1539 | { 1540 | "name": "sebastian/recursion-context", 1541 | "version": "dev-main", 1542 | "source": { 1543 | "type": "git", 1544 | "url": "https://github.com/sebastianbergmann/recursion-context.git", 1545 | "reference": "05909fb5bc7df4c52992396d0116aed689f93712" 1546 | }, 1547 | "dist": { 1548 | "type": "zip", 1549 | "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/05909fb5bc7df4c52992396d0116aed689f93712", 1550 | "reference": "05909fb5bc7df4c52992396d0116aed689f93712", 1551 | "shasum": "" 1552 | }, 1553 | "require": { 1554 | "php": ">=8.1" 1555 | }, 1556 | "require-dev": { 1557 | "phpunit/phpunit": "^10.0" 1558 | }, 1559 | "default-branch": true, 1560 | "type": "library", 1561 | "extra": { 1562 | "branch-alias": { 1563 | "dev-main": "5.0-dev" 1564 | } 1565 | }, 1566 | "autoload": { 1567 | "classmap": [ 1568 | "src/" 1569 | ] 1570 | }, 1571 | "notification-url": "https://packagist.org/downloads/", 1572 | "license": [ 1573 | "BSD-3-Clause" 1574 | ], 1575 | "authors": [ 1576 | { 1577 | "name": "Sebastian Bergmann", 1578 | "email": "sebastian@phpunit.de" 1579 | }, 1580 | { 1581 | "name": "Jeff Welch", 1582 | "email": "whatthejeff@gmail.com" 1583 | }, 1584 | { 1585 | "name": "Adam Harvey", 1586 | "email": "aharvey@php.net" 1587 | } 1588 | ], 1589 | "description": "Provides functionality to recursively process PHP variables", 1590 | "homepage": "https://github.com/sebastianbergmann/recursion-context", 1591 | "support": { 1592 | "issues": "https://github.com/sebastianbergmann/recursion-context/issues", 1593 | "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.0" 1594 | }, 1595 | "funding": [ 1596 | { 1597 | "url": "https://github.com/sebastianbergmann", 1598 | "type": "github" 1599 | } 1600 | ], 1601 | "time": "2023-02-03T07:05:40+00:00" 1602 | }, 1603 | { 1604 | "name": "sebastian/type", 1605 | "version": "dev-main", 1606 | "source": { 1607 | "type": "git", 1608 | "url": "https://github.com/sebastianbergmann/type.git", 1609 | "reference": "462699a16464c3944eefc02ebdd77882bd3925bf" 1610 | }, 1611 | "dist": { 1612 | "type": "zip", 1613 | "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/462699a16464c3944eefc02ebdd77882bd3925bf", 1614 | "reference": "462699a16464c3944eefc02ebdd77882bd3925bf", 1615 | "shasum": "" 1616 | }, 1617 | "require": { 1618 | "php": ">=8.1" 1619 | }, 1620 | "require-dev": { 1621 | "phpunit/phpunit": "^10.0" 1622 | }, 1623 | "default-branch": true, 1624 | "type": "library", 1625 | "extra": { 1626 | "branch-alias": { 1627 | "dev-main": "4.0-dev" 1628 | } 1629 | }, 1630 | "autoload": { 1631 | "classmap": [ 1632 | "src/" 1633 | ] 1634 | }, 1635 | "notification-url": "https://packagist.org/downloads/", 1636 | "license": [ 1637 | "BSD-3-Clause" 1638 | ], 1639 | "authors": [ 1640 | { 1641 | "name": "Sebastian Bergmann", 1642 | "email": "sebastian@phpunit.de", 1643 | "role": "lead" 1644 | } 1645 | ], 1646 | "description": "Collection of value objects that represent the types of the PHP type system", 1647 | "homepage": "https://github.com/sebastianbergmann/type", 1648 | "support": { 1649 | "issues": "https://github.com/sebastianbergmann/type/issues", 1650 | "source": "https://github.com/sebastianbergmann/type/tree/4.0.0" 1651 | }, 1652 | "funding": [ 1653 | { 1654 | "url": "https://github.com/sebastianbergmann", 1655 | "type": "github" 1656 | } 1657 | ], 1658 | "time": "2023-02-03T07:10:45+00:00" 1659 | }, 1660 | { 1661 | "name": "sebastian/version", 1662 | "version": "dev-main", 1663 | "source": { 1664 | "type": "git", 1665 | "url": "https://github.com/sebastianbergmann/version.git", 1666 | "reference": "5facf5a20311ac44f79221274cdeb6c569ca11dd" 1667 | }, 1668 | "dist": { 1669 | "type": "zip", 1670 | "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/5facf5a20311ac44f79221274cdeb6c569ca11dd", 1671 | "reference": "5facf5a20311ac44f79221274cdeb6c569ca11dd", 1672 | "shasum": "" 1673 | }, 1674 | "require": { 1675 | "php": ">=8.1" 1676 | }, 1677 | "default-branch": true, 1678 | "type": "library", 1679 | "extra": { 1680 | "branch-alias": { 1681 | "dev-main": "4.0-dev" 1682 | } 1683 | }, 1684 | "autoload": { 1685 | "classmap": [ 1686 | "src/" 1687 | ] 1688 | }, 1689 | "notification-url": "https://packagist.org/downloads/", 1690 | "license": [ 1691 | "BSD-3-Clause" 1692 | ], 1693 | "authors": [ 1694 | { 1695 | "name": "Sebastian Bergmann", 1696 | "email": "sebastian@phpunit.de", 1697 | "role": "lead" 1698 | } 1699 | ], 1700 | "description": "Library that helps with managing the version number of Git-hosted PHP projects", 1701 | "homepage": "https://github.com/sebastianbergmann/version", 1702 | "support": { 1703 | "issues": "https://github.com/sebastianbergmann/version/issues", 1704 | "source": "https://github.com/sebastianbergmann/version/tree/4.0.0" 1705 | }, 1706 | "funding": [ 1707 | { 1708 | "url": "https://github.com/sebastianbergmann", 1709 | "type": "github" 1710 | } 1711 | ], 1712 | "time": "2023-02-03T07:11:37+00:00" 1713 | }, 1714 | { 1715 | "name": "symfony/polyfill-mbstring", 1716 | "version": "dev-main", 1717 | "source": { 1718 | "type": "git", 1719 | "url": "https://github.com/symfony/polyfill-mbstring.git", 1720 | "reference": "f9c7affe77a00ae32ca127ca6833d034e6d33f25" 1721 | }, 1722 | "dist": { 1723 | "type": "zip", 1724 | "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/f9c7affe77a00ae32ca127ca6833d034e6d33f25", 1725 | "reference": "f9c7affe77a00ae32ca127ca6833d034e6d33f25", 1726 | "shasum": "" 1727 | }, 1728 | "require": { 1729 | "php": ">=7.1" 1730 | }, 1731 | "provide": { 1732 | "ext-mbstring": "*" 1733 | }, 1734 | "suggest": { 1735 | "ext-mbstring": "For best performance" 1736 | }, 1737 | "default-branch": true, 1738 | "type": "library", 1739 | "extra": { 1740 | "branch-alias": { 1741 | "dev-main": "1.28-dev" 1742 | }, 1743 | "thanks": { 1744 | "name": "symfony/polyfill", 1745 | "url": "https://github.com/symfony/polyfill" 1746 | } 1747 | }, 1748 | "autoload": { 1749 | "files": [ 1750 | "bootstrap.php" 1751 | ], 1752 | "psr-4": { 1753 | "Symfony\\Polyfill\\Mbstring\\": "" 1754 | } 1755 | }, 1756 | "notification-url": "https://packagist.org/downloads/", 1757 | "license": [ 1758 | "MIT" 1759 | ], 1760 | "authors": [ 1761 | { 1762 | "name": "Nicolas Grekas", 1763 | "email": "p@tchwork.com" 1764 | }, 1765 | { 1766 | "name": "Symfony Community", 1767 | "homepage": "https://symfony.com/contributors" 1768 | } 1769 | ], 1770 | "description": "Symfony polyfill for the Mbstring extension", 1771 | "homepage": "https://symfony.com", 1772 | "keywords": [ 1773 | "compatibility", 1774 | "mbstring", 1775 | "polyfill", 1776 | "portable", 1777 | "shim" 1778 | ], 1779 | "support": { 1780 | "source": "https://github.com/symfony/polyfill-mbstring/tree/main" 1781 | }, 1782 | "funding": [ 1783 | { 1784 | "url": "https://symfony.com/sponsor", 1785 | "type": "custom" 1786 | }, 1787 | { 1788 | "url": "https://github.com/fabpot", 1789 | "type": "github" 1790 | }, 1791 | { 1792 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 1793 | "type": "tidelift" 1794 | } 1795 | ], 1796 | "time": "2023-01-30T17:25:47+00:00" 1797 | }, 1798 | { 1799 | "name": "symfony/var-dumper", 1800 | "version": "6.3.x-dev", 1801 | "source": { 1802 | "type": "git", 1803 | "url": "https://github.com/symfony/var-dumper.git", 1804 | "reference": "967f55b434dbdc00edb570ed7a7c6fcce8bb272a" 1805 | }, 1806 | "dist": { 1807 | "type": "zip", 1808 | "url": "https://api.github.com/repos/symfony/var-dumper/zipball/967f55b434dbdc00edb570ed7a7c6fcce8bb272a", 1809 | "reference": "967f55b434dbdc00edb570ed7a7c6fcce8bb272a", 1810 | "shasum": "" 1811 | }, 1812 | "require": { 1813 | "php": ">=8.1", 1814 | "symfony/polyfill-mbstring": "~1.0" 1815 | }, 1816 | "conflict": { 1817 | "phpunit/phpunit": "<5.4.3", 1818 | "symfony/console": "<5.4" 1819 | }, 1820 | "require-dev": { 1821 | "ext-iconv": "*", 1822 | "symfony/console": "^5.4|^6.0", 1823 | "symfony/process": "^5.4|^6.0", 1824 | "symfony/uid": "^5.4|^6.0", 1825 | "twig/twig": "^2.13|^3.0.4" 1826 | }, 1827 | "suggest": { 1828 | "ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).", 1829 | "ext-intl": "To show region name in time zone dump", 1830 | "symfony/console": "To use the ServerDumpCommand and/or the bin/var-dump-server script" 1831 | }, 1832 | "bin": [ 1833 | "Resources/bin/var-dump-server" 1834 | ], 1835 | "type": "library", 1836 | "autoload": { 1837 | "files": [ 1838 | "Resources/functions/dump.php" 1839 | ], 1840 | "psr-4": { 1841 | "Symfony\\Component\\VarDumper\\": "" 1842 | }, 1843 | "exclude-from-classmap": [ 1844 | "/Tests/" 1845 | ] 1846 | }, 1847 | "notification-url": "https://packagist.org/downloads/", 1848 | "license": [ 1849 | "MIT" 1850 | ], 1851 | "authors": [ 1852 | { 1853 | "name": "Nicolas Grekas", 1854 | "email": "p@tchwork.com" 1855 | }, 1856 | { 1857 | "name": "Symfony Community", 1858 | "homepage": "https://symfony.com/contributors" 1859 | } 1860 | ], 1861 | "description": "Provides mechanisms for walking through any arbitrary PHP variable", 1862 | "homepage": "https://symfony.com", 1863 | "keywords": [ 1864 | "debug", 1865 | "dump" 1866 | ], 1867 | "support": { 1868 | "source": "https://github.com/symfony/var-dumper/tree/6.3" 1869 | }, 1870 | "funding": [ 1871 | { 1872 | "url": "https://symfony.com/sponsor", 1873 | "type": "custom" 1874 | }, 1875 | { 1876 | "url": "https://github.com/fabpot", 1877 | "type": "github" 1878 | }, 1879 | { 1880 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 1881 | "type": "tidelift" 1882 | } 1883 | ], 1884 | "time": "2023-01-26T14:58:07+00:00" 1885 | }, 1886 | { 1887 | "name": "theseer/tokenizer", 1888 | "version": "1.2.1", 1889 | "source": { 1890 | "type": "git", 1891 | "url": "https://github.com/theseer/tokenizer.git", 1892 | "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" 1893 | }, 1894 | "dist": { 1895 | "type": "zip", 1896 | "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", 1897 | "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", 1898 | "shasum": "" 1899 | }, 1900 | "require": { 1901 | "ext-dom": "*", 1902 | "ext-tokenizer": "*", 1903 | "ext-xmlwriter": "*", 1904 | "php": "^7.2 || ^8.0" 1905 | }, 1906 | "type": "library", 1907 | "autoload": { 1908 | "classmap": [ 1909 | "src/" 1910 | ] 1911 | }, 1912 | "notification-url": "https://packagist.org/downloads/", 1913 | "license": [ 1914 | "BSD-3-Clause" 1915 | ], 1916 | "authors": [ 1917 | { 1918 | "name": "Arne Blankerts", 1919 | "email": "arne@blankerts.de", 1920 | "role": "Developer" 1921 | } 1922 | ], 1923 | "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", 1924 | "support": { 1925 | "issues": "https://github.com/theseer/tokenizer/issues", 1926 | "source": "https://github.com/theseer/tokenizer/tree/1.2.1" 1927 | }, 1928 | "funding": [ 1929 | { 1930 | "url": "https://github.com/theseer", 1931 | "type": "github" 1932 | } 1933 | ], 1934 | "time": "2021-07-28T10:34:58+00:00" 1935 | } 1936 | ], 1937 | "aliases": [], 1938 | "minimum-stability": "dev", 1939 | "stability-flags": { 1940 | "nikic/fast-route": 20, 1941 | "psr/container": 20, 1942 | "symfony/var-dumper": 20, 1943 | "phpunit/phpunit": 20 1944 | }, 1945 | "prefer-stable": false, 1946 | "prefer-lowest": false, 1947 | "platform": [], 1948 | "platform-dev": [], 1949 | "plugin-api-version": "2.3.0" 1950 | } 1951 | -------------------------------------------------------------------------------- /framework/src/Authentication/AuthRepositoryInterface.php: -------------------------------------------------------------------------------- 1 | authRepository->findByUsername($username); 23 | 24 | if (!$user) { 25 | return false; 26 | } 27 | 28 | // Does the hashed user pw match the hash of the attempted password 29 | if (!password_verify($password, $user->getPassword())) { 30 | 31 | return false; 32 | } 33 | 34 | $this->login($user); 35 | 36 | return true; 37 | } 38 | 39 | public function login(AuthUserInterface $user) 40 | { 41 | // Start a session 42 | $this->session->start(); 43 | 44 | // Log the user in 45 | $this->session->set(Session::AUTH_KEY, $user->getAuthId()); 46 | 47 | // Set the user 48 | $this->user = $user; 49 | } 50 | 51 | public function logout() 52 | { 53 | $this->session->remove(Session::AUTH_KEY); 54 | } 55 | 56 | public function getUser(): AuthUserInterface 57 | { 58 | return $this->user; 59 | } 60 | 61 | } -------------------------------------------------------------------------------- /framework/src/Console/Application.php: -------------------------------------------------------------------------------- 1 | container->get($commandName); 26 | 27 | // Parse variables to obtain options and args 28 | $args = array_slice($argv, 2); 29 | 30 | $options = $this->parseOptions($args); 31 | 32 | // Execute the command, returning the status code 33 | $status = $command->execute($options); 34 | 35 | // Return the status code 36 | return $status; 37 | } 38 | 39 | private function parseOptions(array $args): array 40 | { 41 | $options = []; 42 | 43 | foreach ($args as $arg) { 44 | if (str_starts_with($arg, '--')) { 45 | // This is an option 46 | $option = explode('=', substr($arg, 2)); 47 | $options[$option[0]] = $option[1] ?? true; 48 | 49 | } 50 | } 51 | 52 | return $options; 53 | } 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | } -------------------------------------------------------------------------------- /framework/src/Console/Command/CommandInterface.php: -------------------------------------------------------------------------------- 1 | createMigrationsTable(); 26 | 27 | $this->connection->beginTransaction(); 28 | 29 | // Get $appliedMigrations which are already in the database.migrations table 30 | $appliedMigrations = $this->getAppliedMigrations(); 31 | 32 | // Get the $migrationFiles from the migrations folder 33 | $migrationFiles = $this->getMigrationFiles(); 34 | 35 | // Get the migrations to apply. i.e. they are in $migrationFiles but not in $appliedMigrations 36 | $migrationsToApply = array_diff($migrationFiles, $appliedMigrations); 37 | 38 | $schema = new Schema(); 39 | 40 | // Create SQL for any migrations which have not been run ..i.e. which are not in the database 41 | foreach ($migrationsToApply as $migration) { 42 | 43 | // require the object 44 | $migrationObject = require $this->migrationsPath . '/' . $migration; 45 | 46 | // call up method 47 | $migrationObject->up($schema); 48 | 49 | // Add migration to database 50 | $this->insertMigration($migration); 51 | } 52 | 53 | // Execute the SQL query 54 | $sqlArray = $schema->toSql($this->connection->getDatabasePlatform()); 55 | 56 | foreach ($sqlArray as $sql) { 57 | $this->connection->executeQuery($sql); 58 | } 59 | 60 | $this->connection->commit(); 61 | 62 | return 0; 63 | 64 | } catch (\Throwable $throwable) { 65 | 66 | $this->connection->rollBack(); 67 | 68 | throw $throwable; 69 | } 70 | } 71 | 72 | private function insertMigration(string $migration): void 73 | { 74 | $sql = "INSERT INTO migrations (migration) VALUES (?)"; 75 | 76 | $stmt = $this->connection->prepare($sql); 77 | 78 | $stmt->bindValue(1, $migration); 79 | 80 | $stmt->executeStatement(); 81 | } 82 | 83 | private function getMigrationFiles(): array 84 | { 85 | $migrationFiles = scandir($this->migrationsPath); 86 | 87 | $filteredFiles = array_filter($migrationFiles, function($file) { 88 | return !in_array($file, ['.', '..']); 89 | }); 90 | 91 | return $filteredFiles; 92 | } 93 | 94 | private function getAppliedMigrations(): array 95 | { 96 | $sql = "SELECT migration FROM migrations;"; 97 | 98 | $appliedMigrations = $this->connection->executeQuery($sql)->fetchFirstColumn(); 99 | 100 | return $appliedMigrations; 101 | } 102 | 103 | private function createMigrationsTable(): void 104 | { 105 | $schemaManager = $this->connection->createSchemaManager(); 106 | 107 | if (!$schemaManager->tablesExist('migrations')) { 108 | $schema = new Schema(); 109 | $table = $schema->createTable('migrations'); 110 | $table->addColumn('id', Types::INTEGER, ['unsigned' => true, 'autoincrement' => true]); 111 | $table->addColumn('migration', Types::STRING); 112 | $table->addColumn('created_at', Types::DATETIME_IMMUTABLE, ['default' => 'CURRENT_TIMESTAMP']); 113 | $table->setPrimaryKey(['id']); 114 | 115 | $sqlArray = $schema->toSql($this->connection->getDatabasePlatform()); 116 | 117 | $this->connection->executeQuery($sqlArray[0]); 118 | 119 | echo 'migrations table created' . PHP_EOL; 120 | } 121 | } 122 | } 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | -------------------------------------------------------------------------------- /framework/src/Console/ConsoleException.php: -------------------------------------------------------------------------------- 1 | registerCommands(); 21 | 22 | // Run the console application, returning a status code 23 | $status = $this->application->run(); 24 | 25 | // return the status code 26 | return $status; 27 | } 28 | 29 | private function registerCommands(): void 30 | { 31 | // === Register All Built In Commands === 32 | // Get all files in the Commands dir 33 | $commandFiles = new \DirectoryIterator(__DIR__ . '/Command'); 34 | 35 | $namespace = $this->container->get('base-commands-namespace'); 36 | 37 | // Loop over all files in the commands folder 38 | foreach ($commandFiles as $commandFile) { 39 | 40 | if (!$commandFile->isFile()) { 41 | continue; 42 | } 43 | 44 | // Get the Command class name..using psr4 this will be same as filename 45 | $command = $namespace.pathinfo($commandFile, PATHINFO_FILENAME); 46 | 47 | // If it is a subclass of CommandInterface 48 | if (is_subclass_of($command, CommandInterface::class)) { 49 | // Add to the container, using the name as the ID e.g. $container->add('database:migrations:migrate', MigrateDatabase::class) 50 | $commandName = (new \ReflectionClass($command))->getProperty('name')->getDefaultValue(); 51 | 52 | $this->container->add($commandName, $command); 53 | } 54 | } 55 | 56 | // === Register all user-defined commands (@todo) === 57 | } 58 | } -------------------------------------------------------------------------------- /framework/src/Container/Container.php: -------------------------------------------------------------------------------- 1 | services[$id] = $concrete; 25 | } 26 | 27 | public function get(string $id) 28 | { 29 | if (!$this->has($id)) { 30 | if (!class_exists($id)) { 31 | throw new ContainerException("Service $id could not be resolved"); 32 | } 33 | 34 | $this->add($id); 35 | } 36 | 37 | $object = $this->resolve($this->services[$id]); 38 | 39 | return $object; 40 | } 41 | 42 | private function resolve($class): object 43 | { 44 | // 1. Instantiate a Reflection class (dump and check) 45 | $reflectionClass = new \ReflectionClass($class); 46 | 47 | // 2. Use Reflection to try to obtain a class constructor 48 | $constructor = $reflectionClass->getConstructor(); 49 | 50 | // 3. If there is no constructor, simply instantiate 51 | if (null === $constructor) { 52 | return $reflectionClass->newInstance(); 53 | } 54 | 55 | // 4. Get the constructor parameters 56 | $constructorParams = $constructor->getParameters(); 57 | 58 | // 5. Obtain dependencies 59 | $classDependencies = $this->resolveClassDependencies($constructorParams); 60 | 61 | // 6. Instantiate with dependencies 62 | $service = $reflectionClass->newInstanceArgs($classDependencies); 63 | 64 | // 7. Return the object 65 | return $service; 66 | } 67 | 68 | private function resolveClassDependencies(array $reflectionParameters): array 69 | { 70 | // 1. Initialize empty dependencies array (required by newInstanceArgs) 71 | $classDependencies = []; 72 | 73 | // 2. Try to locate and instantiate each parameter 74 | /** @var \ReflectionParameter $parameter */ 75 | foreach ($reflectionParameters as $parameter) { 76 | 77 | // Get the parameter's ReflectionNamedType as $serviceType 78 | $serviceType = $parameter->getType(); 79 | 80 | // Try to instantiate using $serviceType's name 81 | $service = $this->get($serviceType->getName()); 82 | 83 | // Add the service to the classDependencies array 84 | $classDependencies[] = $service; 85 | } 86 | 87 | // 3. Return the classDependencies array 88 | return $classDependencies; 89 | } 90 | 91 | public function has(string $id): bool 92 | { 93 | return array_key_exists($id, $this->services); 94 | } 95 | } -------------------------------------------------------------------------------- /framework/src/Container/ContainerException.php: -------------------------------------------------------------------------------- 1 | container = $container; 17 | } 18 | 19 | public function setRequest(Request $request): void 20 | { 21 | $this->request = $request; 22 | } 23 | 24 | public function render(string $template, array $parameters = [], Response $response = null): Response 25 | { 26 | $content = $this->container->get('twig')->render($template, $parameters); 27 | 28 | $response ??= new Response(); 29 | 30 | $response->setContent($content); 31 | 32 | return $response; 33 | } 34 | } -------------------------------------------------------------------------------- /framework/src/Dbal/ConnectionFactory.php: -------------------------------------------------------------------------------- 1 | $this->databaseUrl]); 17 | } 18 | } -------------------------------------------------------------------------------- /framework/src/Dbal/DataMapper.php: -------------------------------------------------------------------------------- 1 | connection; 21 | } 22 | 23 | public function save(Entity $subject): int|string|null 24 | { 25 | // Dispatch PostPersist event 26 | $this->eventDispatcher->dispatch(new PostPersist($subject)); 27 | 28 | // Return lastInsertId 29 | return $this->connection->lastInsertId(); 30 | } 31 | } 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /framework/src/Dbal/Entity.php: -------------------------------------------------------------------------------- 1 | propagationStopped; 14 | } 15 | 16 | public function stopPropagation(): void 17 | { 18 | $this->propagationStopped = true; 19 | } 20 | } -------------------------------------------------------------------------------- /framework/src/EventDispatcher/EventDispatcher.php: -------------------------------------------------------------------------------- 1 | getListenersForEvent($event) as $listener) { 16 | // Break if propagation stopped 17 | if ($event instanceof StoppableEventInterface && $event->isPropagationStopped()) { 18 | return $event; 19 | } 20 | 21 | // Call the listener, passing in the event (each listener will be a callable) 22 | $listener($event); 23 | } 24 | 25 | return $event; 26 | } 27 | 28 | // $eventName e.g. Framework\EventDispatcher\ResponseEvent 29 | public function addListener(string $eventName, callable $listener): self 30 | { 31 | $this->listeners[$eventName][] = $listener; 32 | 33 | return $this; 34 | } 35 | 36 | /** 37 | * @param object $event 38 | * An event for which to return the relevant listeners. 39 | * @return iterable 40 | * An iterable (array, iterator, or generator) of callables. Each 41 | * callable MUST be type-compatible with $event. 42 | */ 43 | public function getListenersForEvent(object $event) : iterable 44 | { 45 | $eventName = get_class($event); 46 | 47 | if (array_key_exists($eventName, $this->listeners)) { 48 | return $this->listeners[$eventName]; 49 | } 50 | 51 | return []; 52 | } 53 | } -------------------------------------------------------------------------------- /framework/src/Http/Event/ResponseEvent.php: -------------------------------------------------------------------------------- 1 | request; 21 | } 22 | 23 | public function getResponse(): Response 24 | { 25 | return $this->response; 26 | } 27 | } -------------------------------------------------------------------------------- /framework/src/Http/HttpException.php: -------------------------------------------------------------------------------- 1 | statusCode; 12 | } 13 | 14 | public function setStatusCode(int $statusCode): void 15 | { 16 | $this->statusCode = $statusCode; 17 | } 18 | } -------------------------------------------------------------------------------- /framework/src/Http/HttpRequestMethodException.php: -------------------------------------------------------------------------------- 1 | appEnv = $this->container->get('APP_ENV'); 24 | } 25 | 26 | public function handle(Request $request): Response 27 | { 28 | try { 29 | 30 | $response = $this->requestHandler->handle($request); 31 | 32 | } catch (\Exception $exception) { 33 | $response = $this->createExceptionResponse($exception); 34 | } 35 | 36 | $this->eventDispatcher->dispatch(new ResponseEvent($request, $response)); 37 | 38 | return $response; 39 | } 40 | 41 | /** 42 | * @throws \Exception $exception 43 | */ 44 | private function createExceptionResponse(\Exception $exception): Response 45 | { 46 | if (in_array($this->appEnv, ['dev', 'test'])) { 47 | throw $exception; 48 | } 49 | 50 | if ($exception instanceof HttpException) { 51 | return new Response($exception->getMessage(), $exception->getStatusCode()); 52 | } 53 | 54 | return new Response('Server error', Response::HTTP_INTERNAL_SERVER_ERROR); 55 | } 56 | 57 | public function terminate(Request $request, Response $response): void 58 | { 59 | $request->getSession()?->clearFlash(); 60 | } 61 | } -------------------------------------------------------------------------------- /framework/src/Http/Middleware/Authenticate.php: -------------------------------------------------------------------------------- 1 | session->start(); 21 | 22 | if (!$this->session->has(Session::AUTH_KEY)) { 23 | $this->session->setFlash('error', 'Please sign in'); 24 | return new RedirectResponse('/login'); 25 | } 26 | 27 | return $requestHandler->handle($request); 28 | } 29 | } 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /framework/src/Http/Middleware/Dummy.php: -------------------------------------------------------------------------------- 1 | handle($request); 13 | } 14 | } -------------------------------------------------------------------------------- /framework/src/Http/Middleware/ExtractRouteInfo.php: -------------------------------------------------------------------------------- 1 | routes as $route) { 25 | $routeCollector->addRoute(...$route); 26 | } 27 | }); 28 | 29 | // Dispatch a URI, to obtain the route info 30 | $routeInfo = $dispatcher->dispatch( 31 | $request->getMethod(), 32 | $request->getPathInfo() 33 | ); 34 | 35 | switch ($routeInfo[0]) { 36 | case Dispatcher::FOUND: 37 | // Set $request->routeHandler 38 | $request->setRouteHandler($routeInfo[1]); 39 | // Set $request->routeHandlerArgs 40 | $request->setRouteHandlerArgs($routeInfo[2]); 41 | // Inject route middleware on handler 42 | if (is_array($routeInfo[1]) && isset($routeInfo[1][2])) { 43 | $requestHandler->injectMiddleware($routeInfo[1][2]); 44 | } 45 | break; 46 | case Dispatcher::METHOD_NOT_ALLOWED: 47 | $allowedMethods = implode(', ', $routeInfo[1]); 48 | $e = new HttpRequestMethodException("The allowed methods are $allowedMethods"); 49 | $e->setStatusCode(405); 50 | throw $e; 51 | default: 52 | $e = new HttpException('Not found'); 53 | $e->setStatusCode(404); 54 | throw $e; 55 | } 56 | 57 | return $requestHandler->handle($request); 58 | } 59 | } -------------------------------------------------------------------------------- /framework/src/Http/Middleware/Guest.php: -------------------------------------------------------------------------------- 1 | session->start(); 21 | 22 | if ($this->session->has(Session::AUTH_KEY)) { 23 | return new RedirectResponse('/dashboard'); 24 | } 25 | 26 | return $requestHandler->handle($request); 27 | } 28 | } 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /framework/src/Http/Middleware/MiddlewareInterface.php: -------------------------------------------------------------------------------- 1 | middleware)) { 27 | return new Response("It's totally borked, mate. Contact support", 500); 28 | } 29 | 30 | // Get the next middleware class to execute 31 | $middlewareClass = array_shift($this->middleware); 32 | 33 | $middleware = $this->container->get($middlewareClass); 34 | 35 | // Create a new instance of the middleware call process on it 36 | $response = $middleware->process($request, $this); 37 | 38 | return $response; 39 | } 40 | 41 | public function injectMiddleware(array $middleware): void 42 | { 43 | array_splice($this->middleware, 0, 0, $middleware); 44 | } 45 | } 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /framework/src/Http/Middleware/RequestHandlerInterface.php: -------------------------------------------------------------------------------- 1 | router->dispatch($request, $this->container); 22 | 23 | $response = call_user_func_array($routeHandler, $vars); 24 | 25 | return $response; 26 | } 27 | } -------------------------------------------------------------------------------- /framework/src/Http/Middleware/StartSession.php: -------------------------------------------------------------------------------- 1 | getPathInfo(), $this->apiPrefix)) { 21 | $this->session->start(); 22 | 23 | $request->setSession($this->session); 24 | } 25 | 26 | return $requestHandler->handle($request); 27 | } 28 | } 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /framework/src/Http/Middleware/Success.php: -------------------------------------------------------------------------------- 1 | $url]); 10 | } 11 | 12 | public function send(): void 13 | { 14 | header('Location: ' . $this->getHeader('location'), true, $this->getStatus()); 15 | exit; 16 | } 17 | } -------------------------------------------------------------------------------- /framework/src/Http/Request.php: -------------------------------------------------------------------------------- 1 | server['REQUEST_URI'], '?'); 31 | } 32 | 33 | public function getMethod(): string 34 | { 35 | return $this->server['REQUEST_METHOD']; 36 | } 37 | 38 | public function getSession(): SessionInterface 39 | { 40 | return $this->session; 41 | } 42 | 43 | public function setSession(SessionInterface $session): void 44 | { 45 | $this->session = $session; 46 | } 47 | 48 | public function input($key): mixed 49 | { 50 | return $this->postParams[$key]; 51 | } 52 | 53 | public function getRouteHandler(): mixed 54 | { 55 | return $this->routeHandler; 56 | } 57 | 58 | public function setRouteHandler(mixed $routeHandler): void 59 | { 60 | $this->routeHandler = $routeHandler; 61 | } 62 | 63 | public function getRouteHandlerArgs(): array 64 | { 65 | return $this->routeHandlerArgs; 66 | } 67 | 68 | public function setRouteHandlerArgs(array $routeHandlerArgs): void 69 | { 70 | $this->routeHandlerArgs = $routeHandlerArgs; 71 | } 72 | } -------------------------------------------------------------------------------- /framework/src/Http/Response.php: -------------------------------------------------------------------------------- 1 | status); 18 | } 19 | 20 | public function send(): void 21 | { 22 | // start output buffering 23 | ob_start(); 24 | 25 | // send headers 26 | foreach ($this->headers as $key => $value) { 27 | header("$key: $value"); 28 | } 29 | 30 | // This will actually add the content to the buffer 31 | echo $this->content; 32 | 33 | // Flush the buffer, sending the content to the client 34 | ob_end_flush(); 35 | } 36 | 37 | public function setContent(?string $content): void 38 | { 39 | $this->content = $content; 40 | } 41 | 42 | public function getStatus(): int 43 | { 44 | return $this->status; 45 | } 46 | 47 | public function setStatus(int $status): void 48 | { 49 | $this->status = $status; 50 | } 51 | 52 | public function getHeader(string $header): mixed 53 | { 54 | return $this->headers[$header]; 55 | } 56 | 57 | public function getHeaders(): array 58 | { 59 | return $this->headers; 60 | } 61 | 62 | public function setHeader($key, $value): void 63 | { 64 | $this->headers[$key] = $value; 65 | } 66 | 67 | public function getContent(): ?string 68 | { 69 | return $this->content; 70 | } 71 | } -------------------------------------------------------------------------------- /framework/src/Routing/Router.php: -------------------------------------------------------------------------------- 1 | extractRouteInfo($request); 21 | 22 | [$handler, $vars] = $routeInfo; 23 | 24 | if (is_array($handler)) { 25 | [$controllerId, $method] = $handler; 26 | $controller = $container->get($controllerId); 27 | if (is_subclass_of($controller, AbstractController::class)) { 28 | $controller->setRequest($request); 29 | } 30 | $handler = [$controller, $method]; 31 | } 32 | 33 | return [$handler, $vars]; 34 | } 35 | 36 | public function setRoutes(array $routes): void 37 | { 38 | $this->routes = $routes; 39 | } 40 | 41 | private function extractRouteInfo(Request $request): array 42 | { 43 | // Create a dispatcher 44 | $dispatcher = simpleDispatcher(function (RouteCollector $routeCollector) { 45 | 46 | foreach ($this->routes as $route) { 47 | $routeCollector->addRoute(...$route); 48 | } 49 | }); 50 | 51 | // Dispatch a URI, to obtain the route info 52 | $routeInfo = $dispatcher->dispatch( 53 | $request->getMethod(), 54 | $request->getPathInfo() 55 | ); 56 | 57 | switch ($routeInfo[0]) { 58 | case Dispatcher::FOUND: 59 | return [$routeInfo[1], $routeInfo[2]]; // routeHandler, vars 60 | case Dispatcher::METHOD_NOT_ALLOWED: 61 | $allowedMethods = implode(', ', $routeInfo[1]); 62 | $e = new HttpRequestMethodException("The allowed methods are $allowedMethods"); 63 | $e->setStatusCode(405); 64 | throw $e; 65 | default: 66 | $e = new HttpException('Not found'); 67 | $e->setStatusCode(404); 68 | throw $e; 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /framework/src/Routing/RouterInterface.php: -------------------------------------------------------------------------------- 1 | get(self::FLASH_KEY) ?? []; 40 | if (isset($flash[$type])) { 41 | $messages = $flash[$type]; 42 | unset($flash[$type]); 43 | $this->set(self::FLASH_KEY, $flash); 44 | return $messages; 45 | } 46 | 47 | return []; 48 | } 49 | 50 | public function setFlash(string $type, string $message): void 51 | { 52 | $flash = $this->get(self::FLASH_KEY) ?? []; 53 | $flash[$type][] = $message; 54 | $this->set(self::FLASH_KEY, $flash); 55 | } 56 | 57 | public function hasFlash(string $type): bool 58 | { 59 | return isset($_SESSION[self::FLASH_KEY][$type]); 60 | } 61 | 62 | public function clearFlash(): void 63 | { 64 | unset($_SESSION[self::FLASH_KEY]); 65 | } 66 | 67 | public function isAuthenticated(): bool 68 | { 69 | return $this->has(self::AUTH_KEY); 70 | } 71 | } -------------------------------------------------------------------------------- /framework/src/Session/SessionInterface.php: -------------------------------------------------------------------------------- 1 | templatesPath); 24 | 25 | // instantiate Twig Environment with loader 26 | $twig = new Environment($loader, [ 27 | 'debug' => true, 28 | 'cache' => false, 29 | ]); 30 | 31 | // add new twig session() function to Environment 32 | $twig->addExtension(new DebugExtension()); 33 | $twig->addFunction(new TwigFunction('session', [$this, 'getSession'])); 34 | 35 | return $twig; 36 | } 37 | 38 | public function getSession(): SessionInterface 39 | { 40 | return $this->session; 41 | } 42 | } 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /framework/tests/ContainerTest.php: -------------------------------------------------------------------------------- 1 | add('dependant-class', DependantClass::class); 20 | 21 | // Make assertions 22 | $this->assertInstanceOf(DependantClass::class, $container->get('dependant-class')); 23 | } 24 | 25 | /** @test */ 26 | public function a_ContainerException_is_thrown_if_a_service_cannot_be_found() 27 | { 28 | // Setup 29 | $container = new Container(); 30 | 31 | // Expect exception 32 | $this->expectException(ContainerException::class); 33 | 34 | // Do something 35 | $container->add('foobar'); 36 | } 37 | 38 | /** @test */ 39 | public function can_check_if_the_container_has_a_service(): void 40 | { 41 | // Setup 42 | $container = new Container(); 43 | 44 | // Do something 45 | $container->add('dependant-class', DependantClass::class); 46 | 47 | $this->assertTrue($container->has('dependant-class')); 48 | $this->assertFalse($container->has('non-existent-class')); 49 | } 50 | 51 | /** @test */ 52 | public function services_can_be_recursively_autowired() 53 | { 54 | $container = new Container(); 55 | 56 | $dependantService = $container->get(DependantClass::class); 57 | 58 | $dependancyService = $dependantService->getDependency(); 59 | 60 | $this->assertInstanceOf(DependencyClass::class, $dependancyService); 61 | $this->assertInstanceOf(SubDependencyClass::class, $dependancyService->getSubDependency()); 62 | } 63 | } 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /framework/tests/DependantClass.php: -------------------------------------------------------------------------------- 1 | dependency; 14 | } 15 | } -------------------------------------------------------------------------------- /framework/tests/DependencyClass.php: -------------------------------------------------------------------------------- 1 | subDependency; 14 | } 15 | } -------------------------------------------------------------------------------- /framework/tests/SessionTest.php: -------------------------------------------------------------------------------- 1 | setFlash('success', 'Great job!'); 19 | $session->setFlash('error', 'Bad job!'); 20 | $this->assertTrue($session->hasFlash('success')); 21 | $this->assertTrue($session->hasFlash('error')); 22 | $this->assertEquals(['Great job!'], $session->getFlash('success')); 23 | $this->assertEquals(['Bad job!'], $session->getFlash('error')); 24 | $this->assertEquals([], $session->getFlash('warning')); 25 | } 26 | } -------------------------------------------------------------------------------- /framework/tests/SubDependencyClass.php: -------------------------------------------------------------------------------- 1 | createTable('posts'); 11 | $table->addColumn('id', Types::INTEGER, ['autoincrement' => true, 'unsigned' => true]); 12 | $table->addColumn('title', Types::STRING, ['length' => 255]); 13 | $table->addColumn('body', Types::TEXT); 14 | $table->addColumn('created_at', Types::DATETIME_IMMUTABLE, ['default' => 'CURRENT_TIMESTAMP']); 15 | $table->setPrimaryKey(['id']); 16 | } 17 | 18 | public function down(): void 19 | { 20 | // Table drop / modification code goes here 21 | 22 | echo get_class($this) . ' "down" method called' . PHP_EOL; 23 | } 24 | }; 25 | 26 | -------------------------------------------------------------------------------- /migrations/20230407133000.php: -------------------------------------------------------------------------------- 1 | createTable('users'); 11 | $table->addColumn('id', Types::INTEGER, ['autoincrement' => true, 'unsigned' => true]); 12 | $table->addColumn('username', Types::STRING, ['length' => 255]); 13 | $table->addColumn('password', Types::STRING, ['length' => 60]); 14 | $table->addColumn('created_at', Types::DATETIME_IMMUTABLE, ['default' => 'CURRENT_TIMESTAMP']); 15 | $table->setPrimaryKey(['id']); 16 | } 17 | 18 | public function down(Schema $schema): void 19 | { 20 | echo get_class($this) . ' "down" method called' . PHP_EOL; 21 | } 22 | }; 23 | 24 | -------------------------------------------------------------------------------- /public/index.php: -------------------------------------------------------------------------------- 1 | get(Kernel::class); 20 | 21 | // send response (string of content) 22 | $response = $kernel->handle($request); 23 | 24 | $response->send(); 25 | 26 | $kernel->terminate($request, $response); -------------------------------------------------------------------------------- /routes/web.php: -------------------------------------------------------------------------------- 1 | render('dashboard.html.twig'); 13 | } 14 | } -------------------------------------------------------------------------------- /src/Controller/HomeController.php: -------------------------------------------------------------------------------- 1 | render('home.html.twig'); 19 | } 20 | } -------------------------------------------------------------------------------- /src/Controller/LoginController.php: -------------------------------------------------------------------------------- 1 | render('login.html.twig'); 19 | } 20 | 21 | public function login(): Response 22 | { 23 | // Attempt to authenticate the user using a security component (bool) 24 | // create a session for the user 25 | $userIsAuthenticated = $this->authComponent->authenticate( 26 | $this->request->input('username'), 27 | $this->request->input('password') 28 | ); 29 | 30 | // If successful, retrieve the user 31 | if (!$userIsAuthenticated) { 32 | $this->request->getSession()->setFlash('error', 'Bad creds'); 33 | return new RedirectResponse('/login'); 34 | } 35 | 36 | $user = $this->authComponent->getUser(); 37 | 38 | $this->request->getSession()->setFlash('success', 'You are now logged in'); 39 | 40 | // Redirect the user to intended location 41 | return new RedirectResponse('/dashboard'); 42 | } 43 | 44 | public function logout(): Response 45 | { 46 | // Log the user out 47 | $this->authComponent->logout(); 48 | 49 | // Set a logout session message 50 | $this->request->getSession()->setFlash('success', 'Bye..see you soon!'); 51 | 52 | // Redirect to login page 53 | return new RedirectResponse('/login'); 54 | } 55 | } -------------------------------------------------------------------------------- /src/Controller/PostsController.php: -------------------------------------------------------------------------------- 1 | postRepository->findOrFail($id); 26 | 27 | return $this->render('post.html.twig', [ 28 | 'post' => $post 29 | ]); 30 | } 31 | 32 | public function create(): Response 33 | { 34 | return $this->render('create-post.html.twig'); 35 | } 36 | 37 | public function store(): Response 38 | { 39 | $title = $this->request->postParams['title']; 40 | $body = $this->request->postParams['body']; 41 | 42 | $post = Post::create($title, $body); 43 | 44 | $this->postMapper->save($post); 45 | 46 | $this->request->getSession()->setFlash( 47 | 'success', 48 | sprintf('Post "%s" successfully created', $title) 49 | ); 50 | 51 | return new RedirectResponse('/posts'); 52 | } 53 | } -------------------------------------------------------------------------------- /src/Controller/RegistrationController.php: -------------------------------------------------------------------------------- 1 | render('register.html.twig'); 25 | } 26 | 27 | public function register(): Response 28 | { 29 | // Create a form model which will: 30 | // - validate fields 31 | // - map the fields to User object properties 32 | // - ultimately save the new User to the DB 33 | $form = new RegistrationForm($this->userMapper); 34 | $form->setFields( 35 | $this->request->input('username'), 36 | $this->request->input('password') 37 | ); 38 | 39 | // Validate 40 | // If validation errors, 41 | if ($form->hasValidationErrors()) { 42 | // add to session, redirect to form 43 | foreach ($form->getValidationErrors() as $error) { 44 | $this->request->getSession()->setFlash('error', $error); 45 | } 46 | return new RedirectResponse('/register'); 47 | } 48 | 49 | // register the user by calling $form->save() 50 | $user = $form->save(); 51 | 52 | // Add a session success message 53 | $this->request->getSession()->setFlash( 54 | 'success', 55 | sprintf('User %s created', $user->getUsername()) 56 | ); 57 | 58 | // Log the user in 59 | $this->authComponent->login($user); 60 | 61 | // Redirect to somewhere useful 62 | return new RedirectResponse('/dashboard'); 63 | } 64 | } -------------------------------------------------------------------------------- /src/Entity/Post.php: -------------------------------------------------------------------------------- 1 | id; 31 | } 32 | 33 | public function setId(?int $id): void 34 | { 35 | $this->id = $id; 36 | } 37 | 38 | public function getTitle(): string 39 | { 40 | return $this->title; 41 | } 42 | 43 | public function setTitle(string $title): void 44 | { 45 | $this->title = $title; 46 | } 47 | 48 | public function getBody(): string 49 | { 50 | return $this->body; 51 | } 52 | 53 | public function setBody(string $body): void 54 | { 55 | $this->body = $body; 56 | } 57 | 58 | public function getCreatedAt(): \DateTimeImmutable 59 | { 60 | return $this->createdAt; 61 | } 62 | 63 | public function setCreatedAt(\DateTimeImmutable $createdAt): void 64 | { 65 | $this->createdAt = $createdAt; 66 | } 67 | } -------------------------------------------------------------------------------- /src/Entity/User.php: -------------------------------------------------------------------------------- 1 | id = $id; 32 | } 33 | 34 | public function getAuthId(): int 35 | { 36 | return $this->id; 37 | } 38 | 39 | public function getUsername(): string 40 | { 41 | return $this->username; 42 | } 43 | 44 | public function getPassword(): string 45 | { 46 | return $this->password; 47 | } 48 | 49 | public function getCreatedAt(): \DateTimeImmutable 50 | { 51 | return $this->createdAt; 52 | } 53 | } -------------------------------------------------------------------------------- /src/EventListener/ContentLengthListener.php: -------------------------------------------------------------------------------- 1 | getResponse(); 12 | 13 | if (!array_key_exists('Content-Length', $response->getHeaders())) { 14 | $response->setHeader('Content-Length', strlen($response->getContent())); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/EventListener/InternalErrorListener.php: -------------------------------------------------------------------------------- 1 | getResponse()->getStatus(); 14 | 15 | if ($status > self::INTERNAL_ERROR_MIN_VALUE) { 16 | $event->stopPropagation(); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /src/Form/User/RegistrationForm.php: -------------------------------------------------------------------------------- 1 | username = $username; 21 | $this->password = $password; 22 | } 23 | 24 | public function save(): User 25 | { 26 | $user = User::create($this->username, $this->password); 27 | 28 | $this->userMapper->save($user); 29 | 30 | return $user; 31 | } 32 | 33 | public function hasValidationErrors(): bool 34 | { 35 | return count($this->getValidationErrors()) > 0; 36 | } 37 | 38 | public function getValidationErrors(): array 39 | { 40 | if (!empty($this->errors)) { 41 | return $this->errors; 42 | } 43 | 44 | // username length 45 | if (strlen($this->username) < 5 || strlen($this->username) > 20) { 46 | $this->errors[] = 'Username must be between 5 and 20 characters'; 47 | } 48 | 49 | // username char type 50 | if (!preg_match('/^\w+$/', $this->username)) { 51 | $this->errors[] = 'Username can only consist of word characters without spaces'; 52 | } 53 | 54 | // password length 55 | if (strlen($this->password) < 8) { 56 | $this->errors[] = 'Password must be at least 8 characters'; 57 | } 58 | 59 | return $this->errors; 60 | } 61 | } -------------------------------------------------------------------------------- /src/Provider/EventServiceProvider.php: -------------------------------------------------------------------------------- 1 | [ 16 | InternalErrorListener::class, 17 | ContentLengthListener::class 18 | ], 19 | PostPersist::class => [ 20 | ] 21 | ]; 22 | 23 | public function __construct(private EventDispatcher $eventDispatcher) 24 | { 25 | } 26 | 27 | public function register(): void 28 | { 29 | // loop over each event in the listen array 30 | foreach ($this->listen as $eventName => $listeners) { 31 | // loop over each listener 32 | foreach (array_unique($listeners) as $listener) { 33 | // call eventDispatcher->addListener 34 | $this->eventDispatcher->addListener($eventName, new $listener()); 35 | } 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /src/Repository/PostMapper.php: -------------------------------------------------------------------------------- 1 | dataMapper->getConnection()->prepare(" 18 | INSERT INTO posts (title, body, created_at) 19 | VALUES (:title, :body, :created_at) 20 | "); 21 | 22 | $stmt->bindValue(':title', $post->getTitle()); 23 | $stmt->bindValue(':body', $post->getBody()); 24 | $stmt->bindValue(':created_at', $post->getCreatedAt()->format('Y-m-d H:i:s')); 25 | 26 | $stmt->executeStatement(); 27 | 28 | $id = $this->dataMapper->save($post); 29 | 30 | $post->setId($id); 31 | } 32 | } 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /src/Repository/PostRepository.php: -------------------------------------------------------------------------------- 1 | connection->createQueryBuilder(); 18 | 19 | $queryBuilder 20 | ->select('id', 'title', 'body', 'created_at') 21 | ->from('posts') 22 | ->where('id = :id') 23 | ->setParameter('id', $id); 24 | 25 | $result = $queryBuilder->executeQuery(); 26 | 27 | $row = $result->fetchAssociative(); 28 | 29 | if (!$row) { 30 | return null; 31 | } 32 | 33 | $post = Post::create( 34 | id: $row['id'], 35 | title: $row['title'], 36 | body: $row['body'], 37 | createdAt: new \DateTimeImmutable($row['created_at']) 38 | ); 39 | 40 | return $post; 41 | } 42 | 43 | public function findOrFail(int $id): Post 44 | { 45 | $post = $this->findById($id); 46 | 47 | if (!$post) { 48 | throw new NotFoundException(sprintf('Post with ID %d not found', $id)); 49 | } 50 | 51 | return $post; 52 | } 53 | } 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /src/Repository/UserMapper.php: -------------------------------------------------------------------------------- 1 | dataMapper->getConnection()->prepare(" 18 | INSERT INTO users (username, password, created_at) 19 | VALUES (:username, :password, :created_at) 20 | "); 21 | 22 | $stmt->bindValue(':username', $user->getUsername()); 23 | $stmt->bindValue(':password', $user->getPassword()); 24 | $stmt->bindValue(':created_at', $user->getCreatedAt()->format('Y-m-d H:i:s')); 25 | 26 | $stmt->executeStatement(); 27 | 28 | $id = $this->dataMapper->save($user); 29 | 30 | $user->setId($id); 31 | } 32 | } -------------------------------------------------------------------------------- /src/Repository/UserRepository.php: -------------------------------------------------------------------------------- 1 | connection->createQueryBuilder(); 20 | 21 | $queryBuilder 22 | ->select('id', 'username', 'password', 'created_at') 23 | ->from('users') 24 | ->where('username = :username') 25 | ->setParameter('username', $username); 26 | 27 | $result = $queryBuilder->executeQuery(); 28 | 29 | $row = $result->fetchAssociative(); 30 | 31 | if (!$row) { 32 | return null; 33 | } 34 | 35 | $user = new User( 36 | id: $row['id'], 37 | username: $row['username'], 38 | password: $row['password'], 39 | createdAt: new \DateTimeImmutable($row['created_at']) 40 | ); 41 | 42 | return $user; 43 | } 44 | } -------------------------------------------------------------------------------- /src/Widget.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | PHP Framework PRO 8 | 9 | 10 | 11 | 69 | 70 | 71 | 72 | 73 | 83 | 84 |
85 | {% if session().hasFlash('success') %} 86 | 93 | {% endif %} 94 | 95 | {% if session().hasFlash('error') %} 96 | 103 | {% endif %} 104 | 105 |
106 | {% block main %} {% endblock %} 107 |
108 |
109 | 110 | 111 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /templates/create-post.html.twig: -------------------------------------------------------------------------------- 1 | {% extends 'base.html.twig' %} 2 | 3 | {% block main %} 4 |
5 |
6 | 7 | 8 |
Give your post a catchy title.
9 |
10 |
11 | 12 | 13 |
14 | 15 |
16 | {% endblock %} -------------------------------------------------------------------------------- /templates/dashboard.html.twig: -------------------------------------------------------------------------------- 1 | {% extends 'base.html.twig' %} 2 | {% block main %} 3 |

Dashboard

4 | {% endblock %} 5 | -------------------------------------------------------------------------------- /templates/home.html.twig: -------------------------------------------------------------------------------- 1 | {% extends 'base.html.twig' %} 2 | 3 | {% block main %} 4 |

garyclarke.tech

5 |

Welcome to garyclarke.tech. I hope you're learning lots of cool stuff!

6 | {% endblock %} -------------------------------------------------------------------------------- /templates/login.html.twig: -------------------------------------------------------------------------------- 1 | {% extends 'base.html.twig' %} 2 | 3 | {% block main %} 4 | 5 |

Login

6 | 7 |
8 |
9 | 10 | 11 |
12 |
13 | 14 | 15 |
16 | 17 |
18 | 19 | {% endblock %} -------------------------------------------------------------------------------- /templates/post.html.twig: -------------------------------------------------------------------------------- 1 | {% extends 'base.html.twig' %} 2 | 3 | {% block main %} 4 |

{{ post.title }}

5 |

{{ post.body }}

6 | {% endblock %} -------------------------------------------------------------------------------- /templates/register.html.twig: -------------------------------------------------------------------------------- 1 | {% extends 'base.html.twig' %} 2 | 3 | {% block main %} 4 | 5 |

Register

6 | 7 |
8 |
9 | 10 | 11 |
12 |
13 | 14 | 15 |
16 | 17 |
18 | 19 | {% endblock %} -------------------------------------------------------------------------------- /var/.gitignore: -------------------------------------------------------------------------------- 1 | db.sqlite --------------------------------------------------------------------------------