From 90ab76a1d7e6bcd9be4df5f78b8d6303c9b7a4bc Mon Sep 17 00:00:00 2001 From: Adrien Crivelli Date: Sun, 28 Jun 2026 21:33:59 +0200 Subject: [PATCH 01/12] =?UTF-8?q?mQueue=20v2=20=F0=9F=A4=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/main.yml | 6 - .gitignore | 24 +- .php-cs-fixer.dist.php | 99 +- .zfproject.xml | 160 - README.md | 2 +- application/Bootstrap.php | 162 - application/Debug.php | 62 - application/configs/application.sample.ini | 29 - application/controllers/AboutController.php | 14 - .../controllers/ActivityController.php | 46 - application/controllers/CssController.php | 17 - application/controllers/ErrorController.php | 58 - application/controllers/FaqController.php | 14 - application/controllers/IndexController.php | 20 - application/controllers/JsController.php | 20 - application/controllers/MovieController.php | 132 - application/controllers/StatusController.php | 124 - application/controllers/UserController.php | 97 - application/layouts/scripts/jsonp.phtml | 3 - application/layouts/scripts/layout.phtml | 61 - .../ActionHelper/CreatePaginator.php | 65 - .../Controller/ActionHelper/CreateSorting.php | 70 - .../Controller/ActionHelper/Headers.php | 38 - application/mQueue/Form/Filter.php | 132 - application/mQueue/Form/Filters.php | 135 - application/mQueue/Form/Login.php | 41 - application/mQueue/Form/Movie.php | 30 - application/mQueue/Form/QuickSearch.php | 35 - application/mQueue/Form/User.php | 65 - application/mQueue/Form/Validate/User.php | 30 - application/mQueue/Model/AbstractMapper.php | 41 - application/mQueue/Model/AbstractModel.php | 9 - application/mQueue/Model/DbTable/Movie.php | 18 - application/mQueue/Model/DbTable/Setting.php | 18 - application/mQueue/Model/DbTable/Status.php | 18 - application/mQueue/Model/DbTable/User.php | 18 - application/mQueue/Model/Movie.php | 174 - application/mQueue/Model/MovieMapper.php | 183 - application/mQueue/Model/Setting.php | 37 - application/mQueue/Model/SettingMapper.php | 27 - application/mQueue/Model/Status.php | 69 - application/mQueue/Model/StatusMapper.php | 237 - application/mQueue/Model/User.php | 72 - application/mQueue/Model/UserMapper.php | 69 - application/mQueue/View/Helper/Activity.php | 74 - application/mQueue/View/Helper/CacheStamp.php | 31 - .../mQueue/View/Helper/FlashMessenger.php | 142 - application/mQueue/View/Helper/Footer.php | 26 - .../mQueue/View/Helper/GoogleAnalytics.php | 44 - application/mQueue/View/Helper/Graph.php | 51 - application/mQueue/View/Helper/Gravatar.php | 25 - application/mQueue/View/Helper/HeadLink.php | 26 - application/mQueue/View/Helper/HeadScript.php | 62 - application/mQueue/View/Helper/LoginState.php | 48 - application/mQueue/View/Helper/Movie.php | 30 - application/mQueue/View/Helper/Rating.php | 21 - application/mQueue/View/Helper/Sort.php | 36 - application/mQueue/View/Helper/StatusHelp.php | 27 - .../mQueue/View/Helper/StatusLinks.php | 42 - application/views/scripts/about/index.phtml | 6 - .../views/scripts/activity/index.phtml | 9 - .../views/scripts/activity/index.rss.phtml | 71 - application/views/scripts/error/error.phtml | 19 - application/views/scripts/index/index.phtml | 43 - application/views/scripts/movie/add.phtml | 37 - application/views/scripts/movie/index.phtml | 51 - .../views/scripts/movie/index.rss.phtml | 58 - application/views/scripts/movie/view.phtml | 33 - application/views/scripts/status/list.phtml | 5 - application/views/scripts/user/index.phtml | 44 - application/views/scripts/user/view.phtml | 48 - bin/build.sh | 6 +- bin/clear-config-cache.php | 39 + bin/doctrine | 49 + ...ch_movie_data.php => fetch-movie-data.php} | 8 +- bin/insert_test_data.php | 48 - bin/migrate.sql | 17 + bin/pre-commit.sh | 2 +- bin/search-movie-source.php | 17 + bin/search_movie_source.php | 88 - bin/sql/version.0.sql | 80 - bin/sql/version.1.sql | 14 - bin/sql/version.2.sql | 4 - bin/sql/version.3.sql | 2 - bin/sql/version.4.sql | 4 - bin/sql/version.5.sql | 8 - bin/sql/version.6.sql | 5 - bin/sql/version.7.sql | 6 - bin/sql/version.8.sql | 7 - bin/sql/version.9.sql | 5 - bin/update_database.php | 135 - composer.json | 75 +- composer.lock | 9741 ++++++++++++++--- config/autoload/dependencies.global.php | 36 + config/autoload/development.local.php.dist | 15 + config/autoload/doctrine.global.php | 59 + config/autoload/global.php | 121 + config/autoload/local.php.dist | 13 + config/autoload/mezzio.global.php | 25 + config/config.php | 48 + config/container.php | 23 + config/development.config.php.dist | 31 + config/migration-template.txt | 16 + config/pipeline.php | 86 + config/routes.php | 65 + .../nova/__init__.py => data/cache/.gitkeep | 0 {application/localization => data/i18n}/fr.mo | Bin {application/localization => data/i18n}/fr.po | 0 {application/localization => data/i18n}/ko.mo | Bin {application/localization => data/i18n}/ko.po | 0 doc/Doxyfile | 1600 --- library/searchengine/nova/engines/__init__.py | 0 library/searchengine/nova/engines/btbit.py | 128 - library/searchengine/nova/engines/btdb.py | 144 - library/searchengine/nova/engines/eztv.py | 79 - .../searchengine/nova/engines/jackett.json | 5 - library/searchengine/nova/engines/jackett.py | 205 - library/searchengine/nova/engines/leetx.py | 168 - .../nova/engines/legittorrents.py | 104 - .../searchengine/nova/engines/limetorrents.py | 127 - .../searchengine/nova/engines/piratebay.py | 191 - library/searchengine/nova/engines/rarbg.py | 88 - library/searchengine/nova/engines/torlock.py | 101 - .../searchengine/nova/engines/versions.txt | 11 - library/searchengine/nova/engines/zooqle.py | 119 - library/searchengine/nova/fix_encoding.py | 371 - library/searchengine/nova/helpers.py | 122 - library/searchengine/nova/nova2.py | 191 - library/searchengine/nova/nova2dl.py | 63 - library/searchengine/nova/novaprinter.py | 73 - library/searchengine/nova/socks.py | 391 - old/bin/insert_test_data.php | 42 + {tests => old/tests}/test_scores.php | 2 +- package.json | 8 +- phpstan-baseline.neon | 40 +- phpstan.neon.dist | 21 +- phpunit.xml | 26 - phpunit.xml.dist | 26 + pnpm-lock.yaml | 545 + pnpm-workspace.yaml | 3 + public/.htaccess | 13 - public/images/browser_ie.svg | 474 - public/images/browser_opera.gif | Bin 1032 -> 0 bytes public/images/browser_safari.gif | Bin 1044 -> 0 bytes public/index.php | 62 +- .../DBAL/Logging/MiddlewareFactory.php | 41 + src/Application/DBAL/Types/ChronosType.php | 58 + src/Application/DBAL/Types/DateType.php | 57 + src/Application/Enum/Rating.php | 51 + src/Application/Form/Filter.php | 115 + src/Application/Form/Filters.php | 160 + src/Application/Form/Login.php | 44 + src/Application/Form/Movie.php | 40 + src/Application/Form/QuickSearch.php | 31 + src/Application/Form/User.php | 60 + src/Application/Form/Validate/User.php | 43 + src/Application/Handler/AboutPageHandler.php | 17 + src/Application/Handler/ActivityHandler.php | 56 + src/Application/Handler/FaqPageHandler.php | 17 + .../Handler/GravatarPageHandler.php | 21 + src/Application/Handler/HomePageHandler.php | 23 + .../Handler/JsMqueueUserHandler.php | 14 + src/Application/Handler/JsRemoteHandler.php | 20 + src/Application/Handler/Movie/AddHandler.php | 35 + .../Handler/Movie/IndexHandler.php | 112 + src/Application/Handler/Movie/ViewHandler.php | 45 + src/Application/Handler/PageHandler.php | 81 + .../Handler/PageHandlerFactory.php | 44 + .../Handler/Status/GraphHandler.php | 58 + .../Handler/Status/IndexHandler.php | 56 + .../Handler/Status/ListHandler.php | 47 + src/Application/Handler/User/IndexHandler.php | 21 + src/Application/Handler/User/LoginHandler.php | 67 + .../Handler/User/LogoutHandler.php | 24 + src/Application/Handler/User/NewHandler.php | 43 + src/Application/Handler/User/ViewHandler.php | 41 + .../Middleware/AuthenticationFactory.php | 19 + .../Middleware/AuthenticationMiddleware.php | 46 + .../Middleware/DetectBrowserLocaleFactory.php | 18 + .../DetectBrowserLocaleMiddleware.php | 64 + .../Migration/Version20260412140844.php | 31 + src/Application/Model/AbstractModel.php | 34 + src/Application/Model/Movie.php | 186 + src/Application/Model/Status.php | 41 + src/Application/Model/User.php | 51 + .../Paginator/PaginatorFactory.php | 53 + src/Application/Paginator/QueryAdapter.php | 20 + .../Repository/AbstractRepository.php | 14 + .../Repository/MovieRepository.php | 181 + .../Repository/StatusRepository.php | 263 + src/Application/Repository/UserRepository.php | 53 + .../Response/JsonCallbackResponse.php | 38 + src/Application/Service/Logger.php | 59 + src/Application/Service/Processor.php | 78 + .../Application}/Service/SearchEngine.php | 61 +- src/Application/Service/Sorting.php | 42 + src/Application/Validator/NoRecordExists.php | 55 + src/Application/View/Helper/Activity.php | 68 + .../View/Helper/ActivityFactory.php | 25 + .../View/Helper/AlternateFormats.php | 30 +- .../View/Helper/AlternateFormatsFactory.php | 21 + src/Application/View/Helper/CacheStamp.php | 29 + .../View/Helper/CacheStampFactory.php | 19 + .../View/Helper/FlashMessenger.php | 33 + .../View/Helper/FlashMessengerFactory.php | 24 + src/Application/View/Helper/Footer.php | 20 + src/Application/View/Helper/FooterFactory.php | 22 + .../View/Helper/GoogleAnalytics.php | 33 + .../View/Helper/GoogleAnalyticsFactory.php | 18 + src/Application/View/Helper/Graph.php | 51 + src/Application/View/Helper/GraphFactory.php | 23 + src/Application/View/Helper/Gravatar.php | 29 + .../View/Helper/GravatarFactory.php | 22 + src/Application/View/Helper/HeadLink.php | 26 + .../View/Helper/HeadLinkFactory.php | 21 + src/Application/View/Helper/HeadScript.php | 44 + .../View/Helper/HeadScriptFactory.php | 23 + .../View/Helper/LanguageSelector.php | 17 +- .../View/Helper/LanguageSelectorFactory.php | 21 + .../Application}/View/Helper/Link.php | 10 +- src/Application/View/Helper/LoginState.php | 35 + .../View/Helper/LoginStateFactory.php | 25 + src/Application/View/Helper/Movie.php | 37 + src/Application/View/Helper/MovieFactory.php | 25 + src/Application/View/Helper/Rating.php | 16 + src/Application/View/Helper/Sort.php | 37 + src/Application/View/Helper/SortFactory.php | 25 + src/Application/View/Helper/StatusHelp.php | 26 + .../View/Helper/StatusHelpFactory.php | 21 + src/Application/View/Helper/StatusLinks.php | 45 + .../View/Helper/StatusLinksFactory.php | 24 + .../Application}/View/Helper/UrlParams.php | 25 +- .../View/Helper/UrlParamsFactory.php | 22 + src/functions.php | 67 + {application => src}/sass/_handheld.scss | 0 {application => src}/sass/_local.scss | 123 +- {application => src}/sass/_print.scss | 0 {application => src}/sass/_status.scss | 0 {application => src}/sass/_variables.scss | 0 {application => src}/sass/application.scss | 0 {application => src}/sass/both.scss | 0 templates/app/about.phtml | 6 + templates/app/activity-rss.phtml | 49 + templates/app/activity.phtml | 9 + .../index.phtml => templates/app/faq.phtml | 10 +- .../css => templates/app}/gravatar-css.phtml | 2 +- templates/app/movie/add.phtml | 39 + templates/app/movie/index-rss.phtml | 50 + .../app}/movie/index.csv.phtml | 0 templates/app/movie/index.phtml | 68 + templates/app/movie/view.phtml | 27 + .../js => templates/app}/mqueue-user-js.phtml | 6 +- .../js => templates/app}/remote-js.phtml | 4 +- .../app}/status/index.phtml | 0 templates/app/user/index.phtml | 33 + .../app}/user/login.phtml | 2 +- .../app}/user/logout.phtml | 0 .../scripts => templates/app}/user/new.phtml | 2 +- templates/app/user/view.phtml | 40 + templates/error/404.phtml | 4 + templates/error/error.phtml | 10 + templates/layout/default.phtml | 61 + templates/layout/error.phtml | 5 + .../partial}/pagination.phtml | 26 +- .../DBAL/Types/ChronosTypeTest.php | 71 + .../DBAL/Types/DateTypeTest.php | 63 + .../Handler/AboutPageHandlerTest.php | 17 + .../Handler/AbstractHandler.php | 27 + .../Handler/ActivityHandlerTest.php | 42 + .../Handler/FaqPageHandlerTest.php | 18 + .../Handler/GravatarPageHandlerTest.php | 19 + .../Handler/HomePageHandlerTest.php | 31 + .../Handler/JsMqueueUserHandlerTest.php | 18 + .../Handler/JsRemoteHandlerTest.php | 19 + .../Handler/Movie/AddHandlerTest.php | 27 + .../Handler/Movie/IndexHandlerTest.php | 65 + .../Handler/Movie/ViewHandlerTest.php | 26 + .../Handler/PageHandlerFactoryTest.php | 23 + .../Handler/Status/GraphHandlerTest.php | 31 + .../Handler/Status/IndexHandlerTest.php | 51 + .../Handler/Status/ListHandlerTest.php | 38 + .../Handler/User/IndexHandlerTest.php | 18 + .../Handler/User/LoginHandlerTest.php | 80 + .../Handler/User/NewHandlerTest.php | 43 + .../Handler/User/ViewHandlerTest.php | 29 + tests/ApplicationTest/InMemoryContainer.php | 53 + .../AuthenticationMiddlewareTest.php | 90 + .../DetectBrowserLocaleMiddlewareTest.php | 48 + tests/ApplicationTest/Model/MovieTest.php | 92 + .../Repository/MovieRepositoryTest.php | 178 + .../Repository/StatusRepositoryTest.php | 339 + .../Repository/UserRepositoryTest.php | 60 + .../Traits/TestWithTransactionAndUser.php | 72 + tests/bootstrap.php | 8 - tests/data/fixture.sql | 28 + tests/library/bootstrap.php | 0 .../Controller/AboutControllerTest.php | 20 - .../Controller/AbstractControllerTestCase.php | 85 - .../Controller/ActivityControllerTest.php | 49 - .../Controller/CssControllerTest.php | 22 - .../Controller/FaqControllerTest.php | 21 - .../Controller/IndexControllerTest.php | 20 - .../Controller/JsControllerTest.php | 37 - .../Controller/MovieControllerTest.php | 36 - .../Controller/StatusControllerTest.php | 50 - .../Controller/UserControllerTest.php | 119 - tests/mQueueTest/bootstrap.php | 0 tests/object-manager.php | 7 + yarn.lock | 359 - 309 files changed, 15605 insertions(+), 11859 deletions(-) delete mode 100644 .zfproject.xml delete mode 100644 application/Bootstrap.php delete mode 100644 application/Debug.php delete mode 100644 application/configs/application.sample.ini delete mode 100644 application/controllers/AboutController.php delete mode 100644 application/controllers/ActivityController.php delete mode 100644 application/controllers/CssController.php delete mode 100644 application/controllers/ErrorController.php delete mode 100644 application/controllers/FaqController.php delete mode 100644 application/controllers/IndexController.php delete mode 100644 application/controllers/JsController.php delete mode 100644 application/controllers/MovieController.php delete mode 100644 application/controllers/StatusController.php delete mode 100644 application/controllers/UserController.php delete mode 100644 application/layouts/scripts/jsonp.phtml delete mode 100644 application/layouts/scripts/layout.phtml delete mode 100644 application/mQueue/Controller/ActionHelper/CreatePaginator.php delete mode 100644 application/mQueue/Controller/ActionHelper/CreateSorting.php delete mode 100644 application/mQueue/Controller/ActionHelper/Headers.php delete mode 100644 application/mQueue/Form/Filter.php delete mode 100644 application/mQueue/Form/Filters.php delete mode 100644 application/mQueue/Form/Login.php delete mode 100644 application/mQueue/Form/Movie.php delete mode 100644 application/mQueue/Form/QuickSearch.php delete mode 100644 application/mQueue/Form/User.php delete mode 100644 application/mQueue/Form/Validate/User.php delete mode 100644 application/mQueue/Model/AbstractMapper.php delete mode 100644 application/mQueue/Model/AbstractModel.php delete mode 100644 application/mQueue/Model/DbTable/Movie.php delete mode 100644 application/mQueue/Model/DbTable/Setting.php delete mode 100644 application/mQueue/Model/DbTable/Status.php delete mode 100644 application/mQueue/Model/DbTable/User.php delete mode 100644 application/mQueue/Model/Movie.php delete mode 100644 application/mQueue/Model/MovieMapper.php delete mode 100644 application/mQueue/Model/Setting.php delete mode 100644 application/mQueue/Model/SettingMapper.php delete mode 100644 application/mQueue/Model/Status.php delete mode 100644 application/mQueue/Model/StatusMapper.php delete mode 100644 application/mQueue/Model/User.php delete mode 100644 application/mQueue/Model/UserMapper.php delete mode 100644 application/mQueue/View/Helper/Activity.php delete mode 100644 application/mQueue/View/Helper/CacheStamp.php delete mode 100644 application/mQueue/View/Helper/FlashMessenger.php delete mode 100644 application/mQueue/View/Helper/Footer.php delete mode 100644 application/mQueue/View/Helper/GoogleAnalytics.php delete mode 100644 application/mQueue/View/Helper/Graph.php delete mode 100644 application/mQueue/View/Helper/Gravatar.php delete mode 100644 application/mQueue/View/Helper/HeadLink.php delete mode 100644 application/mQueue/View/Helper/HeadScript.php delete mode 100644 application/mQueue/View/Helper/LoginState.php delete mode 100644 application/mQueue/View/Helper/Movie.php delete mode 100644 application/mQueue/View/Helper/Rating.php delete mode 100644 application/mQueue/View/Helper/Sort.php delete mode 100644 application/mQueue/View/Helper/StatusHelp.php delete mode 100644 application/mQueue/View/Helper/StatusLinks.php delete mode 100644 application/views/scripts/about/index.phtml delete mode 100644 application/views/scripts/activity/index.phtml delete mode 100644 application/views/scripts/activity/index.rss.phtml delete mode 100644 application/views/scripts/error/error.phtml delete mode 100644 application/views/scripts/index/index.phtml delete mode 100644 application/views/scripts/movie/add.phtml delete mode 100644 application/views/scripts/movie/index.phtml delete mode 100644 application/views/scripts/movie/index.rss.phtml delete mode 100644 application/views/scripts/movie/view.phtml delete mode 100644 application/views/scripts/status/list.phtml delete mode 100644 application/views/scripts/user/index.phtml delete mode 100644 application/views/scripts/user/view.phtml create mode 100644 bin/clear-config-cache.php create mode 100755 bin/doctrine rename bin/{fetch_movie_data.php => fetch-movie-data.php} (50%) mode change 100644 => 100755 delete mode 100644 bin/insert_test_data.php create mode 100644 bin/migrate.sql create mode 100644 bin/search-movie-source.php delete mode 100644 bin/search_movie_source.php delete mode 100644 bin/sql/version.0.sql delete mode 100644 bin/sql/version.1.sql delete mode 100644 bin/sql/version.2.sql delete mode 100644 bin/sql/version.3.sql delete mode 100644 bin/sql/version.4.sql delete mode 100644 bin/sql/version.5.sql delete mode 100644 bin/sql/version.6.sql delete mode 100644 bin/sql/version.7.sql delete mode 100644 bin/sql/version.8.sql delete mode 100644 bin/sql/version.9.sql delete mode 100644 bin/update_database.php create mode 100644 config/autoload/dependencies.global.php create mode 100644 config/autoload/development.local.php.dist create mode 100644 config/autoload/doctrine.global.php create mode 100644 config/autoload/global.php create mode 100644 config/autoload/local.php.dist create mode 100644 config/autoload/mezzio.global.php create mode 100644 config/config.php create mode 100644 config/container.php create mode 100644 config/development.config.php.dist create mode 100644 config/migration-template.txt create mode 100644 config/pipeline.php create mode 100644 config/routes.php rename library/searchengine/nova/__init__.py => data/cache/.gitkeep (100%) rename {application/localization => data/i18n}/fr.mo (100%) rename {application/localization => data/i18n}/fr.po (100%) rename {application/localization => data/i18n}/ko.mo (100%) rename {application/localization => data/i18n}/ko.po (100%) delete mode 100644 doc/Doxyfile delete mode 100644 library/searchengine/nova/engines/__init__.py delete mode 100644 library/searchengine/nova/engines/btbit.py delete mode 100644 library/searchengine/nova/engines/btdb.py delete mode 100644 library/searchengine/nova/engines/eztv.py delete mode 100644 library/searchengine/nova/engines/jackett.json delete mode 100644 library/searchengine/nova/engines/jackett.py delete mode 100644 library/searchengine/nova/engines/leetx.py delete mode 100644 library/searchengine/nova/engines/legittorrents.py delete mode 100644 library/searchengine/nova/engines/limetorrents.py delete mode 100644 library/searchengine/nova/engines/piratebay.py delete mode 100644 library/searchengine/nova/engines/rarbg.py delete mode 100644 library/searchengine/nova/engines/torlock.py delete mode 100644 library/searchengine/nova/engines/versions.txt delete mode 100644 library/searchengine/nova/engines/zooqle.py delete mode 100644 library/searchengine/nova/fix_encoding.py delete mode 100644 library/searchengine/nova/helpers.py delete mode 100644 library/searchengine/nova/nova2.py delete mode 100644 library/searchengine/nova/nova2dl.py delete mode 100644 library/searchengine/nova/novaprinter.py delete mode 100644 library/searchengine/nova/socks.py create mode 100644 old/bin/insert_test_data.php rename {tests => old/tests}/test_scores.php (94%) delete mode 100644 phpunit.xml create mode 100644 phpunit.xml.dist create mode 100644 pnpm-lock.yaml create mode 100644 pnpm-workspace.yaml delete mode 100644 public/.htaccess delete mode 100644 public/images/browser_ie.svg delete mode 100644 public/images/browser_opera.gif delete mode 100644 public/images/browser_safari.gif create mode 100644 src/Application/DBAL/Logging/MiddlewareFactory.php create mode 100644 src/Application/DBAL/Types/ChronosType.php create mode 100644 src/Application/DBAL/Types/DateType.php create mode 100644 src/Application/Enum/Rating.php create mode 100644 src/Application/Form/Filter.php create mode 100644 src/Application/Form/Filters.php create mode 100644 src/Application/Form/Login.php create mode 100644 src/Application/Form/Movie.php create mode 100644 src/Application/Form/QuickSearch.php create mode 100644 src/Application/Form/User.php create mode 100644 src/Application/Form/Validate/User.php create mode 100644 src/Application/Handler/AboutPageHandler.php create mode 100644 src/Application/Handler/ActivityHandler.php create mode 100644 src/Application/Handler/FaqPageHandler.php create mode 100644 src/Application/Handler/GravatarPageHandler.php create mode 100644 src/Application/Handler/HomePageHandler.php create mode 100644 src/Application/Handler/JsMqueueUserHandler.php create mode 100644 src/Application/Handler/JsRemoteHandler.php create mode 100644 src/Application/Handler/Movie/AddHandler.php create mode 100644 src/Application/Handler/Movie/IndexHandler.php create mode 100644 src/Application/Handler/Movie/ViewHandler.php create mode 100644 src/Application/Handler/PageHandler.php create mode 100644 src/Application/Handler/PageHandlerFactory.php create mode 100644 src/Application/Handler/Status/GraphHandler.php create mode 100644 src/Application/Handler/Status/IndexHandler.php create mode 100644 src/Application/Handler/Status/ListHandler.php create mode 100644 src/Application/Handler/User/IndexHandler.php create mode 100644 src/Application/Handler/User/LoginHandler.php create mode 100644 src/Application/Handler/User/LogoutHandler.php create mode 100644 src/Application/Handler/User/NewHandler.php create mode 100644 src/Application/Handler/User/ViewHandler.php create mode 100644 src/Application/Middleware/AuthenticationFactory.php create mode 100644 src/Application/Middleware/AuthenticationMiddleware.php create mode 100644 src/Application/Middleware/DetectBrowserLocaleFactory.php create mode 100644 src/Application/Middleware/DetectBrowserLocaleMiddleware.php create mode 100644 src/Application/Migration/Version20260412140844.php create mode 100644 src/Application/Model/AbstractModel.php create mode 100644 src/Application/Model/Movie.php create mode 100644 src/Application/Model/Status.php create mode 100644 src/Application/Model/User.php create mode 100644 src/Application/Paginator/PaginatorFactory.php create mode 100644 src/Application/Paginator/QueryAdapter.php create mode 100644 src/Application/Repository/AbstractRepository.php create mode 100644 src/Application/Repository/MovieRepository.php create mode 100644 src/Application/Repository/StatusRepository.php create mode 100644 src/Application/Repository/UserRepository.php create mode 100644 src/Application/Response/JsonCallbackResponse.php create mode 100644 src/Application/Service/Logger.php create mode 100644 src/Application/Service/Processor.php rename {application/mQueue => src/Application}/Service/SearchEngine.php (83%) create mode 100644 src/Application/Service/Sorting.php create mode 100644 src/Application/Validator/NoRecordExists.php create mode 100644 src/Application/View/Helper/Activity.php create mode 100644 src/Application/View/Helper/ActivityFactory.php rename {application/mQueue => src/Application}/View/Helper/AlternateFormats.php (53%) create mode 100644 src/Application/View/Helper/AlternateFormatsFactory.php create mode 100644 src/Application/View/Helper/CacheStamp.php create mode 100644 src/Application/View/Helper/CacheStampFactory.php create mode 100644 src/Application/View/Helper/FlashMessenger.php create mode 100644 src/Application/View/Helper/FlashMessengerFactory.php create mode 100644 src/Application/View/Helper/Footer.php create mode 100644 src/Application/View/Helper/FooterFactory.php create mode 100644 src/Application/View/Helper/GoogleAnalytics.php create mode 100644 src/Application/View/Helper/GoogleAnalyticsFactory.php create mode 100644 src/Application/View/Helper/Graph.php create mode 100644 src/Application/View/Helper/GraphFactory.php create mode 100644 src/Application/View/Helper/Gravatar.php create mode 100644 src/Application/View/Helper/GravatarFactory.php create mode 100644 src/Application/View/Helper/HeadLink.php create mode 100644 src/Application/View/Helper/HeadLinkFactory.php create mode 100644 src/Application/View/Helper/HeadScript.php create mode 100644 src/Application/View/Helper/HeadScriptFactory.php rename {application/mQueue => src/Application}/View/Helper/LanguageSelector.php (63%) create mode 100644 src/Application/View/Helper/LanguageSelectorFactory.php rename {application/mQueue => src/Application}/View/Helper/Link.php (58%) create mode 100644 src/Application/View/Helper/LoginState.php create mode 100644 src/Application/View/Helper/LoginStateFactory.php create mode 100644 src/Application/View/Helper/Movie.php create mode 100644 src/Application/View/Helper/MovieFactory.php create mode 100644 src/Application/View/Helper/Rating.php create mode 100644 src/Application/View/Helper/Sort.php create mode 100644 src/Application/View/Helper/SortFactory.php create mode 100644 src/Application/View/Helper/StatusHelp.php create mode 100644 src/Application/View/Helper/StatusHelpFactory.php create mode 100644 src/Application/View/Helper/StatusLinks.php create mode 100644 src/Application/View/Helper/StatusLinksFactory.php rename {application/mQueue => src/Application}/View/Helper/UrlParams.php (60%) create mode 100644 src/Application/View/Helper/UrlParamsFactory.php create mode 100644 src/functions.php rename {application => src}/sass/_handheld.scss (100%) rename {application => src}/sass/_local.scss (83%) rename {application => src}/sass/_print.scss (100%) rename {application => src}/sass/_status.scss (100%) rename {application => src}/sass/_variables.scss (100%) rename {application => src}/sass/application.scss (100%) rename {application => src}/sass/both.scss (100%) create mode 100644 templates/app/about.phtml create mode 100644 templates/app/activity-rss.phtml create mode 100644 templates/app/activity.phtml rename application/views/scripts/faq/index.phtml => templates/app/faq.phtml (65%) rename {application/views/scripts/css => templates/app}/gravatar-css.phtml (82%) create mode 100644 templates/app/movie/add.phtml create mode 100644 templates/app/movie/index-rss.phtml rename {application/views/scripts => templates/app}/movie/index.csv.phtml (100%) create mode 100644 templates/app/movie/index.phtml create mode 100644 templates/app/movie/view.phtml rename {application/views/scripts/js => templates/app}/mqueue-user-js.phtml (69%) rename {application/views/scripts/js => templates/app}/remote-js.phtml (59%) rename {application/views/scripts => templates/app}/status/index.phtml (100%) create mode 100644 templates/app/user/index.phtml rename {application/views/scripts => templates/app}/user/login.phtml (69%) rename {application/views/scripts => templates/app}/user/logout.phtml (100%) rename {application/views/scripts => templates/app}/user/new.phtml (72%) create mode 100644 templates/app/user/view.phtml create mode 100644 templates/error/404.phtml create mode 100644 templates/error/error.phtml create mode 100644 templates/layout/default.phtml create mode 100644 templates/layout/error.phtml rename {application/views/scripts => templates/partial}/pagination.phtml (72%) create mode 100644 tests/ApplicationTest/DBAL/Types/ChronosTypeTest.php create mode 100644 tests/ApplicationTest/DBAL/Types/DateTypeTest.php create mode 100644 tests/ApplicationTest/Handler/AboutPageHandlerTest.php create mode 100644 tests/ApplicationTest/Handler/AbstractHandler.php create mode 100644 tests/ApplicationTest/Handler/ActivityHandlerTest.php create mode 100644 tests/ApplicationTest/Handler/FaqPageHandlerTest.php create mode 100644 tests/ApplicationTest/Handler/GravatarPageHandlerTest.php create mode 100644 tests/ApplicationTest/Handler/HomePageHandlerTest.php create mode 100644 tests/ApplicationTest/Handler/JsMqueueUserHandlerTest.php create mode 100644 tests/ApplicationTest/Handler/JsRemoteHandlerTest.php create mode 100644 tests/ApplicationTest/Handler/Movie/AddHandlerTest.php create mode 100644 tests/ApplicationTest/Handler/Movie/IndexHandlerTest.php create mode 100644 tests/ApplicationTest/Handler/Movie/ViewHandlerTest.php create mode 100644 tests/ApplicationTest/Handler/PageHandlerFactoryTest.php create mode 100644 tests/ApplicationTest/Handler/Status/GraphHandlerTest.php create mode 100644 tests/ApplicationTest/Handler/Status/IndexHandlerTest.php create mode 100644 tests/ApplicationTest/Handler/Status/ListHandlerTest.php create mode 100644 tests/ApplicationTest/Handler/User/IndexHandlerTest.php create mode 100644 tests/ApplicationTest/Handler/User/LoginHandlerTest.php create mode 100644 tests/ApplicationTest/Handler/User/NewHandlerTest.php create mode 100644 tests/ApplicationTest/Handler/User/ViewHandlerTest.php create mode 100644 tests/ApplicationTest/InMemoryContainer.php create mode 100644 tests/ApplicationTest/Middleware/AuthenticationMiddlewareTest.php create mode 100644 tests/ApplicationTest/Middleware/DetectBrowserLocaleMiddlewareTest.php create mode 100644 tests/ApplicationTest/Model/MovieTest.php create mode 100644 tests/ApplicationTest/Repository/MovieRepositoryTest.php create mode 100644 tests/ApplicationTest/Repository/StatusRepositoryTest.php create mode 100644 tests/ApplicationTest/Repository/UserRepositoryTest.php create mode 100644 tests/ApplicationTest/Traits/TestWithTransactionAndUser.php delete mode 100644 tests/bootstrap.php create mode 100644 tests/data/fixture.sql delete mode 100644 tests/library/bootstrap.php delete mode 100644 tests/mQueueTest/Controller/AboutControllerTest.php delete mode 100644 tests/mQueueTest/Controller/AbstractControllerTestCase.php delete mode 100644 tests/mQueueTest/Controller/ActivityControllerTest.php delete mode 100644 tests/mQueueTest/Controller/CssControllerTest.php delete mode 100644 tests/mQueueTest/Controller/FaqControllerTest.php delete mode 100644 tests/mQueueTest/Controller/IndexControllerTest.php delete mode 100644 tests/mQueueTest/Controller/JsControllerTest.php delete mode 100644 tests/mQueueTest/Controller/MovieControllerTest.php delete mode 100644 tests/mQueueTest/Controller/StatusControllerTest.php delete mode 100644 tests/mQueueTest/Controller/UserControllerTest.php delete mode 100644 tests/mQueueTest/bootstrap.php create mode 100644 tests/object-manager.php delete mode 100644 yarn.lock diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 632ecf3..41f5dad 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -28,12 +28,6 @@ jobs: key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} restore-keys: ${{ runner.os }}-composer- - - uses: actions/cache@v4 - with: - path: ~/.cache/yarn - key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} - restore-keys: ${{ runner.os }}-yarn- - - name: Setup problem matchers for PHP run: echo "::add-matcher::${{ runner.tool_cache }}/php.json" diff --git a/.gitignore b/.gitignore index f2a5560..e65f29f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,17 @@ +*.pyc +/.idea/ /.php_cs.cache -/application/configs/application.ini -/doc/html -/.idea -/node_modules -/public/css -/public/js/min -/tests/data -/tests/log +/config/autoload/*.local.php +/config/autoload/local.php +/config/development.config.php +/data/!.gitignore +/data/!cache +/data/!cache/.gitkeep +/data/cache/ +/data/tmp/ +/library/searchengine/nova3/engines/jackett.json +/logs/ +/node_modules/ +/public/css/ +/public/js/min/ /vendor/ -*.pyc diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php index 43c7018..be75a23 100644 --- a/.php-cs-fixer.dist.php +++ b/.php-cs-fixer.dist.php @@ -4,12 +4,14 @@ $finder = PhpCsFixer\Finder::create() ->exclude('library') + ->exclude('data') ->exclude('node_modules') ->exclude('vendor') ->name('*.phtml') ->in(__DIR__); -return (new PhpCsFixer\Config()) +return new PhpCsFixer\Config() + ->setParallelConfig(PhpCsFixer\Runner\Parallel\ParallelConfigFactory::detect()) ->setRiskyAllowed(true) ->setFinder($finder) ->setCacheFile(sys_get_temp_dir() . '/php-cs-fixer' . preg_replace('~\W~', '-', __DIR__)) @@ -19,28 +21,39 @@ 'array_push' => true, 'array_syntax' => ['syntax' => 'short'], 'assign_null_coalescing_to_coalesce_equal' => true, + 'attribute_empty_parentheses' => true, 'backtick_to_shell_exec' => false, // Backticks are a very concise way to execute shell 'binary_operator_spaces' => true, 'blank_line_after_namespace' => true, 'blank_line_after_opening_tag' => true, 'blank_line_before_statement' => true, - 'braces' => false, // Mess up our templates, eg: application/layouts/scripts/layout.phtml + 'blank_line_between_import_groups' => true, + 'blank_lines_before_namespace' => true, + 'braces' => false, // Deprecated + 'braces_position' => false, // Mess up our templates, eg: application/layouts/scripts/layout.phtml 'cast_spaces' => true, 'class_attributes_separation' => ['elements' => ['method' => 'one', 'property' => 'one']], // const are often grouped with other related const 'class_definition' => true, + 'class_keyword' => false, // Because risky, and sometimes we prefer to keep FQCN as is 'class_keyword_remove' => false, // Deprecated, and ::class keyword gives us better support in IDE + 'class_reference_name_casing' => true, + 'clean_namespace' => true, 'combine_consecutive_issets' => true, 'combine_consecutive_unsets' => true, 'combine_nested_dirname' => true, 'comment_to_phpdoc' => true, - 'compact_nullable_typehint' => true, + 'compact_nullable_type_declaration' => true, + 'compact_nullable_typehint' => false, // Deprecated 'concat_space' => ['spacing' => 'one'], 'constant_case' => true, + 'control_structure_braces' => true, 'control_structure_continuation_position' => true, + 'curly_braces_position' => false, // Deprecated + 'date_time_create_from_format_call' => false, // Because risky 'date_time_immutable' => true, 'declare_equal_normalize' => true, 'declare_parentheses' => true, - 'declare_strict_types' => false, // Too early to adopt strict types + 'declare_strict_types' => false, 'dir_constant' => true, 'doctrine_annotation_array_assignment' => true, 'doctrine_annotation_braces' => true, @@ -53,7 +66,7 @@ 'encoding' => true, 'ereg_to_preg' => true, 'error_suppression' => true, - 'escape_implicit_backslashes' => true, + 'escape_implicit_backslashes' => false, // Deprecated 'explicit_indirect_variable' => false, // I feel it makes the code actually harder to read 'explicit_string_variable' => false, // I feel it makes the code actually harder to read 'final_class' => false, // We need non-final classes @@ -65,12 +78,15 @@ 'fully_qualified_strict_types' => true, 'function_declaration' => true, 'function_to_constant' => true, - 'function_typehint_space' => true, + 'function_typehint_space' => false, // Deprecated + 'general_attribute_remove' => true, 'general_phpdoc_annotation_remove' => ['annotations' => ['author', 'category', 'copyright', 'package', 'throws']], 'general_phpdoc_tag_rename' => true, + 'get_class_to_class_keyword' => true, 'global_namespace_import' => true, 'group_import' => false, // I feel it makes the code actually harder to read 'header_comment' => false, // We don't use common header in all our files + 'heredoc_closing_marker' => ['closing_marker' => 'STRING'], 'heredoc_indentation' => true, 'heredoc_to_nowdoc' => false, // We often use variable in heredoc 'implode_call' => true, @@ -82,8 +98,9 @@ 'lambda_not_used_import' => true, 'line_ending' => true, 'linebreak_after_opening_tag' => true, - 'list_syntax' => ['syntax' => 'short'], + 'list_syntax' => true, 'logical_operators' => true, + 'long_to_shorthand_operator' => false, // Because risky 'lowercase_cast' => true, 'lowercase_keywords' => true, 'lowercase_static_reference' => true, @@ -95,19 +112,24 @@ 'modernize_strpos' => true, 'modernize_types_casting' => true, 'multiline_comment_opening_closing' => true, + 'multiline_promoted_properties' => true, // Experimental + 'multiline_string_to_heredoc' => false, // We prefer to keep some freedom 'multiline_whitespace_before_semicolons' => true, 'native_constant_invocation' => false, // Micro optimization that look messy 'native_function_casing' => true, 'native_function_invocation' => false, // I suppose this would be best, but I am still unconvinced about the visual aspect of it - 'native_function_type_declaration_casing' => true, - 'new_with_braces' => true, + 'native_function_type_declaration_casing' => false, // Deprecated + 'native_type_declaration_casing' => true, + 'new_expression_parentheses' => true, + 'new_with_braces' => false, // Deprecated + 'new_with_parentheses' => true, 'no_alias_functions' => true, 'no_alias_language_construct_call' => true, 'no_alternative_syntax' => false, // We want to use alternative syntax in .phtml 'no_binary_string' => true, 'no_blank_lines_after_class_opening' => true, 'no_blank_lines_after_phpdoc' => true, - 'no_blank_lines_before_namespace' => false, // we want 1 blank line before namespace + 'no_blank_lines_before_namespace' => false, // Deprecated 'no_break_comment' => true, 'no_closing_tag' => true, 'no_empty_comment' => true, @@ -119,29 +141,35 @@ 'no_leading_namespace_whitespace' => true, 'no_mixed_echo_print' => true, 'no_multiline_whitespace_around_double_arrow' => true, + 'no_multiple_statements_per_line' => true, 'no_null_property_initialization' => true, - 'no_php4_constructor' => false, // Because our BootstrapCli class is affected by https://github.com/FriendsOfPHP/PHP-CS-Fixer/issues/3096 + 'no_php4_constructor' => true, 'no_short_bool_cast' => true, 'no_singleline_whitespace_before_semicolons' => true, 'no_space_around_double_colon' => true, 'no_spaces_after_function_name' => true, 'no_spaces_around_offset' => true, - 'no_spaces_inside_parenthesis' => true, + 'no_spaces_inside_parenthesis' => false, // Deprecated 'no_superfluous_elseif' => true, 'no_superfluous_phpdoc_tags' => ['allow_mixed' => true], - 'no_trailing_comma_in_list_call' => true, - 'no_trailing_comma_in_singleline_array' => true, + 'no_trailing_comma_in_list_call' => false, // Deprecated + 'no_trailing_comma_in_singleline' => true, + 'no_trailing_comma_in_singleline_array' => false, // Deprecated + 'no_trailing_comma_in_singleline_function_call' => true, // Deprecated 'no_trailing_whitespace' => true, 'no_trailing_whitespace_in_comment' => true, 'no_trailing_whitespace_in_string' => false, // Too dangerous 'no_unneeded_control_parentheses' => true, - 'no_unneeded_curly_braces' => true, + 'no_unneeded_curly_braces' => false, // Deprecated 'no_unneeded_final_method' => true, + 'no_unneeded_import_alias' => true, 'no_unreachable_default_argument_value' => true, 'no_unset_cast' => true, 'no_unset_on_property' => true, 'no_unused_imports' => true, + 'no_useless_concat_operator' => true, 'no_useless_else' => true, + 'no_useless_nullsafe_operator' => true, 'no_useless_return' => true, 'no_useless_sprintf' => true, 'no_whitespace_before_comma_in_array' => true, @@ -150,15 +178,25 @@ 'normalize_index_brace' => true, 'not_operator_with_space' => false, // No we prefer to keep '!' without spaces 'not_operator_with_successor_space' => false, // idem + 'nullable_type_declaration' => true, 'nullable_type_declaration_for_default_null_value' => true, + 'numeric_literal_separator' => false, // Maybe later... 'object_operator_without_whitespace' => true, 'octal_notation' => true, 'operator_linebreak' => true, + 'ordered_attributes' => false, // We prefer to keep some freedom 'ordered_class_elements' => false, // We prefer to keep some freedom 'ordered_imports' => true, 'ordered_interfaces' => true, 'ordered_traits' => true, + 'ordered_types' => false, // We prefer to keep some freedom + 'php_unit_assert_new_names' => true, + 'php_unit_attributes' => true, 'php_unit_construct' => true, + 'php_unit_data_provider_method_order' => true, + 'php_unit_data_provider_name' => ['prefix' => 'provider', 'suffix' => ''], + 'php_unit_data_provider_return_type' => true, + 'php_unit_data_provider_static' => true, 'php_unit_dedicate_assert' => true, 'php_unit_dedicate_assert_internal_type' => true, 'php_unit_expectation' => true, @@ -178,16 +216,17 @@ 'phpdoc_add_missing_param_annotation' => true, 'phpdoc_align' => false, // Waste of time 'phpdoc_annotation_without_dot' => true, + 'phpdoc_array_type' => false, // We prefer `T[]` instead of `array` 'phpdoc_indent' => true, 'phpdoc_inline_tag_normalizer' => true, 'phpdoc_line_span' => true, + 'phpdoc_list_type' => false, // Because we are not always sure what type it should actually be 'phpdoc_no_access' => true, - 'phpdoc_no_alias_tag' => true, 'phpdoc_no_empty_return' => true, - 'phpdoc_no_package' => true, - 'phpdoc_no_useless_inheritdoc' => true, 'phpdoc_order' => true, 'phpdoc_order_by_value' => true, + 'phpdoc_param_order' => true, + 'phpdoc_readonly_class_comment_to_keyword' => true, 'phpdoc_return_self_reference' => true, 'phpdoc_scalar' => true, 'phpdoc_separation' => true, @@ -207,10 +246,11 @@ 'phpdoc_var_without_name' => true, 'pow_to_exponentiation' => true, 'protected_to_private' => true, - 'psr_autoloading' => false, // We unfortunately cannot be entirely PSR-0 compliant with ZF1 because of classes like Admin_ChapterController + 'psr_autoloading' => true, 'random_api_migration' => true, 'regular_callable_call' => true, 'return_assignment' => false, // Sometimes useful for clarity or debug + 'return_to_yield_from' => false, // That seems useless 'return_type_declaration' => true, 'self_accessor' => true, 'self_static_accessor' => true, @@ -221,21 +261,28 @@ 'simplified_if_return' => false, // Even if technically correct we prefer to be explicit 'simplified_null_return' => false, // Even if technically correct we prefer to be explicit 'single_blank_line_at_eof' => true, - 'single_blank_line_before_namespace' => true, + 'single_blank_line_before_namespace' => false, // Deprecated 'single_class_element_per_statement' => true, 'single_import_per_statement' => true, 'single_line_after_imports' => true, + 'single_line_comment_spacing' => true, 'single_line_comment_style' => true, + 'single_line_empty_body' => true, 'single_line_throw' => false, // I don't see any reason for having a special case for Exception 'single_quote' => true, - 'single_space_after_construct' => true, + 'single_space_after_construct' => false, // Deprecated + 'single_space_around_construct' => true, 'single_trait_insert_per_statement' => true, 'space_after_semicolon' => true, + 'spaces_inside_parentheses' => true, 'standardize_increment' => true, 'standardize_not_equals' => true, + 'statement_indentation' => true, 'static_lambda' => false, // Risky if we can't guarantee nobody use `bindTo()` - 'strict_comparison' => false, // We sometimes need to compare DateTime objects with non-strict operator - 'strict_param' => false, // No, too dangerous to change that + 'static_private_method' => false, // In PHP we'd rather avoid static as a rule of thumb + 'strict_comparison' => true, + 'strict_param' => true, + 'string_implicit_backslashes' => false, // It's incompatible with generated code 'string_length_to_empty' => true, 'string_line_ending' => true, 'switch_case_semicolon_to_colon' => true, @@ -244,13 +291,15 @@ 'ternary_operator_spaces' => true, 'ternary_to_elvis_operator' => true, 'ternary_to_null_coalescing' => true, - 'trailing_comma_in_multiline' => true, + 'trailing_comma_in_multiline' => ['elements' => ['arguments', 'array_destructuring', 'arrays', 'match', 'parameters']], 'trim_array_spaces' => true, + 'type_declaration_spaces' => true, 'types_spaces' => true, 'unary_operator_spaces' => true, 'use_arrow_functions' => true, 'visibility_required' => true, 'void_return' => true, 'whitespace_after_comma_in_array' => true, - 'yoda_style' => false, + 'yield_from_array_to_yields' => true, + 'yoda_style' => false, // Like Yoda we speak not ]); diff --git a/.zfproject.xml b/.zfproject.xml deleted file mode 100644 index b6fa6a5..0000000 --- a/.zfproject.xml +++ /dev/null @@ -1,160 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/README.md b/README.md index e6c19f5..5031b68 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ an IMDb movie). This is possible thanks to the use of user-script (greasemonkey 1. Create a database and a user in MariaDB (eg: "mqueue") 2. Download latest version: `git clone https://github.com/PowerKiKi/mqueue.git` -3. In `application/configs/`, copy `application.sample.ini` to `application.ini` and edit database configuration +3. Configure database in `config/autoload/local.php` (see example `config/autoload/local.php.dist`) 4. Run `./bin/build.sh` 5. Open mQueue in your browser (something similar to http://mqueue/ or http://localhost/mqueue/public/) diff --git a/application/Bootstrap.php b/application/Bootstrap.php deleted file mode 100644 index 2c2ef2a..0000000 --- a/application/Bootstrap.php +++ /dev/null @@ -1,162 +0,0 @@ -bootstrap('view'); - $view = $this->getResource('view'); - - // Enable our own View Helpers - $view->addHelperPath(APPLICATION_PATH . '/mQueue/View/Helper', 'mQueue\\View\\Helper'); - } - - protected function _initNavigation(): void - { - $this->bootstrap('view'); - $view = $this->getResource('view'); - - $navigation = new Zend_Navigation([ - [ - 'label' => $view->translate('Movies'), - 'controller' => 'movie', - 'route' => 'default', - ], - [ - 'label' => $view->translate('Add movie'), - 'controller' => 'movie', - 'action' => 'add', - 'route' => 'default', - ], - [ - 'label' => $view->translate('Activity'), - 'controller' => 'activity', - 'route' => 'default', - ], - [ - 'label' => $view->translate('Users'), - 'controller' => 'user', - 'route' => 'default', - ], - [ - 'label' => $view->translate('FAQ'), - 'controller' => 'faq', - 'route' => 'default', - ], - ]); - - $view->navigation($navigation); - } - - protected function _initSession(): void - { - // If no open session yet, open it - if (isset($_SESSION)) { - return; - } - - // We need to receive cookie from any third party sites, so we inject SameSite - // into path, because PHP 7.2 does not have a way to do it properly - $cookieParams = session_get_cookie_params(); - session_set_cookie_params( - $cookieParams['lifetime'], - $cookieParams['path'] . '; SameSite=None', - $cookieParams['domain'], - true, - true - ); - - Zend_Session::setOptions(['name' => 'mqueue']); - - if (!Zend_Session::sessionExists()) { - Zend_Session::rememberMe(1 * 60 * 60 * 24 * 31 * 12); // Cookie for 1 year - } - } - - protected function _initLanguage(): void - { - $session = new Zend_Session_Namespace(); - - // handle language switch - if (isset($_GET['lang'])) { - $session->locale = $_GET['lang']; - } - - if (isset($session->locale)) { - $locale = new Zend_Locale($session->locale); - } else { - $locale = new Zend_Locale(); // autodetect browser - } - Zend_Registry::set(Zend_Locale::class, $locale); - - $adapter = new Zend_Translate('gettext', APPLICATION_PATH . '/localization', $locale, ['scan' => Zend_Translate::LOCALE_FILENAME, 'disableNotices' => true]); - Zend_Registry::set(Zend_Translate::class, $adapter); - Zend_Form::setDefaultTranslator($adapter); - self::$translator = $adapter; - } - - protected function _initPagination(): void - { - Zend_Paginator::setDefaultScrollingStyle('Elastic'); - Zend_View_Helper_PaginationControl::setDefaultViewPartial('pagination.phtml'); - } - - protected function _initRoutes(): void - { - $front = Zend_Controller_Front::getInstance(); - $router = $front->getRouter(); - - // Required for unit tests - $router->addDefaultRoutes(); - - // A route for single id (typically view a single movie/user) - $router->addRoute('singleid', new Zend_Controller_Router_Route(':controller/:action/:id', ['action' => 'view'])); - - // A route for activities - $router->addRoute('activity', new Zend_Controller_Router_Route('activity/*', ['controller' => 'activity', 'action' => 'index'])); - $router->addRoute('activityMovie', new Zend_Controller_Router_Route('activity/movie/:movie/*', ['controller' => 'activity', 'action' => 'index'])); - $router->addRoute('activityUser', new Zend_Controller_Router_Route('activity/user/:user/*', ['controller' => 'activity', 'action' => 'index'])); - - // For backward compatibility with RSS readers we keep the old route - $router->addRoute('activityOld', new Zend_Controller_Router_Route('activity/index/*', ['controller' => 'activity', 'action' => 'index'])); - $router->addRoute('activityMovieOld', new Zend_Controller_Router_Route('activity/index/movie/:movie/*', ['controller' => 'activity', 'action' => 'index'])); - $router->addRoute('activityUserOld', new Zend_Controller_Router_Route('activity/index/user/:user/*', ['controller' => 'activity', 'action' => 'index'])); - - // Routes to define and view statuses - $router->addRoute('status', new Zend_Controller_Router_Route_Regex('status/(\d+)/(\d)', ['controller' => 'status', 'action' => 'index'], [1 => 'movie', 2 => 'rating'], 'status/%s/%s')); - $router->addRoute('statusView', new Zend_Controller_Router_Route_Regex('status/(\d+)', ['controller' => 'status', 'action' => 'index'], [1 => 'movie'], 'status/%s')); - } - - /** - * Add the Zend_Db_Adapter to the registry if we need to call it outside of the modules. - * - * @return Zend_Db_Adapter_Abstract - */ - protected function _initMyDb() - { - $db = $this->getPluginResource('db')->getDbAdapter(); - Zend_Registry::set('db', $db); - - return $db; - } -} - -/** - * Global shortcut method that returns localized strings. - * - * @param string $msgId the original string to translate - * - * @return string the translated string - */ -function _tr($msgId) -{ - return Bootstrap::$translator->translate($msgId); -} diff --git a/application/Debug.php b/application/Debug.php deleted file mode 100644 index 6ddaa33..0000000 --- a/application/Debug.php +++ /dev/null @@ -1,62 +0,0 @@ - $value) { - $result .= str_repeat(' ', 4 * ($level + 1)); - if ($needKey) { - $result .= self::export($key, true, $level + 1); - $result .= ' => '; - } - - $result .= self::export($value, true, $level + 1); - $result .= ',' . PHP_EOL; - } - $result .= str_repeat(' ', 4 * $level) . ']'; - } else { - $result .= var_export($data, true); - } - - if (!$return) { - echo $result; - } - - return $result; - } -} - -function ve($a, bool $return = false) -{ - return Debug::export($a, $return); -} - -function v(): void -{ - var_dump(func_get_args()); -} - -function w(): void -{ - $isHtml = (PHP_SAPI !== 'cli'); - echo "\n_________________________________________________________________________________________________________________________" . ($isHtml ? '
' : '') . "\n"; - var_dump(func_get_args()); - echo "\n" . ($isHtml ? '
' : '') . '_________________________________________________________________________________________________________________________' . ($isHtml ? '
' : '') . "\n";
-    debug_print_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
-    echo '' . ($isHtml ? '
' : '') . '_________________________________________________________________________________________________________________________' . ($isHtml ? '
' : '') . "\n"; - exit("script aborted on purpose.\n"); -} diff --git a/application/configs/application.sample.ini b/application/configs/application.sample.ini deleted file mode 100644 index 291c93a..0000000 --- a/application/configs/application.sample.ini +++ /dev/null @@ -1,29 +0,0 @@ -[production] -phpSettings.display_startup_errors = 0 -phpSettings.display_errors = 0 -includePaths.library = APPLICATION_PATH "/../library" -bootstrap.path = APPLICATION_PATH "/Bootstrap.php" -bootstrap.class = "Bootstrap" -resources.frontController.controllerDirectory = APPLICATION_PATH "/controllers" -resources.frontController.params.displayExceptions = 0 -resources.layout.layoutPath = APPLICATION_PATH "/layouts/scripts" -resources.view[] = -resources.db.adapter = PDO_MYSQL -resources.db.params.charset = utf8 -resources.db.params.host = 127.0.0.1 -resources.db.params.username = root -resources.db.params.password = -resources.db.params.dbname = mqueue -; googleAnalyticsTrackingCode = UA-XXXXX-X - -[staging : production] - -[testing : production] -resources.frontController.params.displayExceptions = 1 -phpSettings.display_startup_errors = 1 -phpSettings.display_errors = 1 - -[development : production] -resources.frontController.params.displayExceptions = 1 -phpSettings.display_startup_errors = 1 -phpSettings.display_errors = 1 diff --git a/application/controllers/AboutController.php b/application/controllers/AboutController.php deleted file mode 100644 index 1704de0..0000000 --- a/application/controllers/AboutController.php +++ /dev/null @@ -1,14 +0,0 @@ -_helper->contextSwitch(); - - // Add the new context - $contextSwitch->setContexts([ - 'rss' => ['suffix' => 'rss'], - ]); - - $contextSwitch->addActionContext('index', 'rss')->initContext(); - } - - public function indexAction(): void - { - // By default we show overall activity - $item = null; - $this->view->title = $this->view->translate('Overall activity'); - - // Try to show user's activity - if ($this->getRequest()->getParam('user')) { - $item = UserMapper::find($this->getRequest()->getParam('user')); - if ($item) { - $this->view->title = $this->view->translate('Activity for %s', [$item->nickname]); - } - } - - // Try to show movie's activity - if ($this->getRequest()->getParam('movie')) { - $item = MovieMapper::find($this->getRequest()->getParam('movie')); - if ($item) { - $this->view->title = $this->view->translate('Activity for %s', [$item->getTitle()]); - } - } - - $this->view->activity = $this->_helper->createPaginator(StatusMapper::getActivityQuery($item)); - } -} diff --git a/application/controllers/CssController.php b/application/controllers/CssController.php deleted file mode 100644 index b0e8ef0..0000000 --- a/application/controllers/CssController.php +++ /dev/null @@ -1,17 +0,0 @@ -_helper->layout->disableLayout(); - $this->_helper->headers('text/css'); - } - - public function gravatarCssAction(): void - { - $this->view->users = UserMapper::fetchAll(); - } -} diff --git a/application/controllers/ErrorController.php b/application/controllers/ErrorController.php deleted file mode 100644 index 402ca53..0000000 --- a/application/controllers/ErrorController.php +++ /dev/null @@ -1,58 +0,0 @@ -_getParam('error_handler'); - - if (!$errors || !$errors instanceof ArrayObject) { - $this->view->message = 'You have reached the error page'; - - return; - } - - switch ($errors->type) { - case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_ROUTE: - case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_CONTROLLER: - case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_ACTION: - // 404 error -- controller or action not found - $this->getResponse()->setHttpResponseCode(404); - $priority = Zend_Log::NOTICE; - $this->view->message = 'Page not found'; - - break; - default: - // application error - $this->getResponse()->setHttpResponseCode(500); - $priority = Zend_Log::CRIT; - $this->view->message = 'Application error'; - - break; - } - - // Log exception, if logger available - if ($log = $this->getLog()) { - $log->log($this->view->message, $priority, $errors->exception); - $log->log('Request Parameters', $priority, $errors->request->getParams()); - } - - // conditionally display exceptions - if ($this->getInvokeArg('displayExceptions') == true) { - $this->view->exception = $errors->exception; - } - - $this->view->request = $errors->request; - } - - public function getLog() - { - $bootstrap = $this->getInvokeArg('bootstrap'); - if (!$bootstrap->hasResource('Log')) { - return false; - } - $log = $bootstrap->getResource('Log'); - - return $log; - } -} diff --git a/application/controllers/FaqController.php b/application/controllers/FaqController.php deleted file mode 100644 index 48f689a..0000000 --- a/application/controllers/FaqController.php +++ /dev/null @@ -1,14 +0,0 @@ -_helper->redirector('index', 'movie'); - } else { - $this->_helper->redirector('index', 'activity'); - } - } -} diff --git a/application/controllers/JsController.php b/application/controllers/JsController.php deleted file mode 100644 index 022e429..0000000 --- a/application/controllers/JsController.php +++ /dev/null @@ -1,20 +0,0 @@ -_helper->layout->disableLayout(); - $this->_helper->headers('application/javascript'); - } - - public function remoteJsAction(): void - { - // action body - } - - public function mqueueUserJsAction(): void - { - // action body - } -} diff --git a/application/controllers/MovieController.php b/application/controllers/MovieController.php deleted file mode 100644 index 7544bab..0000000 --- a/application/controllers/MovieController.php +++ /dev/null @@ -1,132 +0,0 @@ -_helper->contextSwitch(); - - // Add the new context - $contextSwitch->setContexts([ - 'csv' => ['suffix' => 'csv'], - 'rss' => ['suffix' => 'rss'], - ]); - - $contextSwitch->addActionContext('index', 'csv')->addActionContext('index', 'rss')->initContext(); - } - - public function indexAction(): void - { - // Check there is at least one user, otherwise the whole page will crash - if (!User::getCurrent() && !UserMapper::getDbTable()->fetchRow()) { - throw new Exception('At least one user must exist to access this page'); - } - - $form = new Filters(); - $this->view->formFilter = $form; - - // Detect if at least one filter was submitted - $submitted = false; - foreach ($this->getRequest()->getParams() as $key => $filter) { - if (preg_match('/^filter\d+$/', $key)) { - $submitted = true; - } - } - - // If was submitted, try to validate values - if ($submitted) { - if (!$form->isValid($this->getRequest()->getParams())) { - $this->_helper->FlashMessenger(['warning' => _tr('Filter is invalid.')]); - $form->setDefaults([]); - } - } // If we submitted a quicksearch, set default values to search with any status - elseif ($this->_getParam('search')) { - $form->setDefaults([ - 'filter1' => [ - 'user' => User::getCurrent() ? 0 : UserMapper::fetchAll()->current()->id, - 'condition' => 'is', - 'status' => array_merge([0], array_keys(mQueue\Model\Status::$ratings)), - 'title' => $this->_getParam('search'), - ], - ]); - } // Otherwise clear the filter - else { - $form->setDefaults([]); - } - - // Gather users selected in filters - $this->view->users = []; - $filters = $form->getValues(); - foreach ($filters as $key => $filter) { - if (!preg_match('/^filter\d+$/', $key)) { - continue; - } - - $this->view->users[$filter['user']] = UserMapper::find($filter['user']); - } - - // If we ouput rss, we force sorting by date - if ($this->_helper->contextSwitch()->getCurrentContext() == 'rss') { - $this->getRequest()->setParam('sort', $filters['filter1']['withSource'] ? 'dateSearch' : 'date'); - $this->getRequest()->setParam('sortOrder', 'desc'); - } - $this->view->permanentParams = $form->getValues(); - $this->view->filterName = $form->getValuesText(); - unset($this->view->permanentParams['addFilter']); - - $allowedSortingKey = ['title', 'date', 'dateSearch']; - $usersCount = count($this->view->users); - for ($i = 0; $i < $usersCount; ++$i) { - $allowedSortingKey[] = 'status' . $i; - } - $sort = $this->_helper->createSorting('sort', $allowedSortingKey); - - // Set up the paginator: Apply pagination only if there is no special context (so it is normal html rendering) - $this->view->paginator = $this->_helper->createPaginator(MovieMapper::getFilteredQuery($filters, $sort)); - } - - public function viewAction(): void - { - if ($this->getRequest()->getParam('id')) { - $this->view->movie = MovieMapper::find($this->getRequest()->getParam('id')); - } - - if (!$this->view->movie) { - throw new Exception($this->view->translate('Movie not found')); - } - - $this->view->users = UserMapper::fetchAll(); - $this->view->movieActivity = $this->_helper->createPaginator(StatusMapper::getActivityQuery($this->view->movie)); - } - - public function addAction(): void - { - $request = $this->getRequest(); - $form = new \mQueue\Form\Movie(); - - if ($this->_getParam('id')) { - if ($form->isValid($request->getParams())) { - $values = $form->getValues(); - $movie = MovieMapper::find(Movie::extractId($values['id'])); - if (!$movie) { - $movie = MovieMapper::getDbTable()->createRow(); - $movie->setId($values['id']); - $movie->save(); - $this->_helper->FlashMessenger(_tr('A movie was added.')); - } - - $this->view->movies = [$movie]; - } - } - - $this->view->form = $form; - } -} diff --git a/application/controllers/StatusController.php b/application/controllers/StatusController.php deleted file mode 100644 index 21d77d2..0000000 --- a/application/controllers/StatusController.php +++ /dev/null @@ -1,124 +0,0 @@ -_helper->getHelper('contextSwitch'); - $contextSwitch->addActionContext('list', 'json') - ->addActionContext('index', 'json') - ->initContext(); - } - - public function indexAction(): void - { - $jsonCallback = $this->_request->getParam('jsoncallback'); - if ($jsonCallback) { - $this->_helper->layout->setLayout('jsonp'); - $this->view->jsonCallback = $jsonCallback; - } - - $idMovie = Movie::extractId($this->_request->getParam('movie')); - - if ($idMovie == null) { - throw new Exception('no valid movie specified.'); - } - - // If new rating is specified and we are logged in, save it and create movie if needed - $rating = $this->_request->getParam('rating'); - if (isset($rating) && User::getCurrent()) { - $movie = MovieMapper::find($idMovie); - - if ($movie == null) { - $movie = MovieMapper::getDbTable()->createRow(); - $movie->setId($idMovie); - $movie->save(); - } - $status = $movie->setStatus(User::getCurrent(), $rating); - } else { - $status = StatusMapper::find($idMovie, User::getCurrent()); - } - - if (!$jsonCallback) { - $this->view->status = $status; - } else { - $html = $this->view->statusLinks($status); - $this->view->status = $html; - $this->view->id = $status->getUniqueId(); - } - } - - public function listAction(): void - { - $jsonCallback = $this->_request->getParam('jsoncallback'); - if ($jsonCallback) { - $this->_helper->layout->setLayout('jsonp'); - $this->view->jsonCallback = $jsonCallback; - } - - $idMovies = []; - foreach (explode(',', trim($this->_request->getParam('movies'), ',')) as $idMovie) { - $idMovie = Movie::extractId($idMovie); - if ($idMovie) { - $idMovies[] = $idMovie; - } - } - - $statuses = StatusMapper::findAll($idMovies, User::getCurrent()); - - $json = []; - foreach ($statuses as $s) { - $html = $this->view->statusLinks($s); - $json[$s->getUniqueId()] = $html; - } - - $this->view->status = $json; - } - - public function graphAction(): void - { - $percent = $this->_request->getParam('percent'); - $user = UserMapper::find($this->getParam('user')); - $data = StatusMapper::getGraph($user, $percent); - $chart = [ - 'chart' => [ - 'zoomType' => 'x', - ], - 'title' => [ - 'text' => '', - ], - 'xAxis' => [ - 'type' => 'datetime', - ], - 'yAxis' => [ - 'title' => [ - 'text' => 'Movies', - ], - 'min' => 0, - ], - 'series' => $data, - ]; - - if ($percent) { - $chart['chart']['type'] = 'area'; - $chart['yAxis']['title']['text'] = $chart['yAxis']['title']['text'] . ' [%]'; - $chart['plotOptions'] = [ - 'area' => [ - 'stacking' => 'percent', - 'marker' => [ - 'enabled' => false, - ], - ], - ]; - } - - echo Zend_Json::encode($chart, Zend_Json::TYPE_ARRAY); - exit(); - } -} diff --git a/application/controllers/UserController.php b/application/controllers/UserController.php deleted file mode 100644 index cbae326..0000000 --- a/application/controllers/UserController.php +++ /dev/null @@ -1,97 +0,0 @@ -view->users = UserMapper::fetchAll(); - } - - public function newAction() - { - $request = $this->getRequest(); - $form = new \mQueue\Form\User(); - - if ($this->getRequest()->isPost()) { - if ($form->isValid($request->getPost())) { - $values = $form->getValues(); - $user = UserMapper::insertUser($values); - - User::setCurrent($user); - - $this->_helper->FlashMessenger('Subscription complete.'); - - return $this->_helper->redirector('index', 'movie'); - } - } - - $this->view->form = $form; - } - - public function loginAction() - { - $request = $this->getRequest(); - $form = new Login(); - - if ($this->getRequest()->isPost()) { - if ($form->isValid($request->getPost())) { - $values = $form->getValues(); - - $user = UserMapper::findEmailPassword($values['email'], $values['password']); - if ($user) { - User::setCurrent($user); - - $this->_helper->FlashMessenger('Logged in.'); - - $referrer = $values['referrer']; - - // If we have a valid referer to one page of ours (except login or logout), redirect to it - if (mb_strpos($referrer, $this->view->serverUrl() . $this->view->baseUrl()) === 0 - && mb_strpos($referrer, $this->view->serverUrl() . $this->view->url(['controller' => 'user', 'action' => 'login'])) !== 0 - && mb_strpos($referrer, $this->view->serverUrl() . $this->view->url(['controller' => 'user', 'action' => 'logout'])) !== 0) { - return $this->redirect($values['referrer']); - } - - return $this->_helper->redirector('index', 'movie'); - } - $this->_helper->FlashMessenger(['error' => 'Login failed.']); - } - } else { - $form->setDefaults([ - 'referrer' => $this->getRequest()->getServer('HTTP_REFERER'), - ]); - } - - $this->view->form = $form; - } - - public function logoutAction(): void - { - User::setCurrent(null); - } - - public function viewAction(): void - { - if ($this->getRequest()->getParam('id')) { - $this->view->user = UserMapper::find($this->getRequest()->getParam('id')); - } else { - $this->view->user = User::getCurrent(); - } - - if (!$this->view->user) { - throw new Exception($this->view->translate('User not found')); - } - - $this->view->userActivity = $this->_helper->createPaginator(StatusMapper::getActivityQuery($this->view->user)); - } -} diff --git a/application/layouts/scripts/jsonp.phtml b/application/layouts/scripts/jsonp.phtml deleted file mode 100644 index 5b54d3d..0000000 --- a/application/layouts/scripts/jsonp.phtml +++ /dev/null @@ -1,3 +0,0 @@ -jsonCallback . '(' . $this->layout()->content . ')'; diff --git a/application/layouts/scripts/layout.phtml b/application/layouts/scripts/layout.phtml deleted file mode 100644 index 5d3a01e..0000000 --- a/application/layouts/scripts/layout.phtml +++ /dev/null @@ -1,61 +0,0 @@ -getLanguage(); -?> - - - - - - - googleAnalytics(); - echo $this->headTitle('mQueue')->setSeparator(' - '); - echo $this->headLink([ - 'rel' => 'icon', - 'href' => $this->serverUrl() . $this->baseUrl('/images/favicon.png'), - 'PREPEND', - ]) - ->appendStylesheet('/css/application.css', 'all') - ->appendStylesheet($this->serverUrl() . $this->baseUrl('/css/gravatar.css'), 'all') - ->prependAlternate($this->serverUrl() . $this->url(['controller' => 'activity', 'action' => 'index'], 'activity', true) . '?format=rss&lang=' . $lang, 'application/rss+xml', $this->translate('mQueue - Overall activity')) - . "\n"; - - // Set localization file for timeago - $path = '/js/min/jquery.timeago.' . $lang . '.js'; - if (is_file(APPLICATION_PATH . '/../public' . $path)) { - $this->headScript()->prependFile($path); - } - - echo $this->headScript() - ->prependFile([ - '/js/min/application.js', - [ - '/js/application', - ], - ]); - ?> - - -
- -
- flashMessenger(); ?> - layout()->content; ?> -
-
- - - diff --git a/application/mQueue/Controller/ActionHelper/CreatePaginator.php b/application/mQueue/Controller/ActionHelper/CreatePaginator.php deleted file mode 100644 index 098b018..0000000 --- a/application/mQueue/Controller/ActionHelper/CreatePaginator.php +++ /dev/null @@ -1,65 +0,0 @@ -perPage)) { - $perPage = $session->perPage; - } - - $userPerPage = $this->getRequest()->getParam('perPage'); - if ($userPerPage) { - $perPage = $userPerPage; - $session->perPage = $perPage; - } - - // If we export to csv or rss, override perPage parameter - $currentContext = $this->getActionController()->getHelper('contextSwitch')->getCurrentContext(); - switch ($currentContext) { - case 'csv': - $perPage = -1; - - break; - case 'rss': - $perPage = 200; - - break; - } - - // Defines the paginator - $paginator = Zend_Paginator::factory($data); - $paginator->setCurrentPageNumber($this->getRequest()->getParam('page')); - $paginator->setItemCountPerPage($perPage); - - return $paginator; - } - - /** - * Strategy pattern: call helper as broker method. - * - * @param mixed $data - * - * @return Zend_Paginator - */ - public function direct($data) - { - return $this->createPaginator($data); - } -} diff --git a/application/mQueue/Controller/ActionHelper/CreateSorting.php b/application/mQueue/Controller/ActionHelper/CreateSorting.php deleted file mode 100644 index 5e47726..0000000 --- a/application/mQueue/Controller/ActionHelper/CreateSorting.php +++ /dev/null @@ -1,70 +0,0 @@ -getRequest()->getParam($sortParameterName); - $order = $this->getRequest()->getParam($sortOrderParameterName); - - $view = $this->getActionController()->view; - $view->sortParameterName = $sortParameterName; - $view->sortSelectedKey = $key; - $view->sortSelectedOrder = $order; - - if (is_array($view->permanentParams)) { - $view->permanentParams[$sortParameterName] = $key; - $view->permanentParams[$sortOrderParameterName] = $order; - } - - return self::getSorting($key, $order, $allowedKeys); - } - - /** - * Returns a valid SQL sorting snippet. - * - * @param string $key - * @param string $order - * - * @return string similar to 'name DESC' - */ - private static function getSorting($key, $order, array $allowedKeys) - { - if (!in_array($key, $allowedKeys)) { - $key = reset($allowedKeys); - } - - $order = strcasecmp($order, 'desc') == 0 ? 'DESC' : 'ASC'; - - return $key . ' ' . $order; - } - - /** - * Strategy pattern: call helper as broker method. - * - * @param string $sortParameterName used to look for parameters in request and as key in $_SESSION - * - * @return string similar to 'name DESC' - */ - public function direct($sortParameterName, array $allowedKeys) - { - return $this->createSorting($sortParameterName, $allowedKeys); - } -} diff --git a/application/mQueue/Controller/ActionHelper/Headers.php b/application/mQueue/Controller/ActionHelper/Headers.php deleted file mode 100644 index 90eb0b5..0000000 --- a/application/mQueue/Controller/ActionHelper/Headers.php +++ /dev/null @@ -1,38 +0,0 @@ -getActionController()->getResponse(); - $response->setHeader('Content-Type', $contentType); - $response->setHeader('Cache-Control', 'max-age=604800'); - - // This check is required when running via unit tests - if (headers_sent()) { - return; - } - - header_remove('Pragma'); - header_remove('Expires'); - } - - /** - * Strategy pattern: call helper as broker method. - * - * @param mixed $data - */ - public function direct($data): void - { - $this->headers($data); - } -} diff --git a/application/mQueue/Form/Filter.php b/application/mQueue/Form/Filter.php deleted file mode 100644 index 5889378..0000000 --- a/application/mQueue/Form/Filter.php +++ /dev/null @@ -1,132 +0,0 @@ -setMethod('get'); - - $users = []; - if (\mQueue\Model\User::getCurrent()) { - $users = [0 => _tr('<< me >>')]; - } - - foreach (UserMapper::fetchAll() as $user) { - $users[$user->id] = $user->nickname; - } - - $this->addElement('select', 'user', [ - 'multiOptions' => $users, - 'required' => true, - 'class' => 'filterUser', - 'validators' => [ - ['validator' => new Validate\User()], - ], - 'filters' => [ - ['int'], - ], - ]); - - $this->addElement('select', 'condition', [ - 'multiOptions' => ['is' => _tr('rating is'), 'isnot' => _tr('rating is not')], - 'required' => true, - ]); - - $statuses = []; - foreach (Status::$ratings as $rating => $label) { - $statuses[$rating] = $this->getView()->rating($rating); - } - $statuses = $statuses + [0 => _tr('nothing')]; - - $this->addElement('multiCheckbox', 'status', [ - 'escape' => false, - 'multiOptions' => $statuses, - 'required' => true, - 'separator' => ' ', - 'filters' => [ - ['int'], - ], - ]); - $this->status->getDecorator('HtmlTag')->setOption('class', 'filterStatus'); - - // Add the title element - $this->addElement('text', 'title', [ - 'placeholder' => _tr('title'), - 'autofocus' => true, - 'filters' => [ - ['stringTrim'], - ], - ]); - - // Add the filter element - $this->addElement('checkbox', 'withSource', [ - 'label' => _tr('With source'), - ]); - $this->withSource->getDecorator('Label')->setOptions(['placement' => 'append']); - - $this->setDecorators([ - 'FormElements', - [['row' => 'HtmlTag'], ['tag' => 'dl', 'class' => 'filter']], - ]); - } - - /** - * Disable extra field elements. - */ - public function disableExtraFields(): void - { - $this->removeElement('title'); - $this->removeElement('withSource'); - } - - /** - * Override getValues() to replace special '0' value with current user. - * - * @param bool $suppressArrayNotation - * - * @return array values - */ - public function getValues($suppressArrayNotation = false) - { - $values = parent::getValues($suppressArrayNotation); - - if ($values['user'] == '0') { - $values['user'] = \mQueue\Model\User::getCurrent()->id; - } - - return $values; - } - - /** - * Returns values as readable text for end-user. - * - * @return string - */ - public function getValuesText() - { - $text = ''; - $values = $this->getValues(true); - - if (@$values['title']) { - $text = _tr('title') . ':"' . $values['title'] . '" + '; - } - - $users = $this->getElement('user')->getMultiOptions(); - $statuses = $this->getElement('status')->getMultiOptions(); - - $statusLabels = []; - foreach ($values['status'] as $status) { - $statusLabels[] = Status::$ratings[$status] ?? $statuses[$status]; - } - $text .= $users[$values['user']] . ':' . implode('+', $statusLabels); - - return $text; - } -} diff --git a/application/mQueue/Form/Filters.php b/application/mQueue/Form/Filters.php deleted file mode 100644 index 6ade0d9..0000000 --- a/application/mQueue/Form/Filters.php +++ /dev/null @@ -1,135 +0,0 @@ -setMethod('get'); - $this->setName('filters'); - - // Add the submit button - $this->addElement('submit', 'submit', [ - 'ignore' => true, - 'label' => _tr('Apply'), - ]); - - $this->addDecorator('Fieldset'); - - $this->setDecorators([ - 'FormElements', - [['fieldset' => 'Fieldset'], ['legend' => 'Filter']], - 'Form', - ]); - - $this->addDisplayGroup(['submit'], 'filters', ['legend' => _tr('Filter')]); - - $viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer'); - $view = $viewRenderer->view; - - $this->addElement('image', 'addFilter', [ - 'src' => $view->serverUrl() . $view->baseUrl('/images/add.png'), - 'imageValue' => '1', - ]); - $this->addDisplayGroup(['addFilter'], 'addFilterGroup', ['class' => 'addFilter']); - - $this->setDisplayGroupDecorators([ - 'FormElements', - [['row' => 'HtmlTag'], ['tag' => 'dl', 'class' => 'buttons']], - ]); - } - - /** - * Overrides isValid to dynamically generate subforms which will be used for validation. - * - * @param array $data - * - * @return bool - */ - public function isValid($data) - { - $data = $this->createSubForms($data); - - return parent::isValid($data); - } - - /** - * Override setDefaults to dynamically generate subforms. - * - * @return Zend_Form - */ - public function setDefaults(array $defaults) - { - $defaults = $this->createSubForms($defaults); - - // set defaults, which will propagate to newly created subforms - return parent::setDefaults($defaults); - } - - /** - * Create actual filter as subforms according to filter values given. - * It will at least create one subform. It may add a default subform - * if 'addFilter_x' value is given. - * - * @param array $defaults values of form - * - * @return array $defaults modified values with additional filter - */ - private function createSubForms(array $defaults) - { - // Find out the highest filter number - $max = 0; - foreach (array_keys($defaults) as $key) { - if (preg_match('/^filter(\d+)$/', $key, $m)) { - if ($m[1] > $max) { - $max = $m[1]; - } - } - } - - // If we specifically asked to add a filter or if there is none, then add a new filter with default value - if ((isset($defaults['addFilter_x'])) || $max == 0) { - $defaults['filter' . ($max + 1)] = [ - 'user' => \mQueue\Model\User::getCurrent() ? 0 : UserMapper::fetchAll()->current()->id, - 'condition' => 'is', - 'status' => array_keys(Status::$ratings), - ]; - } - - // Create all filters - $position = 1; - foreach (array_keys($defaults) as $key) { - if (preg_match('/^filter(\d+)$/', $key, $m)) { - $subform = new Filter(); - if ($position > 1) { - $subform->disableExtraFields(); - } - $this->addSubForm($subform, $key, $position++); - } - } - - return $defaults; - } - - /** - * Returns values as readable text for end-user. - * - * @return string - */ - public function getValuesText() - { - $text = []; - foreach ($this->getSubForms() as $subForm) { - $text[] = $subForm->getValuesText(); - } - - return implode(' + ', $text); - } -} diff --git a/application/mQueue/Form/Login.php b/application/mQueue/Form/Login.php deleted file mode 100644 index fe16378..0000000 --- a/application/mQueue/Form/Login.php +++ /dev/null @@ -1,41 +0,0 @@ -setMethod('post'); - - // Add the email element - $this->addElement('text', 'email', [ - 'label' => _tr('Email:'), - 'autofocus' => true, - 'required' => true, - 'filters' => ['filter' => ['filter' => 'stringTrim']], - 'validators' => [ - ['validator' => 'emailAddress'], - ], - ]); - - // Add the password element - $this->addElement('password', 'password', [ - 'label' => _tr('Password:'), - 'required' => true, - 'filters' => ['filter' => ['filter' => 'stringTrim']], - ]); - - // Add the submit button - $this->addElement('submit', 'submit', [ - 'ignore' => true, - 'label' => _tr('Login'), - ]); - - // Add referrer to redirect after login - $this->addElement('hidden', 'referrer'); - } -} diff --git a/application/mQueue/Form/Movie.php b/application/mQueue/Form/Movie.php deleted file mode 100644 index f6d0bbf..0000000 --- a/application/mQueue/Form/Movie.php +++ /dev/null @@ -1,30 +0,0 @@ -setMethod('get'); - - // Add the comment element - $this->addElement('text', 'id', [ - 'label' => _tr('IMDb url or id:'), - 'required' => true, - 'autofocus' => true, - 'validators' => [ - ['validator' => 'Regex', 'options' => ['/(\\d{7,})/']], - ], - ]); - - // Add the submit button - $this->addElement('submit', 'submit', [ - 'ignore' => true, - 'label' => _tr('Add movie'), - ]); - } -} diff --git a/application/mQueue/Form/QuickSearch.php b/application/mQueue/Form/QuickSearch.php deleted file mode 100644 index 45e6fd2..0000000 --- a/application/mQueue/Form/QuickSearch.php +++ /dev/null @@ -1,35 +0,0 @@ -setMethod('get'); - $this->setName('quickSearch'); - $viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer'); - $view = $viewRenderer->view; - - $this->setAction($view->serverUrl() . $view->url(['controller' => 'movie', 'action' => 'index'], 'default', false)); - - // Add the comment element - $this->addElement('text', 'search', [ - 'placeholder' => _tr('search movie…'), - 'decorators' => ['ViewHelper'], - ]); - - // Add the submit button - $this->addElement('submit', 'searchSubmit', [ - 'label' => _tr('Search'), - 'decorators' => ['ViewHelper'], - ]); - - $this->addDecorator('FormElements') - ->addDecorator('HtmlTag', ['tag' => 'span']) - ->addDecorator('Form'); - } -} diff --git a/application/mQueue/Form/User.php b/application/mQueue/Form/User.php deleted file mode 100644 index 825aa5a..0000000 --- a/application/mQueue/Form/User.php +++ /dev/null @@ -1,65 +0,0 @@ -setMethod('post'); - - // Add the nickname element - $this->addElement('text', 'nickname', [ - 'label' => _tr('Nickname:'), - 'autofocus' => true, - 'required' => true, - 'filters' => ['filter' => ['filter' => 'stringTrim']], - 'validators' => [ - ['validator' => new Zend_Validate_Db_NoRecordExists(['table' => 'user', 'field' => 'nickname'])], - ], - ]); - - // Add the email element - $this->addElement('text', 'email', [ - 'label' => _tr('Email:'), - 'required' => true, - 'filters' => ['filter' => ['filter' => 'stringTrim']], - 'validators' => [ - ['validator' => 'emailAddress'], - ['validator' => new Zend_Validate_Db_NoRecordExists(['table' => 'user', 'field' => 'email'])], - ], - ]); - - // Add the password ail element - $this->addElement('password', 'password', [ - 'label' => _tr('Password:'), - 'required' => true, - ]); - - // Add a captcha - $this->addElement('captcha', 'captcha', [ - 'label' => _tr('Are you a robot?'), - 'required' => true, - 'captcha' => 'figlet', - 'captchaOptions' => [ - 'outputWidth' => 2000, - ], - ]); - - // Add the submit button - $this->addElement('submit', 'submit', [ - 'ignore' => true, - 'label' => _tr('Subscribe'), - ]); - - // And finally add some CSRF protection - $this->addElement('hash', 'csrf', [ - 'ignore' => true, - 'decorators' => ['ViewHelper'], - ]); - } -} diff --git a/application/mQueue/Form/Validate/User.php b/application/mQueue/Form/Validate/User.php deleted file mode 100644 index 02a4483..0000000 --- a/application/mQueue/Form/Validate/User.php +++ /dev/null @@ -1,30 +0,0 @@ - 'user', 'field' => 'id']); - } - - /** - * Defined by Zend_Validate_Interface. - * - * Returns true if and only if $value contains a valid User ID - * - * @param int $value - * - * @return bool - */ - public function isValid($value) - { - return ($value === 0) || parent::isValid($value); - } -} diff --git a/application/mQueue/Model/AbstractMapper.php b/application/mQueue/Model/AbstractMapper.php deleted file mode 100644 index 8868707..0000000 --- a/application/mQueue/Model/AbstractMapper.php +++ /dev/null @@ -1,41 +0,0 @@ -title)) { - $this->fetchData(); - } - - return $this->title; - } - - /** - * Fetch data from IMDb and store in database (possibly overwriting). - */ - public function fetchData(): void - { - $ch = curl_init($this->getImdbUrl()); - curl_setopt($ch, CURLOPT_HTTPHEADER, ['Accept-Language: en-US,en;q=0.8']); - curl_setopt($ch, CURLOPT_HEADER, false); - curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - - $file = curl_exec($ch); - curl_close($ch); - - $document = new DOMDocument(); - @$document->loadHTML($file); - $xpath = new DOMXPath($document); - - // Extract title - $titleEntries = $xpath->evaluate('//meta[contains(@property, "og:title")]/@content'); - if ($titleEntries->length == 1) { - $rawTitle = $titleEntries->item(0)->value; - $this->title = preg_replace('~ - IMDb$~', '', $rawTitle); - } else { - $this->title = '[title not available, could not fetch from IMDb]'; - - return; // If there is not even title give up everything - } - - // Extract release date - $jsonLd = $xpath->evaluate('//script[@type="application/ld+json"]/text()'); - if ($jsonLd->length == 1) { - $json = json_decode($jsonLd->item(0)->data, true); - $this->dateRelease = $json['datePublished'] ?? null; - } else { - $this->dateRelease = null; - } - - $this->dateUpdate = Zend_Date::now()->get(Zend_Date::ISO_8601); - $this->setReadOnly(false); // If the movie is coming from a joined query, we need to set non-readonly before saving - $this->save(); - } - - /** - * Sets the ID for the movie from any string containing a valid ID. - * - * @param string $id - * - * @return Movie - */ - public function setId($id) - { - $extractedId = self::extractId($id); - if (!$extractedId) { - throw new Exception(sprintf('Invalid Id for movie. Given "%1$s", extracted "%2$s"', $id, $extractedId)); - } - - $this->id = $extractedId; - - return $this; - } - - /** - * Returns the IMDb url for the movie. - */ - public function getImdbUrl(): string - { - return 'https://www.imdb.com/title/tt' . self::paddedId($this->id) . '/'; - } - - /** - * Returns the status for this movie and the specified user. - * - * @param User $user - * - * @return Status - */ - public function getStatus(?User $user = null) - { - return StatusMapper::find($this->id, $user); - } - - /** - * Set the status for the specified user. - * - * @param int $rating @see \mQueue\Model\Status - * - * @return Status - */ - public function setStatus(User $user, $rating) - { - $status = StatusMapper::set($this, $user, $rating); - - return $status; - } - - /** - * Set the source for the movie if any. In any case record the search date and count. - * - * @param array|false $source - */ - public function setSource($source): void - { - $this->dateSearch = Zend_Date::now()->get(Zend_Date::ISO_8601); - ++$this->searchCount; - if ($source && @$source['score']) { - $this->identity = $source['identity']; - $this->quality = $source['quality']; - $this->score = $source['score']; - $this->source = $source['link']; - } - } -} diff --git a/application/mQueue/Model/MovieMapper.php b/application/mQueue/Model/MovieMapper.php deleted file mode 100644 index 3991500..0000000 --- a/application/mQueue/Model/MovieMapper.php +++ /dev/null @@ -1,183 +0,0 @@ -find([$id]); - - return $result->current(); - } - - /** - * Returns all movies. - * - * @return Movie[] - */ - public static function fetchAll() - { - $resultSet = self::getDbTable()->fetchAll(); - - return $resultSet; - } - - /** - * Returns movies for search. - * - * @return Movie[] - */ - public static function findAllForSearch() - { - $futureYears = []; - $date = Zend_Date::now(); - for ($i = 0; $i < 10; ++$i) { - $date->addYear(1); - $futureYears[] = $date->get(Zend_Date::YEAR_8601); - } - - $select = self::getDbTable()->select()->setIntegrityCheck(false) - ->from('movie') - ->join('status', 'status.idMovie = movie.id AND status.isLatest AND rating = ' . Status::Need, []) - ->where('source IS NULL') - ->where('dateSearch IS NULL OR dateSearch < DATE_SUB(NOW(), INTERVAL searchCount MONTH)')// Search for same movie with an incrementally longer interval (1 month, 2 month, 3 month, etc.) - ->where('dateRelease IS NOT NULL')// Movie must be released ... - ->where('dateRelease < DATE_SUB(NOW(), INTERVAL 1 MONTH)')// ...at least released one month ago, or longer - ->group('movie.id') - ->order('COUNT(movie.id) DESC')// First, order by popularity, so get the most needed first - ->order('RAND() ASC')// Then, randomize a little bit so we don't always look for the same movies - ->limit(5); - - $records = self::getDbTable()->fetchAll($select); - - return $records; - } - - /** - * Returns movies for data fetching. - * - * @param int $limit - * - * @return Movie[] - */ - public static function findAllForFetching($limit = null) - { - $select = self::getDbTable()->select()->setIntegrityCheck(false) - ->from('movie') - ->where('dateUpdate IS NULL OR dateUpdate < DATE_SUB(NOW(), INTERVAL 1 MONTH)')// Don't fetch data for movies, more than once a month - ->order('RAND()'); // Randomize order, so we don't watch only old movies - - if ($limit) { - $select->limit(20); - } - - $records = self::getDbTable()->fetchAll($select); - - return $records; - } - - /** - * Returns a query filtered according to parameters. This query may be used with paginator. - * - * @param string $orderBy valid SQL sorting snippet - * - * @return Zend_Db_Table_Select - */ - public static function getFilteredQuery(array $filters, string $orderBy) - { - $orderBy = preg_replace('/^(status\d+)(.*)/', '\\1.rating\\2', $orderBy); - - $select = self::getDbTable()->select(); - $select->setIntegrityCheck(false) - ->from('movie') - ->order($orderBy); - - $i = 0; - $maxDate = ''; - $filtersDone = []; - foreach ($filters as $key => $filter) { - if (!is_array($filter)) { - continue; - } - - $filterUniqueId = $filter['user']; - if (!preg_match('/^filter\d+$/', $key) || in_array($filterUniqueId, $filtersDone)) { - continue; - } - - $filtersDone[] = $filterUniqueId; - $showNoRating = in_array(0, $filter['status']); - $alias = 'status' . $i++; - $allowNull = ' OR ' . $alias . '.idUser IS NULL'; - if ($filter['condition'] === 'is') { - $condition = ''; - $allowNull = $showNoRating ? $allowNull : ''; - } else { - $condition = 'NOT '; - $allowNull = !$showNoRating ? $allowNull : ''; - } - $select->joinLeft([$alias => 'status'], '(movie.id = ' . $alias . '.idMovie AND ' . $alias . '.idUser = ' . $filter['user'] . ')' . $allowNull, []); - - $select->where($alias . '.isLatest = 1 OR ' . $alias . '.isLatest IS NULL'); - - // Filter by status - $select->where($alias . '.rating ' . $condition . 'IN (?)' . $allowNull, $filter['status']); - - // Filter by title - if (isset($filter['title'])) { - $title = $filter['title']; - $id = Movie::extractId($title); - $titles = explode(' ', trim($title)); - foreach ($titles as $part) { - if ($part) { - $select->where('movie.title LIKE ? OR movie.id = "' . $id . '"', '%' . $part . '%'); - } - } - } - - // Filter by presence of source - if (isset($filter['withSource']) && $filter['withSource']) { - $select->where('movie.source IS NOT NULL'); - } - - if ($maxDate) { - $maxDate = 'IF(`' . $alias . '`.`dateUpdate` IS NULL OR `' . $alias . '`.`dateUpdate` < ' . $maxDate . ', ' . $maxDate . ', `' . $alias . '`.`dateUpdate`)'; - } else { - $maxDate = '`' . $alias . '`.`dateUpdate`'; - } - } - - $select->columns(['date' => new Zend_Db_Expr($maxDate)]); - - return $select; - } - - /** - * Delete obsolete sources for all movies. - * An obsolete source is either a source older than 3 months, or - * a source which is not needed anymore (nobody need the movie anymore). - */ - public static function deleteObsoleteSources(): void - { - $db = self::getDbTable()->getAdapter(); - $update = 'UPDATE `movie` SET dateUpdate = dateUpdate, dateSearch = NULL, searchCount = 0, identity = 0, quality = 0, score = 0, source = NULL'; - - // Delete sources older than 6 months - $db->query($update . ' WHERE `source` IS NOT NULL AND `dateSearch` < DATE_SUB(NOW(), INTERVAL 6 MONTH)'); - - // Delete non-needed sources - $db->query($update . ' WHERE `movie`.`id` NOT IN (SELECT `status`.`idMovie` FROM `status` WHERE `rating` = ? AND `isLatest`)', [Status::Need]); - } -} diff --git a/application/mQueue/Model/Setting.php b/application/mQueue/Model/Setting.php deleted file mode 100644 index 57e5bd5..0000000 --- a/application/mQueue/Model/Setting.php +++ /dev/null @@ -1,37 +0,0 @@ -value = $value; - $setting->save(); - } -} diff --git a/application/mQueue/Model/SettingMapper.php b/application/mQueue/Model/SettingMapper.php deleted file mode 100644 index 22e1be7..0000000 --- a/application/mQueue/Model/SettingMapper.php +++ /dev/null @@ -1,27 +0,0 @@ -find([$id])->current(); - - if ($result == null) { - $result = self::getDbTable()->createRow(); - $result->id = $id; - $result->value = $defaultValue; - } - - return $result; - } -} diff --git a/application/mQueue/Model/Status.php b/application/mQueue/Model/Status.php deleted file mode 100644 index 012e378..0000000 --- a/application/mQueue/Model/Status.php +++ /dev/null @@ -1,69 +0,0 @@ -idMovie) . '_' . $this->idUser; - } - - /** - * Returns the name. - * - * @return string - */ - public function getName() - { - if ($this->rating == 0) { - return _tr('Not rated'); - } - - return self::$ratings[$this->rating]; - } - - /** - * Returns the date of last update. - * - * @return Zend_Date - */ - public function getDateUpdate() - { - return new Zend_Date($this->dateUpdate, Zend_Date::ISO_8601); - } -} - -// Defines ratings names -Status::$ratings = [ - DefaultModelStatus::Need => _tr('Need'), - DefaultModelStatus::Bad => _tr('Bad'), - DefaultModelStatus::Ok => _tr('Ok'), - DefaultModelStatus::Excellent => _tr('Excellent'), - DefaultModelStatus::Favorite => _tr('Favorite'), -]; diff --git a/application/mQueue/Model/StatusMapper.php b/application/mQueue/Model/StatusMapper.php deleted file mode 100644 index 9b8a9f4..0000000 --- a/application/mQueue/Model/StatusMapper.php +++ /dev/null @@ -1,237 +0,0 @@ -getAdapter(); - $db->beginTransaction(); - - // Find out if a very recent status exist to be replaced, so user can change their mind "quickly" - $select = self::getDbTable()->select() - ->where('idUser = ?', $user->id) - ->where('idMovie = ?', $movie->id) - ->where('dateUpdate > DATE_SUB(NOW(), INTERVAL 5 MINUTE)'); - - $status = self::getDbTable()->fetchRow($select); - - // Otherwise create a brand new one and set all existing one as "old" - if (!$status) { - $status = self::getDbTable()->createRow(); - $status->idUser = $user->id; - $status->idMovie = $movie->id; - $status->isLatest = true; - - // Here we must set dateUpdate to itself to avoid auto-update of the timestamp field by MySql - $db->query('UPDATE `status` SET isLatest = 0, dateUpdate = dateUpdate WHERE idUser = ? AND idMovie = ?', [$user->id, $movie->id]); - } - - $status->rating = $rating; - $status->save(); - - $db->commit(); - - return $status; - } - - /** - * Find a status by its user and movie. If not found it will be created (but not saved). - * - * @param int $idMovie - * - * @return Status - */ - public static function find($idMovie, ?User $user = null) - { - $statuses = self::findAll([$idMovie], $user); - - return reset($statuses); - } - - /** - * Returns an array of Status containing all statuses for specified ids - * (if they don't exist in database, they will be created with default values but not saved). - * - * @return array of \mQueue\Model\Status - */ - public static function findAll(array $idMovies, ?User $user = null) - { - $statuses = []; - if (!count($idMovies)) { - return $statuses; - } - - // Do not hit database if we know there won't be any result anyway - if ($user) { - $select = self::getDbTable()->select() - ->where('idUser = ?', $user->id) - ->where('idMovie IN (?)', $idMovies) - ->where('isLatest = 1'); - - $records = self::getDbTable()->fetchAll($select); - - foreach ($records as $record) { - $statuses[$record->idMovie] = $record; - } - } - - // Fill non existing statuses in databases - foreach ($idMovies as $id) { - $id = (int) $id; - if (!array_key_exists($id, $statuses)) { - $status = self::getDbTable()->createRow(); - if ($user) { - $status->idUser = $user->id; - } - $status->idMovie = $id; - $statuses[$status->idMovie] = $status; - } - } - - return $statuses; - } - - /** - * Build statistic for the given user. - * - * @return array statistics - */ - public static function getStatistics(User $user) - { - $select = self::getDbTable()->select()->setIntegrityCheck(false) - ->from('status', [ - 'rating' => 'IFNULL(rating, 0)', - 'count' => 'COUNT(IFNULL(rating, 0))', - ]) - ->joinRight('movie', 'movie.id = status.idMovie AND status.idUser = ' . $user->id, []) - ->where('isLatest = 1 OR isLatest IS NULL') - ->group('IFNULL(rating, 0)'); - - $records = self::getDbTable()->fetchAll($select); - - // Set all count to 0 - $result = ['total' => 0, 'rated' => 0, Status::Nothing => 0]; - foreach (Status::$ratings as $val => $name) { - $result[$val] = 0; - } - - // Fetch real counts - foreach ($records->toArray() as $row) { - $result[$row['rating']] = $row['count']; - if ($row['rating'] != Status::Nothing) { - $result['rated'] += $row['count']; - } - $result['total'] += $row['count']; - } - - return $result; - } - - /** - * Build statistic for the given user. - * - * @param User $user - * @param bool $percent - * - * @return array statistics - */ - public static function getGraph(?User $user = null, $percent = false) - { - $select = self::getDbTable()->select() - ->order('dateUpdate'); - - if ($user) { - $select->where('idUser = ?', $user->id); - } - - $records = self::getDbTable()->fetchAll($select); - - // Set all count to 0 - $cumulatedStatuses = [Status::Nothing => 0]; - $graphData = []; - foreach (Status::$ratings as $val => $name) { - $cumulatedStatuses[$val] = 0; - $graphData[$val] = []; - } - - // Fetch real counts - $lastStatuses = []; - foreach ($records as $row) { - // Add new status - ++$cumulatedStatuses[$row->rating]; - $changed = [$row->rating]; - - // Substract old status - if (isset($lastStatuses[$row->idUser][$row->idMovie])) { - --$cumulatedStatuses[$lastStatuses[$row->idUser][$row->idMovie]]; - $changed[] = $lastStatuses[$row->idUser][$row->idMovie]; - } - $lastStatuses[$row->idUser][$row->idMovie] = $row->rating; - - $time = new DateTimeImmutable($row->dateUpdate); - $time->setTimezone(new DateTimeZone('GMT')); - $epoch = (int) $time->format('U') * 1000; - - // If we are in percent mode, we need all status for each timestamp - if ($percent) { - $changed = array_keys(Status::$ratings); - } - - // Keep for the graph only the changed values (and overwrite previous value if it happened at exactly the same time) - foreach ($changed as $val) { - $graphData[$val][$epoch] = [ - $epoch, - $cumulatedStatuses[$val], - ]; - } - } - - // Format everything in a more output friendly way - $result = []; - foreach (Status::$ratings as $val => $name) { - $result[] = [ - 'name' => $name, - 'data' => array_values($graphData[$val]), - ]; - } - - return $result; - } - - /** - * Returns the query to get activity for either the whole system, or a specific user, or a specific movie. - * - * @param null|Movie|User $item - * - * @return Zend_Db_Table_Select - */ - public static function getActivityQuery($item = null) - { - $select = self::getDbTable()->select(); - $select->from('status') - ->order('dateUpdate DESC'); - - if ($item instanceof User) { - $select->where('idUser = ?', $item->id); - } elseif ($item instanceof Movie) { - $select->where('idMovie = ?', $item->id); - } - - return $select; - } -} diff --git a/application/mQueue/Model/User.php b/application/mQueue/Model/User.php deleted file mode 100644 index beddfa0..0000000 --- a/application/mQueue/Model/User.php +++ /dev/null @@ -1,72 +0,0 @@ -idUser)) { - self::$currentUser = UserMapper::find($session->idUser); - } else { - self::$currentUser = null; - } - } - - return self::$currentUser; - } - - /** - * Set the user currently logged in, or log him out. - * - * @param User $user - */ - public static function setCurrent(?DefaultModelUser $user = null): void - { - $session = new Zend_Session_Namespace(); - $session->idUser = $user ? $user->id : null; - self::$currentUser = $user; - } - - /** - * Returns movie ratings statistics. - * - * @return array of count of movies per ratings - */ - public function getStatistics() - { - return StatusMapper::getStatistics($this); - } - - /** - * Override parent to auto-logout when deleting logged in user. - */ - public function delete() - { - if (DefaultModelUser::getCurrent() == $this) { - DefaultModelUser::setCurrent(null); - } - - return parent::delete(); - } -} diff --git a/application/mQueue/Model/UserMapper.php b/application/mQueue/Model/UserMapper.php deleted file mode 100644 index 79ffe5f..0000000 --- a/application/mQueue/Model/UserMapper.php +++ /dev/null @@ -1,69 +0,0 @@ -createRow(); - $user->nickname = $values['nickname']; - $user->email = $values['email']; - $user->password = sha1($values['password']); - $user->save(); - - return $user; - } - - /** - * Finds a user by its email and password (not hashed). - * - * @param string $email - * @param string $password - * - * @return null|User - */ - public static function findEmailPassword($email, $password) - { - $select = self::getDbTable()->select() - ->where('email = ?', $email) - ->where('password = SHA1(?)', $password); - - $record = self::getDbTable()->fetchRow($select); - - return $record; - } - - /** - * Finds a user by its ID. - * - * @param int $id - * - * @return null|User - */ - public static function find($id) - { - $result = self::getDbTable()->find([$id]); - - return $result->current(); - } - - /** - * Finds all users. - * - * @return User[] - */ - public static function fetchAll() - { - $resultSet = self::getDbTable()->fetchAll(null, 'LOWER(nickname)'); - - return $resultSet; - } -} diff --git a/application/mQueue/View/Helper/Activity.php b/application/mQueue/View/Helper/Activity.php deleted file mode 100644 index 8e9580f..0000000 --- a/application/mQueue/View/Helper/Activity.php +++ /dev/null @@ -1,74 +0,0 @@ -'; - - $cacheUser = []; - $cacheMovie = []; - foreach ($activity as $status) { - if (!array_key_exists($status->idUser, $cacheUser)) { - $cacheUser[$status->idUser] = UserMapper::find($status->idUser); - } - $user = $cacheUser[$status->idUser]; - - if (!array_key_exists($status->idMovie, $cacheMovie)) { - $cacheMovie[$status->idMovie] = MovieMapper::find($status->idMovie); - } - $movie = $cacheMovie[$status->idMovie]; - - $result .= '
'; - - if (in_array('user', $columns)) { - $result .= ''; - } - - $result .= '
'; - - if (in_array('movie', $columns)) { - $result .= '
' . $this->view->movie($movie) . '
'; - } - if (in_array('status', $columns)) { - $result .= '' . $this->view->statusLinks($status) . ' '; - } - - if (in_array('date', $columns)) { - $result .= ''; - } - - $result .= '
'; - - $result .= '
'; - } - - if ($activity->getTotalItemCount() == 0) { - $result .= '

' . $this->view->translate('There is no activity to show.') . '

'; - } - - return $result . ''; - } -} diff --git a/application/mQueue/View/Helper/CacheStamp.php b/application/mQueue/View/Helper/CacheStamp.php deleted file mode 100644 index d94e757..0000000 --- a/application/mQueue/View/Helper/CacheStamp.php +++ /dev/null @@ -1,31 +0,0 @@ -view->serverUrl() . $this->view->baseUrl($fileName) . '?' . filemtime($fullPath); - } - - return $fileName; - } -} diff --git a/application/mQueue/View/Helper/FlashMessenger.php b/application/mQueue/View/Helper/FlashMessenger.php deleted file mode 100644 index 1b0ea43..0000000 --- a/application/mQueue/View/Helper/FlashMessenger.php +++ /dev/null @@ -1,142 +0,0 @@ - $value)` pairs in FlashMessenger's messages array. - * If such a pair is found, $key is taken as the "message level", $value as the - * message. (Simple strings are provided a default level of 'warning'.) - * - * NOTE: MESSAGES ARE PRESUMED TO BE SAFE HTML. IF REDISPLAYING USER - * INPUT, ESCAPE ALL MESSAGES PRIOR TO ADDING TO FLASHMESSENGER. - */ -class FlashMessenger extends Zend_View_Helper_Abstract -{ - /** - * @var Zend_Controller_Action_Helper_FlashMessenger - */ - private $_flashMessenger; - - /** - * If the flashmessenger is postoned it will do nothing on first call. - * - * @var bool - */ - private $isPostponed = false; - - /** - * Initializing stuff. Provides fluid interface. - */ - public function flashMessenger() - { - return $this; - } - - /** - * Render the flash messages. - * - * @return string flash messages formatted as div - */ - public function __toString() - { - if ($this->isPostponed) { - $this->isPostponed = false; - - return ''; - } - - $flashMessenger = $this->_getFlashMessenger(); - - //get messages from previous requests - $messages = $flashMessenger->getMessages(); - - //add any messages from this request - if ($flashMessenger->hasCurrentMessages()) { - $messages = array_merge( - $messages, - $flashMessenger->getCurrentMessages() - ); - //we don't need to display them twice. - $flashMessenger->clearCurrentMessages(); - } - - //initialise return string - $output = ''; - - //process messages - foreach ($messages as $message) { - $level = 'notice'; - if (is_array($message)) { - [$level, $message] = each($message); - } - $output .= '

' . $message . '
'; - } - - return $output; - } - - /** - * Postpone the flash messages to the next next call. The next call will return empty string. - */ - public function postpone(bool $isPostponed = true): void - { - $this->isPostponed = $isPostponed; - } - - /** - * Returns the flash messenger. - * - * @return Zend_Controller_Action_Helper_FlashMessenger - */ - protected function _getFlashMessenger() - { - if (null === $this->_flashMessenger) { - /** @var Zend_Controller_Action_Helper_FlashMessenger $broker */ - $broker = Zend_Controller_Action_HelperBroker::getStaticHelper('FlashMessenger'); - $this->_flashMessenger = $broker; - } - - return $this->_flashMessenger; - } -} diff --git a/application/mQueue/View/Helper/Footer.php b/application/mQueue/View/Helper/Footer.php deleted file mode 100644 index 1369fdf..0000000 --- a/application/mQueue/View/Helper/Footer.php +++ /dev/null @@ -1,26 +0,0 @@ -view->serverUrl() . $this->view->url( - ['controller' => 'about'], - 'default', - true - ) . '">' . $this->view->translate('about mQueue') . ' '; - - return $result; - } -} diff --git a/application/mQueue/View/Helper/GoogleAnalytics.php b/application/mQueue/View/Helper/GoogleAnalytics.php deleted file mode 100644 index 14d8fc7..0000000 --- a/application/mQueue/View/Helper/GoogleAnalytics.php +++ /dev/null @@ -1,44 +0,0 @@ -getOption('googleAnalyticsTrackingCode'); - } - } - - $trackingCode = trim($trackingCode); - if (!is_string($trackingCode) || empty($trackingCode)) { - return ''; - } - - $result = << - -STRING; - - return $result; - } -} diff --git a/application/mQueue/View/Helper/Graph.php b/application/mQueue/View/Helper/Graph.php deleted file mode 100644 index e1667d0..0000000 --- a/application/mQueue/View/Helper/Graph.php +++ /dev/null @@ -1,51 +0,0 @@ - 'status', 'action' => 'graph']; - if ($user) { - $params['user'] = $user->id; - } - $url = $this->view->serverUrl() . $this->view->url($params, 'default'); - - $js = <<view->headScript() - ->appendFile('/js/min/highcharts.js') - ->appendScript($js); - - return $html; - } -} diff --git a/application/mQueue/View/Helper/Gravatar.php b/application/mQueue/View/Helper/Gravatar.php deleted file mode 100644 index 38a835a..0000000 --- a/application/mQueue/View/Helper/Gravatar.php +++ /dev/null @@ -1,25 +0,0 @@ - 'gravatar user_' . $user->id . ' ' . $size, - 'title' => $user->nickname, - ]; - $result = '_htmlAttribs($attribs) . '">'; - - return $result; - } -} diff --git a/application/mQueue/View/Helper/HeadLink.php b/application/mQueue/View/Helper/HeadLink.php deleted file mode 100644 index d363ad3..0000000 --- a/application/mQueue/View/Helper/HeadLink.php +++ /dev/null @@ -1,26 +0,0 @@ -view->cacheStamp($args[0]); - } - - return parent::__call($method, $args); - } -} diff --git a/application/mQueue/View/Helper/HeadScript.php b/application/mQueue/View/Helper/HeadScript.php deleted file mode 100644 index 5b53416..0000000 --- a/application/mQueue/View/Helper/HeadScript.php +++ /dev/null @@ -1,62 +0,0 @@ -includeDirectory($file, $method, $args); - } else { - $args[0] = $this->view->cacheStamp(str_replace($prefix . 'public/', '', $file)); - parent::__call($method, $args); - } - } - } - - /** - * Override parent to support timestamp, compilation and concatenation. - * Compiled and concatenated files must pre-exist (compiled by external tools). - * - * @param string $method - * @param array $args - * - * @return self - */ - public function __call($method, $args) - { - if (mb_strpos($method, 'File')) { - $fileName = $args[0]; - - // If file will be concatenated, use concatenation system instead - if (is_array($fileName)) { - // If we are in development, actually don't concatenate anything - if (APPLICATION_ENV == 'development') { - foreach ($fileName[1] as $f) { - $this->includeDirectory('public' . $f, $method, $args); - } - - return $this; - } - - // Otherwise use pre-existing concatenated file - $fileName = $fileName[0]; - } - - $args[0] = $this->view->cacheStamp($fileName); - } - - return parent::__call($method, $args); - } -} diff --git a/application/mQueue/View/Helper/LoginState.php b/application/mQueue/View/Helper/LoginState.php deleted file mode 100644 index a017fd6..0000000 --- a/application/mQueue/View/Helper/LoginState.php +++ /dev/null @@ -1,48 +0,0 @@ -'; - - $user = User::getCurrent(); - if ($user) { - $result .= '' . $this->view->gravatar($user) . ' ' . $this->view->escape($user->nickname) . ' '; - - $result .= '' . $this->view->translate('Logout') . ' '; - } else { - $result .= ' ' . $this->view->translate('Subscribe') . ' '; - - $result .= '' . $this->view->translate('Login') . ''; - } - - return $result . ''; - } -} diff --git a/application/mQueue/View/Helper/Movie.php b/application/mQueue/View/Helper/Movie.php deleted file mode 100644 index dd4addc..0000000 --- a/application/mQueue/View/Helper/Movie.php +++ /dev/null @@ -1,30 +0,0 @@ -view->link($movie); - - $user = User::getCurrent(); - if ($user) { - $status = $movie->getStatus($user); - $title = $this->view->translate('Your rating is : %s', [$status->getName()]); - } else { - $title = $this->view->translate('You are not logged in'); - } - - $movieUrl = $this->view->url(['controller' => 'movie', 'action' => 'view', 'id' => $movie->id], 'singleid', true); - $result .= ' ' . $this->view->escape($movie->getTitle()) . ''; - - return $result; - } -} diff --git a/application/mQueue/View/Helper/Rating.php b/application/mQueue/View/Helper/Rating.php deleted file mode 100644 index 495eda8..0000000 --- a/application/mQueue/View/Helper/Rating.php +++ /dev/null @@ -1,21 +0,0 @@ -' . Status::$ratings[$rating] . ''; - - return $result; - } -} diff --git a/application/mQueue/View/Helper/Sort.php b/application/mQueue/View/Helper/Sort.php deleted file mode 100644 index 9ecbd47..0000000 --- a/application/mQueue/View/Helper/Sort.php +++ /dev/null @@ -1,36 +0,0 @@ - 'asc', 'asc' => 'desc']; - if (!in_array($selectedSortOrder, $orders)) { - $selectedSortOrder = reset($orders); - } - - $url = $this->view->urlParams(array_merge($additionalParameters, ['sort' => $column, 'sortOrder' => $orders[$selectedSortOrder]])); - $result = '' . $this->view->escape($label) . ''; - - return $result; - } -} diff --git a/application/mQueue/View/Helper/StatusHelp.php b/application/mQueue/View/Helper/StatusHelp.php deleted file mode 100644 index b4aa71f..0000000 --- a/application/mQueue/View/Helper/StatusHelp.php +++ /dev/null @@ -1,27 +0,0 @@ -'; - $result .= '
  • ' . $this->view->rating(Status::Need) . ': ' . $this->view->translate('I want to see this movie') . '
  • '; - $result .= '
  • ' . $this->view->rating(Status::Bad) . ': ' . $this->view->translate('Boring movie, I wasted my time') . '
  • '; - $result .= '
  • ' . $this->view->rating(Status::Ok) . ': ' . $this->view->translate('Enjoyable movie (most movies)') . '
  • '; - $result .= '
  • ' . $this->view->rating(Status::Excellent) . ': ' . $this->view->translate('Excellent, I would watch it twice') . '
  • '; - $result .= '
  • ' . $this->view->rating(Status::Favorite) . ': ' . $this->view->translate('Incredibly awesome, the kind of movie you must watch many times regularly') . '
  • '; - $result .= ''; - - return $result; - } -} diff --git a/application/mQueue/View/Helper/StatusLinks.php b/application/mQueue/View/Helper/StatusLinks.php deleted file mode 100644 index d041635..0000000 --- a/application/mQueue/View/Helper/StatusLinks.php +++ /dev/null @@ -1,42 +0,0 @@ -getUniqueId() . '">'; - $user = User::getCurrent(); - - // Deactivate links if no logged user - if ($user) { - $tag = 'a'; - } else { - $tag = 'span'; - } - - foreach (Status::$ratings as $val => $name) { - $class = $val . ($status->rating == $val ? ' current' : ''); - $url = $this->view->serverUrl() . $this->view->url([ - 'controller' => 'status', - 'movie' => $status->idMovie, - 'rating' => ($val == $status->rating && $user && $user->id == $status->idUser) ? 0 : $val, - ], 'status', true); - - $result .= '<' . $tag . ' class="mqueue_status mqueue_status_' . $class . '"' . ($tag == 'a' ? ' href="' . $url . '"' : '') . ' title="' . $name . '">' . $name . ''; - } - $result .= ''; - - return $result; - } -} diff --git a/application/views/scripts/about/index.phtml b/application/views/scripts/about/index.phtml deleted file mode 100644 index 46ae684..0000000 --- a/application/views/scripts/about/index.phtml +++ /dev/null @@ -1,6 +0,0 @@ -headTitle(_tr('About')); ?> -

    translate('About mQueue'); ?>

    -

    translate('This project started as a personal need. It is written in PHP and is an opportunity to explore Zend Framework and jQuery. It also uses FatCow\'s icon set.'); ?> -

    -

    translate('Source code is freely available on http://github.com/PowerKiKi/mqueue.'); ?> -

    \ No newline at end of file diff --git a/application/views/scripts/activity/index.phtml b/application/views/scripts/activity/index.phtml deleted file mode 100644 index 7bfe3a0..0000000 --- a/application/views/scripts/activity/index.phtml +++ /dev/null @@ -1,9 +0,0 @@ -headTitle($this->title); ?> -

    escape($this->title); ?>

    -activity($this->activity); ?> -alternateFormats([ - 'rss' => $this->serverUrl() . $this->url(), -], $this->title == $this->translate('Overall activity') ? null : 'mQueue - ' . $this->title); // Don't give title to avoid adding a duplicate alternate link to overall activity -?> -paginationControl($this->activity, null, null, ['permanentParams' => []]); ?> \ No newline at end of file diff --git a/application/views/scripts/activity/index.rss.phtml b/application/views/scripts/activity/index.rss.phtml deleted file mode 100644 index 9a0d732..0000000 --- a/application/views/scripts/activity/index.rss.phtml +++ /dev/null @@ -1,71 +0,0 @@ -activity as $status) { - if (!array_key_exists($status->idUser, $cacheUser)) { - $cacheUser[$status->idUser] = \mQueue\Model\UserMapper::find($status->idUser); - } - $user = $cacheUser[$status->idUser]; - - if (!array_key_exists($status->idMovie, $cacheMovie)) { - $cacheMovie[$status->idMovie] = \mQueue\Model\MovieMapper::find($status->idMovie); - } - $movie = $cacheMovie[$status->idMovie]; - - $date = new Zend_Date($status->dateUpdate); - $text = '

    ' . $this->gravatar($user) . ' ' . $this->escape($user->nickname) . ' ' - . '' . $this->escape($movie->getTitle()) . ' ' - . $status->getName() . '

    '; - $text .= '

    ' . $movie->getImdbUrl() . '

    '; - - $title = $this->escape($user->nickname) . ' - ' . $this->escape($movie->getTitle()) . ' - ' . $status->getName(); - - $entry = [ - 'author' => 'mQueue', - 'title' => $title, - 'link' => $this->serverUrl() . $this->url(['controller' => 'movie', 'action' => 'view', 'id' => $movie->id], 'singleid', true), - 'description' => $title, - 'content' => $text, - 'lastUpdate' => $date->getTimestamp(), - ]; - - if ($movie->source) { - $entry['enclosure'] = [ - [ - 'url' => $movie->source, - 'type' => 'application/x-bittorrent', - ], - ]; - } - - $entries[] = $entry; -} - -// Create the RSS array -$rss = [ - 'title' => 'mQueue - ' . $this->title, - 'link' => $this->serverUrl() . $this->url([]), - 'charset' => 'UTF-8', - 'entries' => $entries, -]; - -// Import the array -$activity = Zend_Feed::importArray($rss, 'rss'); - -// Write the activity to a variable -$activity->send(); diff --git a/application/views/scripts/error/error.phtml b/application/views/scripts/error/error.phtml deleted file mode 100644 index 03a5dc1..0000000 --- a/application/views/scripts/error/error.phtml +++ /dev/null @@ -1,19 +0,0 @@ -

    An error occurred

    -

    message ?>

    - -exception)): ?> - -

    Exception information:

    -

    - Message: exception->getMessage() ?> -

    - -

    Stack trace:

    -
    exception->getTraceAsString() ?>
    -    
    - -

    Request Parameters:

    -
    escape(var_export($this->request->getParams(), true)) ?>
    -    
    - - diff --git a/application/views/scripts/index/index.phtml b/application/views/scripts/index/index.phtml deleted file mode 100644 index 4b38f0a..0000000 --- a/application/views/scripts/index/index.phtml +++ /dev/null @@ -1,43 +0,0 @@ - -
    -

    Welcome to the Zend Framework!

    - -

    This is your project's main page

    - -
    -

    -

    - Helpful Links:
    - Zend Framework Website | - Zend Framework Manual -

    -
    -
    \ No newline at end of file diff --git a/application/views/scripts/movie/add.phtml b/application/views/scripts/movie/add.phtml deleted file mode 100644 index 33218c4..0000000 --- a/application/views/scripts/movie/add.phtml +++ /dev/null @@ -1,37 +0,0 @@ -headTitle(_tr('Add movie')); ?> -

    translate('Add a new movie'); ?>

    -form; ?> -movies)): ?> -

    translate('Movie found'); ?>

    - - - - - - - movies) == 0): ?> - - - movies as $entry): ?> - - - - - - -
    translate('Rating'); ?>translate('Title'); ?>
    - translate('There is no movie to show. Please read the %s to learn how to add movies.', ['' . _tr('FAQ') . '']); ?> -
    statusLinks($entry->getStatus(\mQueue\Model\User::getCurrent())); ?>movie($entry); ?>
    - -
    statusHelp(); ?>
    - - - -
    - translate('See the %s to learn how to add movies directly from www.imdb.com in a single click !', [ - '' . $this->translate('FAQ') . '', - ]); - ?> -
    - \ No newline at end of file diff --git a/application/views/scripts/movie/index.phtml b/application/views/scripts/movie/index.phtml deleted file mode 100644 index 259bfde..0000000 --- a/application/views/scripts/movie/index.phtml +++ /dev/null @@ -1,51 +0,0 @@ -headTitle(_tr('Movies')); -?> -

    translate('Movies list'); ?>

    -formFilter; ?> - - - users as $user): - ?> - - - - - paginator->getTotalItemCount() == 0): ?> - - - paginator as $entry): ?> - - users as $user): ?> - - - - - - -
    sort($user->nickname, 'status' . $i++, $this->sortSelectedKey, $this->sortSelectedOrder, $this->permanentParams); ?>sort($this->translate('Title'), 'title', $this->sortSelectedKey, $this->sortSelectedOrder, $this->permanentParams); ?>
    - translate('There is no movie to show.'); ?> -
    statusLinks($entry->getStatus($user)) ?>movie($entry); ?>
    -alternateFormats([ - 'rss' => $this->urlParams($this->permanentParams, false), - 'csv' => $this->urlParams($this->permanentParams, false), -], 'mQueue - ' . $this->filterName); -?> -paginationControl($this->paginator, null, null, ['permanentParams' => $this->permanentParams]); -?> - -
    - paginator->getTotalItemCount() != 0): ?> - statusHelp(); ?> - - translate('See the %s to learn how to add movies directly from www.imdb.com in a single click !', [ - '' . $this->translate('FAQ') . '', - ]); - ?> - -
    \ No newline at end of file diff --git a/application/views/scripts/movie/index.rss.phtml b/application/views/scripts/movie/index.rss.phtml deleted file mode 100644 index 720056b..0000000 --- a/application/views/scripts/movie/index.rss.phtml +++ /dev/null @@ -1,58 +0,0 @@ -paginator as $movie) { - $date = new Zend_Date($movie->dateUpdate); - $text = '

    ' . $movie->getImdbUrl() . '

    '; - - foreach ($this->users as $user) { - $status = $movie->getStatus($user); - $text .= '

    ' . $this->gravatar($user) . ' ' . $this->escape($user->nickname) . ' ' - . $status->getName() . '

    '; - } - - $title = $movie->getTitle(); - - $entry = [ - 'author' => 'mQueue', - 'title' => $title, - 'link' => $this->serverUrl() . $this->url(['controller' => 'movie', 'action' => 'view', 'id' => $movie->id], 'singleid', true), - 'description' => $title, - 'content' => $text, - 'lastUpdate' => $date->getTimestamp(), - ]; - - if ($movie->source) { - $entry['enclosure'] = [ - [ - 'url' => $movie->source, - 'type' => 'application/x-bittorrent', - ], - ]; - } - - $entries[] = $entry; -} - -// Create the RSS array -$rss = [ - 'title' => 'mQueue - ' . $this->formFilter->getValuesText(), - 'link' => $this->urlParams($this->permanentParams), - 'charset' => 'UTF-8', - 'entries' => $entries, -]; - -// Import the array -$activity = Zend_Feed::importArray($rss, 'rss'); - -// Write the activity to a variable -$activity->send(); diff --git a/application/views/scripts/movie/view.phtml b/application/views/scripts/movie/view.phtml deleted file mode 100644 index 15f24dc..0000000 --- a/application/views/scripts/movie/view.phtml +++ /dev/null @@ -1,33 +0,0 @@ -headTitle($this->movie->getTitle()); ?> -

    escape($this->movie->getTitle()); ?>

    -

    link($this->movie, true) ?>

    -

    translate('Current status'); ?>

    - - - - - - users as $user) { - echo ''; - echo ''; - echo ''; - - echo ''; - } - ?> -
    translate('User'); ?>translate('Rating'); ?>
    ' . $this->gravatar($user) . ' ' . $this->escape($user->nickname) . '' . $this->statusLinks($this->movie->getStatus($user)) . '
    -

    translate('Activity'); ?>

    -activity($this->movieActivity, ['movie']); ?> -alternateFormats([ - 'rss' => $this->serverUrl() . $this->url(['controller' => 'activity', 'action' => 'index', 'movie' => $this->movie->id], 'activityMovie', true), -], 'mQueue - ' . $this->translate('Activity for %s', [$this->movie->getTitle()])); -?> - -paginationControl($this->movieActivity); ?> \ No newline at end of file diff --git a/application/views/scripts/status/list.phtml b/application/views/scripts/status/list.phtml deleted file mode 100644 index b563f40..0000000 --- a/application/views/scripts/status/list.phtml +++ /dev/null @@ -1,5 +0,0 @@ -status as $status) { - echo '
    ' . $status . '
    '; -} diff --git a/application/views/scripts/user/index.phtml b/application/views/scripts/user/index.phtml deleted file mode 100644 index e70839c..0000000 --- a/application/views/scripts/user/index.phtml +++ /dev/null @@ -1,44 +0,0 @@ -headTitle(_tr('Users')); ?> -

    translate('Users list'); ?>

    - - '; - echo ''; - foreach (\mQueue\Model\Status::$ratings as $val => $name) { - echo ''; - } - echo ''; - echo ''; - echo ''; - -// Table data - foreach ($this->users as $user) { - echo ''; - echo ''; - $stats = $user->getStatistics(); - foreach (\mQueue\Model\Status::$ratings as $val => $name) { - echo ''; - } - echo ''; - echo ''; - echo ''; - } - ?> -
    ' . $this->translate('User') . '' . $name . '' . $this->translate('Rated') . '' . $this->translate('Not rated') . '
    ' . $this->gravatar($user) . ' ' . $this->escape($user->nickname) . '' . $stats[$val] . '' . $stats['rated'] . '' . $stats[\mQueue\Model\Status::Nothing] . '
    - -

    translate('Timeline'); ?>

    -graph(); ?> diff --git a/application/views/scripts/user/view.phtml b/application/views/scripts/user/view.phtml deleted file mode 100644 index 84c89d9..0000000 --- a/application/views/scripts/user/view.phtml +++ /dev/null @@ -1,48 +0,0 @@ -headTitle($this->user->nickname); ?> -gravatar($this->user, 'big'); ?> -

    escape($this->user->nickname); ?>

    -

    translate('Statistics'); ?>

    - - - '; - foreach (\mQueue\Model\Status::$ratings as $val => $name) { - echo ''; - } - echo ''; - echo ''; - echo ''; - - $stats = $this->user->getStatistics(); - echo ''; - foreach (\mQueue\Model\Status::$ratings as $val => $name) { - echo ''; - } - echo ''; - echo ''; - echo ''; - ?> -
    ' . $name . '' . $this->translate('Rated') . '' . $this->translate('Not rated') . '
    ' . $stats[$val] . '' . $stats['rated'] . '' . $stats[\mQueue\Model\Status::Nothing] . '
    - -

    translate('Timeline'); ?>

    -graph($this->user); ?> - -

    translate('Activity'); ?>

    -activity($this->userActivity, ['user']); ?> - -alternateFormats([ - 'rss' => $this->serverUrl() . $this->url(['controller' => 'activity', 'action' => 'index', 'user' => $this->user->id], 'activityUser', true), -], 'mQueue - ' . $this->translate('Activity for %s', [$this->user->nickname])); -?> -paginationControl($this->userActivity); ?> diff --git a/bin/build.sh b/bin/build.sh index 6664f7d..3287f19 100755 --- a/bin/build.sh +++ b/bin/build.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash # Try to use specific PHP, or fallback to default version -PHP=`which php7.2` || PHP='php' +PHP=`which php8.4` || PHP='php' COMPOSER="$PHP `which composer` --no-interaction --no-plugins" # Exit script on any error @@ -11,7 +11,7 @@ echo "Installing git hooks..." ln -fs ../../bin/pre-commit.sh .git/hooks/pre-commit echo "Updating Node.js packages..." -yarn install +pnpm ci echo "Updating all PHP dependencies via composer..." $COMPOSER install --classmap-authoritative --ignore-platform-reqs @@ -20,7 +20,7 @@ echo "Updating database..." $PHP ./bin/update_database.php echo "Compiling CSS..." -./node_modules/.bin/sass --style=compressed --no-source-map application/sass/:public/css/ +./node_modules/.bin/sass --style=compressed --no-source-map src/sass/:public/css/ echo "Compiling JavaScript..." ./node_modules/.bin/esbuild --minify public/js/*.js --outdir=public/js/min diff --git a/bin/clear-config-cache.php b/bin/clear-config-cache.php new file mode 100644 index 0000000..328e791 --- /dev/null +++ b/bin/clear-config-cache.php @@ -0,0 +1,39 @@ +get(EntityManager::class); + +$dependencyFactory = $container->get(DependencyFactory::class); +$commands = [ + new CurrentCommand($dependencyFactory), + new DumpSchemaCommand($dependencyFactory), + new ExecuteCommand($dependencyFactory), + new GenerateCommand($dependencyFactory), + new LatestCommand($dependencyFactory), + new MigrateCommand($dependencyFactory), + new RollupCommand($dependencyFactory), + new StatusCommand($dependencyFactory), + new VersionCommand($dependencyFactory), + new UpToDateCommand($dependencyFactory), + new SyncMetadataCommand($dependencyFactory), + new ListCommand($dependencyFactory), + new DiffCommand($dependencyFactory), +]; + +ConsoleRunner::run( + new SingleManagerProvider($entityManager), + $commands +); diff --git a/bin/fetch_movie_data.php b/bin/fetch-movie-data.php old mode 100644 new mode 100755 similarity index 50% rename from bin/fetch_movie_data.php rename to bin/fetch-movie-data.php index deef0a1..5781186 --- a/bin/fetch_movie_data.php +++ b/bin/fetch-movie-data.php @@ -4,6 +4,10 @@ * This script fetch data from IMDb for all movies currently in our database and save it. * It will overwrite existing data (update). */ -require_once __DIR__ . '/search_movie_source.php'; -fetchMovieData(null, 3); +use Application\Service\Processor; + +require_once __DIR__ . '/../public/index.php'; + +$processor = new Processor(); +$processor->fetchMovieData(null, 3); diff --git a/bin/insert_test_data.php b/bin/insert_test_data.php deleted file mode 100644 index 7210b65..0000000 --- a/bin/insert_test_data.php +++ /dev/null @@ -1,48 +0,0 @@ -beginTransaction(); - -try { - // Insert users - $user1 = \mQueue\Model\UserMapper::getDbTable()->createRow(); - $user1->nickname = 'user1'; - $user1->email = 'user1@mail.com'; - $user1->password = sha1('user1'); - $user1->save(); - - $user2 = \mQueue\Model\UserMapper::getDbTable()->createRow(); - $user2->nickname = 'user2'; - $user2->email = 'user2@mail.com'; - $user2->password = sha1('user2'); - $user2->save(); - - // Insert movies - $movie1 = \mQueue\Model\MovieMapper::getDbTable()->createRow(); - $movie1->setId('0082198'); - $movie1->save(); - - $movie2 = \mQueue\Model\MovieMapper::getDbTable()->createRow(); - $movie2->setId('0095016'); - $movie2->save(); - - $movie3 = \mQueue\Model\MovieMapper::getDbTable()->createRow(); - $movie3->setId('0096446'); - $movie3->save(); - - $db->commit(); -} catch (Exception $e) { - $db->rollBack(); - echo 'FATAL: test data insertion cancelled' . PHP_EOL; - - throw $e; -} - -// Insert statuses -$movie1->setStatus($user1, \mQueue\Model\Status::Favorite); -$movie2->setStatus($user1, \mQueue\Model\Status::Excellent); -$movie3->setStatus($user1, \mQueue\Model\Status::Ok); -$movie1->setStatus($user2, \mQueue\Model\Status::Bad); -$movie2->setStatus($user2, \mQueue\Model\Status::Need); diff --git a/bin/migrate.sql b/bin/migrate.sql new file mode 100644 index 0000000..0d4a202 --- /dev/null +++ b/bin/migrate.sql @@ -0,0 +1,17 @@ +DROP TABLE setting; +DROP TABLE setting; +ALTER TABLE movie CHANGE dateUpdate date_update DATETIME NOT NULL, CHANGE searchCount search_count SMALLINT UNSIGNED DEFAULT 0 NOT NULL, CHANGE id id INT AUTO_INCREMENT NOT NULL, CHANGE title title VARCHAR(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci NOT NULL, CHANGE source source VARCHAR(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci DEFAULT NULL, ADD start_year SMALLINT UNSIGNED DEFAULT NULL AFTER title, ADD dateSearch date_search DATETIME DEFAULT NULL; +UPDATE movie SET start_year = YEAR(releaseDate); +ALTER TABLE movie DROP dateRelease; +ALTER TABLE status DROP FOREIGN KEY `status_ibfk_1`; +ALTER TABLE status DROP FOREIGN KEY `status_ibfk_2`; +DROP INDEX idMovie ON status; +DROP INDEX idUser ON status; +ALTER TABLE status CHANGE dateUpdate date_update DATETIME NOT NULL, CHANGE isLatest is_latest TINYINT DEFAULT 0 NOT NULL, CHANGE idMovie movie_id INT NOT NULL, CHANGE idUser user_id INT NOT NULL, CHANGE rating rating INT UNSIGNED DEFAULT 0 NOT NULL; +ALTER TABLE status ADD CONSTRAINT FK_7B00651C8F93B6FC FOREIGN KEY (movie_id) REFERENCES movie (id) ON DELETE CASCADE; +ALTER TABLE status ADD CONSTRAINT FK_7B00651CA76ED395 FOREIGN KEY (user_id) REFERENCES user (id) ON DELETE CASCADE; +CREATE INDEX IDX_7B00651C8F93B6FC ON status (movie_id); +CREATE INDEX IDX_7B00651CA76ED395 ON status (user_id); +ALTER TABLE user CHANGE dateUpdate date_update DATETIME NOT NULL, CHANGE nickname nickname VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci NOT NULL, CHANGE email email VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci NOT NULL, CHANGE password password VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci NOT NULL; +ALTER TABLE user RENAME INDEX nickname TO UNIQ_8D93D649A188FE64; +ALTER TABLE user RENAME INDEX email TO UNIQ_8D93D649E7927C74; diff --git a/bin/pre-commit.sh b/bin/pre-commit.sh index 9fa3b5b..31dcfa8 100755 --- a/bin/pre-commit.sh +++ b/bin/pre-commit.sh @@ -14,7 +14,7 @@ if [ "$files" != "" ]; then done # Run php-cs-fixer validation before commit - echo "$files" | xargs php7.2 ./vendor/bin/php-cs-fixer fix --diff --config .php-cs-fixer.dist.php + echo "$files" | PHP_CS_FIXER_IGNORE_ENV=1 xargs php8.4 ./vendor/bin/php-cs-fixer fix --diff --config .php-cs-fixer.dist.php if [ $? -ne 0 ]; then pass=false fi diff --git a/bin/search-movie-source.php b/bin/search-movie-source.php new file mode 100644 index 0000000..61bf24d --- /dev/null +++ b/bin/search-movie-source.php @@ -0,0 +1,17 @@ +getRepository(Movie::class)->deleteObsoleteSources(); + +$processor = new Processor(); +$processor->fetchMovieData(20, 1 * 60); +$processor->searchSource(); diff --git a/bin/search_movie_source.php b/bin/search_movie_source.php deleted file mode 100644 index 3cd69dc..0000000 --- a/bin/search_movie_source.php +++ /dev/null @@ -1,88 +0,0 @@ -count(); - $count = 0; - foreach ($movies as $movie) { - echo '[' . str_pad(++$count, 5, ' ', STR_PAD_LEFT) . '/' . str_pad($total, 5, ' ', STR_PAD_LEFT) . '] ' . $movie->getImdbUrl() . "\t"; - flush(); - - $func($movie); - - echo "\n"; - - sleep($seconds); - } - - return $total; -} - -/** - * Search source. - */ -function searchSource(): void -{ - $movies = \mQueue\Model\MovieMapper::findAllForSearch(); - $searcher = function (mQueue\Model\Movie $movie): void { - $searchEngine = new mQueue\Service\SearchEngine(); - - $movie->fetchData(); // Refresh movie data to be sure we have latest available title - $title = $movie->getTitle(); - $data = $searchEngine->search($title); - $scores = $searchEngine->computeScores($title, $data); - - $best = @reset($scores); - $movie->setSource($best); - $movie->save(); - - echo $movie->source ?: '[source not found]'; - }; - - // 5 minutes pause between search, not to stress third-party servers - $total = movieProcessor($searcher, $movies, 5 * 60); - - echo $total . " movie sources updated in database\n"; -} - -/** - * Fetch movie data for those that need it. - * - * @param int $limit - * @param int $sleep seconds to sleep between each movie - */ -function fetchMovieData($limit = null, $sleep = 0): void -{ - $movies = \mQueue\Model\MovieMapper::findAllForFetching($limit); - $fetcher = function (mQueue\Model\Movie $movie): void { - $movie->fetchData(); - echo $movie->title; - }; - - $total = movieProcessor($fetcher, $movies, $sleep); - - echo $total . " movies updated in database\n"; -} - -// we only do things if this file were NOT included (otherwise, the file was included to access misc functions) -if (realpath(__FILE__) == realpath($_SERVER['SCRIPT_FILENAME'])) { - // Clean up obsolete sources - \mQueue\Model\MovieMapper::deleteObsoleteSources(); - - // Fetch movie data to update title and release date - fetchMovieData(20, 1 * 60); - - // Search source for movies with release date - searchSource(); -} diff --git a/bin/sql/version.0.sql b/bin/sql/version.0.sql deleted file mode 100644 index a9e936c..0000000 --- a/bin/sql/version.0.sql +++ /dev/null @@ -1,80 +0,0 @@ --- phpMyAdmin SQL Dump --- version 3.1.1 --- http://www.phpmyadmin.net --- --- Host: localhost --- Generation Time: Feb 06, 2010 at 06:51 PM --- Server version: 5.0.75 --- PHP Version: 5.2.6-3ubuntu4.4 - -SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO"; - --- --- Database: `mqueue` --- - --- -------------------------------------------------------- - --- --- Table structure for table `movie` --- - -CREATE TABLE IF NOT EXISTS `movie` ( - `id` varchar(8) collate utf8_bin NOT NULL, - `dateUpdate` timestamp NOT NULL default CURRENT_TIMESTAMP, - `title` varchar(512) collate utf8_bin NOT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin; - --- -------------------------------------------------------- - --- --- Table structure for table `status` --- - -CREATE TABLE IF NOT EXISTS `status` ( - `id` int(11) NOT NULL auto_increment, - `idUser` int(11) NOT NULL, - `idMovie` varchar(8) collate utf8_bin NOT NULL, - `rating` tinyint(4) NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `unique_status` (`idUser`,`idMovie`), - KEY `idUser` (`idUser`), - KEY `idMovie` (`idMovie`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin AUTO_INCREMENT=427 ; - --- -------------------------------------------------------- - --- --- Table structure for table `user` --- - -CREATE TABLE IF NOT EXISTS `user` ( - `id` int(11) NOT NULL auto_increment, - `nickname` varchar(32) collate utf8_bin NOT NULL, - `email` varchar(100) collate utf8_bin NOT NULL, - `password` varchar(32) collate utf8_bin NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `nickname` (`nickname`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin AUTO_INCREMENT=5 ; - --- --- Table structure for table `setting` --- - -CREATE TABLE IF NOT EXISTS `setting` ( - `id` varchar(64) COLLATE utf8_bin NOT NULL, - `value` longtext COLLATE utf8_bin NOT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin; - --- --- Constraints for dumped tables --- - --- --- Constraints for table `status` --- -ALTER TABLE `status` - ADD CONSTRAINT `status_ibfk_1` FOREIGN KEY (`idUser`) REFERENCES `user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, - ADD CONSTRAINT `status_ibfk_2` FOREIGN KEY (`idMovie`) REFERENCES `movie` (`id`) ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/bin/sql/version.1.sql b/bin/sql/version.1.sql deleted file mode 100644 index 98d80ef..0000000 --- a/bin/sql/version.1.sql +++ /dev/null @@ -1,14 +0,0 @@ - --- Add timestamp fields -ALTER TABLE `status` ADD `dateUpdate` TIMESTAMP ON UPDATE CURRENT_TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP AFTER `rating`; -UPDATE `status` SET `dateUpdate` = '2010-01-01 00:00:00' WHERE `dateUpdate` = '0000-00-00 00:00:00'; - -ALTER TABLE `user` ADD `dateUpdate` TIMESTAMP ON UPDATE CURRENT_TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP AFTER `password`; -UPDATE `user` SET `dateUpdate` = '2010-01-01 00:00:00' WHERE `dateUpdate` = '0000-00-00 00:00:00'; - -ALTER TABLE `movie` CHANGE `dateUpdate` `dateUpdate` TIMESTAMP ON UPDATE CURRENT_TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP; - - --- Encrypt existing passwords -ALTER TABLE `user` CHANGE `password` `password` VARCHAR( 40 ) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL; -UPDATE `user` AS u1, `user` AS u2 SET u1.password = SHA1( u2.password ) WHERE u1.id = u2.id; diff --git a/bin/sql/version.2.sql b/bin/sql/version.2.sql deleted file mode 100644 index 3c38006..0000000 --- a/bin/sql/version.2.sql +++ /dev/null @@ -1,4 +0,0 @@ --- Change collation to have case insensitive search -ALTER TABLE `movie` CHANGE `title` `title` VARCHAR( 512 ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL; -ALTER TABLE `user` CHANGE `nickname` `nickname` VARCHAR( 32 ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL; -ALTER TABLE `user` CHANGE `email` `email` VARCHAR( 100 ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL; diff --git a/bin/sql/version.3.sql b/bin/sql/version.3.sql deleted file mode 100644 index 09aceec..0000000 --- a/bin/sql/version.3.sql +++ /dev/null @@ -1,2 +0,0 @@ --- Make email unique to prevent duplicate registration -ALTER TABLE `user` ADD UNIQUE (`email`); diff --git a/bin/sql/version.4.sql b/bin/sql/version.4.sql deleted file mode 100644 index a74b8c5..0000000 --- a/bin/sql/version.4.sql +++ /dev/null @@ -1,4 +0,0 @@ --- Allow to record the whole history of statuses, not only the last one -ALTER TABLE `status` DROP INDEX `unique_status`; -ALTER TABLE `status` ADD `isLatest` BOOLEAN NOT NULL AFTER `rating`; -UPDATE `status` SET `isLatest` = 1, `dateUpdate` = `dateUpdate`; diff --git a/bin/sql/version.5.sql b/bin/sql/version.5.sql deleted file mode 100644 index 3ddef53..0000000 --- a/bin/sql/version.5.sql +++ /dev/null @@ -1,8 +0,0 @@ --- Store source for movies -ALTER TABLE `movie` -ADD `dateSearch` DATETIME NULL DEFAULT NULL, -ADD `searchCount` SMALLINT UNSIGNED NOT NULL, -ADD `identity` SMALLINT UNSIGNED NOT NULL, -ADD `quality` SMALLINT UNSIGNED NOT NULL, -ADD `score` SMALLINT UNSIGNED NOT NULL, -ADD `source` VARCHAR( 1024 ) CHARACTER SET utf8 COLLATE utf8_general_ci NULL; diff --git a/bin/sql/version.6.sql b/bin/sql/version.6.sql deleted file mode 100644 index 4a08dde..0000000 --- a/bin/sql/version.6.sql +++ /dev/null @@ -1,5 +0,0 @@ --- Will manually update dateUpdate since now -ALTER TABLE `movie` CHANGE `dateUpdate` `dateUpdate` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP; - --- Store movie release date -ALTER TABLE `movie` ADD `dateRelease` DATE NULL DEFAULT NULL AFTER `title`; diff --git a/bin/sql/version.7.sql b/bin/sql/version.7.sql deleted file mode 100644 index 761855e..0000000 --- a/bin/sql/version.7.sql +++ /dev/null @@ -1,6 +0,0 @@ --- Support ID up to 8 chars -ALTER TABLE `movie` CHANGE `id` `id` VARCHAR(8) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL; -ALTER TABLE `status` CHANGE `idMovie` `idMovie` VARCHAR(8) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL; - --- Remove suffix -UPDATE `movie` SET title = REPLACE(title, ' - IMDb', ''); diff --git a/bin/sql/version.8.sql b/bin/sql/version.8.sql deleted file mode 100644 index 797102f..0000000 --- a/bin/sql/version.8.sql +++ /dev/null @@ -1,7 +0,0 @@ --- Support ID with any digits -ALTER TABLE `status` - DROP FOREIGN KEY `status_ibfk_2` ; -ALTER TABLE `status` CHANGE `idMovie` `idMovie` int(11) NOT NULL; -ALTER TABLE `movie` CHANGE `id` `id` int(11) NOT NULL; -ALTER TABLE `status` - ADD CONSTRAINT `status_ibfk_2` FOREIGN KEY (`idMovie`) REFERENCES `movie` (`id`) ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/bin/sql/version.9.sql b/bin/sql/version.9.sql deleted file mode 100644 index b13c8c7..0000000 --- a/bin/sql/version.9.sql +++ /dev/null @@ -1,5 +0,0 @@ -ALTER TABLE `movie` - CHANGE `searchCount` `searchCount` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0', - CHANGE `identity` `identity` SMALLINT(5) UNSIGNED NOT NULL DEFAULT '0', - CHANGE `quality` `quality` SMALLINT(5) UNSIGNED NOT NULL DEFAULT '0', - CHANGE `score` `score` SMALLINT(5) UNSIGNED NOT NULL DEFAULT '0'; diff --git a/bin/update_database.php b/bin/update_database.php deleted file mode 100644 index 7195c23..0000000 --- a/bin/update_database.php +++ /dev/null @@ -1,135 +0,0 @@ -read())) { - if (preg_match('/^version\.(\d+)\.sql$/i', $entry, $a)) { - if ((int) $a[1] > $lastVersion) { - $lastVersion = (int) $a[1]; - } - } - } - $d->close(); - - return $lastVersion; -} - -/** - * Returns the whole SQL (enclosed in transaction) needed to update from - * specified version to specified target version. - * - * @param int $currentVersion the version currently found in database - * @param int $targetVersion the target version to reach wich patches - * - * @return string the SQL - */ -function buildSQL($currentVersion, $targetVersion) -{ - global $sqlPath; - - if ($currentVersion > $targetVersion) { - throw new Exception('Cannot downgrade versions. Target version must be higher than current version'); - } - - $sql = "START TRANSACTION;\n"; - - $missingVersions = []; - for ($v = $currentVersion + 1; $v <= $targetVersion; ++$v) { - $file = $sqlPath . 'version.' . $v . '.sql'; - if (is_file($file)) { - $sql .= "\n-- -------- VERSION $v BEGINS ------------------------\n"; - $sql .= file_get_contents($file); - $sql .= "\n-- -------- VERSION $v ENDS --------------------------\n"; - } else { - $missingVersions[] = $v; - } - } - - $sql .= "\nCOMMIT;\n"; - - if (count($missingVersions)) { - throw new Exception('Missing SQL file for versions: ' . implode(',', $missingVersions)); - } - - return $sql; -} - -/** - * Executes a batch of SQL commands. - * (This is a workaround to Zend limitation to have only one command at once). - * - * @param string $sql to be executed - */ -function executeBatchSql($sql): void -{ - $affectedRows = 0; - $queries = preg_split("/;+(?=([^'|^\\\\']*['|\\\\'][^'|^\\\\']*['|\\\\'])*[^'|^\\\\']*[^'|^\\\\']$)/", $sql); - foreach ($queries as $query) { - if (mb_strlen(trim($query)) > 0) { - try { - $result = Zend_Registry::get('db')->query($query); - } catch (\Exception $exception) { - echo 'FAILED QUERY: ' . $query . PHP_EOL; - - throw $exception; - } - - $affectedRows += $result->rowCount(); - } - } - - echo "\n" . 'affected rows count: ' . $affectedRows . "\n"; -} - -/** - * Do the actual update. - */ -function doUpdate(): void -{ - global $settingName; - - try { - $currentVersion = (int) \mQueue\Model\Setting::get($settingName, 0)->value; - } catch (Exception $e) { - if (mb_strpos($e->getMessage(), 'SQLSTATE[42S02]') >= 0) { - $currentVersion = -1; - } else { - exit('Caught exception: ' . $e->getMessage() . "\n"); - } - } - - $targetVersion = getPatchVersion(); - - echo 'current version is: ' . $currentVersion . "\n"; - echo 'target version is : ' . $targetVersion . "\n"; - - if ($currentVersion == $targetVersion) { - echo "already up-to-date\n"; - - return; - } - - $sql = buildSQL($currentVersion, $targetVersion); - echo $sql; - echo "\n_________________________________________________\n"; - echo "updating...\n"; - executeBatchSql($sql); - \mQueue\Model\Setting::set($settingName, $targetVersion); - - echo "\nsuccessful update to version $targetVersion !\n"; -} - -doUpdate(); diff --git a/composer.json b/composer.json index 516b45a..71f845c 100644 --- a/composer.json +++ b/composer.json @@ -8,36 +8,83 @@ "email": "adrien.crivelli@gmail.com" } ], + "config": { + "sort-packages": true + }, + "scripts": { + "development-disable": "laminas-development-mode disable", + "development-enable": "laminas-development-mode enable", + "development-status": "laminas-development-mode status", + "mezzio": "laminas --ansi", + "check": [ + "PHP_CS_FIXER_IGNORE_ENV=1 php-cs-fixer fix --ansi --dry-run --diff", + "phpstan analyse --ansi", + "@test" + ], + "fix": [ + "PHP_CS_FIXER_IGNORE_ENV=1 php-cs-fixer fix --ansi" + ], + "clear-config-cache": "php bin/clear-config-cache.php", + "cs-check": "phpcs", + "cs-fix": "phpcbf", + "serve": [ + "Composer\\Config::disableProcessTimeout", + "php -S 0.0.0.0:8080 -t public/" + ], + "static-analysis": "psalm --stats", + "static-analysis-update-baseline": "psalm --stats --update-baseline", + "test": "phpunit --colors=always", + "test-coverage": "phpunit --colors=always --coverage-clover clover.xml" + }, "autoload": { "psr-4": { - "mQueue\\": "application/mQueue/" + "Application\\": "src/Application/" }, - "classmap": [ - "application/controllers/", - "application/Bootstrap.php" + "files": [ + "src/functions.php" ] }, "autoload-dev": { "psr-4": { - "mQueueTest\\": "tests/mQueueTest" + "ApplicationTest\\": "tests/ApplicationTest/" } }, - "config": { - "sort-packages": true - }, "require": { - "php": "~7.2.0", + "php": "^8.4", "ext-curl": "*", "ext-dom": "*", "ext-iconv": "*", - "ext-json": "*", + "ext-intl": "*", "ext-posix": "*", - "zendframework/zendframework1": "^1.12" + "beberlei/doctrineextensions": "^1.5", + "cakephp/chronos": "^3.5", + "doctrine/migrations": "^3.9", + "doctrine/orm": "^3.6", + "laminas/laminas-captcha": "^2.20", + "laminas/laminas-config-aggregator": "^1.19", + "laminas/laminas-diactoros": "^3.8", + "laminas/laminas-feed": "^2.26", + "laminas/laminas-form": "^3.24", + "laminas/laminas-i18n": "^2.32", + "laminas/laminas-paginator": "^2.22", + "laminas/laminas-servicemanager": "^3.24", + "laminas/laminas-stdlib": "^3.21", + "mezzio/mezzio": "^3.23", + "mezzio/mezzio-flash": "^1.10", + "mezzio/mezzio-helpers": "^5.20", + "mezzio/mezzio-laminasrouter": "^3.12", + "mezzio/mezzio-laminasviewrenderer": "^2.19", + "mezzio/mezzio-session": "^1.17", + "mezzio/mezzio-session-ext": "^1.21", + "mimmi20/mezzio-navigation-laminasviewrenderer": "^7.0", + "roave/psr-container-doctrine": "^6.1" }, "require-dev": { "friendsofphp/php-cs-fixer": "@stable", - "pcov/clobber": "@stable", - "phpstan/phpstan": "@stable", - "phpunit/phpunit": "^5.7" + "laminas/laminas-development-mode": "@stable", + "mezzio/mezzio-tooling": "@stable", + "phpstan/phpstan-doctrine": "@stable", + "phpstan/phpstan-phpunit": "@stable", + "phpunit/phpunit": "@stable" } } diff --git a/composer.lock b/composer.lock index c181188..b77d789 100644 --- a/composer.lock +++ b/composer.lock @@ -4,163 +4,148 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "60eb9469ae1cef0e0ccade08994738b7", + "content-hash": "12b81c549f6749061ddc21ef9cef9328", "packages": [ { - "name": "zendframework/zendframework1", - "version": "1.12.20", + "name": "beberlei/doctrineextensions", + "version": "v1.5.0", "source": { "type": "git", - "url": "https://github.com/zendframework/zf1.git", - "reference": "737ef159654fbbef37cf9af742b2c8f9690c2ece" + "url": "https://github.com/beberlei/DoctrineExtensions.git", + "reference": "281f1650641c2f438b0a54d8eaa7ba50ac7e3eb6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zf1/zipball/737ef159654fbbef37cf9af742b2c8f9690c2ece", - "reference": "737ef159654fbbef37cf9af742b2c8f9690c2ece", + "url": "https://api.github.com/repos/beberlei/DoctrineExtensions/zipball/281f1650641c2f438b0a54d8eaa7ba50ac7e3eb6", + "reference": "281f1650641c2f438b0a54d8eaa7ba50ac7e3eb6", "shasum": "" }, "require": { - "php": ">=5.2.11" + "doctrine/orm": "^2.19 || ^3.0", + "php": "^7.2 || ^8.0" }, "require-dev": { - "phpunit/dbunit": "1.3.*", - "phpunit/phpunit": "3.7.*" + "doctrine/annotations": "^1.14 || ^2", + "doctrine/coding-standard": "^9.0.2 || ^12.0", + "nesbot/carbon": "^2.72 || ^3", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^8.5 || ^9.6", + "squizlabs/php_codesniffer": "^3.8", + "symfony/cache": "^5.4 || ^6.4 || ^7.0", + "symfony/yaml": "^5.4 || ^6.4 || ^7.0", + "vimeo/psalm": "^3.18 || ^5.22", + "zf1/zend-date": "^1.12", + "zf1/zend-registry": "^1.12" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.12.x-dev" - } - }, "autoload": { - "psr-0": { - "Zend_": "library/" + "psr-4": { + "DoctrineExtensions\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "library/" - ], "license": [ "BSD-3-Clause" ], - "description": "Zend Framework 1", - "homepage": "http://framework.zend.com/", + "authors": [ + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Steve Lacey", + "email": "steve@steve.ly" + } + ], + "description": "A set of extensions to Doctrine 2 that add support for additional query functions available in MySQL, Oracle, PostgreSQL and SQLite.", "keywords": [ - "ZF1", - "framework" + "database", + "doctrine", + "orm" ], "support": { - "issues": "https://github.com/zendframework/zf1/issues", - "source": "https://github.com/zendframework/zf1/tree/release-1.12.20" + "source": "https://github.com/beberlei/DoctrineExtensions/tree/v1.5.0" }, - "abandoned": "zendframework/zendframework", - "time": "2016-09-08T14:50:34+00:00" - } - ], - "packages-dev": [ + "time": "2024-03-03T17:55:15+00:00" + }, { - "name": "composer/pcre", - "version": "1.0.1", + "name": "brick/varexporter", + "version": "0.6.0", "source": { "type": "git", - "url": "https://github.com/composer/pcre.git", - "reference": "67a32d7d6f9f560b726ab25a061b38ff3a80c560" + "url": "https://github.com/brick/varexporter.git", + "reference": "af98bfc2b702a312abbcaff37656dbe419cec5bc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/pcre/zipball/67a32d7d6f9f560b726ab25a061b38ff3a80c560", - "reference": "67a32d7d6f9f560b726ab25a061b38ff3a80c560", + "url": "https://api.github.com/repos/brick/varexporter/zipball/af98bfc2b702a312abbcaff37656dbe419cec5bc", + "reference": "af98bfc2b702a312abbcaff37656dbe419cec5bc", "shasum": "" }, "require": { - "php": "^5.3.2 || ^7.0 || ^8.0" + "nikic/php-parser": "^5.0", + "php": "^8.1" }, "require-dev": { - "phpstan/phpstan": "^1.3", - "phpstan/phpstan-strict-rules": "^1.1", - "symfony/phpunit-bridge": "^4.2 || ^5" + "php-coveralls/php-coveralls": "^2.2", + "phpunit/phpunit": "^10.5", + "vimeo/psalm": "6.8.4" }, "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.x-dev" - } - }, "autoload": { "psr-4": { - "Composer\\Pcre\\": "src" + "Brick\\VarExporter\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "authors": [ - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - } - ], - "description": "PCRE wrapping library that offers type-safe preg_* replacements.", + "description": "A powerful alternative to var_export(), which can export closures and objects without __set_state()", "keywords": [ - "PCRE", - "preg", - "regex", - "regular expression" + "var_export" ], "support": { - "issues": "https://github.com/composer/pcre/issues", - "source": "https://github.com/composer/pcre/tree/1.0.1" + "issues": "https://github.com/brick/varexporter/issues", + "source": "https://github.com/brick/varexporter/tree/0.6.0" }, "funding": [ { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", + "url": "https://github.com/BenMorel", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" } ], - "time": "2022-01-21T20:24:37+00:00" + "time": "2025-02-20T17:42:39+00:00" }, { - "name": "composer/semver", - "version": "3.4.3", + "name": "cakephp/chronos", + "version": "3.5.0", "source": { "type": "git", - "url": "https://github.com/composer/semver.git", - "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12" + "url": "https://github.com/cakephp/chronos.git", + "reference": "e6e777b534244911566face8a5dbdbd7f7bda5a6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/4313d26ada5e0c4edfbd1dc481a92ff7bff91f12", - "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12", + "url": "https://api.github.com/repos/cakephp/chronos/zipball/e6e777b534244911566face8a5dbdbd7f7bda5a6", + "reference": "e6e777b534244911566face8a5dbdbd7f7bda5a6", "shasum": "" }, "require": { - "php": "^5.3.2 || ^7.0 || ^8.0" + "php": ">=8.1", + "psr/clock": "^1.0" + }, + "provide": { + "psr/clock-implementation": "1.0" }, "require-dev": { - "phpstan/phpstan": "^1.11", - "symfony/phpunit-bridge": "^3 || ^7" + "cakephp/cakephp-codesniffer": "^5.0", + "phpunit/phpunit": "^10.5.58 || ^11.5.3 || ^12.1.3" }, "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.x-dev" - } - }, "autoload": { "psr-4": { - "Composer\\Semver\\": "src" + "Cake\\Chronos\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -169,77 +154,66 @@ ], "authors": [ { - "name": "Nils Adermann", - "email": "naderman@naderman.de", - "homepage": "http://www.naderman.de" - }, - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" + "name": "Brian Nesbitt", + "email": "brian@nesbot.com", + "homepage": "http://nesbot.com" }, { - "name": "Rob Bast", - "email": "rob.bast@gmail.com", - "homepage": "http://robbast.nl" + "name": "The CakePHP Team", + "homepage": "https://cakephp.org" } ], - "description": "Semver library that offers utilities, version constraint parsing and validation.", + "description": "A simple API extension for DateTime.", + "homepage": "https://cakephp.org", "keywords": [ - "semantic", - "semver", - "validation", - "versioning" + "date", + "datetime", + "time" ], "support": { - "irc": "ircs://irc.libera.chat:6697/composer", - "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/tree/3.4.3" + "issues": "https://github.com/cakephp/chronos/issues", + "source": "https://github.com/cakephp/chronos" }, - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" - } - ], - "time": "2024-09-19T14:15:21+00:00" + "time": "2026-04-10T02:50:39+00:00" }, { - "name": "composer/xdebug-handler", - "version": "2.0.5", + "name": "dflydev/fig-cookies", + "version": "v3.2.0", "source": { "type": "git", - "url": "https://github.com/composer/xdebug-handler.git", - "reference": "9e36aeed4616366d2b690bdce11f71e9178c579a" + "url": "https://github.com/dflydev/dflydev-fig-cookies.git", + "reference": "f9c63878e75483800477db4f897237b36556617b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/9e36aeed4616366d2b690bdce11f71e9178c579a", - "reference": "9e36aeed4616366d2b690bdce11f71e9178c579a", + "url": "https://api.github.com/repos/dflydev/dflydev-fig-cookies/zipball/f9c63878e75483800477db4f897237b36556617b", + "reference": "f9c63878e75483800477db4f897237b36556617b", "shasum": "" }, "require": { - "composer/pcre": "^1", - "php": "^5.3.2 || ^7.0 || ^8.0", - "psr/log": "^1 || ^2 || ^3" + "ext-pcre": "*", + "php": "^7.2 || ^8.0", + "psr/http-message": "^1.0.1 || ^2" }, "require-dev": { - "phpstan/phpstan": "^1.0", - "phpstan/phpstan-strict-rules": "^1.1", - "symfony/phpunit-bridge": "^4.2 || ^5.0 || ^6.0" + "doctrine/coding-standard": "^8", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^0.12", + "phpstan/phpstan-phpunit": "^0.12.16", + "phpunit/phpunit": "^7.2.6 || ^9", + "scrutinizer/ocular": "^1.8", + "squizlabs/php_codesniffer": "^3.3", + "vimeo/psalm": "^4.4" }, "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0.x-dev" + } + }, "autoload": { "psr-4": { - "Composer\\XdebugHandler\\": "src" + "Dflydev\\FigCookies\\": "src/Dflydev/FigCookies" } }, "notification-url": "https://packagist.org/downloads/", @@ -248,71 +222,52 @@ ], "authors": [ { - "name": "John Stevenson", - "email": "john-stevenson@blueyonder.co.uk" + "name": "Beau Simensen", + "email": "beau@dflydev.com" } ], - "description": "Restarts a process without Xdebug.", + "description": "Cookies for PSR-7 HTTP Message Interface.", "keywords": [ - "Xdebug", - "performance" + "cookies", + "psr-7", + "psr7" ], "support": { - "irc": "irc://irc.freenode.org/composer", - "issues": "https://github.com/composer/xdebug-handler/issues", - "source": "https://github.com/composer/xdebug-handler/tree/2.0.5" + "issues": "https://github.com/dflydev/dflydev-fig-cookies/issues", + "source": "https://github.com/dflydev/dflydev-fig-cookies/tree/v3.2.0" }, - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" - } - ], - "time": "2022-02-24T20:20:32+00:00" + "time": "2025-09-03T20:01:04+00:00" }, { - "name": "doctrine/annotations", - "version": "1.14.4", + "name": "doctrine/collections", + "version": "2.6.0", "source": { "type": "git", - "url": "https://github.com/doctrine/annotations.git", - "reference": "253dca476f70808a5aeed3a47cc2cc88c5cab915" + "url": "https://github.com/doctrine/collections.git", + "reference": "7713da39d8e237f28411d6a616a3dce5e20d5de2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/253dca476f70808a5aeed3a47cc2cc88c5cab915", - "reference": "253dca476f70808a5aeed3a47cc2cc88c5cab915", + "url": "https://api.github.com/repos/doctrine/collections/zipball/7713da39d8e237f28411d6a616a3dce5e20d5de2", + "reference": "7713da39d8e237f28411d6a616a3dce5e20d5de2", "shasum": "" }, "require": { - "doctrine/lexer": "^1 || ^2", - "ext-tokenizer": "*", - "php": "^7.1 || ^8.0", - "psr/cache": "^1 || ^2 || ^3" + "doctrine/deprecations": "^1", + "php": "^8.1", + "symfony/polyfill-php84": "^1.30" }, "require-dev": { - "doctrine/cache": "^1.11 || ^2.0", - "doctrine/coding-standard": "^9 || ^12", - "phpstan/phpstan": "~1.4.10 || ^1.10.28", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "symfony/cache": "^4.4 || ^5.4 || ^6.4 || ^7", - "vimeo/psalm": "^4.30 || ^5.14" - }, - "suggest": { - "php": "PHP 8.0 or higher comes with attributes, a native replacement for annotations" + "doctrine/coding-standard": "^14", + "ext-json": "*", + "phpstan/phpstan": "^2.1.30", + "phpstan/phpstan-phpunit": "^2.0.7", + "phpunit/phpunit": "^10.5.58 || ^11.5.42 || ^12.4" }, "type": "library", "autoload": { "psr-4": { - "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" + "Doctrine\\Common\\Collections\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -341,95 +296,66 @@ "email": "schmittjoh@gmail.com" } ], - "description": "Docblock Annotations Parser", - "homepage": "https://www.doctrine-project.org/projects/annotations.html", + "description": "PHP Doctrine Collections library that adds additional functionality on top of PHP arrays.", + "homepage": "https://www.doctrine-project.org/projects/collections.html", "keywords": [ - "annotations", - "docblock", - "parser" + "array", + "collections", + "iterators", + "php" ], "support": { - "issues": "https://github.com/doctrine/annotations/issues", - "source": "https://github.com/doctrine/annotations/tree/1.14.4" - }, - "time": "2024-09-05T10:15:52+00:00" - }, - { - "name": "doctrine/deprecations", - "version": "1.1.4", - "source": { - "type": "git", - "url": "https://github.com/doctrine/deprecations.git", - "reference": "31610dbb31faa98e6b5447b62340826f54fbc4e9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/31610dbb31faa98e6b5447b62340826f54fbc4e9", - "reference": "31610dbb31faa98e6b5447b62340826f54fbc4e9", - "shasum": "" + "issues": "https://github.com/doctrine/collections/issues", + "source": "https://github.com/doctrine/collections/tree/2.6.0" }, - "require": { - "php": "^7.1 || ^8.0" - }, - "require-dev": { - "doctrine/coding-standard": "^9 || ^12", - "phpstan/phpstan": "1.4.10 || 2.0.3", - "phpstan/phpstan-phpunit": "^1.0 || ^2", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "psr/log": "^1 || ^2 || ^3" - }, - "suggest": { - "psr/log": "Allows logging deprecations via PSR-3 logger implementation" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Deprecations\\": "src" + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fcollections", + "type": "tidelift" } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" ], - "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", - "homepage": "https://www.doctrine-project.org/", - "support": { - "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/1.1.4" - }, - "time": "2024-12-07T21:18:45+00:00" + "time": "2026-01-15T10:01:58+00:00" }, { - "name": "doctrine/instantiator", - "version": "1.5.0", + "name": "doctrine/common", + "version": "3.5.0", "source": { "type": "git", - "url": "https://github.com/doctrine/instantiator.git", - "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b" + "url": "https://github.com/doctrine/common.git", + "reference": "d9ea4a54ca2586db781f0265d36bea731ac66ec5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b", - "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b", + "url": "https://api.github.com/repos/doctrine/common/zipball/d9ea4a54ca2586db781f0265d36bea731ac66ec5", + "reference": "d9ea4a54ca2586db781f0265d36bea731ac66ec5", "shasum": "" }, "require": { + "doctrine/persistence": "^2.0 || ^3.0 || ^4.0", "php": "^7.1 || ^8.0" }, "require-dev": { - "doctrine/coding-standard": "^9 || ^11", - "ext-pdo": "*", - "ext-phar": "*", - "phpbench/phpbench": "^0.16 || ^1", - "phpstan/phpstan": "^1.4", + "doctrine/coding-standard": "^9.0 || ^10.0", + "doctrine/collections": "^1", + "phpstan/phpstan": "^1.4.1", "phpstan/phpstan-phpunit": "^1", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "vimeo/psalm": "^4.30 || ^5.4" + "phpunit/phpunit": "^7.5.20 || ^8.5 || ^9.0", + "squizlabs/php_codesniffer": "^3.0", + "symfony/phpunit-bridge": "^6.1", + "vimeo/psalm": "^4.4" }, "type": "library", "autoload": { "psr-4": { - "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + "Doctrine\\Common\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -437,21 +363,41 @@ "MIT" ], "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + }, { "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "homepage": "https://ocramius.github.io/" + "email": "ocramius@gmail.com" } ], - "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://www.doctrine-project.org/projects/instantiator.html", + "description": "PHP Doctrine Common project is a library that provides additional functionality that other Doctrine projects depend on such as better reflection support, proxies and much more.", + "homepage": "https://www.doctrine-project.org/projects/common.html", "keywords": [ - "constructor", - "instantiate" + "common", + "doctrine", + "php" ], "support": { - "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/1.5.0" + "issues": "https://github.com/doctrine/common/issues", + "source": "https://github.com/doctrine/common/tree/3.5.0" }, "funding": [ { @@ -463,41 +409,52 @@ "type": "patreon" }, { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fcommon", "type": "tidelift" } ], - "time": "2022-12-30T00:15:36+00:00" + "time": "2025-01-01T22:12:03+00:00" }, { - "name": "doctrine/lexer", - "version": "2.1.1", + "name": "doctrine/dbal", + "version": "4.4.3", "source": { "type": "git", - "url": "https://github.com/doctrine/lexer.git", - "reference": "861c870e8b75f7c8f69c146c7f89cc1c0f1b49b6" + "url": "https://github.com/doctrine/dbal.git", + "reference": "61e730f1658814821a85f2402c945f3883407dec" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/lexer/zipball/861c870e8b75f7c8f69c146c7f89cc1c0f1b49b6", - "reference": "861c870e8b75f7c8f69c146c7f89cc1c0f1b49b6", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/61e730f1658814821a85f2402c945f3883407dec", + "reference": "61e730f1658814821a85f2402c945f3883407dec", "shasum": "" }, "require": { - "doctrine/deprecations": "^1.0", - "php": "^7.1 || ^8.0" + "doctrine/deprecations": "^1.1.5", + "php": "^8.2", + "psr/cache": "^1|^2|^3", + "psr/log": "^1|^2|^3" }, "require-dev": { - "doctrine/coding-standard": "^9 || ^12", - "phpstan/phpstan": "^1.3", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6", - "psalm/plugin-phpunit": "^0.18.3", - "vimeo/psalm": "^4.11 || ^5.21" + "doctrine/coding-standard": "14.0.0", + "fig/log-test": "^1", + "jetbrains/phpstorm-stubs": "2023.2", + "phpstan/phpstan": "2.1.30", + "phpstan/phpstan-phpunit": "2.0.7", + "phpstan/phpstan-strict-rules": "^2", + "phpunit/phpunit": "11.5.50", + "slevomat/coding-standard": "8.27.1", + "squizlabs/php_codesniffer": "4.0.1", + "symfony/cache": "^6.3.8|^7.0|^8.0", + "symfony/console": "^5.4|^6.3|^7.0|^8.0" + }, + "suggest": { + "symfony/console": "For helpful console commands such as SQL execution and import of files." }, "type": "library", "autoload": { "psr-4": { - "Doctrine\\Common\\Lexer\\": "src" + "Doctrine\\DBAL\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -514,22 +471,39 @@ "email": "roman@code-factory.org" }, { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" } ], - "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", - "homepage": "https://www.doctrine-project.org/projects/lexer.html", + "description": "Powerful PHP database abstraction layer (DBAL) with many features for database schema introspection and management.", + "homepage": "https://www.doctrine-project.org/projects/dbal.html", "keywords": [ - "annotations", - "docblock", - "lexer", - "parser", - "php" + "abstraction", + "database", + "db2", + "dbal", + "mariadb", + "mssql", + "mysql", + "oci8", + "oracle", + "pdo", + "pgsql", + "postgresql", + "queryobject", + "sasql", + "sql", + "sqlite", + "sqlserver", + "sqlsrv" ], "support": { - "issues": "https://github.com/doctrine/lexer/issues", - "source": "https://github.com/doctrine/lexer/tree/2.1.1" + "issues": "https://github.com/doctrine/dbal/issues", + "source": "https://github.com/doctrine/dbal/tree/4.4.3" }, "funding": [ { @@ -541,72 +515,5749 @@ "type": "patreon" }, { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer", + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fdbal", "type": "tidelift" } ], - "time": "2024-02-05T11:35:39+00:00" + "time": "2026-03-20T08:52:12+00:00" }, { - "name": "friendsofphp/php-cs-fixer", - "version": "v3.4.0", + "name": "doctrine/deprecations", + "version": "1.1.6", "source": { "type": "git", - "url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git", - "reference": "47177af1cfb9dab5d1cc4daf91b7179c2efe7fad" + "url": "https://github.com/doctrine/deprecations.git", + "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/47177af1cfb9dab5d1cc4daf91b7179c2efe7fad", - "reference": "47177af1cfb9dab5d1cc4daf91b7179c2efe7fad", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca", + "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca", "shasum": "" }, "require": { - "composer/semver": "^3.2", - "composer/xdebug-handler": "^2.0", - "doctrine/annotations": "^1.12", - "ext-json": "*", - "ext-tokenizer": "*", - "php": "^7.2.5 || ^8.0", - "php-cs-fixer/diff": "^2.0", - "symfony/console": "^4.4.20 || ^5.1.3 || ^6.0", - "symfony/event-dispatcher": "^4.4.20 || ^5.0 || ^6.0", - "symfony/filesystem": "^4.4.20 || ^5.0 || ^6.0", - "symfony/finder": "^4.4.20 || ^5.0 || ^6.0", - "symfony/options-resolver": "^4.4.20 || ^5.0 || ^6.0", - "symfony/polyfill-mbstring": "^1.23", - "symfony/polyfill-php80": "^1.23", - "symfony/polyfill-php81": "^1.23", - "symfony/process": "^4.4.20 || ^5.0 || ^6.0", - "symfony/stopwatch": "^4.4.20 || ^5.0 || ^6.0" - }, - "require-dev": { - "justinrainbow/json-schema": "^5.2", - "keradus/cli-executor": "^1.5", - "mikey179/vfsstream": "^1.6.8", - "php-coveralls/php-coveralls": "^2.5.2", - "php-cs-fixer/accessible-object": "^1.1", - "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.2", - "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.2.1", - "phpspec/prophecy": "^1.15", - "phpspec/prophecy-phpunit": "^1.1 || ^2.0", - "phpunit/phpunit": "^8.5.21 || ^9.5", - "phpunitgoodpractices/polyfill": "^1.5", - "phpunitgoodpractices/traits": "^1.9.1", - "symfony/phpunit-bridge": "^5.2.4 || ^6.0", - "symfony/yaml": "^4.4.20 || ^5.0 || ^6.0" + "php": "^7.1 || ^8.0" + }, + "conflict": { + "phpunit/phpunit": "<=7.5 || >=14" + }, + "require-dev": { + "doctrine/coding-standard": "^9 || ^12 || ^14", + "phpstan/phpstan": "1.4.10 || 2.1.30", + "phpstan/phpstan-phpunit": "^1.0 || ^2", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12.4 || ^13.0", + "psr/log": "^1 || ^2 || ^3" }, "suggest": { - "ext-dom": "For handling output formats in XML", - "ext-mbstring": "For handling non-UTF8 characters." + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Deprecations\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", + "support": { + "issues": "https://github.com/doctrine/deprecations/issues", + "source": "https://github.com/doctrine/deprecations/tree/1.1.6" + }, + "time": "2026-02-07T07:09:04+00:00" + }, + { + "name": "doctrine/event-manager", + "version": "2.1.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/event-manager.git", + "reference": "dda33921b198841ca8dbad2eaa5d4d34769d18cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/event-manager/zipball/dda33921b198841ca8dbad2eaa5d4d34769d18cf", + "reference": "dda33921b198841ca8dbad2eaa5d4d34769d18cf", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "conflict": { + "doctrine/common": "<2.9" + }, + "require-dev": { + "doctrine/coding-standard": "^14", + "phpdocumentor/guides-cli": "^1.4", + "phpstan/phpstan": "^2.1.32", + "phpunit/phpunit": "^10.5.58" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + }, + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com" + } + ], + "description": "The Doctrine Event Manager is a simple PHP event system that was built to be used with the various Doctrine projects.", + "homepage": "https://www.doctrine-project.org/projects/event-manager.html", + "keywords": [ + "event", + "event dispatcher", + "event manager", + "event system", + "events" + ], + "support": { + "issues": "https://github.com/doctrine/event-manager/issues", + "source": "https://github.com/doctrine/event-manager/tree/2.1.1" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fevent-manager", + "type": "tidelift" + } + ], + "time": "2026-01-29T07:11:08+00:00" + }, + { + "name": "doctrine/inflector", + "version": "2.1.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/inflector.git", + "reference": "6d6c96277ea252fc1304627204c3d5e6e15faa3b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/6d6c96277ea252fc1304627204c3d5e6e15faa3b", + "reference": "6d6c96277ea252fc1304627204c3d5e6e15faa3b", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^12.0 || ^13.0", + "phpstan/phpstan": "^1.12 || ^2.0", + "phpstan/phpstan-phpunit": "^1.4 || ^2.0", + "phpstan/phpstan-strict-rules": "^1.6 || ^2.0", + "phpunit/phpunit": "^8.5 || ^12.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Inflector\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Inflector is a small library that can perform string manipulations with regard to upper/lowercase and singular/plural forms of words.", + "homepage": "https://www.doctrine-project.org/projects/inflector.html", + "keywords": [ + "inflection", + "inflector", + "lowercase", + "manipulation", + "php", + "plural", + "singular", + "strings", + "uppercase", + "words" + ], + "support": { + "issues": "https://github.com/doctrine/inflector/issues", + "source": "https://github.com/doctrine/inflector/tree/2.1.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finflector", + "type": "tidelift" + } + ], + "time": "2025-08-10T19:31:58+00:00" + }, + { + "name": "doctrine/instantiator", + "version": "2.1.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "23da848e1a2308728fe5fdddabf4be17ff9720c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/23da848e1a2308728fe5fdddabf4be17ff9720c7", + "reference": "23da848e1a2308728fe5fdddabf4be17ff9720c7", + "shasum": "" + }, + "require": { + "php": "^8.4" + }, + "require-dev": { + "doctrine/coding-standard": "^14", + "ext-pdo": "*", + "ext-phar": "*", + "phpbench/phpbench": "^1.2", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^10.5.58" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "https://ocramius.github.io/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://www.doctrine-project.org/projects/instantiator.html", + "keywords": [ + "constructor", + "instantiate" + ], + "support": { + "issues": "https://github.com/doctrine/instantiator/issues", + "source": "https://github.com/doctrine/instantiator/tree/2.1.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", + "type": "tidelift" + } + ], + "time": "2026-01-05T06:47:08+00:00" + }, + { + "name": "doctrine/lexer", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/lexer.git", + "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", + "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "doctrine/coding-standard": "^12", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^10.5", + "psalm/plugin-phpunit": "^0.18.3", + "vimeo/psalm": "^5.21" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\Lexer\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "https://www.doctrine-project.org/projects/lexer.html", + "keywords": [ + "annotations", + "docblock", + "lexer", + "parser", + "php" + ], + "support": { + "issues": "https://github.com/doctrine/lexer/issues", + "source": "https://github.com/doctrine/lexer/tree/3.0.1" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer", + "type": "tidelift" + } + ], + "time": "2024-02-05T11:56:58+00:00" + }, + { + "name": "doctrine/migrations", + "version": "3.9.7", + "source": { + "type": "git", + "url": "https://github.com/doctrine/migrations.git", + "reference": "96cb2a89b56c9efb0bac38e606dc0b0f13e650ec" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/migrations/zipball/96cb2a89b56c9efb0bac38e606dc0b0f13e650ec", + "reference": "96cb2a89b56c9efb0bac38e606dc0b0f13e650ec", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2", + "doctrine/dbal": "^3.6 || ^4", + "doctrine/deprecations": "^0.5.3 || ^1", + "doctrine/event-manager": "^1.2 || ^2.0", + "php": "^8.1", + "psr/log": "^1.1.3 || ^2 || ^3", + "symfony/console": "^5.4 || ^6.0 || ^7.0 || ^8.0", + "symfony/stopwatch": "^5.4 || ^6.0 || ^7.0 || ^8.0", + "symfony/var-exporter": "^6.2 || ^7.0 || ^8.0" + }, + "conflict": { + "doctrine/orm": "<2.12 || >=4" + }, + "require-dev": { + "doctrine/coding-standard": "^14", + "doctrine/orm": "^2.13 || ^3", + "doctrine/persistence": "^2 || ^3 || ^4", + "doctrine/sql-formatter": "^1.0", + "ext-pdo_sqlite": "*", + "fig/log-test": "^1", + "phpstan/phpstan": "^2", + "phpstan/phpstan-deprecation-rules": "^2", + "phpstan/phpstan-phpunit": "^2", + "phpstan/phpstan-strict-rules": "^2", + "phpstan/phpstan-symfony": "^2", + "phpunit/phpunit": "^10.3 || ^11.0 || ^12.0", + "symfony/cache": "^5.4 || ^6.0 || ^7.0 || ^8.0", + "symfony/process": "^5.4 || ^6.0 || ^7.0 || ^8.0", + "symfony/yaml": "^5.4 || ^6.0 || ^7.0 || ^8.0" + }, + "suggest": { + "doctrine/sql-formatter": "Allows to generate formatted SQL with the diff command.", + "symfony/yaml": "Allows the use of yaml for migration configuration files." + }, + "bin": [ + "bin/doctrine-migrations" + ], + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Migrations\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Michael Simonson", + "email": "contact@mikesimonson.com" + } + ], + "description": "PHP Doctrine Migrations project offer additional functionality on top of the database abstraction layer (DBAL) for versioning your database schema and easily deploying changes to it. It is a very easy to use and a powerful tool.", + "homepage": "https://www.doctrine-project.org/projects/migrations.html", + "keywords": [ + "database", + "dbal", + "migrations" + ], + "support": { + "issues": "https://github.com/doctrine/migrations/issues", + "source": "https://github.com/doctrine/migrations/tree/3.9.7" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fmigrations", + "type": "tidelift" + } + ], + "time": "2026-04-23T19:33:20+00:00" + }, + { + "name": "doctrine/orm", + "version": "3.6.4", + "source": { + "type": "git", + "url": "https://github.com/doctrine/orm.git", + "reference": "156f3b5a984e7eaa72d440bb6de1d3b6f8d2d6fd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/orm/zipball/156f3b5a984e7eaa72d440bb6de1d3b6f8d2d6fd", + "reference": "156f3b5a984e7eaa72d440bb6de1d3b6f8d2d6fd", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2", + "doctrine/collections": "^2.2", + "doctrine/dbal": "^3.8.2 || ^4", + "doctrine/deprecations": "^0.5.3 || ^1", + "doctrine/event-manager": "^1.2 || ^2", + "doctrine/inflector": "^1.4 || ^2.0", + "doctrine/instantiator": "^1.3 || ^2", + "doctrine/lexer": "^3", + "doctrine/persistence": "^3.3.1 || ^4", + "ext-ctype": "*", + "php": "^8.1", + "psr/cache": "^1 || ^2 || ^3", + "symfony/console": "^5.4 || ^6.0 || ^7.0 || ^8.0", + "symfony/var-exporter": "^6.3.9 || ^7.0 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^14.0", + "phpbench/phpbench": "^1.0", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "2.1.23", + "phpstan/phpstan-deprecation-rules": "^2", + "phpunit/phpunit": "^10.5.0 || ^11.5", + "psr/log": "^1 || ^2 || ^3", + "symfony/cache": "^5.4 || ^6.2 || ^7.0 || ^8.0" + }, + "suggest": { + "ext-dom": "Provides support for XSD validation for XML mapping files", + "symfony/cache": "Provides cache support for Setup Tool with doctrine/cache 2.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\ORM\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com" + } + ], + "description": "Object-Relational-Mapper for PHP", + "homepage": "https://www.doctrine-project.org/projects/orm.html", + "keywords": [ + "database", + "orm" + ], + "support": { + "issues": "https://github.com/doctrine/orm/issues", + "source": "https://github.com/doctrine/orm/tree/3.6.4" + }, + "time": "2026-05-07T07:04:34+00:00" + }, + { + "name": "doctrine/persistence", + "version": "4.2.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/persistence.git", + "reference": "49ab73e0d3e2ac8d1f5ecda3dd8acd5503781e8b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/persistence/zipball/49ab73e0d3e2ac8d1f5ecda3dd8acd5503781e8b", + "reference": "49ab73e0d3e2ac8d1f5ecda3dd8acd5503781e8b", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1", + "doctrine/event-manager": "^1 || ^2", + "php": "^8.1", + "psr/cache": "^1.0 || ^2.0 || ^3.0" + }, + "require-dev": { + "doctrine/coding-standard": "^14", + "phpstan/phpstan": "2.1.30", + "phpstan/phpstan-phpunit": "^2", + "phpstan/phpstan-strict-rules": "^2", + "phpunit/phpunit": "^10.5.58 || ^12", + "symfony/cache": "^4.4 || ^5.4 || ^6.0 || ^7.0 || ^8.0", + "symfony/finder": "^4.4 || ^5.4 || ^6.0 || ^7.0 || ^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Persistence\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + }, + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com" + } + ], + "description": "The Doctrine Persistence project is a set of shared interfaces and functionality that the different Doctrine object mappers share.", + "homepage": "https://www.doctrine-project.org/projects/persistence.html", + "keywords": [ + "mapper", + "object", + "odm", + "orm", + "persistence" + ], + "support": { + "issues": "https://github.com/doctrine/persistence/issues", + "source": "https://github.com/doctrine/persistence/tree/4.2.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fpersistence", + "type": "tidelift" + } + ], + "time": "2026-04-26T12:12:52+00:00" + }, + { + "name": "fig/http-message-util", + "version": "1.1.5", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message-util.git", + "reference": "9d94dc0154230ac39e5bf89398b324a86f63f765" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message-util/zipball/9d94dc0154230ac39e5bf89398b324a86f63f765", + "reference": "9d94dc0154230ac39e5bf89398b324a86f63f765", + "shasum": "" + }, + "require": { + "php": "^5.3 || ^7.0 || ^8.0" + }, + "suggest": { + "psr/http-message": "The package containing the PSR-7 interfaces" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Fig\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Utility classes and constants for use with PSR-7 (psr/http-message)", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "issues": "https://github.com/php-fig/http-message-util/issues", + "source": "https://github.com/php-fig/http-message-util/tree/1.1.5" + }, + "time": "2020-11-24T22:02:12+00:00" + }, + { + "name": "laminas/laminas-captcha", + "version": "2.20.0", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-captcha.git", + "reference": "78905e9f4147142ed875a8a265a9c8a4da924600" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-captcha/zipball/78905e9f4147142ed875a8a265a9c8a4da924600", + "reference": "78905e9f4147142ed875a8a265a9c8a4da924600", + "shasum": "" + }, + "require": { + "laminas/laminas-recaptcha": "^3.4.0", + "laminas/laminas-session": "^2.25", + "laminas/laminas-stdlib": "^3.10.1", + "laminas/laminas-text": "^2.12.1", + "laminas/laminas-validator": "^2.19.0", + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" + }, + "conflict": { + "zendframework/zend-captcha": "*" + }, + "require-dev": { + "ext-gd": "*", + "laminas/laminas-coding-standard": "~2.5.0", + "phpunit/phpunit": "^9.5.26", + "psalm/plugin-phpunit": "^0.19.0", + "vimeo/psalm": "^6.13" + }, + "suggest": { + "laminas/laminas-i18n-resources": "Translations of captcha messages" + }, + "type": "library", + "autoload": { + "psr-4": { + "Laminas\\Captcha\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Generate and validate CAPTCHAs using Figlets, images, ReCaptcha, and more", + "homepage": "https://laminas.dev", + "keywords": [ + "captcha", + "laminas" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.laminas.dev/laminas-captcha/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/laminas/laminas-captcha/issues", + "rss": "https://github.com/laminas/laminas-captcha/releases.atom", + "source": "https://github.com/laminas/laminas-captcha" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2026-03-31T17:11:06+00:00" + }, + { + "name": "laminas/laminas-config-aggregator", + "version": "1.19.0", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-config-aggregator.git", + "reference": "612343ce135c340fc667da3615e50d865a86b4d9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-config-aggregator/zipball/612343ce135c340fc667da3615e50d865a86b4d9", + "reference": "612343ce135c340fc667da3615e50d865a86b4d9", + "shasum": "" + }, + "require": { + "brick/varexporter": "^0.5.0 || ^0.4.0 || ^0.6.0", + "laminas/laminas-stdlib": "^3.18.0", + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", + "webimpress/safe-writer": "^2.2.0" + }, + "conflict": { + "nikic/php-parser": "<4.12", + "zendframework/zend-config-aggregator": "*" + }, + "require-dev": { + "laminas/laminas-coding-standard": "~3.1.0", + "laminas/laminas-config": "^3.10.1", + "phpunit/phpunit": "^11.5.42", + "psalm/plugin-phpunit": "^0.19.5", + "vimeo/psalm": "^6.13.1" + }, + "suggest": { + "laminas/laminas-config": "Allows loading configuration from XML, INI, YAML, and JSON files", + "laminas/laminas-config-aggregator-modulemanager": "Allows loading configuration from laminas-mvc Module classes", + "laminas/laminas-config-aggregator-parameters": "Allows usage of templated parameters within your configuration" + }, + "type": "library", + "autoload": { + "psr-4": { + "Laminas\\ConfigAggregator\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Lightweight library for collecting and merging configuration from different sources", + "homepage": "https://laminas.dev", + "keywords": [ + "config-aggregator", + "laminas" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.laminas.dev/laminas-config-aggregator/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/laminas/laminas-config-aggregator/issues", + "rss": "https://github.com/laminas/laminas-config-aggregator/releases.atom", + "source": "https://github.com/laminas/laminas-config-aggregator" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2025-10-14T19:57:01+00:00" + }, + { + "name": "laminas/laminas-diactoros", + "version": "3.8.0", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-diactoros.git", + "reference": "60c182916b2749480895601649563970f3f12ec4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-diactoros/zipball/60c182916b2749480895601649563970f3f12ec4", + "reference": "60c182916b2749480895601649563970f3f12ec4", + "shasum": "" + }, + "require": { + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", + "psr/http-factory": "^1.1", + "psr/http-message": "^1.1 || ^2.0" + }, + "conflict": { + "amphp/amp": "<2.6.4" + }, + "provide": { + "psr/http-factory-implementation": "^1.0", + "psr/http-message-implementation": "^1.1 || ^2.0" + }, + "require-dev": { + "ext-curl": "*", + "ext-dom": "*", + "ext-gd": "*", + "ext-libxml": "*", + "http-interop/http-factory-tests": "^2.2.0", + "laminas/laminas-coding-standard": "~3.1.0", + "php-http/psr7-integration-tests": "^1.4.0", + "phpunit/phpunit": "^10.5.36", + "psalm/plugin-phpunit": "^0.19.5", + "vimeo/psalm": "^6.13" + }, + "type": "library", + "extra": { + "laminas": { + "module": "Laminas\\Diactoros", + "config-provider": "Laminas\\Diactoros\\ConfigProvider" + } + }, + "autoload": { + "files": [ + "src/functions/create_uploaded_file.php", + "src/functions/marshal_headers_from_sapi.php", + "src/functions/marshal_method_from_sapi.php", + "src/functions/marshal_protocol_version_from_sapi.php", + "src/functions/normalize_server.php", + "src/functions/normalize_uploaded_files.php", + "src/functions/parse_cookie_header.php" + ], + "psr-4": { + "Laminas\\Diactoros\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "PSR HTTP Message implementations", + "homepage": "https://laminas.dev", + "keywords": [ + "http", + "laminas", + "psr", + "psr-17", + "psr-7" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.laminas.dev/laminas-diactoros/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/laminas/laminas-diactoros/issues", + "rss": "https://github.com/laminas/laminas-diactoros/releases.atom", + "source": "https://github.com/laminas/laminas-diactoros" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2025-10-12T15:31:36+00:00" + }, + { + "name": "laminas/laminas-escaper", + "version": "2.18.0", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-escaper.git", + "reference": "06f211dfffff18d91844c1f55250d5d13c007e18" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-escaper/zipball/06f211dfffff18d91844c1f55250d5d13c007e18", + "reference": "06f211dfffff18d91844c1f55250d5d13c007e18", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-mbstring": "*", + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" + }, + "conflict": { + "zendframework/zend-escaper": "*" + }, + "require-dev": { + "infection/infection": "^0.31.0", + "laminas/laminas-coding-standard": "~3.1.0", + "phpunit/phpunit": "^11.5.42", + "psalm/plugin-phpunit": "^0.19.5", + "vimeo/psalm": "^6.13.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Laminas\\Escaper\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Securely and safely escape HTML, HTML attributes, JavaScript, CSS, and URLs", + "homepage": "https://laminas.dev", + "keywords": [ + "escaper", + "laminas" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.laminas.dev/laminas-escaper/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/laminas/laminas-escaper/issues", + "rss": "https://github.com/laminas/laminas-escaper/releases.atom", + "source": "https://github.com/laminas/laminas-escaper" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2025-10-14T18:31:13+00:00" + }, + { + "name": "laminas/laminas-eventmanager", + "version": "3.15.0", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-eventmanager.git", + "reference": "90b4bd33264629af8e39caf5aa83473ac03aa04c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-eventmanager/zipball/90b4bd33264629af8e39caf5aa83473ac03aa04c", + "reference": "90b4bd33264629af8e39caf5aa83473ac03aa04c", + "shasum": "" + }, + "require": { + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" + }, + "conflict": { + "container-interop/container-interop": "<1.2", + "zendframework/zend-eventmanager": "*" + }, + "require-dev": { + "amphp/dns": "^2.2", + "amphp/socket": "^2.3.1", + "laminas/laminas-coding-standard": "~3.1.0", + "laminas/laminas-stdlib": "^3.20", + "phpbench/phpbench": "^1.3.1", + "phpunit/phpunit": "^10.5.58", + "psalm/plugin-phpunit": "^0.19.0", + "psr/container": "^1.1.2 || ^2.0.2", + "sebastian/recursion-context": "^5.0.1", + "vimeo/psalm": "^6.13" + }, + "suggest": { + "laminas/laminas-stdlib": "^2.7.3 || ^3.0, to use the FilterChain feature", + "psr/container": "^1.1.2 || ^2.0.2, to use the lazy listeners feature" + }, + "type": "library", + "autoload": { + "psr-4": { + "Laminas\\EventManager\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Trigger and listen to events within a PHP application", + "homepage": "https://laminas.dev", + "keywords": [ + "event", + "eventmanager", + "events", + "laminas" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.laminas.dev/laminas-eventmanager/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/laminas/laminas-eventmanager/issues", + "rss": "https://github.com/laminas/laminas-eventmanager/releases.atom", + "source": "https://github.com/laminas/laminas-eventmanager" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2025-10-31T10:29:01+00:00" + }, + { + "name": "laminas/laminas-feed", + "version": "2.26.1", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-feed.git", + "reference": "8b651d72f2a4ce0df53e1999de3fe16c78e37a2c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-feed/zipball/8b651d72f2a4ce0df53e1999de3fe16c78e37a2c", + "reference": "8b651d72f2a4ce0df53e1999de3fe16c78e37a2c", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-filter": "*", + "ext-libxml": "*", + "laminas/laminas-escaper": "^2.9", + "laminas/laminas-stdlib": "^3.6", + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" + }, + "conflict": { + "laminas/laminas-servicemanager": "<3.3", + "zendframework/zend-feed": "*" + }, + "require-dev": { + "laminas/laminas-cache": "^2.13.2 || ^3.12", + "laminas/laminas-cache-storage-adapter-memory": "^1.1.0 || ^2.3", + "laminas/laminas-coding-standard": "~2.5.0", + "laminas/laminas-db": "^2.18", + "laminas/laminas-http": "^2.19", + "laminas/laminas-servicemanager": "^3.22.1", + "laminas/laminas-validator": "^2.46", + "phpunit/phpunit": "^10.5.5", + "psalm/plugin-phpunit": "^0.19.0", + "psr/http-message": "^2.0", + "vimeo/psalm": "^5.18.0" + }, + "suggest": { + "laminas/laminas-cache": "Laminas\\Cache component, for optionally caching feeds between requests", + "laminas/laminas-db": "Laminas\\Db component, for use with PubSubHubbub", + "laminas/laminas-http": "Laminas\\Http for PubSubHubbub, and optionally for use with Laminas\\Feed\\Reader", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component, for easily extending ExtensionManager implementations", + "laminas/laminas-validator": "Laminas\\Validator component, for validating email addresses used in Atom feeds and entries when using the Writer subcomponent", + "psr/http-message": "PSR-7 ^1.0.1, if you wish to use Laminas\\Feed\\Reader\\Http\\Psr7ResponseDecorator" + }, + "type": "library", + "autoload": { + "psr-4": { + "Laminas\\Feed\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "provides functionality for creating and consuming RSS and Atom feeds", + "homepage": "https://laminas.dev", + "keywords": [ + "atom", + "feed", + "laminas", + "rss" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.laminas.dev/laminas-feed/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/laminas/laminas-feed/issues", + "rss": "https://github.com/laminas/laminas-feed/releases.atom", + "source": "https://github.com/laminas/laminas-feed" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2026-03-03T09:55:19+00:00" + }, + { + "name": "laminas/laminas-filter", + "version": "2.42.0", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-filter.git", + "reference": "985d27bd42daf51b415ce1ee889e0978cc1e59ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-filter/zipball/985d27bd42daf51b415ce1ee889e0978cc1e59ed", + "reference": "985d27bd42daf51b415ce1ee889e0978cc1e59ed", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "laminas/laminas-servicemanager": "^3.21.0", + "laminas/laminas-stdlib": "^3.19.0", + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" + }, + "conflict": { + "laminas/laminas-validator": "<2.10.1", + "zendframework/zend-filter": "*" + }, + "require-dev": { + "laminas/laminas-coding-standard": "^3.1", + "laminas/laminas-crypt": "^3.12", + "laminas/laminas-i18n": "^2.30.0", + "laminas/laminas-uri": "^2.13", + "pear/archive_tar": "^1.6.0", + "phpunit/phpunit": "^10.5.58", + "psalm/plugin-phpunit": "^0.19.0", + "psr/http-factory": "^1.1.0", + "vimeo/psalm": "^5.26.1" + }, + "suggest": { + "laminas/laminas-crypt": "Laminas\\Crypt component, for encryption filters", + "laminas/laminas-i18n": "Laminas\\I18n component for filters depending on i18n functionality", + "laminas/laminas-uri": "Laminas\\Uri component, for the UriNormalize filter", + "psr/http-factory-implementation": "psr/http-factory-implementation, for creating file upload instances when consuming PSR-7 in file upload filters" + }, + "type": "library", + "extra": { + "laminas": { + "component": "Laminas\\Filter", + "config-provider": "Laminas\\Filter\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Laminas\\Filter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Programmatically filter and normalize data and files", + "homepage": "https://laminas.dev", + "keywords": [ + "filter", + "laminas" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.laminas.dev/laminas-filter/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/laminas/laminas-filter/issues", + "rss": "https://github.com/laminas/laminas-filter/releases.atom", + "source": "https://github.com/laminas/laminas-filter" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2025-10-13T15:44:52+00:00" + }, + { + "name": "laminas/laminas-form", + "version": "3.24.2", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-form.git", + "reference": "92d46a8333ca428244cf8b647167477aefbbe891" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-form/zipball/92d46a8333ca428244cf8b647167477aefbbe891", + "reference": "92d46a8333ca428244cf8b647167477aefbbe891", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "laminas/laminas-escaper": "^2", + "laminas/laminas-filter": "^2", + "laminas/laminas-hydrator": "^4.16.0", + "laminas/laminas-inputfilter": "^2.32.0", + "laminas/laminas-servicemanager": "^3.22.1", + "laminas/laminas-stdlib": "^3.20.0", + "laminas/laminas-validator": "^2", + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", + "psr/container": "^1.1.2" + }, + "conflict": { + "doctrine/annotations": "<1.14.0", + "laminas/laminas-captcha": "<2.16.0", + "laminas/laminas-eventmanager": "<3.10.0", + "laminas/laminas-i18n": "<2.21.0", + "laminas/laminas-recaptcha": "<3.6.0", + "laminas/laminas-servicemanager": "<3.20.0", + "laminas/laminas-view": "<2.27.0" + }, + "require-dev": { + "doctrine/annotations": "^1.14.3 || ^2.0.2", + "ext-gd": "*", + "ext-intl": "*", + "laminas/laminas-captcha": "^2.19", + "laminas/laminas-coding-standard": "^3.1.0", + "laminas/laminas-eventmanager": "^3.15.0", + "laminas/laminas-i18n": "^2.31.0", + "laminas/laminas-modulemanager": "^2.19.0", + "laminas/laminas-recaptcha": "^3.8", + "laminas/laminas-session": "^2.25.1", + "laminas/laminas-text": "^2.12.1", + "laminas/laminas-view": "^2.43", + "phpunit/phpunit": "^11.5.43", + "psalm/plugin-phpunit": "^0.19.5", + "vimeo/psalm": "^6.13.1" + }, + "suggest": { + "doctrine/annotations": "^1.14, required to use laminas-form annotations support", + "laminas/laminas-captcha": "^2.16, required for using CAPTCHA form elements", + "laminas/laminas-eventmanager": "^3.10, required for laminas-form annotations support", + "laminas/laminas-i18n": "^2.21, required when using laminas-form view helpers", + "laminas/laminas-recaptcha": "^3.6, in order to use the ReCaptcha form element", + "laminas/laminas-servicemanager": "^3.20, required to use the form factories or provide services", + "laminas/laminas-view": "^2.27, required for using the laminas-form view helpers" + }, + "type": "library", + "extra": { + "laminas": { + "component": "Laminas\\Form", + "config-provider": "Laminas\\Form\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Laminas\\Form\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Validate and display simple and complex forms, casting forms to business objects and vice versa", + "homepage": "https://laminas.dev", + "keywords": [ + "form", + "laminas" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.laminas.dev/laminas-form/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/laminas/laminas-form/issues", + "rss": "https://github.com/laminas/laminas-form/releases.atom", + "source": "https://github.com/laminas/laminas-form" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2026-01-12T14:20:53+00:00" + }, + { + "name": "laminas/laminas-http", + "version": "2.23.0", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-http.git", + "reference": "9462fc84330d25b23383823831380abb33907fdd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-http/zipball/9462fc84330d25b23383823831380abb33907fdd", + "reference": "9462fc84330d25b23383823831380abb33907fdd", + "shasum": "" + }, + "require": { + "laminas/laminas-loader": "^2.10", + "laminas/laminas-stdlib": "^3.6", + "laminas/laminas-uri": "^2.14", + "laminas/laminas-validator": "^2.15 || ^3.0", + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" + }, + "conflict": { + "zendframework/zend-http": "*" + }, + "require-dev": { + "ext-curl": "*", + "laminas/laminas-coding-standard": "^3.0.1", + "phpunit/phpunit": "^10.5.38" + }, + "suggest": { + "paragonie/certainty": "For automated management of cacert.pem" + }, + "type": "library", + "autoload": { + "psr-4": { + "Laminas\\Http\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Provides an easy interface for performing Hyper-Text Transfer Protocol (HTTP) requests", + "homepage": "https://laminas.dev", + "keywords": [ + "http", + "http client", + "laminas" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.laminas.dev/laminas-http/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/laminas/laminas-http/issues", + "rss": "https://github.com/laminas/laminas-http/releases.atom", + "source": "https://github.com/laminas/laminas-http" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2025-12-05T11:02:08+00:00" + }, + { + "name": "laminas/laminas-httphandlerrunner", + "version": "2.13.0", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-httphandlerrunner.git", + "reference": "181eaeeb838ad3d80fbbcfb0657a46bc212bbd4e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-httphandlerrunner/zipball/181eaeeb838ad3d80fbbcfb0657a46bc212bbd4e", + "reference": "181eaeeb838ad3d80fbbcfb0657a46bc212bbd4e", + "shasum": "" + }, + "require": { + "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", + "psr/http-message": "^1.0 || ^2.0", + "psr/http-message-implementation": "^1.0 || ^2.0", + "psr/http-server-handler": "^1.0" + }, + "require-dev": { + "laminas/laminas-coding-standard": "~3.1.0", + "laminas/laminas-diactoros": "^3.6.0", + "phpunit/phpunit": "^10.5.46", + "psalm/plugin-phpunit": "^0.19.5", + "vimeo/psalm": "^6.10.3" + }, + "type": "library", + "extra": { + "laminas": { + "config-provider": "Laminas\\HttpHandlerRunner\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Laminas\\HttpHandlerRunner\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Execute PSR-15 RequestHandlerInterface instances and emit responses they generate.", + "homepage": "https://laminas.dev", + "keywords": [ + "components", + "laminas", + "mezzio", + "psr-15", + "psr-7" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.laminas.dev/laminas-httphandlerrunner/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/laminas/laminas-httphandlerrunner/issues", + "rss": "https://github.com/laminas/laminas-httphandlerrunner/releases.atom", + "source": "https://github.com/laminas/laminas-httphandlerrunner" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2025-10-12T20:58:29+00:00" + }, + { + "name": "laminas/laminas-hydrator", + "version": "4.18.1", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-hydrator.git", + "reference": "93a8ea33dccd2639ab8f07fbbfbc40911774aa9b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-hydrator/zipball/93a8ea33dccd2639ab8f07fbbfbc40911774aa9b", + "reference": "93a8ea33dccd2639ab8f07fbbfbc40911774aa9b", + "shasum": "" + }, + "require": { + "laminas/laminas-stdlib": "^3.20", + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", + "webmozart/assert": "^1.11 || ^2.0" + }, + "conflict": { + "laminas/laminas-servicemanager": "<3.14.0", + "zendframework/zend-hydrator": "*" + }, + "require-dev": { + "laminas/laminas-coding-standard": "~3.0", + "laminas/laminas-eventmanager": "^3.13.1", + "laminas/laminas-modulemanager": "^2.16.0", + "laminas/laminas-serializer": "^2.17.0", + "laminas/laminas-servicemanager": "^3.24.0", + "phpbench/phpbench": "^1.3.1", + "phpunit/phpunit": "^11.5.42", + "psalm/plugin-phpunit": "^0.19.0", + "vimeo/psalm": "^6.13.1" + }, + "suggest": { + "laminas/laminas-eventmanager": "^3.13, to support aggregate hydrator usage", + "laminas/laminas-serializer": "^2.17, to use the SerializableStrategy", + "laminas/laminas-servicemanager": "^3.22, to support hydrator plugin manager usage" + }, + "type": "library", + "extra": { + "laminas": { + "component": "Laminas\\Hydrator", + "config-provider": "Laminas\\Hydrator\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Laminas\\Hydrator\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Serialize objects to arrays, and vice versa", + "homepage": "https://laminas.dev", + "keywords": [ + "hydrator", + "laminas" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.laminas.dev/laminas-hydrator/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/laminas/laminas-hydrator/issues", + "rss": "https://github.com/laminas/laminas-hydrator/releases.atom", + "source": "https://github.com/laminas/laminas-hydrator" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2026-05-04T08:00:40+00:00" + }, + { + "name": "laminas/laminas-i18n", + "version": "2.32.1", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-i18n.git", + "reference": "8e71b40318f0df6253329e837188e1d77cf83aea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-i18n/zipball/8e71b40318f0df6253329e837188e1d77cf83aea", + "reference": "8e71b40318f0df6253329e837188e1d77cf83aea", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-intl": "*", + "laminas/laminas-escaper": "^2.0", + "laminas/laminas-servicemanager": "^3.21.0", + "laminas/laminas-stdlib": "^3.0", + "laminas/laminas-translator": "^1.0", + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", + "psr/container": "^1.0.0" + }, + "conflict": { + "laminas/laminas-view": "<2.20.0", + "zendframework/zend-i18n": "*" + }, + "require-dev": { + "laminas/laminas-cache": "^3.13.0", + "laminas/laminas-cache-storage-adapter-memory": "^2.4.0", + "laminas/laminas-cache-storage-deprecated-factory": "^1.3", + "laminas/laminas-coding-standard": "^3.1", + "laminas/laminas-config": "^3.10.1", + "laminas/laminas-eventmanager": "^3.15.0", + "laminas/laminas-filter": "^2.42", + "laminas/laminas-validator": "^2.65.0", + "laminas/laminas-view": "^2.44", + "phpunit/phpunit": "^11.5.46", + "psalm/plugin-phpunit": "^0.19.5", + "vimeo/psalm": "^6.14.2" + }, + "suggest": { + "laminas/laminas-cache": "You should install this package to cache the translations", + "laminas/laminas-config": "You should install this package to use the INI translation format", + "laminas/laminas-eventmanager": "You should install this package to use the events in the translator", + "laminas/laminas-filter": "You should install this package to use the provided filters", + "laminas/laminas-i18n-resources": "This package provides validator and captcha translations", + "laminas/laminas-validator": "You should install this package to use the provided validators", + "laminas/laminas-view": "You should install this package to use the provided view helpers" + }, + "type": "library", + "extra": { + "laminas": { + "component": "Laminas\\I18n", + "config-provider": "Laminas\\I18n\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Laminas\\I18n\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Provide translations for your application, and filter and validate internationalized values", + "homepage": "https://laminas.dev", + "keywords": [ + "i18n", + "laminas" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.laminas.dev/laminas-i18n/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/laminas/laminas-i18n/issues", + "rss": "https://github.com/laminas/laminas-i18n/releases.atom", + "source": "https://github.com/laminas/laminas-i18n" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2025-12-15T14:23:40+00:00" + }, + { + "name": "laminas/laminas-inputfilter", + "version": "2.35.0", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-inputfilter.git", + "reference": "326d2dac38814f70902a3a9e0062f740d06f89c5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-inputfilter/zipball/326d2dac38814f70902a3a9e0062f740d06f89c5", + "reference": "326d2dac38814f70902a3a9e0062f740d06f89c5", + "shasum": "" + }, + "require": { + "laminas/laminas-filter": "^2.19", + "laminas/laminas-servicemanager": "^3.21.0", + "laminas/laminas-stdlib": "^3.19", + "laminas/laminas-validator": "^2.60.0", + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", + "psr/container": "^1.1 || ^2.0" + }, + "conflict": { + "zendframework/zend-inputfilter": "*" + }, + "require-dev": { + "ext-json": "*", + "laminas/laminas-coding-standard": "^3.1.0", + "phpunit/phpunit": "^11.5.46", + "psalm/plugin-phpunit": "^0.19.5", + "psr/http-message": "^2.0", + "vimeo/psalm": "^6.14.3" + }, + "suggest": { + "psr/http-message-implementation": "PSR-7 is required if you wish to validate PSR-7 UploadedFileInterface payloads" + }, + "type": "library", + "extra": { + "laminas": { + "component": "Laminas\\InputFilter", + "config-provider": "Laminas\\InputFilter\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Laminas\\InputFilter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Normalize and validate input sets from the web, APIs, the CLI, and more, including files", + "homepage": "https://laminas.dev", + "keywords": [ + "inputfilter", + "laminas" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.laminas.dev/laminas-inputfilter/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/laminas/laminas-inputfilter/issues", + "rss": "https://github.com/laminas/laminas-inputfilter/releases.atom", + "source": "https://github.com/laminas/laminas-inputfilter" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2026-01-10T15:07:43+00:00" + }, + { + "name": "laminas/laminas-json", + "version": "3.8.0", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-json.git", + "reference": "5448d357c4520d79f5caf4d46a1fe03ec8b8e4b0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-json/zipball/5448d357c4520d79f5caf4d46a1fe03ec8b8e4b0", + "reference": "5448d357c4520d79f5caf4d46a1fe03ec8b8e4b0", + "shasum": "" + }, + "require": { + "php": "^8.1.0" + }, + "conflict": { + "zendframework/zend-json": "*" + }, + "require-dev": { + "laminas/laminas-coding-standard": "~2.4.0", + "laminas/laminas-stdlib": "^2.7.7 || ^3.19", + "phpunit/phpunit": "^9.5.25" + }, + "suggest": { + "laminas/laminas-json-server": "For implementing JSON-RPC servers", + "laminas/laminas-xml2json": "For converting XML documents to JSON" + }, + "type": "library", + "autoload": { + "psr-4": { + "Laminas\\Json\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "provides convenience methods for serializing native PHP to JSON and decoding JSON to native PHP", + "homepage": "https://laminas.dev", + "keywords": [ + "json", + "laminas" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.laminas.dev/laminas-json/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/laminas/laminas-json/issues", + "rss": "https://github.com/laminas/laminas-json/releases.atom", + "source": "https://github.com/laminas/laminas-json" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "abandoned": true, + "time": "2026-01-23T11:10:11+00:00" + }, + { + "name": "laminas/laminas-loader", + "version": "2.12.0", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-loader.git", + "reference": "ec8cee33fb254ee4d9c8e8908c870e5c797e1272" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-loader/zipball/ec8cee33fb254ee4d9c8e8908c870e5c797e1272", + "reference": "ec8cee33fb254ee4d9c8e8908c870e5c797e1272", + "shasum": "" + }, + "require": { + "php": "^8.0.0" + }, + "conflict": { + "zendframework/zend-loader": "*" + }, + "require-dev": { + "laminas/laminas-coding-standard": "~2.4.0", + "phpunit/phpunit": "~9.5.25" + }, + "type": "library", + "autoload": { + "psr-4": { + "Laminas\\Loader\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Autoloading and plugin loading strategies", + "homepage": "https://laminas.dev", + "keywords": [ + "laminas", + "loader" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.laminas.dev/laminas-loader/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/laminas/laminas-loader/issues", + "rss": "https://github.com/laminas/laminas-loader/releases.atom", + "source": "https://github.com/laminas/laminas-loader" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "abandoned": true, + "time": "2025-12-30T11:30:39+00:00" + }, + { + "name": "laminas/laminas-paginator", + "version": "2.22.0", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-paginator.git", + "reference": "46e62444f467b000a9a0e3959551a29b3223d7e8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-paginator/zipball/46e62444f467b000a9a0e3959551a29b3223d7e8", + "reference": "46e62444f467b000a9a0e3959551a29b3223d7e8", + "shasum": "" + }, + "require": { + "ext-json": "*", + "laminas/laminas-stdlib": "^3.10.1", + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" + }, + "conflict": { + "zendframework/zend-paginator": "*" + }, + "require-dev": { + "laminas/laminas-cache": "^3.13.0", + "laminas/laminas-cache-storage-adapter-memory": "^2.4.0", + "laminas/laminas-coding-standard": "^3.1.0", + "laminas/laminas-config": "^3.10.1", + "laminas/laminas-filter": "^2.42", + "laminas/laminas-servicemanager": "^3.24.0", + "laminas/laminas-view": "^2.43", + "phpunit/phpunit": "^11.5.42", + "psalm/plugin-phpunit": "^0.19.5", + "vimeo/psalm": "^6.13.1" + }, + "suggest": { + "laminas/laminas-cache": "Laminas\\Cache component to support cache features", + "laminas/laminas-filter": "Laminas\\Filter component", + "laminas/laminas-paginator-adapter-laminasdb": "Provides pagination adapters for Select statements and TableGateway instances", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component", + "laminas/laminas-view": "Laminas\\View component" + }, + "type": "library", + "extra": { + "laminas": { + "component": "Laminas\\Paginator", + "config-provider": "Laminas\\Paginator\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Laminas\\Paginator\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Paginate collections of data from arbitrary sources", + "homepage": "https://laminas.dev", + "keywords": [ + "laminas", + "paginator" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.laminas.dev/laminas-paginator/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/laminas/laminas-paginator/issues", + "rss": "https://github.com/laminas/laminas-paginator/releases.atom", + "source": "https://github.com/laminas/laminas-paginator" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2025-11-06T17:36:56+00:00" + }, + { + "name": "laminas/laminas-psr7bridge", + "version": "1.13.0", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-psr7bridge.git", + "reference": "cf6499184739c8c3facc7504d834550ba2c1a3b3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-psr7bridge/zipball/cf6499184739c8c3facc7504d834550ba2c1a3b3", + "reference": "cf6499184739c8c3facc7504d834550ba2c1a3b3", + "shasum": "" + }, + "require": { + "laminas/laminas-diactoros": "^2.25.2 || ^3.0.0", + "laminas/laminas-http": "^2.19.0", + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", + "psr/http-message": "^1.1 || ^2.0" + }, + "require-dev": { + "laminas/laminas-coding-standard": "~3.1.0", + "phpunit/phpunit": "^11.5.48", + "psalm/plugin-phpunit": "^0.19.5", + "vimeo/psalm": "^6.14.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Laminas\\Psr7Bridge\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Bidirectional conversions between PSR-7 and laminas-http messages", + "homepage": "https://laminas.dev", + "keywords": [ + "http", + "laminas", + "psr", + "psr-7" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.laminas.dev/laminas-psr7bridge/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/laminas/laminas-psr7bridge/issues", + "rss": "https://github.com/laminas/laminas-psr7bridge/releases.atom", + "source": "https://github.com/laminas/laminas-psr7bridge" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2026-02-26T14:00:16+00:00" + }, + { + "name": "laminas/laminas-recaptcha", + "version": "3.9.0", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-recaptcha.git", + "reference": "d8c710d6ed90f245db5ee85bd024279b35476f3f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-recaptcha/zipball/d8c710d6ed90f245db5ee85bd024279b35476f3f", + "reference": "d8c710d6ed90f245db5ee85bd024279b35476f3f", + "shasum": "" + }, + "require": { + "ext-json": "*", + "laminas/laminas-http": "^2.15", + "laminas/laminas-stdlib": "^3.10.1", + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" + }, + "conflict": { + "zendframework/zendservice-recaptcha": "*" + }, + "require-dev": { + "laminas/laminas-coding-standard": "~2.5.0", + "laminas/laminas-config": "^3.9", + "laminas/laminas-validator": "^2.30.1", + "phpunit/phpunit": "^11.5", + "psalm/plugin-phpunit": "^0.19.0", + "vimeo/psalm": "^6.13" + }, + "suggest": { + "laminas/laminas-validator": "~2.0, if using ReCaptcha's Mailhide API" + }, + "type": "library", + "autoload": { + "psr-4": { + "Laminas\\ReCaptcha\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "OOP wrapper for the ReCaptcha web service", + "homepage": "https://laminas.dev", + "keywords": [ + "laminas", + "recaptcha" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.laminas.dev/laminas-recaptcha/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/laminas/laminas-recaptcha/issues", + "rss": "https://github.com/laminas/laminas-recaptcha/releases.atom", + "source": "https://github.com/laminas/laminas-recaptcha" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2025-12-05T10:07:10+00:00" + }, + { + "name": "laminas/laminas-router", + "version": "3.18.0", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-router.git", + "reference": "e9a3dcf704cfc2ea47da6c2275409c2187034f8f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-router/zipball/e9a3dcf704cfc2ea47da6c2275409c2187034f8f", + "reference": "e9a3dcf704cfc2ea47da6c2275409c2187034f8f", + "shasum": "" + }, + "require": { + "laminas/laminas-servicemanager": "^3.14.0", + "laminas/laminas-stdlib": "^3.10.1", + "laminas/laminas-uri": "^2.14", + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", + "psr/container": "^1 || ^2.0.2" + }, + "conflict": { + "zendframework/zend-router": "*" + }, + "require-dev": { + "laminas/laminas-coding-standard": "~3.1.0", + "laminas/laminas-http": "^2.22", + "laminas/laminas-i18n": "^2.31.0", + "phpunit/phpunit": "^11.5.43", + "psalm/plugin-phpunit": "^0.19.5", + "vimeo/psalm": "^6.13.1" + }, + "suggest": { + "laminas/laminas-i18n": "^2.15.0 if defining translatable HTTP path segments" + }, + "type": "library", + "extra": { + "laminas": { + "component": "Laminas\\Router", + "config-provider": "Laminas\\Router\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Laminas\\Router\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Flexible routing system for HTTP and console applications", + "homepage": "https://laminas.dev", + "keywords": [ + "laminas", + "routing" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.laminas.dev/laminas-router/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/laminas/laminas-router/issues", + "rss": "https://github.com/laminas/laminas-router/releases.atom", + "source": "https://github.com/laminas/laminas-router" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2026-03-02T14:54:55+00:00" + }, + { + "name": "laminas/laminas-servicemanager", + "version": "3.24.0", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-servicemanager.git", + "reference": "b172a0df568bf37ebdfb3658263156eefe3c1e8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-servicemanager/zipball/b172a0df568bf37ebdfb3658263156eefe3c1e8c", + "reference": "b172a0df568bf37ebdfb3658263156eefe3c1e8c", + "shasum": "" + }, + "require": { + "laminas/laminas-stdlib": "^3.19", + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", + "psr/container": "^1.0" + }, + "conflict": { + "ext-psr": "*", + "laminas/laminas-code": "<4.10.0", + "zendframework/zend-code": "<3.3.1", + "zendframework/zend-servicemanager": "*" + }, + "provide": { + "psr/container-implementation": "^1.0" + }, + "replace": { + "container-interop/container-interop": "^1.2.0" + }, + "require-dev": { + "composer/package-versions-deprecated": "^1.11.99.5", + "friendsofphp/proxy-manager-lts": "^1.0.18", + "laminas/laminas-code": "^4.16.0", + "laminas/laminas-coding-standard": "~2.5.0", + "laminas/laminas-container-config-test": "^0.8", + "mikey179/vfsstream": "^1.6.12", + "phpbench/phpbench": "^1.4.1", + "phpunit/phpunit": "^10.5.58", + "psalm/plugin-phpunit": "^0.18.4", + "vimeo/psalm": "^5.26.1" + }, + "suggest": { + "friendsofphp/proxy-manager-lts": "ProxyManager ^2.1.1 to handle lazy initialization of services" + }, + "bin": [ + "bin/generate-deps-for-config-factory", + "bin/generate-factory-for-class" + ], + "type": "library", + "autoload": { + "files": [ + "src/autoload.php" + ], + "psr-4": { + "Laminas\\ServiceManager\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Factory-Driven Dependency Injection Container", + "homepage": "https://laminas.dev", + "keywords": [ + "PSR-11", + "dependency-injection", + "di", + "dic", + "laminas", + "service-manager", + "servicemanager" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.laminas.dev/laminas-servicemanager/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/laminas/laminas-servicemanager/issues", + "rss": "https://github.com/laminas/laminas-servicemanager/releases.atom", + "source": "https://github.com/laminas/laminas-servicemanager" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2025-10-14T09:03:51+00:00" + }, + { + "name": "laminas/laminas-session", + "version": "2.26.0", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-session.git", + "reference": "1c4f1a80ccc6403fff85bb13fb928da79dd1636e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-session/zipball/1c4f1a80ccc6403fff85bb13fb928da79dd1636e", + "reference": "1c4f1a80ccc6403fff85bb13fb928da79dd1636e", + "shasum": "" + }, + "require": { + "laminas/laminas-eventmanager": "^3.14.0", + "laminas/laminas-servicemanager": "^3.23.1", + "laminas/laminas-stdlib": "^3.20.0", + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" + }, + "conflict": { + "amphp/amp": "<2.6.4", + "zendframework/zend-session": "*" + }, + "require-dev": { + "amphp/dns": "^2.4.0", + "amphp/socket": "^2.3.1", + "ext-xdebug": "*", + "laminas/laminas-cache": "^3.13.0", + "laminas/laminas-cache-storage-adapter-memory": "^2.4", + "laminas/laminas-coding-standard": "~3.1.0", + "laminas/laminas-db": "^2.20.0", + "laminas/laminas-http": "^2.22", + "laminas/laminas-validator": "^2.64.4", + "mongodb/mongodb": "~2.1.2", + "phpunit/phpunit": "^11.5.42", + "psalm/plugin-phpunit": "^0.19.5", + "vimeo/psalm": "^6.13.1" + }, + "suggest": { + "laminas/laminas-cache": "Laminas\\Cache component", + "laminas/laminas-db": "Laminas\\Db component", + "laminas/laminas-http": "Laminas\\Http component", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component", + "laminas/laminas-validator": "Laminas\\Validator component", + "mongodb/mongodb": "If you want to use the MongoDB session save handler" + }, + "type": "library", + "extra": { + "laminas": { + "component": "Laminas\\Session", + "config-provider": "Laminas\\Session\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Laminas\\Session\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Object-oriented interface to PHP sessions and storage", + "homepage": "https://laminas.dev", + "keywords": [ + "laminas", + "session" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.laminas.dev/laminas-session/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/laminas/laminas-session/issues", + "rss": "https://github.com/laminas/laminas-session/releases.atom", + "source": "https://github.com/laminas/laminas-session" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2025-11-14T12:52:38+00:00" + }, + { + "name": "laminas/laminas-stdlib", + "version": "3.21.0", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-stdlib.git", + "reference": "b1c81514cfe158aadf724c42b34d3d0a8164c096" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-stdlib/zipball/b1c81514cfe158aadf724c42b34d3d0a8164c096", + "reference": "b1c81514cfe158aadf724c42b34d3d0a8164c096", + "shasum": "" + }, + "require": { + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" + }, + "conflict": { + "zendframework/zend-stdlib": "*" + }, + "require-dev": { + "laminas/laminas-coding-standard": "^3.1.0", + "phpbench/phpbench": "^1.4.1", + "phpunit/phpunit": "^11.5.42", + "psalm/plugin-phpunit": "^0.19.5", + "vimeo/psalm": "^6.13.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Laminas\\Stdlib\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "SPL extensions, array utilities, error handlers, and more", + "homepage": "https://laminas.dev", + "keywords": [ + "laminas", + "stdlib" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.laminas.dev/laminas-stdlib/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/laminas/laminas-stdlib/issues", + "rss": "https://github.com/laminas/laminas-stdlib/releases.atom", + "source": "https://github.com/laminas/laminas-stdlib" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2025-10-11T18:13:12+00:00" + }, + { + "name": "laminas/laminas-stratigility", + "version": "3.14.1", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-stratigility.git", + "reference": "01368bf7b0be76a5f15d0727607da8253d451225" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-stratigility/zipball/01368bf7b0be76a5f15d0727607da8253d451225", + "reference": "01368bf7b0be76a5f15d0727607da8253d451225", + "shasum": "" + }, + "require": { + "fig/http-message-util": "^1.1", + "laminas/laminas-escaper": "^2.10.0", + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", + "psr/http-message": "^1.0 || ^2.0", + "psr/http-server-middleware": "^1.0.2" + }, + "conflict": { + "zendframework/zend-stratigility": "*" + }, + "require-dev": { + "laminas/laminas-coding-standard": "^3.1.0", + "laminas/laminas-diactoros": "^2.25 || ^3.8.0", + "phpunit/phpunit": "^10.5.58", + "psalm/plugin-phpunit": "^0.19.0", + "vimeo/psalm": "^5.26.1" + }, + "suggest": { + "psr/http-message-implementation": "Please install a psr/http-message-implementation to consume Stratigility; e.g., laminas/laminas-diactoros" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions/double-pass-middleware.php", + "src/functions/host.php", + "src/functions/middleware.php", + "src/functions/path.php", + "src/functions/double-pass-middleware.legacy.php", + "src/functions/host.legacy.php", + "src/functions/middleware.legacy.php", + "src/functions/path.legacy.php" + ], + "psr-4": { + "Laminas\\Stratigility\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "PSR-7 middleware foundation for building and dispatching middleware pipelines", + "homepage": "https://laminas.dev", + "keywords": [ + "http", + "laminas", + "middleware", + "psr-15", + "psr-7" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.laminas.dev/laminas-stratigility/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/laminas/laminas-stratigility/issues", + "rss": "https://github.com/laminas/laminas-stratigility/releases.atom", + "source": "https://github.com/laminas/laminas-stratigility" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2026-05-03T17:52:33+00:00" + }, + { + "name": "laminas/laminas-text", + "version": "2.13.0", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-text.git", + "reference": "d18a04f97dad73f55ca1c66a70a981e663d13003" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-text/zipball/d18a04f97dad73f55ca1c66a70a981e663d13003", + "reference": "d18a04f97dad73f55ca1c66a70a981e663d13003", + "shasum": "" + }, + "require": { + "laminas/laminas-servicemanager": "^3.22.0", + "laminas/laminas-stdlib": "^3.7.1", + "php": "^8.1.0" + }, + "conflict": { + "zendframework/zend-text": "*" + }, + "require-dev": { + "laminas/laminas-coding-standard": "^3.0.1", + "phpunit/phpunit": "^10.5.38", + "psalm/plugin-phpunit": "^0.18.4", + "vimeo/psalm": "^5.26.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Laminas\\Text\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Create FIGlets and text-based tables", + "homepage": "https://laminas.dev", + "keywords": [ + "laminas", + "text" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.laminas.dev/laminas-text/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/laminas/laminas-text/issues", + "rss": "https://github.com/laminas/laminas-text/releases.atom", + "source": "https://github.com/laminas/laminas-text" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "abandoned": true, + "time": "2026-01-27T12:27:12+00:00" + }, + { + "name": "laminas/laminas-translator", + "version": "1.3.0", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-translator.git", + "reference": "1d6bf7d857c3fc310a30f5ea028731b9bf8be9db" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-translator/zipball/1d6bf7d857c3fc310a30f5ea028731b9bf8be9db", + "reference": "1d6bf7d857c3fc310a30f5ea028731b9bf8be9db", + "shasum": "" + }, + "require": { + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" + }, + "require-dev": { + "laminas/laminas-coding-standard": "~3.1.0", + "phpunit/phpunit": "^11.5.46", + "vimeo/psalm": "^6.14.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Laminas\\Translator\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Interfaces for the Translator component of laminas-i18n", + "homepage": "https://laminas.dev", + "keywords": [ + "i18n", + "laminas" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.laminas.dev/laminas-i18n/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/laminas/laminas-translator/issues", + "rss": "https://github.com/laminas/laminas-translator/releases.atom", + "source": "https://github.com/laminas/laminas-translator" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2025-12-30T14:26:45+00:00" + }, + { + "name": "laminas/laminas-uri", + "version": "2.14.0", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-uri.git", + "reference": "e804288f4540988903dc0ede386ce5eec87198df" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-uri/zipball/e804288f4540988903dc0ede386ce5eec87198df", + "reference": "e804288f4540988903dc0ede386ce5eec87198df", + "shasum": "" + }, + "require": { + "laminas/laminas-escaper": "^2.9", + "laminas/laminas-validator": "^2.39 || ^3.0", + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" + }, + "conflict": { + "zendframework/zend-uri": "*" + }, + "require-dev": { + "laminas/laminas-coding-standard": "~2.4.0", + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Laminas\\Uri\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "A component that aids in manipulating and validating » Uniform Resource Identifiers (URIs)", + "homepage": "https://laminas.dev", + "keywords": [ + "laminas", + "uri" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.laminas.dev/laminas-uri/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/laminas/laminas-uri/issues", + "rss": "https://github.com/laminas/laminas-uri/releases.atom", + "source": "https://github.com/laminas/laminas-uri" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2025-12-05T10:02:11+00:00" + }, + { + "name": "laminas/laminas-validator", + "version": "2.65.0", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-validator.git", + "reference": "f0767ca83e0dd91a6f8ccdd4f0887eb132c0ea49" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-validator/zipball/f0767ca83e0dd91a6f8ccdd4f0887eb132c0ea49", + "reference": "f0767ca83e0dd91a6f8ccdd4f0887eb132c0ea49", + "shasum": "" + }, + "require": { + "laminas/laminas-servicemanager": "^3.21.0", + "laminas/laminas-stdlib": "^3.19", + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", + "psr/http-message": "^1.0.1 || ^2.0.0" + }, + "conflict": { + "zendframework/zend-validator": "*" + }, + "require-dev": { + "laminas/laminas-coding-standard": "^2.5", + "laminas/laminas-db": "^2.20", + "laminas/laminas-filter": "^2.41.0", + "laminas/laminas-i18n": "^2.30.0", + "laminas/laminas-session": "^2.25.1", + "laminas/laminas-uri": "^2.13.0", + "phpunit/phpunit": "^10.5.58", + "psalm/plugin-phpunit": "^0.19.0", + "psr/http-client": "^1.0.3", + "psr/http-factory": "^1.1.0", + "vimeo/psalm": "^5.26.1" + }, + "suggest": { + "laminas/laminas-db": "Laminas\\Db component, required by the (No)RecordExists validator", + "laminas/laminas-filter": "Laminas\\Filter component, required by the Digits validator", + "laminas/laminas-i18n": "Laminas\\I18n component to allow translation of validation error messages", + "laminas/laminas-i18n-resources": "Translations of validator messages", + "laminas/laminas-servicemanager": "Laminas\\ServiceManager component to allow using the ValidatorPluginManager and validator chains", + "laminas/laminas-session": "Laminas\\Session component, ^2.8; required by the Csrf validator", + "laminas/laminas-uri": "Laminas\\Uri component, required by the Uri and Sitemap\\Loc validators", + "psr/http-message": "psr/http-message, required when validating PSR-7 UploadedFileInterface instances via the Upload and UploadFile validators" + }, + "type": "library", + "extra": { + "laminas": { + "component": "Laminas\\Validator", + "config-provider": "Laminas\\Validator\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Laminas\\Validator\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Validation classes for a wide range of domains, and the ability to chain validators to create complex validation criteria", + "homepage": "https://laminas.dev", + "keywords": [ + "laminas", + "validator" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.laminas.dev/laminas-validator/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/laminas/laminas-validator/issues", + "rss": "https://github.com/laminas/laminas-validator/releases.atom", + "source": "https://github.com/laminas/laminas-validator" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2025-10-13T14:40:30+00:00" + }, + { + "name": "laminas/laminas-view", + "version": "2.44.0", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-view.git", + "reference": "93f5c8b52af2603f1aa494f1c98ce2013baff18e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-view/zipball/93f5c8b52af2603f1aa494f1c98ce2013baff18e", + "reference": "93f5c8b52af2603f1aa494f1c98ce2013baff18e", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-filter": "*", + "ext-json": "*", + "laminas/laminas-escaper": "^2.5", + "laminas/laminas-eventmanager": "^3.4", + "laminas/laminas-json": "^3.3", + "laminas/laminas-servicemanager": "^3.21.0", + "laminas/laminas-stdlib": "^3.10.1", + "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", + "psr/container": "^1 || ^2" + }, + "conflict": { + "amphp/dns": "<2.1.2", + "amphp/socket": "<2.3.1", + "container-interop/container-interop": "<1.2", + "laminas/laminas-router": "<3.0.1", + "laminas/laminas-session": "<2.12", + "zendframework/zend-view": "*" + }, + "require-dev": { + "laminas/laminas-authentication": "^2.18", + "laminas/laminas-coding-standard": "~3.1.0", + "laminas/laminas-feed": "^2.25.0", + "laminas/laminas-filter": "^2.41", + "laminas/laminas-http": "^2.22", + "laminas/laminas-i18n": "^2.30.0", + "laminas/laminas-modulemanager": "^2.18", + "laminas/laminas-mvc": "^3.8.0", + "laminas/laminas-mvc-i18n": "^1.9", + "laminas/laminas-mvc-plugin-flashmessenger": "^1.11.0", + "laminas/laminas-navigation": "^2.21.0", + "laminas/laminas-paginator": "^2.20.0", + "laminas/laminas-permissions-acl": "^2.17", + "laminas/laminas-router": "^3.14.0", + "laminas/laminas-uri": "^2.13", + "phpunit/phpunit": "^10.5.58", + "psalm/plugin-phpunit": "^0.19.5", + "vimeo/psalm": "^6.13.1" + }, + "suggest": { + "laminas/laminas-authentication": "Laminas\\Authentication component", + "laminas/laminas-feed": "Laminas\\Feed component", + "laminas/laminas-filter": "Laminas\\Filter component", + "laminas/laminas-http": "Laminas\\Http component", + "laminas/laminas-i18n": "Laminas\\I18n component", + "laminas/laminas-mvc": "Laminas\\Mvc component", + "laminas/laminas-mvc-plugin-flashmessenger": "laminas-mvc-plugin-flashmessenger component, if you want to use the FlashMessenger view helper with laminas-mvc versions 3 and up", + "laminas/laminas-navigation": "Laminas\\Navigation component", + "laminas/laminas-paginator": "Laminas\\Paginator component", + "laminas/laminas-permissions-acl": "Laminas\\Permissions\\Acl component", + "laminas/laminas-uri": "Laminas\\Uri component" + }, + "bin": [ + "bin/templatemap_generator.php" + ], + "type": "library", + "autoload": { + "psr-4": { + "Laminas\\View\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Flexible view layer supporting and providing multiple view layers, helpers, and more", + "homepage": "https://laminas.dev", + "keywords": [ + "laminas", + "view" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.laminas.dev/laminas-view/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/laminas/laminas-view/issues", + "rss": "https://github.com/laminas/laminas-view/releases.atom", + "source": "https://github.com/laminas/laminas-view" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2025-11-17T01:59:08+00:00" + }, + { + "name": "mezzio/mezzio", + "version": "3.23.2", + "source": { + "type": "git", + "url": "https://github.com/mezzio/mezzio.git", + "reference": "988d39687683c9ae70d213c68c75c89965caad30" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mezzio/mezzio/zipball/988d39687683c9ae70d213c68c75c89965caad30", + "reference": "988d39687683c9ae70d213c68c75c89965caad30", + "shasum": "" + }, + "require": { + "fig/http-message-util": "^1.1.5", + "laminas/laminas-httphandlerrunner": "^2.1", + "laminas/laminas-stratigility": "^3.5", + "mezzio/mezzio-router": "^3.15.0", + "mezzio/mezzio-template": "^2.2", + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", + "psr/container": "^1.0||^2.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.0.1 || ^2.0.0", + "psr/http-server-middleware": "^1.0", + "webmozart/assert": "^1.11.0" + }, + "conflict": { + "container-interop/container-interop": "<1.2.0", + "filp/whoops": "<2.14.4", + "laminas/laminas-diactoros": "<1.7.1", + "laminas/laminas-http": "<2.15.0", + "laminas/laminas-router": "<3.5.0", + "laminas/laminas-stdlib": "<3.6.0", + "zendframework/zend-expressive": "*" + }, + "require-dev": { + "filp/whoops": "^2.18.4", + "laminas/laminas-coding-standard": "^3.1.0", + "laminas/laminas-diactoros": "^3.8.0", + "laminas/laminas-servicemanager": "^3.23.1", + "mezzio/mezzio-fastroute": "^3.14", + "mezzio/mezzio-laminasrouter": "^3.12", + "phpunit/phpunit": "^11.5.42", + "psalm/plugin-phpunit": "^0.19.5", + "vimeo/psalm": "^6.13.1" + }, + "suggest": { + "filp/whoops": "^2.1 to use the Whoops error handler", + "laminas/laminas-auradi-config": "^2.0 to use Aura.Di dependency injection container", + "laminas/laminas-pimple-config": "^1.0 to use Pimple for dependency injection container", + "laminas/laminas-servicemanager": "^3.3 to use laminas-servicemanager for dependency injection", + "mezzio/mezzio-helpers": "^3.0 for its UrlHelper, ServerUrlHelper, and BodyParseMiddleware", + "mezzio/mezzio-tooling": "^1.0 for migration and development tools; require it with the --dev flag", + "psr/http-message-implementation": "Please install a psr/http-message-implementation to consume Mezzio; e.g., laminas/laminas-diactoros" + }, + "bin": [ + "bin/mezzio-tooling" + ], + "type": "library", + "extra": { + "laminas": { + "config-provider": "Mezzio\\ConfigProvider" + } + }, + "autoload": { + "files": [ + "src/constants.php", + "src/constants.legacy.php" + ], + "psr-4": { + "Mezzio\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "PSR-15 Middleware Microframework", + "homepage": "https://mezzio.dev", + "keywords": [ + "PSR-11", + "http", + "laminas", + "mezzio", + "middleware", + "psr", + "psr-15", + "psr-7" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.mezzio.dev/mezzio/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/mezzio/mezzio/issues", + "rss": "https://github.com/mezzio/mezzio/releases.atom", + "source": "https://github.com/mezzio/mezzio" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2025-10-22T10:56:04+00:00" + }, + { + "name": "mezzio/mezzio-authentication", + "version": "1.13.0", + "source": { + "type": "git", + "url": "https://github.com/mezzio/mezzio-authentication.git", + "reference": "9eec425ab3386ff09dfb686d55887871ba39cc33" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mezzio/mezzio-authentication/zipball/9eec425ab3386ff09dfb686d55887871ba39cc33", + "reference": "9eec425ab3386ff09dfb686d55887871ba39cc33", + "shasum": "" + }, + "require": { + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", + "psr/container": "^1.0 || ^2.0", + "psr/http-message": "^1.0.1 || ^2.0.0", + "psr/http-server-middleware": "^1.0", + "webmozart/assert": "^1.11.0 || ^2.0.0" + }, + "conflict": { + "container-interop/container-interop": "<1.2.0", + "zendframework/zend-expressive-authentication": "*" + }, + "require-dev": { + "ext-sqlite3": "*", + "laminas/laminas-coding-standard": "~3.1.0", + "phpunit/phpunit": "^11.5.42", + "psalm/plugin-phpunit": "^0.19.5", + "vimeo/psalm": "^6.13.1" + }, + "suggest": { + "ext-pdo": "*: for use with the PDO-backed UserRepositoryInterface implementation", + "mezzio/mezzio-authentication-basic": "Provides an HTTP Basic Authentication AuthenticationInterface implementation", + "mezzio/mezzio-authentication-laminasauthentication": "Provides a laminas-authentication AuthenticationInterface implementation", + "mezzio/mezzio-authentication-session": "Provides a username/password + session AuthenticationInterface implementation" + }, + "type": "library", + "extra": { + "laminas": { + "config-provider": "Mezzio\\Authentication\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Mezzio\\Authentication\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Authentication middleware for Mezzio and PSR-7 applications", + "homepage": "https://mezzio.dev", + "keywords": [ + "Authentication", + "http", + "laminas", + "mezzio", + "middleware", + "psr-15", + "psr-7" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.mezzio.dev/mezzio-authentication/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/mezzio/mezzio-authentication/issues", + "rss": "https://github.com/mezzio/mezzio-authentication/releases.atom", + "source": "https://github.com/mezzio/mezzio-authentication" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2026-02-27T12:38:47+00:00" + }, + { + "name": "mezzio/mezzio-flash", + "version": "1.10.0", + "source": { + "type": "git", + "url": "https://github.com/mezzio/mezzio-flash.git", + "reference": "b47a023f5afec7f26af160ae5fa10fdb2463e185" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mezzio/mezzio-flash/zipball/b47a023f5afec7f26af160ae5fa10fdb2463e185", + "reference": "b47a023f5afec7f26af160ae5fa10fdb2463e185", + "shasum": "" + }, + "require": { + "mezzio/mezzio-session": "^1.0", + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", + "psr/http-server-middleware": "^1.0" + }, + "conflict": { + "zendframework/zend-expressive-flash": "*" + }, + "require-dev": { + "laminas/laminas-coding-standard": "~3.1.0", + "phpunit/phpunit": "^11.5.42", + "psalm/plugin-phpunit": "^0.19.5", + "vimeo/psalm": "^6.13.1" + }, + "type": "library", + "extra": { + "laminas": { + "config-provider": "Mezzio\\Flash\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Mezzio\\Flash\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Flash messages for PSR-7 and PSR-15 applications using mezzio-session", + "homepage": "https://mezzio.dev", + "keywords": [ + "flash", + "laminas", + "mezzio", + "middleware", + "psr-15", + "psr-7", + "session" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.mezzio.dev/mezzio-flash/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/mezzio/mezzio-flash/issues", + "rss": "https://github.com/mezzio/mezzio-flash/releases.atom", + "source": "https://github.com/mezzio/mezzio-flash" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2025-10-12T20:02:20+00:00" + }, + { + "name": "mezzio/mezzio-helpers", + "version": "5.20.0", + "source": { + "type": "git", + "url": "https://github.com/mezzio/mezzio-helpers.git", + "reference": "a26ba04bd449d5cdb5ad38b17ce672365dbc9d90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mezzio/mezzio-helpers/zipball/a26ba04bd449d5cdb5ad38b17ce672365dbc9d90", + "reference": "a26ba04bd449d5cdb5ad38b17ce672365dbc9d90", + "shasum": "" + }, + "require": { + "mezzio/mezzio-router": "^3.18 || ^4.0", + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", + "psr/container": "^1.0 || ^2.0", + "psr/http-message": "^1.0.1 || ^2.0.0", + "psr/http-server-middleware": "^1.0" + }, + "conflict": { + "amphp/amp": "<2.6.4", + "amphp/dns": "<2.1.2", + "amphp/socket": "<2.3.1", + "zendframework/zend-expressive-helpers": "*" + }, + "require-dev": { + "ext-json": "*", + "laminas/laminas-coding-standard": "~3.1.0", + "laminas/laminas-diactoros": "^3.6", + "phpunit/phpunit": "^11.5.42", + "psalm/plugin-phpunit": "^0.19.5", + "vimeo/psalm": "^6.13.1" + }, + "suggest": { + "ext-json": "If you wish to use the JsonStrategy with BodyParamsMiddleware" + }, + "type": "library", + "extra": { + "laminas": { + "config-provider": "Mezzio\\Helper\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Mezzio\\Helper\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Helper/Utility classes for Mezzio", + "homepage": "https://mezzio.dev", + "keywords": [ + "http", + "laminas", + "mezzio", + "middleware", + "psr", + "psr-7" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.mezzio.dev/mezzio/features/helpers/intro/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/mezzio/mezzio-helpers/issues", + "rss": "https://github.com/mezzio/mezzio-helpers/releases.atom", + "source": "https://github.com/mezzio/mezzio-helpers" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2025-10-11T08:40:34+00:00" + }, + { + "name": "mezzio/mezzio-laminasrouter", + "version": "3.12.0", + "source": { + "type": "git", + "url": "https://github.com/mezzio/mezzio-laminasrouter.git", + "reference": "99be8034f49a0ecebe3de464cece6fefa40c276c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mezzio/mezzio-laminasrouter/zipball/99be8034f49a0ecebe3de464cece6fefa40c276c", + "reference": "99be8034f49a0ecebe3de464cece6fefa40c276c", + "shasum": "" + }, + "require": { + "fig/http-message-util": "^1.1.5", + "laminas/laminas-psr7bridge": "^1.0.0", + "laminas/laminas-router": "^3.10.0", + "mezzio/mezzio-router": "^3.14 || ^4.0", + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", + "psr/http-message": "^1.0.1 || ^2.0.0" + }, + "conflict": { + "zendframework/zend-expressive-zendrouter": "*" + }, + "require-dev": { + "laminas/laminas-coding-standard": "~3.1.0", + "laminas/laminas-diactoros": "^2.25.2 || ^3.8.0", + "laminas/laminas-i18n": "^2.30.0", + "laminas/laminas-stratigility": "^4.2.0", + "phpunit/phpunit": "^11.5.42", + "psalm/plugin-phpunit": "^0.19.5", + "vimeo/psalm": "^6.13.1" + }, + "type": "library", + "extra": { + "laminas": { + "config-provider": "Mezzio\\Router\\LaminasRouter\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Mezzio\\Router\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "laminas-mvc router support for Mezzio", + "homepage": "https://mezzio.dev", + "keywords": [ + "http", + "laminas", + "mezzio", + "middleware", + "psr", + "psr-7" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.mezzio.dev/mezzio/features/router/laminas-router/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/mezzio/mezzio-laminasrouter/issues", + "rss": "https://github.com/mezzio/mezzio-laminasrouter/releases.atom", + "source": "https://github.com/mezzio/mezzio-laminasrouter" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2025-10-13T10:23:08+00:00" + }, + { + "name": "mezzio/mezzio-laminasviewrenderer", + "version": "2.19.0", + "source": { + "type": "git", + "url": "https://github.com/mezzio/mezzio-laminasviewrenderer.git", + "reference": "ae8439e46908e966d5e288109158b4c6d9508f31" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mezzio/mezzio-laminasviewrenderer/zipball/ae8439e46908e966d5e288109158b4c6d9508f31", + "reference": "ae8439e46908e966d5e288109158b4c6d9508f31", + "shasum": "" + }, + "require": { + "laminas/laminas-servicemanager": "^3.21.0", + "laminas/laminas-view": "^2.27.0", + "mezzio/mezzio-helpers": "^5.15.0", + "mezzio/mezzio-router": "^3.16.1 || ^4.0.0", + "mezzio/mezzio-template": "^2.8.0", + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", + "psr/container": "^1.0", + "psr/http-message": "^1.0.1 || ^2.0.0" + }, + "conflict": { + "container-interop/container-interop": "<1.2.0", + "zendframework/zend-expressive-zendviewrenderer": "*" + }, + "require-dev": { + "laminas/laminas-coding-standard": "~3.1.0", + "phpunit/phpunit": "^11.5.42", + "psalm/plugin-phpunit": "^0.19.5", + "vimeo/psalm": "^6.13.1" + }, + "type": "library", + "extra": { + "laminas": { + "config-provider": "Mezzio\\LaminasView\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Mezzio\\LaminasView\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "laminas-view PhpRenderer integration for Mezzio", + "homepage": "https://mezzio.dev", + "keywords": [ + "http", + "laminas", + "mezzio", + "middleware", + "psr", + "psr-7" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.mezzio.dev/mezzio/features/template/laminas-view/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/mezzio/mezzio-laminasviewrenderer/issues", + "rss": "https://github.com/mezzio/mezzio-laminasviewrenderer/releases.atom", + "source": "https://github.com/mezzio/mezzio-laminasviewrenderer" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2025-10-12T19:04:54+00:00" + }, + { + "name": "mezzio/mezzio-router", + "version": "3.20.0", + "source": { + "type": "git", + "url": "https://github.com/mezzio/mezzio-router.git", + "reference": "be4de58dc8822b4af653dc554f963b4cb1e23355" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mezzio/mezzio-router/zipball/be4de58dc8822b4af653dc554f963b4cb1e23355", + "reference": "be4de58dc8822b4af653dc554f963b4cb1e23355", + "shasum": "" + }, + "require": { + "fig/http-message-util": "^1.1.5", + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", + "psr/container": "^1.1.2 || ^2.0", + "psr/http-factory": "^1.0.2", + "psr/http-message": "^1.0.1 || ^2.0.0", + "psr/http-server-middleware": "^1.0.2", + "webmozart/assert": "^1.11 || ^2.0" + }, + "conflict": { + "mezzio/mezzio": "<3.5", + "zendframework/zend-expressive-router": "*" + }, + "require-dev": { + "laminas/laminas-coding-standard": "~3.1.0", + "laminas/laminas-diactoros": "^3.6.0", + "laminas/laminas-servicemanager": "^4.4.0", + "laminas/laminas-stratigility": "^4.2.0", + "phpunit/phpunit": "^11.5.42", + "psalm/plugin-phpunit": "^0.19.0", + "vimeo/psalm": "^6.13.1" + }, + "suggest": { + "mezzio/mezzio-aurarouter": "^3.0 to use the Aura.Router routing adapter", + "mezzio/mezzio-fastroute": "^3.0 to use the FastRoute routing adapter", + "mezzio/mezzio-laminasrouter": "^3.0 to use the laminas-router routing adapter" + }, + "type": "library", + "extra": { + "laminas": { + "config-provider": "Mezzio\\Router\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Mezzio\\Router\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Router subcomponent for Mezzio", + "homepage": "https://mezzio.dev", + "keywords": [ + "http", + "laminas", + "mezzio", + "middleware", + "psr", + "psr-7" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.mezzio.dev/mezzio/features/router/intro/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/mezzio/mezzio-router/issues", + "rss": "https://github.com/mezzio/mezzio-router/releases.atom", + "source": "https://github.com/mezzio/mezzio-router" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2026-02-19T15:49:48+00:00" + }, + { + "name": "mezzio/mezzio-session", + "version": "1.17.0", + "source": { + "type": "git", + "url": "https://github.com/mezzio/mezzio-session.git", + "reference": "215095b4445283f2d527c1edc3338aa1ffbe3067" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mezzio/mezzio-session/zipball/215095b4445283f2d527c1edc3338aa1ffbe3067", + "reference": "215095b4445283f2d527c1edc3338aa1ffbe3067", + "shasum": "" + }, + "require": { + "dflydev/fig-cookies": "^3.0", + "ext-json": "*", + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", + "psr/container": "^1.0 || ^2.0", + "psr/http-server-middleware": "^1.0" + }, + "conflict": { + "zendframework/zend-expressive-session": "*" + }, + "require-dev": { + "laminas/laminas-coding-standard": "~3.1.0", + "laminas/laminas-diactoros": "^3.6.0", + "phpunit/phpunit": "^11.5.42", + "psalm/plugin-phpunit": "^0.19.5", + "vimeo/psalm": "^6.13.1" + }, + "suggest": { + "mezzio/mezzio-csrf": "^1.0 || ^1.0-dev for CSRF protection capabilities", + "mezzio/mezzio-flash": "^1.0 || ^1.0-dev for flash message capabilities", + "mezzio/mezzio-session-ext": "^1.0 || ^1.0-dev for an ext-session persistence adapter" + }, + "type": "library", + "extra": { + "laminas": { + "config-provider": "Mezzio\\Session\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Mezzio\\Session\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Session container and middleware for PSR-7 applications", + "homepage": "https://mezzio.dev", + "keywords": [ + "laminas", + "mezzio", + "middleware", + "psr-7", + "session" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.mezzio.dev/mezzio-session/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/mezzio/mezzio-session/issues", + "rss": "https://github.com/mezzio/mezzio-session/releases.atom", + "source": "https://github.com/mezzio/mezzio-session" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2025-10-11T18:36:13+00:00" + }, + { + "name": "mezzio/mezzio-session-ext", + "version": "1.21.0", + "source": { + "type": "git", + "url": "https://github.com/mezzio/mezzio-session-ext.git", + "reference": "c8fe92d67961c23ad222653d61fbb004959e40c8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mezzio/mezzio-session-ext/zipball/c8fe92d67961c23ad222653d61fbb004959e40c8", + "reference": "c8fe92d67961c23ad222653d61fbb004959e40c8", + "shasum": "" + }, + "require": { + "ext-session": "*", + "mezzio/mezzio-session": "^1.4", + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" + }, + "conflict": { + "amphp/amp": "<2.6.4", + "zendframework/zend-expressive-session-ext": "*" + }, + "require-dev": { + "amphp/dns": "^1.24 || ^2.1.2", + "amphp/socket": "^1.2.1 || ^2.3.1", + "dflydev/fig-cookies": "^3.2", + "laminas/laminas-coding-standard": "~3.1.0", + "laminas/laminas-diactoros": "^3.7", + "phpunit/phpunit": "^11.5.42", + "psalm/plugin-phpunit": "^0.19.5", + "vimeo/psalm": "^6.13.1" + }, + "type": "library", + "extra": { + "laminas": { + "config-provider": "Mezzio\\Session\\Ext\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Mezzio\\Session\\Ext\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "ext-session persistence adapter for mezzio-session", + "homepage": "https://mezzio.dev", + "keywords": [ + "components", + "ext-session", + "laminas", + "mezzio", + "psr-7", + "session" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.mezzio.dev/mezzio-session-ext/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/mezzio/mezzio-session-ext/issues", + "rss": "https://github.com/mezzio/mezzio-session-ext/releases.atom", + "source": "https://github.com/mezzio/mezzio-session-ext" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2025-10-12T22:31:09+00:00" + }, + { + "name": "mezzio/mezzio-template", + "version": "2.13.0", + "source": { + "type": "git", + "url": "https://github.com/mezzio/mezzio-template.git", + "reference": "ad72bb31036d0639a5c5a502af234217faf6932f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mezzio/mezzio-template/zipball/ad72bb31036d0639a5c5a502af234217faf6932f", + "reference": "ad72bb31036d0639a5c5a502af234217faf6932f", + "shasum": "" + }, + "require": { + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" + }, + "conflict": { + "zendframework/zend-expressive-template": "*" + }, + "require-dev": { + "laminas/laminas-coding-standard": "~3.1.0", + "phpunit/phpunit": "^11.5.42", + "psalm/plugin-phpunit": "^0.19.5", + "vimeo/psalm": "^6.13.1" + }, + "suggest": { + "mezzio/mezzio-laminasviewrenderer": "^2.0 to use the laminas-view PhpRenderer template renderer", + "mezzio/mezzio-platesrenderer": "^2.0 to use the Plates template renderer", + "mezzio/mezzio-twigrenderer": "^2.0 to use the Twig template renderer" + }, + "type": "library", + "autoload": { + "psr-4": { + "Mezzio\\Template\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Template subcomponent for Mezzio", + "homepage": "https://mezzio.dev", + "keywords": [ + "laminas", + "mezzio", + "template" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.mezzio.dev/mezzio/features/template/intro/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/mezzio/mezzio-template/issues", + "rss": "https://github.com/mezzio/mezzio-template/releases.atom", + "source": "https://github.com/mezzio/mezzio-template" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2025-10-11T08:45:28+00:00" + }, + { + "name": "mimmi20/mezzio-generic-authorization", + "version": "4.0.3", + "source": { + "type": "git", + "url": "https://github.com/mimmi20/mezzio-generic-authorization.git", + "reference": "100fbce8935480e8cad642ce1c71a71c4af1d1d4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mimmi20/mezzio-generic-authorization/zipball/100fbce8935480e8cad642ce1c71a71c4af1d1d4", + "reference": "100fbce8935480e8cad642ce1c71a71c4af1d1d4", + "shasum": "" + }, + "require": { + "mezzio/mezzio-authentication": "^1.11.0", + "mezzio/mezzio-router": "^3.18.0", + "php": "~8.3.0 || ~8.4.0 || ~8.5.0", + "psr/container": "^1.1.2 || ^2.0.2", + "psr/http-factory": "^1.1.0", + "psr/http-message": "^1.0.1 || ^2.0", + "psr/http-server-handler": "^1.0.2", + "psr/http-server-middleware": "^1.0.2" + }, + "conflict": { + "container-interop/container-interop": "<1.2.0", + "mezzio/mezzio-authorization": "*" + }, + "require-dev": { + "ext-ctype": "*", + "ext-dom": "*", + "ext-simplexml": "*", + "ext-tokenizer": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "infection/infection": "^0.31.2", + "laminas/laminas-servicemanager": "^4.4.0", + "mimmi20/coding-standard": "^6.2.5", + "nikic/php-parser": "^5.6.1", + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^2.1.30", + "phpstan/phpstan-deprecation-rules": "^2.0.3", + "phpunit/phpunit": "^12.4.0", + "rector/rector": "^2.1.7", + "rector/type-perfect": "^2.1.0", + "shipmonk/composer-dependency-analyser": "^1.8.3", + "symfony/process": "^7.3.4", + "symplify/phpstan-rules": "^14.7.0", + "tomasvotruba/cognitive-complexity": "^1.0.0", + "tomasvotruba/type-coverage": "^2.0.2", + "tomasvotruba/unused-public": "^2.0.1" + }, + "suggest": { + "mimmi20/mezzio-generic-authorization-acl": "provides a laminas-permissions-acl-backed adapter", + "mimmi20/mezzio-generic-authorization-rbac": "provides a laminas-permissions-rbac-backed adapter" + }, + "type": "library", + "extra": { + "laminas": { + "config-provider": "Mimmi20\\Mezzio\\GenericAuthorization\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Mimmi20\\Mezzio\\GenericAuthorization\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Thomas Müller", + "homepage": "https://github.com/mimmi20", + "role": "Developer" + } + ], + "description": "Provides a Generic Authorization middleware for Mezzio.", + "homepage": "https://github.com/mimmi20/mezzio-generic-authorization", + "keywords": [ + "authorization", + "mezzio", + "middleware", + "psr-15", + "psr-7" + ], + "support": { + "issues": "https://github.com/mimmi20/mezzio-generic-authorization/issues", + "source": "https://github.com/mimmi20/mezzio-generic-authorization" + }, + "time": "2025-10-06T17:30:49+00:00" + }, + { + "name": "mimmi20/mezzio-navigation", + "version": "5.0.5", + "source": { + "type": "git", + "url": "https://github.com/mimmi20/mezzio-navigation.git", + "reference": "19fb80664c3fb3d019174a96ad2b39f356961021" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mimmi20/mezzio-navigation/zipball/19fb80664c3fb3d019174a96ad2b39f356961021", + "reference": "19fb80664c3fb3d019174a96ad2b39f356961021", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "laminas/laminas-servicemanager": "^3.23.0", + "laminas/laminas-stdlib": "^3.21.0", + "mimmi20/mezzio-generic-authorization": "^4.0.0", + "php": "~8.3.0 || ~8.4.0 || ~8.5.0", + "psr/container": "^1.1.2", + "psr/http-message": "^2.0", + "psr/http-server-handler": "^1.0.2", + "psr/http-server-middleware": "^1.0.2" + }, + "conflict": { + "container-interop/container-interop": "<1.2.0", + "mezzio/mezzio-authorization": "*" + }, + "require-dev": { + "ext-ctype": "*", + "ext-dom": "*", + "ext-simplexml": "*", + "ext-tokenizer": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "infection/infection": "^0.31.7", + "mezzio/mezzio-helpers": "^5.20.0", + "mezzio/mezzio-router": "^3.18.0", + "mimmi20/coding-standard": "^6.2.5", + "nikic/php-parser": "^5.6.2", + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^2.1.31", + "phpstan/phpstan-deprecation-rules": "^2.0.3", + "phpunit/phpunit": "^12.4.1", + "rector/rector": "^2.2.4", + "rector/type-perfect": "^2.1.1", + "shipmonk/composer-dependency-analyser": "^1.8.3", + "symfony/process": "^7.3.4", + "symplify/phpstan-rules": "^14.8.0", + "tomasvotruba/cognitive-complexity": "^1.0.0", + "tomasvotruba/type-coverage": "^2.0.2", + "tomasvotruba/unused-public": "^2.0.1" + }, + "suggest": { + "laminas/laminas-config": "to provide page configuration (optional, as arrays and Traversables are also allowed)", + "mezzio/mezzio-router": "to use router-based URI generation with pages", + "mimmi20/mezzio-generic-authorization": "to provide access restrictions to pages", + "mimmi20/mezzio-navigation-laminasviewrenderer": "to use the navigation view helpers" + }, + "type": "library", + "extra": { + "laminas": { + "config-provider": "Mimmi20\\Mezzio\\Navigation\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Mimmi20\\Mezzio\\Navigation\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Thomas Müller", + "homepage": "https://github.com/mimmi20", + "role": "Developer" + } + ], + "description": "Provides a Navigation for Mezzio.", + "homepage": "https://github.com/mimmi20/mezzio-navigation", + "keywords": [ + "laminas", + "mezzio" + ], + "support": { + "issues": "https://github.com/mimmi20/mezzio-navigation/issues", + "source": "https://github.com/mimmi20/mezzio-navigation" + }, + "time": "2025-11-10T17:14:22+00:00" + }, + { + "name": "mimmi20/mezzio-navigation-laminasviewrenderer", + "version": "7.0.1", + "source": { + "type": "git", + "url": "https://github.com/mimmi20/mezzio-navigation-laminasviewrenderer.git", + "reference": "c8c16930193748c1911a63c0c943213dd168e608" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mimmi20/mezzio-navigation-laminasviewrenderer/zipball/c8c16930193748c1911a63c0c943213dd168e608", + "reference": "c8c16930193748c1911a63c0c943213dd168e608", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "laminas/laminas-servicemanager": "^3.24.0", + "laminas/laminas-stdlib": "^3.21.0", + "laminas/laminas-view": "^2.44.0", + "mezzio/mezzio-helpers": "^5.20.0", + "mezzio/mezzio-laminasviewrenderer": "^2.19.0", + "mimmi20/mezzio-navigation": "^5.0.0", + "php": "~8.3.0 || ~8.4.0 || ~8.5.0 || ~8.6.0", + "psr/container": "^1.1.2 || ^2.0.2" + }, + "conflict": { + "container-interop/container-interop": "<1.2.0", + "mezzio/mezzio-authorization": "*", + "mezzio/mezzio-authorization-acl": "*" + }, + "require-dev": { + "ergebnis/composer-normalize": "^2.50.0", + "ext-ctype": "*", + "ext-dom": "*", + "ext-intl": "*", + "ext-libxml": "*", + "ext-simplexml": "*", + "ext-tokenizer": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "infection/infection": "^0.32.6", + "jbelien/phpstan-sarif-formatter": "^1.2.0", + "laminas/laminas-i18n": "^2.32.1", + "laminas/laminas-permissions-acl": "^2.18.0", + "laminas/laminas-uri": "^2.14.0", + "laminas/laminas-validator": "^2.64.1", + "mezzio/mezzio-authentication": "^1.13.0", + "mezzio/mezzio-router": "^3.18.0", + "mimmi20/coding-standard": "^6.2.5", + "mimmi20/mezzio-generic-authorization": "^4.0.0", + "mimmi20/mezzio-generic-authorization-acl": "^4.0.1", + "nikic/php-parser": "^5.7.0", + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^2.1.46", + "phpstan/phpstan-deprecation-rules": "^2.0.4", + "phpunit/phpunit": "^12.5.9", + "psr/http-message": "^2.0.0", + "psr/http-server-middleware": "^1.0", + "rector/rector": "^2.4.0", + "rector/type-perfect": "^2.1.2", + "shipmonk/composer-dependency-analyser": "^1.8.4", + "symfony/process": "^7.3.4", + "symplify/phpstan-rules": "^14.9.11", + "tomasvotruba/cognitive-complexity": "^1.1.0", + "tomasvotruba/type-coverage": "^2.1.0", + "tomasvotruba/unused-public": "^2.2.0" + }, + "suggest": { + "ext-dom": "required to use the Sitemap ViewHelper", + "ext-intl": "required to use the Translator", + "ext-libxml": "required to use the Sitemap ViewHelper", + "laminas/laminas-config": "^3.5.0, to provide page configuration (optional, as arrays and Traversables are also allowed)", + "laminas/laminas-i18n": "^2.11.0, required to use the Translator", + "laminas/laminas-servicemanager": "^3.6.4, to use the navigation factories", + "laminas/laminas-uri": "^2.7.1 || ^2.8.0, required to use the Sitemap ViewHelper", + "laminas/laminas-validator": "^2.13.5 || ^2.14.0, required to use the Sitemap ViewHelper", + "mimmi20/mezzio-generic-authorization": "^1.0.1, to use authrization roles or privileges" + }, + "type": "library", + "extra": { + "laminas": { + "config-provider": "Mimmi20\\Mezzio\\Navigation\\LaminasView\\ConfigProvider" + }, + "composer-normalize": { + "indent-size": 2, + "indent-style": "space" + } + }, + "autoload": { + "psr-4": { + "Mimmi20\\Mezzio\\Navigation\\LaminasView\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Thomas Müller", + "homepage": "https://github.com/mimmi20", + "role": "Developer" + } + ], + "description": "Provides viewhelper for Mezzio Navigation", + "homepage": "https://github.com/mimmi20/mezzio-navigation-laminasviewrenderer", + "support": { + "issues": "https://github.com/mimmi20/mezzio-navigation-laminasviewrenderer/issues", + "source": "https://github.com/mimmi20/mezzio-navigation-laminasviewrenderer" + }, + "time": "2026-04-29T17:51:10+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v5.7.0", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dca41cd15c2ac9d055ad70dbfd011130757d1f82", + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-json": "*", + "ext-tokenizer": "*", + "php": ">=7.4" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v5.7.0" + }, + "time": "2025-12-06T11:56:16+00:00" + }, + { + "name": "psr/cache", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/cache.git", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Cache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for caching libraries", + "keywords": [ + "cache", + "psr", + "psr-6" + ], + "support": { + "source": "https://github.com/php-fig/cache/tree/3.0.0" + }, + "time": "2021-02-03T23:26:27+00:00" + }, + { + "name": "psr/clock", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/clock.git", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Clock\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for reading the clock.", + "homepage": "https://github.com/php-fig/clock", + "keywords": [ + "clock", + "now", + "psr", + "psr-20", + "time" + ], + "support": { + "issues": "https://github.com/php-fig/clock/issues", + "source": "https://github.com/php-fig/clock/tree/1.0.0" + }, + "time": "2022-11-25T14:36:26+00:00" + }, + { + "name": "psr/container", + "version": "1.1.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "513e0666f7216c7459170d56df27dfcefe1689ea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea", + "reference": "513e0666f7216c7459170d56df27dfcefe1689ea", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/1.1.2" + }, + "time": "2021-11-05T16:50:12+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory" + }, + "time": "2024-04-15T12:06:14+00:00" + }, + { + "name": "psr/http-message", + "version": "2.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/2.0" + }, + "time": "2023-04-04T09:54:51+00:00" + }, + { + "name": "psr/http-server-handler", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-server-handler.git", + "reference": "84c4fb66179be4caaf8e97bd239203245302e7d4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-server-handler/zipball/84c4fb66179be4caaf8e97bd239203245302e7d4", + "reference": "84c4fb66179be4caaf8e97bd239203245302e7d4", + "shasum": "" + }, + "require": { + "php": ">=7.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Server\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP server-side request handler", + "keywords": [ + "handler", + "http", + "http-interop", + "psr", + "psr-15", + "psr-7", + "request", + "response", + "server" + ], + "support": { + "source": "https://github.com/php-fig/http-server-handler/tree/1.0.2" + }, + "time": "2023-04-10T20:06:20+00:00" + }, + { + "name": "psr/http-server-middleware", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-server-middleware.git", + "reference": "c1481f747daaa6a0782775cd6a8c26a1bf4a3829" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-server-middleware/zipball/c1481f747daaa6a0782775cd6a8c26a1bf4a3829", + "reference": "c1481f747daaa6a0782775cd6a8c26a1bf4a3829", + "shasum": "" + }, + "require": { + "php": ">=7.0", + "psr/http-message": "^1.0 || ^2.0", + "psr/http-server-handler": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Server\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP server-side middleware", + "keywords": [ + "http", + "http-interop", + "middleware", + "psr", + "psr-15", + "psr-7", + "request", + "response" + ], + "support": { + "issues": "https://github.com/php-fig/http-server-middleware/issues", + "source": "https://github.com/php-fig/http-server-middleware/tree/1.0.2" + }, + "time": "2023-04-11T06:14:47+00:00" + }, + { + "name": "psr/log", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.2" + }, + "time": "2024-09-11T13:17:53+00:00" + }, + { + "name": "roave/psr-container-doctrine", + "version": "6.1.0", + "source": { + "type": "git", + "url": "https://github.com/Roave/psr-container-doctrine.git", + "reference": "fee8378c681942f994a2d042ffe084b7cf51de5e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Roave/psr-container-doctrine/zipball/fee8378c681942f994a2d042ffe084b7cf51de5e", + "reference": "fee8378c681942f994a2d042ffe084b7cf51de5e", + "shasum": "" + }, + "require": { + "doctrine/common": "^3.5.0", + "doctrine/dbal": "^4.3.4", + "doctrine/event-manager": "^2.0.1", + "doctrine/migrations": "^3.9.4", + "doctrine/orm": "^3.5.2", + "doctrine/persistence": "^3.4.0 || ^4.1.1", + "php": "~8.4.0 || ~8.5.0", + "psr/cache": "^2.0.0 || ^3.0.0", + "psr/container": "^1.1.2 || ^2.0.2" + }, + "require-dev": { + "doctrine/coding-standard": "^14.0.0", + "phpunit/phpunit": "^12.4.1", + "psalm/plugin-phpunit": "^0.19.5", + "symfony/yaml": "^7.3.3", + "vimeo/psalm": "^6.13.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Roave\\PsrContainerDoctrine\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Ben Scholzen", + "homepage": "https://github.com/dasprid" + }, + { + "name": "James Titcumb", + "email": "james@asgrim.com", + "homepage": "https://github.com/asgrim" + } + ], + "description": "Doctrine Factories for PSR-11 Containers", + "homepage": "https://github.com/Roave/psr-container-doctrine", + "support": { + "issues": "https://github.com/Roave/psr-container-doctrine/issues", + "source": "https://github.com/Roave/psr-container-doctrine/tree/6.1.0" + }, + "time": "2025-10-23T13:28:05+00:00" + }, + { + "name": "symfony/console", + "version": "v8.0.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "7113778e2e91f4709cb3194a75dfa9c0d028d94d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/7113778e2e91f4709cb3194a75dfa9c0d028d94d", + "reference": "7113778e2e91f4709cb3194a75dfa9c0d028d94d", + "shasum": "" + }, + "require": { + "php": ">=8.4", + "symfony/polyfill-mbstring": "^1.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^7.4|^8.0" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/event-dispatcher": "^7.4|^8.0", + "symfony/http-foundation": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/lock": "^7.4|^8.0", + "symfony/messenger": "^7.4|^8.0", + "symfony/process": "^7.4|^8.0", + "symfony/stopwatch": "^7.4|^8.0", + "symfony/var-dumper": "^7.4|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command-line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v8.0.9" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-04-29T15:02:55+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.7.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "50f59d1f3ca46d41ac911f97a78626b6756af35b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/50f59d1f3ca46d41ac911f97a78626b6756af35b", + "reference": "50f59d1f3ca46d41ac911f97a78626b6756af35b", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.7-dev" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.7.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-04-13T15:52:40+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.37.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "141046a8f9477948ff284fa65be2095baafb94f2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/141046a8f9477948ff284fa65be2095baafb94f2", + "reference": "141046a8f9477948ff284fa65be2095baafb94f2", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.37.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-04-10T16:19:22+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.37.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "4864388bfbd3001ce88e234fab652acd91fdc57e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/4864388bfbd3001ce88e234fab652acd91fdc57e", + "reference": "4864388bfbd3001ce88e234fab652acd91fdc57e", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.37.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-04-26T13:13:48+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.37.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "3833d7255cc303546435cb650316bff708a1c75c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", + "reference": "3833d7255cc303546435cb650316bff708a1c75c", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.37.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.37.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "6a21eb99c6973357967f6ce3708cd55a6bec6315" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6a21eb99c6973357967f6ce3708cd55a6bec6315", + "reference": "6a21eb99c6973357967f6ce3708cd55a6bec6315", + "shasum": "" + }, + "require": { + "ext-iconv": "*", + "php": ">=7.2" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.37.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-04-10T17:25:58+00:00" + }, + { + "name": "symfony/polyfill-php84", + "version": "v1.37.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php84.git", + "reference": "88486db2c389b290bf87ff1de7ebc1e13e42bb06" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/88486db2c389b290bf87ff1de7ebc1e13e42bb06", + "reference": "88486db2c389b290bf87ff1de7ebc1e13e42bb06", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php84\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.4+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php84/tree/v1.37.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-04-10T18:47:49+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v3.7.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "d25d82433a80eba6aa0e6c24b61d7370d99e444a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/d25d82433a80eba6aa0e6c24b61d7370d99e444a", + "reference": "d25d82433a80eba6aa0e6c24b61d7370d99e444a", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.7-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v3.7.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-03-28T09:44:51+00:00" + }, + { + "name": "symfony/stopwatch", + "version": "v8.0.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/stopwatch.git", + "reference": "85954ed72d5440ea4dc9a10b7e49e01df766ffa3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/85954ed72d5440ea4dc9a10b7e49e01df766ffa3", + "reference": "85954ed72d5440ea4dc9a10b7e49e01df766ffa3", + "shasum": "" + }, + "require": { + "php": ">=8.4", + "symfony/service-contracts": "^2.5|^3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Stopwatch\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides a way to profile code", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/stopwatch/tree/v8.0.8" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-03-30T15:14:47+00:00" + }, + { + "name": "symfony/string", + "version": "v8.0.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "ae9488f874d7603f9d2dfbf120203882b645d963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/ae9488f874d7603f9d2dfbf120203882b645d963", + "reference": "ae9488f874d7603f9d2dfbf120203882b645d963", + "shasum": "" + }, + "require": { + "php": ">=8.4", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-intl-grapheme": "^1.33", + "symfony/polyfill-intl-normalizer": "^1.0", + "symfony/polyfill-mbstring": "^1.0" + }, + "conflict": { + "symfony/translation-contracts": "<2.5" + }, + "require-dev": { + "symfony/emoji": "^7.4|^8.0", + "symfony/http-client": "^7.4|^8.0", + "symfony/intl": "^7.4|^8.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^7.4|^8.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "homepage": "https://symfony.com", + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], + "support": { + "source": "https://github.com/symfony/string/tree/v8.0.8" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-03-30T15:14:47+00:00" + }, + { + "name": "symfony/var-exporter", + "version": "v8.0.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-exporter.git", + "reference": "24cf67be4dd0926e4413635418682f4fff831412" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/24cf67be4dd0926e4413635418682f4fff831412", + "reference": "24cf67be4dd0926e4413635418682f4fff831412", + "shasum": "" + }, + "require": { + "php": ">=8.4" + }, + "require-dev": { + "symfony/property-access": "^7.4|^8.0", + "symfony/serializer": "^7.4|^8.0", + "symfony/var-dumper": "^7.4|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\VarExporter\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Allows exporting any serializable PHP data structure to plain PHP code", + "homepage": "https://symfony.com", + "keywords": [ + "clone", + "construct", + "export", + "hydrate", + "instantiate", + "lazy-loading", + "proxy", + "serialize" + ], + "support": { + "source": "https://github.com/symfony/var-exporter/tree/v8.0.9" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-04-18T13:51:42+00:00" + }, + { + "name": "webimpress/safe-writer", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/webimpress/safe-writer.git", + "reference": "9d37cc8bee20f7cb2f58f6e23e05097eab5072e6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webimpress/safe-writer/zipball/9d37cc8bee20f7cb2f58f6e23e05097eab5072e6", + "reference": "9d37cc8bee20f7cb2f58f6e23e05097eab5072e6", + "shasum": "" + }, + "require": { + "php": "^7.3 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.5.4", + "vimeo/psalm": "^4.7", + "webimpress/coding-standard": "^1.2.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.2.x-dev", + "dev-develop": "2.3.x-dev", + "dev-release-1.0": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Webimpress\\SafeWriter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "description": "Tool to write files safely, to avoid race conditions", + "keywords": [ + "concurrent write", + "file writer", + "race condition", + "safe writer", + "webimpress" + ], + "support": { + "issues": "https://github.com/webimpress/safe-writer/issues", + "source": "https://github.com/webimpress/safe-writer/tree/2.2.0" + }, + "funding": [ + { + "url": "https://github.com/michalbundyra", + "type": "github" + } + ], + "time": "2021-04-19T16:34:45+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.12.1", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/assert.git", + "reference": "9be6926d8b485f55b9229203f962b51ed377ba68" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/9be6926d8b485f55b9229203f962b51ed377ba68", + "reference": "9be6926d8b485f55b9229203f962b51ed377ba68", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-date": "*", + "ext-filter": "*", + "php": "^7.2 || ^8.0" + }, + "suggest": { + "ext-intl": "", + "ext-simplexml": "", + "ext-spl": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.12.1" + }, + "time": "2025-10-29T15:56:20+00:00" + } + ], + "packages-dev": [ + { + "name": "clue/ndjson-react", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/clue/reactphp-ndjson.git", + "reference": "392dc165fce93b5bb5c637b67e59619223c931b0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/clue/reactphp-ndjson/zipball/392dc165fce93b5bb5c637b67e59619223c931b0", + "reference": "392dc165fce93b5bb5c637b67e59619223c931b0", + "shasum": "" + }, + "require": { + "php": ">=5.3", + "react/stream": "^1.2" + }, + "require-dev": { + "phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.35", + "react/event-loop": "^1.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Clue\\React\\NDJson\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering" + } + ], + "description": "Streaming newline-delimited JSON (NDJSON) parser and encoder for ReactPHP.", + "homepage": "https://github.com/clue/reactphp-ndjson", + "keywords": [ + "NDJSON", + "json", + "jsonlines", + "newline", + "reactphp", + "streaming" + ], + "support": { + "issues": "https://github.com/clue/reactphp-ndjson/issues", + "source": "https://github.com/clue/reactphp-ndjson/tree/v1.3.0" + }, + "funding": [ + { + "url": "https://clue.engineering/support", + "type": "custom" + }, + { + "url": "https://github.com/clue", + "type": "github" + } + ], + "time": "2022-12-23T10:58:28+00:00" + }, + { + "name": "composer/pcre", + "version": "3.3.2", + "source": { + "type": "git", + "url": "https://github.com/composer/pcre.git", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/pcre/zipball/b2bed4734f0cc156ee1fe9c0da2550420d99a21e", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<1.11.10" + }, + "require-dev": { + "phpstan/phpstan": "^1.12 || ^2", + "phpstan/phpstan-strict-rules": "^1 || ^2", + "phpunit/phpunit": "^8 || ^9" + }, + "type": "library", + "extra": { + "phpstan": { + "includes": [ + "extension.neon" + ] + }, + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Pcre\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", + "keywords": [ + "PCRE", + "preg", + "regex", + "regular expression" + ], + "support": { + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/3.3.2" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-11-12T16:29:46+00:00" + }, + { + "name": "composer/semver", + "version": "3.4.4", + "source": { + "type": "git", + "url": "https://github.com/composer/semver.git", + "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/semver/zipball/198166618906cb2de69b95d7d47e5fa8aa1b2b95", + "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.11", + "symfony/phpunit-bridge": "^3 || ^7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Semver\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" + } + ], + "description": "Semver library that offers utilities, version constraint parsing and validation.", + "keywords": [ + "semantic", + "semver", + "validation", + "versioning" + ], + "support": { + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/3.4.4" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + } + ], + "time": "2025-08-20T19:15:30+00:00" + }, + { + "name": "composer/xdebug-handler", + "version": "3.0.5", + "source": { + "type": "git", + "url": "https://github.com/composer/xdebug-handler.git", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/6c1925561632e83d60a44492e0b344cf48ab85ef", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef", + "shasum": "" + }, + "require": { + "composer/pcre": "^1 || ^2 || ^3", + "php": "^7.2.5 || ^8.0", + "psr/log": "^1 || ^2 || ^3" + }, + "require-dev": { + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-strict-rules": "^1.1", + "phpunit/phpunit": "^8.5 || ^9.6 || ^10.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Composer\\XdebugHandler\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "John Stevenson", + "email": "john-stevenson@blueyonder.co.uk" + } + ], + "description": "Restarts a process without Xdebug.", + "keywords": [ + "Xdebug", + "performance" + ], + "support": { + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/xdebug-handler/issues", + "source": "https://github.com/composer/xdebug-handler/tree/3.0.5" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-05-06T16:37:16+00:00" + }, + { + "name": "ergebnis/agent-detector", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/ergebnis/agent-detector.git", + "reference": "e211f17928c8b95a51e06040792d57f5462fb271" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ergebnis/agent-detector/zipball/e211f17928c8b95a51e06040792d57f5462fb271", + "reference": "e211f17928c8b95a51e06040792d57f5462fb271", + "shasum": "" + }, + "require": { + "php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0 || ~8.6.0" + }, + "require-dev": { + "ergebnis/composer-normalize": "^2.51.0", + "ergebnis/license": "^2.7.0", + "ergebnis/php-cs-fixer-config": "^6.60.2", + "ergebnis/phpstan-rules": "^2.13.1", + "ergebnis/phpunit-slow-test-detector": "^2.24.0", + "ergebnis/rector-rules": "^1.18.1", + "fakerphp/faker": "^1.24.1", + "infection/infection": "^0.26.6", + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^2.1.54", + "phpstan/phpstan-deprecation-rules": "^2.0.4", + "phpstan/phpstan-phpunit": "^2.0.16", + "phpstan/phpstan-strict-rules": "^2.0.10", + "phpunit/phpunit": "^9.6.34", + "rector/rector": "^2.4.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.2-dev" + }, + "composer-normalize": { + "indent-size": 2, + "indent-style": "space" + } }, - "bin": [ - "php-cs-fixer" - ], - "type": "application", "autoload": { "psr-4": { - "PhpCsFixer\\": "src/" + "Ergebnis\\AgentDetector\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -615,473 +6266,699 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Dariusz Rumiński", - "email": "dariusz.ruminski@gmail.com" + "name": "Andreas Möller", + "email": "am@localheinz.com", + "homepage": "https://localheinz.com" } ], - "description": "A tool to automatically fix PHP code style", + "description": "Provides a detector for detecting the presence of an agent.", + "homepage": "https://github.com/ergebnis/agent-detector", "support": { - "issues": "https://github.com/FriendsOfPHP/PHP-CS-Fixer/issues", - "source": "https://github.com/FriendsOfPHP/PHP-CS-Fixer/tree/v3.4.0" + "issues": "https://github.com/ergebnis/agent-detector/issues", + "security": "https://github.com/ergebnis/agent-detector/blob/main/.github/SECURITY.md", + "source": "https://github.com/ergebnis/agent-detector" }, - "funding": [ + "time": "2026-05-07T08:19:07+00:00" + }, + { + "name": "evenement/evenement", + "version": "v3.0.2", + "source": { + "type": "git", + "url": "https://github.com/igorw/evenement.git", + "reference": "0a16b0d71ab13284339abb99d9d2bd813640efbc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/igorw/evenement/zipball/0a16b0d71ab13284339abb99d9d2bd813640efbc", + "reference": "0a16b0d71ab13284339abb99d9d2bd813640efbc", + "shasum": "" + }, + "require": { + "php": ">=7.0" + }, + "require-dev": { + "phpunit/phpunit": "^9 || ^6" + }, + "type": "library", + "autoload": { + "psr-4": { + "Evenement\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ { - "url": "https://github.com/keradus", - "type": "github" + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" } ], - "time": "2021-12-11T16:25:08+00:00" + "description": "Événement is a very simple event dispatching library for PHP", + "keywords": [ + "event-dispatcher", + "event-emitter" + ], + "support": { + "issues": "https://github.com/igorw/evenement/issues", + "source": "https://github.com/igorw/evenement/tree/v3.0.2" + }, + "time": "2023-08-08T05:53:35+00:00" }, { - "name": "myclabs/deep-copy", - "version": "1.13.0", + "name": "fidry/cpu-core-counter", + "version": "1.3.0", "source": { "type": "git", - "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "024473a478be9df5fdaca2c793f2232fe788e414" + "url": "https://github.com/theofidry/cpu-core-counter.git", + "reference": "db9508f7b1474469d9d3c53b86f817e344732678" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/024473a478be9df5fdaca2c793f2232fe788e414", - "reference": "024473a478be9df5fdaca2c793f2232fe788e414", + "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/db9508f7b1474469d9d3c53b86f817e344732678", + "reference": "db9508f7b1474469d9d3c53b86f817e344732678", "shasum": "" }, "require": { - "php": "^7.1 || ^8.0" - }, - "conflict": { - "doctrine/collections": "<1.6.8", - "doctrine/common": "<2.13.3 || >=3 <3.2.2" + "php": "^7.2 || ^8.0" }, "require-dev": { - "doctrine/collections": "^1.6.8", - "doctrine/common": "^2.13.3 || ^3.2.2", - "phpspec/prophecy": "^1.10", - "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + "fidry/makefile": "^0.2.0", + "fidry/php-cs-fixer-config": "^1.1.2", + "phpstan/extension-installer": "^1.2.0", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-deprecation-rules": "^2.0.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^8.5.31 || ^9.5.26", + "webmozarts/strict-phpunit": "^7.5" }, "type": "library", "autoload": { - "files": [ - "src/DeepCopy/deep_copy.php" - ], "psr-4": { - "DeepCopy\\": "src/DeepCopy/" + "Fidry\\CpuCoreCounter\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "description": "Create deep copies (clones) of your objects", + "authors": [ + { + "name": "Théo FIDRY", + "email": "theo.fidry@gmail.com" + } + ], + "description": "Tiny utility to get the number of CPU cores.", "keywords": [ - "clone", - "copy", - "duplicate", - "object", - "object graph" + "CPU", + "core" ], "support": { - "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.13.0" + "issues": "https://github.com/theofidry/cpu-core-counter/issues", + "source": "https://github.com/theofidry/cpu-core-counter/tree/1.3.0" }, "funding": [ { - "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", - "type": "tidelift" + "url": "https://github.com/theofidry", + "type": "github" } ], - "time": "2025-02-12T12:17:51+00:00" + "time": "2025-08-14T07:29:31+00:00" }, { - "name": "nikic/php-parser", - "version": "v4.19.4", + "name": "friendsofphp/php-cs-fixer", + "version": "v3.95.1", "source": { "type": "git", - "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "715f4d25e225bc47b293a8b997fe6ce99bf987d2" + "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", + "reference": "a9727678fbd12997f1d9de8f4a37824ed9df1065" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/715f4d25e225bc47b293a8b997fe6ce99bf987d2", - "reference": "715f4d25e225bc47b293a8b997fe6ce99bf987d2", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/a9727678fbd12997f1d9de8f4a37824ed9df1065", + "reference": "a9727678fbd12997f1d9de8f4a37824ed9df1065", "shasum": "" }, "require": { + "clue/ndjson-react": "^1.3", + "composer/semver": "^3.4", + "composer/xdebug-handler": "^3.0.5", + "ergebnis/agent-detector": "^1.1.1", + "ext-filter": "*", + "ext-hash": "*", + "ext-json": "*", "ext-tokenizer": "*", - "php": ">=7.1" + "fidry/cpu-core-counter": "^1.3", + "php": "^7.4 || ^8.0", + "react/child-process": "^0.6.6", + "react/event-loop": "^1.5", + "react/socket": "^1.16", + "react/stream": "^1.4", + "sebastian/diff": "^4.0.6 || ^5.1.1 || ^6.0.2 || ^7.0 || ^8.0", + "symfony/console": "^5.4.47 || ^6.4.24 || ^7.0 || ^8.0", + "symfony/event-dispatcher": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0", + "symfony/filesystem": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0", + "symfony/finder": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0", + "symfony/options-resolver": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0", + "symfony/polyfill-mbstring": "^1.33", + "symfony/polyfill-php80": "^1.33", + "symfony/polyfill-php81": "^1.33", + "symfony/polyfill-php84": "^1.33", + "symfony/process": "^5.4.47 || ^6.4.24 || ^7.2 || ^8.0", + "symfony/stopwatch": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0" }, "require-dev": { - "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" + "facile-it/paraunit": "^1.3.1 || ^2.8.0", + "infection/infection": "^0.32.6", + "justinrainbow/json-schema": "^6.8.0", + "keradus/cli-executor": "^2.3", + "mikey179/vfsstream": "^1.6.12", + "php-coveralls/php-coveralls": "^2.9.1", + "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.8", + "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.8", + "phpunit/phpunit": "^9.6.34 || ^10.5.63 || ^11.5.55", + "symfony/polyfill-php85": "^1.33", + "symfony/var-dumper": "^5.4.48 || ^6.4.32 || ^7.4.4 || ^8.0.8", + "symfony/yaml": "^5.4.45 || ^6.4.30 || ^7.4.1 || ^8.0.8" + }, + "suggest": { + "ext-dom": "For handling output formats in XML", + "ext-mbstring": "For handling non-UTF8 characters." }, "bin": [ - "bin/php-parse" + "php-cs-fixer" ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.9-dev" - } - }, + "type": "application", "autoload": { "psr-4": { - "PhpParser\\": "lib/PhpParser" - } + "PhpCsFixer\\": "src/" + }, + "exclude-from-classmap": [ + "src/**/Internal/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Nikita Popov" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Dariusz Rumiński", + "email": "dariusz.ruminski@gmail.com" } ], - "description": "A PHP parser written in PHP", + "description": "A tool to automatically fix PHP code style", "keywords": [ - "parser", - "php" + "Static code analysis", + "fixer", + "standards", + "static analysis" ], "support": { - "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.19.4" + "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", + "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.95.1" }, - "time": "2024-09-29T15:01:53+00:00" + "funding": [ + { + "url": "https://github.com/keradus", + "type": "github" + } + ], + "time": "2026-04-12T17:00:09+00:00" }, { - "name": "pcov/clobber", - "version": "v2.0.3", + "name": "laminas/laminas-cli", + "version": "1.15.0", "source": { "type": "git", - "url": "https://github.com/krakjoe/pcov-clobber.git", - "reference": "4c30759e912e6e5d5bf833fb3d77b5bd51709f05" + "url": "https://github.com/laminas/laminas-cli.git", + "reference": "926a884548a5ff078128ffa3fe9381cd0a4ae8c4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/krakjoe/pcov-clobber/zipball/4c30759e912e6e5d5bf833fb3d77b5bd51709f05", - "reference": "4c30759e912e6e5d5bf833fb3d77b5bd51709f05", + "url": "https://api.github.com/repos/laminas/laminas-cli/zipball/926a884548a5ff078128ffa3fe9381cd0a4ae8c4", + "reference": "926a884548a5ff078128ffa3fe9381cd0a4ae8c4", "shasum": "" }, "require": { - "ext-pcov": "^1.0", - "nikic/php-parser": "^4.2" + "composer-runtime-api": "^2.0.0", + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", + "psr/container": "^1.0 || ^2.0", + "symfony/console": "^7.4 || ^8.0", + "symfony/event-dispatcher": "^7.4 || ^8.0", + "webmozart/assert": "^1.11 || ^2.1.5" + }, + "conflict": { + "amphp/amp": "<2.6.4" + }, + "require-dev": { + "laminas/laminas-coding-standard": "^3.1.0", + "laminas/laminas-mvc": "^3.8.0", + "laminas/laminas-servicemanager": "^3.24.0", + "mikey179/vfsstream": "2.0.x-dev", + "phpunit/phpunit": "^11.5.55", + "psalm/plugin-phpunit": "^0.19.5", + "vimeo/psalm": "^6.15.1" }, "bin": [ - "bin/pcov" + "bin/laminas" ], "type": "library", "autoload": { "psr-4": { - "pcov\\Clobber\\": "src/pcov/clobber" + "Laminas\\Cli\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Command-line interface for Laminas projects", + "keywords": [ + "cli", + "command", + "console", + "laminas" + ], "support": { - "issues": "https://github.com/krakjoe/pcov-clobber/issues", - "source": "https://github.com/krakjoe/pcov-clobber/tree/v2.0.3" + "chat": "https://laminas.dev/chat", + "docs": "https://docs.laminas.dev/laminas-cli/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/mezzio/laminas-cli/issues", + "rss": "https://github.com/mezzio/laminas-cli/releases.atom", + "source": "https://github.com/mezzio/laminas-cli" }, - "time": "2019-10-29T05:03:37+00:00" + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2026-03-10T09:04:36+00:00" }, { - "name": "php-cs-fixer/diff", - "version": "v2.0.2", + "name": "laminas/laminas-code", + "version": "4.17.0", "source": { "type": "git", - "url": "https://github.com/PHP-CS-Fixer/diff.git", - "reference": "29dc0d507e838c4580d018bd8b5cb412474f7ec3" + "url": "https://github.com/laminas/laminas-code.git", + "reference": "40d61e2899ec17c5d08bbc0a2d586b3ca17ab9bd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-CS-Fixer/diff/zipball/29dc0d507e838c4580d018bd8b5cb412474f7ec3", - "reference": "29dc0d507e838c4580d018bd8b5cb412474f7ec3", + "url": "https://api.github.com/repos/laminas/laminas-code/zipball/40d61e2899ec17c5d08bbc0a2d586b3ca17ab9bd", + "reference": "40d61e2899ec17c5d08bbc0a2d586b3ca17ab9bd", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0 || ^8.0" + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" }, "require-dev": { - "phpunit/phpunit": "^5.7.23 || ^6.4.3 || ^7.0", - "symfony/process": "^3.3" + "doctrine/annotations": "^2.0.1", + "ext-phar": "*", + "laminas/laminas-coding-standard": "^3.0.0", + "laminas/laminas-stdlib": "^3.18.0", + "phpunit/phpunit": "^10.5.58", + "psalm/plugin-phpunit": "^0.19.0", + "vimeo/psalm": "^5.15.0" + }, + "suggest": { + "doctrine/annotations": "Doctrine\\Common\\Annotations >=1.0 for annotation features", + "laminas/laminas-stdlib": "Laminas\\Stdlib component" }, "type": "library", "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Laminas\\Code\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, + "description": "Extensions to the PHP Reflection API, static code scanning, and code generation", + "homepage": "https://laminas.dev", + "keywords": [ + "code", + "laminas", + "laminasframework" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.laminas.dev/laminas-code/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/laminas/laminas-code/issues", + "rss": "https://github.com/laminas/laminas-code/releases.atom", + "source": "https://github.com/laminas/laminas-code" + }, + "funding": [ { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2025-11-01T09:38:14+00:00" + }, + { + "name": "laminas/laminas-development-mode", + "version": "3.15.0", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-development-mode.git", + "reference": "87611d4d742dc314244dcbe4e173a2af11a7c0bc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-development-mode/zipball/87611d4d742dc314244dcbe4e173a2af11a7c0bc", + "reference": "87611d4d742dc314244dcbe4e173a2af11a7c0bc", + "shasum": "" + }, + "require": { + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" + }, + "conflict": { + "zfcampus/zf-development-mode": "*" + }, + "require-dev": { + "laminas/laminas-coding-standard": "~3.1.0", + "mikey179/vfsstream": "^1.6.12", + "phpunit/phpunit": "^11.5.42", + "psalm/plugin-phpunit": "^0.19.5", + "vimeo/psalm": "^6.13.1" + }, + "bin": [ + "bin/laminas-development-mode" + ], + "type": "library", + "autoload": { + "psr-4": { + "Laminas\\DevelopmentMode\\": "src/" } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" ], - "description": "sebastian/diff v3 backport support for PHP 5.6+", - "homepage": "https://github.com/PHP-CS-Fixer", + "description": "Laminas development mode script", + "homepage": "https://laminas.dev", "keywords": [ - "diff" + "framework", + "laminas" ], "support": { - "issues": "https://github.com/PHP-CS-Fixer/diff/issues", - "source": "https://github.com/PHP-CS-Fixer/diff/tree/v2.0.2" + "chat": "https://laminas.dev/chat", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/laminas/laminas-development-mode/issues", + "rss": "https://github.com/laminas/laminas-development-mode/releases.atom", + "source": "https://github.com/laminas/laminas-development-mode" }, - "abandoned": true, - "time": "2020-10-14T08:32:19+00:00" + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2025-10-14T21:17:32+00:00" }, { - "name": "phpdocumentor/reflection-common", - "version": "2.2.0", + "name": "mezzio/mezzio-tooling", + "version": "2.12.0", "source": { "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + "url": "https://github.com/mezzio/mezzio-tooling.git", + "reference": "41e8242b27398d0511223d48bbd9efc97d6a2e40" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "url": "https://api.github.com/repos/mezzio/mezzio-tooling/zipball/41e8242b27398d0511223d48bbd9efc97d6a2e40", + "reference": "41e8242b27398d0511223d48bbd9efc97d6a2e40", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0" + "ext-json": "*", + "laminas/laminas-cli": "^1.7.0", + "laminas/laminas-code": "^4.7.1", + "laminas/laminas-stdlib": "^3.15.0", + "laminas/laminas-stratigility": "^3.9.0", + "mezzio/mezzio": "^3.13.0", + "mezzio/mezzio-router": "^3.9.0", + "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.1", + "symfony/process": "^6.0.11" + }, + "conflict": { + "amphp/amp": "<2.6.4", + "symfony/console": "<5.4.45", + "symfony/string": "<5.4.45" + }, + "require-dev": { + "laminas/laminas-coding-standard": "~2.5.0", + "laminas/laminas-diactoros": "^3.3", + "mikey179/vfsstream": "^1.6.12", + "mockery/mockery": "^1.6.10", + "php-mock/php-mock-phpunit": "^2.9.0", + "phpdocumentor/reflection-docblock": "^5.3.0", + "phpunit/phpunit": "^10.5.35", + "psalm/plugin-mockery": "^0.11.0", + "psalm/plugin-phpunit": "^0.18.4", + "vimeo/psalm": "^5.17.0" }, "type": "library", "extra": { - "branch-alias": { - "dev-2.x": "2.x-dev" + "laminas": { + "config-provider": "Mezzio\\Tooling\\ConfigProvider" } }, "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": "src/" + "Mezzio\\Tooling\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" - } + "BSD-3-Clause" ], - "description": "Common reflection classes used by phpdocumentor to reflect the code structure", - "homepage": "http://www.phpdoc.org", + "description": "Migration and development tooling for Mezzio", + "homepage": "https://mezzio.dev", "keywords": [ - "FQSEN", - "phpDocumentor", - "phpdoc", - "reflection", - "static analysis" + "http", + "laminas", + "mezzio", + "middleware", + "psr", + "psr-7" ], "support": { - "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", - "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + "chat": "https://laminas.dev/chat", + "docs": "https://docs.mezzio.dev/mezzio/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/mezzio/mezzio-tooling/issues", + "rss": "https://github.com/mezzio/mezzio-tooling/releases.atom", + "source": "https://github.com/mezzio/mezzio-tooling" }, - "time": "2020-06-27T09:03:43+00:00" + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2025-09-12T08:39:37+00:00" }, { - "name": "phpdocumentor/reflection-docblock", - "version": "5.3.0", + "name": "myclabs/deep-copy", + "version": "1.13.4", "source": { "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", - "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", "shasum": "" }, "require": { - "ext-filter": "*", - "php": "^7.2 || ^8.0", - "phpdocumentor/reflection-common": "^2.2", - "phpdocumentor/type-resolver": "^1.3", - "webmozart/assert": "^1.9.1" + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3 <3.2.2" }, "require-dev": { - "mockery/mockery": "~1.3.2", - "psalm/phar": "^4.8" + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.x-dev" - } - }, "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], "psr-4": { - "phpDocumentor\\Reflection\\": "src" + "DeepCopy\\": "src/DeepCopy/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - }, - { - "name": "Jaap van Otterdijk", - "email": "account@ijaap.nl" - } + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" ], - "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", "support": { - "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0" + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" }, - "time": "2021-10-19T17:43:47+00:00" + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2025-08-01T08:46:24+00:00" }, { - "name": "phpdocumentor/type-resolver", - "version": "1.6.1", + "name": "phar-io/manifest", + "version": "2.0.4", "source": { "type": "git", - "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "77a32518733312af16a44300404e945338981de3" + "url": "https://github.com/phar-io/manifest.git", + "reference": "54750ef60c58e43759730615a392c31c80e23176" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/77a32518733312af16a44300404e945338981de3", - "reference": "77a32518733312af16a44300404e945338981de3", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0", - "phpdocumentor/reflection-common": "^2.0" - }, - "require-dev": { - "ext-tokenizer": "*", - "psalm/phar": "^4.8" + "ext-dom": "*", + "ext-libxml": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" }, "type": "library", "extra": { "branch-alias": { - "dev-1.x": "1.x-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" } ], - "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", "support": { - "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.1" + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.4" }, - "time": "2022-03-15T21:29:03+00:00" + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+00:00" }, { - "name": "phpspec/prophecy", - "version": "v1.10.3", + "name": "phar-io/version", + "version": "3.2.1", "source": { "type": "git", - "url": "https://github.com/phpspec/prophecy.git", - "reference": "451c3cd1418cf640de218914901e51b064abb093" + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/451c3cd1418cf640de218914901e51b064abb093", - "reference": "451c3cd1418cf640de218914901e51b064abb093", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.0.2", - "php": "^5.3|^7.0", - "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0|^5.0", - "sebastian/comparator": "^1.2.3|^2.0|^3.0|^4.0", - "sebastian/recursion-context": "^1.0|^2.0|^3.0|^4.0" - }, - "require-dev": { - "phpspec/phpspec": "^2.5 || ^3.2", - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" + "php": "^7.2 || ^8.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.10.x-dev" - } - }, "autoload": { - "psr-4": { - "Prophecy\\": "src/Prophecy" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" }, { - "name": "Marcello Duarte", - "email": "marcello.duarte@gmail.com" + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" } ], - "description": "Highly opinionated mocking framework for PHP 5.3+", - "homepage": "https://github.com/phpspec/prophecy", - "keywords": [ - "Double", - "Dummy", - "fake", - "mock", - "spy", - "stub" - ], + "description": "Library for handling version information and constraints", "support": { - "issues": "https://github.com/phpspec/prophecy/issues", - "source": "https://github.com/phpspec/prophecy/tree/v1.10.3" + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" }, - "time": "2020-03-05T15:02:03+00:00" + "time": "2022-02-21T01:04:05+00:00" }, { "name": "phpstan/phpstan", - "version": "1.12.23", - "source": { - "type": "git", - "url": "https://github.com/phpstan/phpstan.git", - "reference": "29201e7a743a6ab36f91394eab51889a82631428" - }, + "version": "2.1.54", "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/29201e7a743a6ab36f91394eab51889a82631428", - "reference": "29201e7a743a6ab36f91394eab51889a82631428", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/8be50c3992107dc837b17da4d140fbbdf9a5c5bd", + "reference": "8be50c3992107dc837b17da4d140fbbdf9a5c5bd", "shasum": "" }, "require": { - "php": "^7.2|^8.0" + "php": "^7.4|^8.0" }, "conflict": { "phpstan/phpstan-shim": "*" @@ -1122,197 +6999,180 @@ "type": "github" } ], - "time": "2025-03-23T14:57:32+00:00" + "time": "2026-04-29T13:31:09+00:00" }, { - "name": "phpunit/php-code-coverage", - "version": "4.0.8", + "name": "phpstan/phpstan-doctrine", + "version": "2.0.21", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "ef7b2f56815df854e66ceaee8ebe9393ae36a40d" + "url": "https://github.com/phpstan/phpstan-doctrine.git", + "reference": "81dac0ee4363c2359128aec844df31efb215dddc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/ef7b2f56815df854e66ceaee8ebe9393ae36a40d", - "reference": "ef7b2f56815df854e66ceaee8ebe9393ae36a40d", + "url": "https://api.github.com/repos/phpstan/phpstan-doctrine/zipball/81dac0ee4363c2359128aec844df31efb215dddc", + "reference": "81dac0ee4363c2359128aec844df31efb215dddc", "shasum": "" }, "require": { - "ext-dom": "*", - "ext-xmlwriter": "*", - "php": "^5.6 || ^7.0", - "phpunit/php-file-iterator": "^1.3", - "phpunit/php-text-template": "^1.2", - "phpunit/php-token-stream": "^1.4.2 || ^2.0", - "sebastian/code-unit-reverse-lookup": "^1.0", - "sebastian/environment": "^1.3.2 || ^2.0", - "sebastian/version": "^1.0 || ^2.0" - }, - "require-dev": { - "ext-xdebug": "^2.1.4", - "phpunit/phpunit": "^5.7" + "php": "^7.4 || ^8.0", + "phpstan/phpstan": "^2.1.34" }, - "suggest": { - "ext-xdebug": "^2.5.1" + "conflict": { + "doctrine/collections": "<1.0", + "doctrine/common": "<2.7", + "doctrine/mongodb-odm": "<1.2", + "doctrine/orm": "<2.5", + "doctrine/persistence": "<1.3" }, - "type": "library", + "require-dev": { + "cache/array-adapter": "^1.1", + "composer/semver": "^3.3.2", + "cweagans/composer-patches": "^1.7.3", + "doctrine/annotations": "^2.0", + "doctrine/collections": "^1.6 || ^2.1", + "doctrine/common": "^2.7 || ^3.0", + "doctrine/dbal": "^3.3.8", + "doctrine/lexer": "^2.0 || ^3.0", + "doctrine/mongodb-odm": "^2.4.3", + "doctrine/orm": "^2.16.0", + "doctrine/persistence": "^2.2.1 || ^3.4.3", + "gedmo/doctrine-extensions": "^3.8", + "nesbot/carbon": "^2.49", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/phpstan-deprecation-rules": "^2.0.2", + "phpstan/phpstan-phpunit": "^2.0.8", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^9.6.20", + "ramsey/uuid": "^4.2", + "symfony/cache": "^5.4", + "symfony/uid": "^5.4 || ^6.4 || ^7.3" + }, + "type": "phpstan-extension", "extra": { - "branch-alias": { - "dev-master": "4.0.x-dev" + "phpstan": { + "includes": [ + "extension.neon", + "rules.neon" + ] } }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "PHPStan\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } + "MIT" ], - "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", - "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "description": "Doctrine extensions for PHPStan", "keywords": [ - "coverage", - "testing", - "xunit" + "static analysis" ], "support": { - "irc": "irc://irc.freenode.net/phpunit", - "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/4.0" + "issues": "https://github.com/phpstan/phpstan-doctrine/issues", + "source": "https://github.com/phpstan/phpstan-doctrine/tree/2.0.21" }, - "time": "2017-04-02T07:44:40+00:00" + "time": "2026-04-17T13:00:39+00:00" }, { - "name": "phpunit/php-file-iterator", - "version": "1.4.5", + "name": "phpstan/phpstan-phpunit", + "version": "2.0.16", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4" + "url": "https://github.com/phpstan/phpstan-phpunit.git", + "reference": "6ab598e1bc106e6827fd346ae4a12b4a5d634c32" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/730b01bc3e867237eaac355e06a36b85dd93a8b4", - "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4", + "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/6ab598e1bc106e6827fd346ae4a12b4a5d634c32", + "reference": "6ab598e1bc106e6827fd346ae4a12b4a5d634c32", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": "^7.4 || ^8.0", + "phpstan/phpstan": "^2.1.32" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.4.x-dev" - } + "conflict": { + "phpunit/phpunit": "<7.0" }, - "autoload": { - "classmap": [ - "src/" - ] + "require-dev": { + "nikic/php-parser": "^5", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/phpstan-deprecation-rules": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^9.6" }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" + "type": "phpstan-extension", + "extra": { + "phpstan": { + "includes": [ + "extension.neon", + "rules.neon" + ] } - ], - "description": "FilterIterator implementation that filters files based on a list of suffixes.", - "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", - "keywords": [ - "filesystem", - "iterator" - ], - "support": { - "irc": "irc://irc.freenode.net/phpunit", - "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/1.4.5" - }, - "time": "2017-11-27T13:52:08+00:00" - }, - { - "name": "phpunit/php-text-template", - "version": "1.2.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" }, - "type": "library", "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "PHPStan\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } + "MIT" ], - "description": "Simple template engine.", - "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "description": "PHPUnit extensions and rules for PHPStan", "keywords": [ - "template" + "static analysis" ], "support": { - "issues": "https://github.com/sebastianbergmann/php-text-template/issues", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/1.2.1" + "issues": "https://github.com/phpstan/phpstan-phpunit/issues", + "source": "https://github.com/phpstan/phpstan-phpunit/tree/2.0.16" }, - "time": "2015-06-21T13:50:34+00:00" + "time": "2026-02-14T09:05:21+00:00" }, { - "name": "phpunit/php-timer", - "version": "1.0.9", + "name": "phpunit/php-code-coverage", + "version": "14.1.7", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f" + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "da6e6b64940901650abcea62430c8c24926b7a71" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", - "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/da6e6b64940901650abcea62430c8c24926b7a71", + "reference": "da6e6b64940901650abcea62430c8c24926b7a71", "shasum": "" }, "require": { - "php": "^5.3.3 || ^7.0" + "ext-dom": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^5.7.0", + "php": ">=8.4", + "phpunit/php-text-template": "^6.0", + "sebastian/complexity": "^6.0", + "sebastian/environment": "^9.2", + "sebastian/git-state": "^1.0", + "sebastian/lines-of-code": "^5.0", + "sebastian/version": "^7.0", + "theseer/tokenizer": "^2.0.1" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" + "phpunit/phpunit": "^13.1" + }, + "suggest": { + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-main": "14.1.x-dev" } }, "autoload": { @@ -1327,46 +7187,66 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", + "email": "sebastian@phpunit.de", "role": "lead" } ], - "description": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", "keywords": [ - "timer" + "coverage", + "testing", + "xunit" ], "support": { - "issues": "https://github.com/sebastianbergmann/php-timer/issues", - "source": "https://github.com/sebastianbergmann/php-timer/tree/master" + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/14.1.7" }, - "time": "2017-02-26T11:10:40+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-code-coverage", + "type": "tidelift" + } + ], + "time": "2026-05-04T15:51:53+00:00" }, { - "name": "phpunit/php-token-stream", - "version": "2.0.2", + "name": "phpunit/php-file-iterator", + "version": "7.0.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "791198a2c6254db10131eecfe8c06670700904db" + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "6e5aa1fb0a95b1703d83e721299ee18bb4e2de50" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/791198a2c6254db10131eecfe8c06670700904db", - "reference": "791198a2c6254db10131eecfe8c06670700904db", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/6e5aa1fb0a95b1703d83e721299ee18bb4e2de50", + "reference": "6e5aa1fb0a95b1703d83e721299ee18bb4e2de50", "shasum": "" }, "require": { - "ext-tokenizer": "*", - "php": "^7.0" + "php": ">=8.4" }, "require-dev": { - "phpunit/phpunit": "^6.2.4" + "phpunit/phpunit": "^13.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -1381,76 +7261,69 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Wrapper around PHP's tokenizer extension.", - "homepage": "https://github.com/sebastianbergmann/php-token-stream/", + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", "keywords": [ - "tokenizer" + "filesystem", + "iterator" ], "support": { - "issues": "https://github.com/sebastianbergmann/php-token-stream/issues", - "source": "https://github.com/sebastianbergmann/php-token-stream/tree/master" + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/7.0.0" }, - "abandoned": true, - "time": "2017-11-27T05:48:46+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-file-iterator", + "type": "tidelift" + } + ], + "time": "2026-02-06T04:33:26+00:00" }, { - "name": "phpunit/phpunit", - "version": "5.7.27", + "name": "phpunit/php-invoker", + "version": "7.0.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "b7803aeca3ccb99ad0a506fa80b64cd6a56bbc0c" + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "42e5c5cae0c65df12d1b1a3ab52bf3f50f244d88" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b7803aeca3ccb99ad0a506fa80b64cd6a56bbc0c", - "reference": "b7803aeca3ccb99ad0a506fa80b64cd6a56bbc0c", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/42e5c5cae0c65df12d1b1a3ab52bf3f50f244d88", + "reference": "42e5c5cae0c65df12d1b1a3ab52bf3f50f244d88", "shasum": "" }, "require": { - "ext-dom": "*", - "ext-json": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "ext-xml": "*", - "myclabs/deep-copy": "~1.3", - "php": "^5.6 || ^7.0", - "phpspec/prophecy": "^1.6.2", - "phpunit/php-code-coverage": "^4.0.4", - "phpunit/php-file-iterator": "~1.4", - "phpunit/php-text-template": "~1.2", - "phpunit/php-timer": "^1.0.6", - "phpunit/phpunit-mock-objects": "^3.2", - "sebastian/comparator": "^1.2.4", - "sebastian/diff": "^1.4.3", - "sebastian/environment": "^1.3.4 || ^2.0", - "sebastian/exporter": "~2.0", - "sebastian/global-state": "^1.1", - "sebastian/object-enumerator": "~2.0", - "sebastian/resource-operations": "~1.0", - "sebastian/version": "^1.0.6|^2.0.1", - "symfony/yaml": "~2.1|~3.0|~4.0" - }, - "conflict": { - "phpdocumentor/reflection-docblock": "3.0.2" + "php": ">=8.4" }, "require-dev": { - "ext-pdo": "*" + "ext-pcntl": "*", + "phpunit/phpunit": "^13.0" }, "suggest": { - "ext-xdebug": "*", - "phpunit/php-invoker": "~1.1" + "ext-pcntl": "*" }, - "bin": [ - "phpunit" - ], "type": "library", "extra": { "branch-alias": { - "dev-master": "5.7.x-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -1469,52 +7342,60 @@ "role": "lead" } ], - "description": "The PHP Unit Testing framework.", - "homepage": "https://phpunit.de/", + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", "keywords": [ - "phpunit", - "testing", - "xunit" + "process" ], "support": { - "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/5.7.27" + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "security": "https://github.com/sebastianbergmann/php-invoker/security/policy", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/7.0.0" }, - "time": "2018-02-01T05:50:59+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-invoker", + "type": "tidelift" + } + ], + "time": "2026-02-06T04:34:47+00:00" }, { - "name": "phpunit/phpunit-mock-objects", - "version": "3.4.4", + "name": "phpunit/php-text-template", + "version": "6.0.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "a23b761686d50a560cc56233b9ecf49597cc9118" + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "a47af19f93f76aa3368303d752aa5272ca3299f4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/a23b761686d50a560cc56233b9ecf49597cc9118", - "reference": "a23b761686d50a560cc56233b9ecf49597cc9118", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/a47af19f93f76aa3368303d752aa5272ca3299f4", + "reference": "a47af19f93f76aa3368303d752aa5272ca3299f4", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.0.2", - "php": "^5.6 || ^7.0", - "phpunit/php-text-template": "^1.2", - "sebastian/exporter": "^1.2 || ^2.0" - }, - "conflict": { - "phpunit/phpunit": "<5.4.0" + "php": ">=8.4" }, "require-dev": { - "phpunit/phpunit": "^5.4" - }, - "suggest": { - "ext-soap": "*" + "phpunit/phpunit": "^13.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.2.x-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -1529,120 +7410,202 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", + "email": "sebastian@phpunit.de", "role": "lead" } ], - "description": "Mock Object library for PHPUnit", - "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", "keywords": [ - "mock", - "xunit" + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/6.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-text-template", + "type": "tidelift" + } ], - "support": { - "irc": "irc://irc.freenode.net/phpunit", - "issues": "https://github.com/sebastianbergmann/phpunit-mock-objects/issues", - "source": "https://github.com/sebastianbergmann/phpunit-mock-objects/tree/3.4" - }, - "abandoned": true, - "time": "2017-06-30T09:13:00+00:00" + "time": "2026-02-06T04:36:37+00:00" }, { - "name": "psr/cache", - "version": "1.0.1", + "name": "phpunit/php-timer", + "version": "9.0.0", "source": { "type": "git", - "url": "https://github.com/php-fig/cache.git", - "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8" + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "a0e12065831f6ab0d83120dc61513eb8d9a966f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/cache/zipball/d11b50ad223250cf17b86e38383413f5a6764bf8", - "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/a0e12065831f6ab0d83120dc61513eb8d9a966f6", + "reference": "a0e12065831f6ab0d83120dc61513eb8d9a966f6", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=8.4" + }, + "require-dev": { + "phpunit/phpunit": "^13.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-main": "9.0-dev" } }, "autoload": { - "psr-4": { - "Psr\\Cache\\": "src/" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Common interface for caching libraries", + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", "keywords": [ - "cache", - "psr", - "psr-6" + "timer" ], "support": { - "source": "https://github.com/php-fig/cache/tree/master" + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "security": "https://github.com/sebastianbergmann/php-timer/security/policy", + "source": "https://github.com/sebastianbergmann/php-timer/tree/9.0.0" }, - "time": "2016-08-06T20:24:11+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-timer", + "type": "tidelift" + } + ], + "time": "2026-02-06T04:37:53+00:00" }, { - "name": "psr/container", - "version": "1.1.1", + "name": "phpunit/phpunit", + "version": "13.1.8", "source": { "type": "git", - "url": "https://github.com/php-fig/container.git", - "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf" + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "f49a2b5e51ffb33421745368cc099cf66830d71b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/8622567409010282b7aeebe4bb841fe98b58dcaf", - "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/f49a2b5e51ffb33421745368cc099cf66830d71b", + "reference": "f49a2b5e51ffb33421745368cc099cf66830d71b", "shasum": "" }, "require": { - "php": ">=7.2.0" + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.13.4", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", + "php": ">=8.4.1", + "phpunit/php-code-coverage": "^14.1.6", + "phpunit/php-file-iterator": "^7.0.0", + "phpunit/php-invoker": "^7.0.0", + "phpunit/php-text-template": "^6.0.0", + "phpunit/php-timer": "^9.0.0", + "sebastian/cli-parser": "^5.0.0", + "sebastian/comparator": "^8.1.2", + "sebastian/diff": "^8.1.0", + "sebastian/environment": "^9.3.0", + "sebastian/exporter": "^8.0.2", + "sebastian/git-state": "^1.0", + "sebastian/global-state": "^9.0.0", + "sebastian/object-enumerator": "^8.0.0", + "sebastian/recursion-context": "^8.0.0", + "sebastian/type": "^7.0.0", + "sebastian/version": "^7.0.0", + "staabm/side-effects-detector": "^1.0.5" }, + "bin": [ + "phpunit" + ], "type": "library", - "autoload": { - "psr-4": { - "Psr\\Container\\": "src/" + "extra": { + "branch-alias": { + "dev-main": "13.1-dev" } }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Common Container Interface (PHP FIG PSR-11)", - "homepage": "https://github.com/php-fig/container", + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", "keywords": [ - "PSR-11", - "container", - "container-interface", - "container-interop", - "psr" + "phpunit", + "testing", + "xunit" ], "support": { - "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/1.1.1" + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/13.1.8" }, - "time": "2021-03-05T17:36:06+00:00" + "funding": [ + { + "url": "https://phpunit.de/sponsoring.html", + "type": "other" + } + ], + "time": "2026-05-01T04:22:45+00:00" }, { "name": "psr/event-dispatcher", @@ -1695,31 +7658,30 @@ "time": "2019-01-08T18:20:26+00:00" }, { - "name": "psr/log", - "version": "1.1.4", + "name": "react/cache", + "version": "v1.2.0", "source": { "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11" + "url": "https://github.com/reactphp/cache.git", + "reference": "d47c472b64aa5608225f47965a484b75c7817d5b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11", + "url": "https://api.github.com/repos/reactphp/cache/zipball/d47c472b64aa5608225f47965a484b75c7817d5b", + "reference": "d47c472b64aa5608225f47965a484b75c7817d5b", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=5.3.0", + "react/promise": "^3.0 || ^2.0 || ^1.1" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1.x-dev" - } + "require-dev": { + "phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.35" }, + "type": "library", "autoload": { "psr-4": { - "Psr\\Log\\": "Psr/Log/" + "React\\Cache\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1728,406 +7690,523 @@ ], "authors": [ { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" } ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", + "description": "Async, Promise-based cache interface for ReactPHP", "keywords": [ - "log", - "psr", - "psr-3" + "cache", + "caching", + "promise", + "reactphp" ], "support": { - "source": "https://github.com/php-fig/log/tree/1.1.4" + "issues": "https://github.com/reactphp/cache/issues", + "source": "https://github.com/reactphp/cache/tree/v1.2.0" }, - "time": "2021-05-03T11:20:27+00:00" + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2022-11-30T15:59:55+00:00" }, { - "name": "sebastian/code-unit-reverse-lookup", - "version": "1.0.3", + "name": "react/child-process", + "version": "v0.6.7", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "92a1a52e86d34cde6caa54f1b5ffa9fda18e5d54" + "url": "https://github.com/reactphp/child-process.git", + "reference": "970f0e71945556422ee4570ccbabaedc3cf04ad3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/92a1a52e86d34cde6caa54f1b5ffa9fda18e5d54", - "reference": "92a1a52e86d34cde6caa54f1b5ffa9fda18e5d54", + "url": "https://api.github.com/repos/reactphp/child-process/zipball/970f0e71945556422ee4570ccbabaedc3cf04ad3", + "reference": "970f0e71945556422ee4570ccbabaedc3cf04ad3", "shasum": "" }, "require": { - "php": ">=5.6" + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", + "php": ">=5.3.0", + "react/event-loop": "^1.2", + "react/stream": "^1.4" }, "require-dev": { - "phpunit/phpunit": "^8.5" + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36", + "react/socket": "^1.16", + "sebastian/environment": "^5.0 || ^3.0 || ^2.0 || ^1.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "React\\ChildProcess\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" } ], - "description": "Looks up which function or method a line of code belongs to", - "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "description": "Event-driven library for executing child processes with ReactPHP.", + "keywords": [ + "event-driven", + "process", + "reactphp" + ], "support": { - "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", - "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/1.0.3" + "issues": "https://github.com/reactphp/child-process/issues", + "source": "https://github.com/reactphp/child-process/tree/v0.6.7" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", - "type": "github" + "url": "https://opencollective.com/reactphp", + "type": "open_collective" } ], - "time": "2024-03-01T13:45:45+00:00" + "time": "2025-12-23T15:25:20+00:00" }, { - "name": "sebastian/comparator", - "version": "1.2.4", + "name": "react/dns", + "version": "v1.14.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be" + "url": "https://github.com/reactphp/dns.git", + "reference": "7562c05391f42701c1fccf189c8225fece1cd7c3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2b7424b55f5047b47ac6e5ccb20b2aea4011d9be", - "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be", + "url": "https://api.github.com/repos/reactphp/dns/zipball/7562c05391f42701c1fccf189c8225fece1cd7c3", + "reference": "7562c05391f42701c1fccf189c8225fece1cd7c3", "shasum": "" }, "require": { - "php": ">=5.3.3", - "sebastian/diff": "~1.2", - "sebastian/exporter": "~1.2 || ~2.0" + "php": ">=5.3.0", + "react/cache": "^1.0 || ^0.6 || ^0.5", + "react/event-loop": "^1.2", + "react/promise": "^3.2 || ^2.7 || ^1.2.1" }, "require-dev": { - "phpunit/phpunit": "~4.4" + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36", + "react/async": "^4.3 || ^3 || ^2", + "react/promise-timer": "^1.11" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.2.x-dev" - } - }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "React\\Dns\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" }, { - "name": "Volker Dusch", - "email": "github@wallbash.com" + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" }, { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" }, { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" } ], - "description": "Provides the functionality to compare PHP values for equality", - "homepage": "http://www.github.com/sebastianbergmann/comparator", + "description": "Async DNS resolver for ReactPHP", "keywords": [ - "comparator", - "compare", - "equality" + "async", + "dns", + "dns-resolver", + "reactphp" ], "support": { - "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/1.2" + "issues": "https://github.com/reactphp/dns/issues", + "source": "https://github.com/reactphp/dns/tree/v1.14.0" }, - "time": "2017-01-29T09:50:25+00:00" + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2025-11-18T19:34:28+00:00" }, { - "name": "sebastian/diff", - "version": "1.4.3", + "name": "react/event-loop", + "version": "v1.6.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4" + "url": "https://github.com/reactphp/event-loop.git", + "reference": "ba276bda6083df7e0050fd9b33f66ad7a4ac747a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7f066a26a962dbe58ddea9f72a4e82874a3975a4", - "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4", + "url": "https://api.github.com/repos/reactphp/event-loop/zipball/ba276bda6083df7e0050fd9b33f66ad7a4ac747a", + "reference": "ba276bda6083df7e0050fd9b33f66ad7a4ac747a", "shasum": "" }, "require": { - "php": "^5.3.3 || ^7.0" + "php": ">=5.3.0" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.4-dev" - } + "suggest": { + "ext-pcntl": "For signal handling support when using the StreamSelectLoop" }, + "type": "library", "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "React\\EventLoop\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" }, { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" } ], - "description": "Diff implementation", - "homepage": "https://github.com/sebastianbergmann/diff", + "description": "ReactPHP's core reactor event loop that libraries can use for evented I/O.", "keywords": [ - "diff" + "asynchronous", + "event-loop" ], "support": { - "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/1.4" + "issues": "https://github.com/reactphp/event-loop/issues", + "source": "https://github.com/reactphp/event-loop/tree/v1.6.0" }, - "time": "2017-05-22T07:24:03+00:00" + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2025-11-17T20:46:25+00:00" }, { - "name": "sebastian/environment", - "version": "2.0.0", + "name": "react/promise", + "version": "v3.3.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "5795ffe5dc5b02460c3e34222fee8cbe245d8fac" + "url": "https://github.com/reactphp/promise.git", + "reference": "23444f53a813a3296c1368bb104793ce8d88f04a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/5795ffe5dc5b02460c3e34222fee8cbe245d8fac", - "reference": "5795ffe5dc5b02460c3e34222fee8cbe245d8fac", + "url": "https://api.github.com/repos/reactphp/promise/zipball/23444f53a813a3296c1368bb104793ce8d88f04a", + "reference": "23444f53a813a3296c1368bb104793ce8d88f04a", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "php": ">=7.1.0" }, "require-dev": { - "phpunit/phpunit": "^5.0" + "phpstan/phpstan": "1.12.28 || 1.4.10", + "phpunit/phpunit": "^9.6 || ^7.5" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, "autoload": { - "classmap": [ - "src/" - ] + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "React\\Promise\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" } ], - "description": "Provides functionality to handle HHVM/PHP environments", - "homepage": "http://www.github.com/sebastianbergmann/environment", + "description": "A lightweight implementation of CommonJS Promises/A for PHP", "keywords": [ - "Xdebug", - "environment", - "hhvm" + "promise", + "promises" ], "support": { - "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/master" + "issues": "https://github.com/reactphp/promise/issues", + "source": "https://github.com/reactphp/promise/tree/v3.3.0" }, - "time": "2016-11-26T07:53:53+00:00" + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2025-08-19T18:57:03+00:00" }, { - "name": "sebastian/exporter", - "version": "2.0.0", + "name": "react/socket", + "version": "v1.17.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4" + "url": "https://github.com/reactphp/socket.git", + "reference": "ef5b17b81f6f60504c539313f94f2d826c5faa08" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4", - "reference": "ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4", + "url": "https://api.github.com/repos/reactphp/socket/zipball/ef5b17b81f6f60504c539313f94f2d826c5faa08", + "reference": "ef5b17b81f6f60504c539313f94f2d826c5faa08", "shasum": "" }, "require": { - "php": ">=5.3.3", - "sebastian/recursion-context": "~2.0" + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", + "php": ">=5.3.0", + "react/dns": "^1.13", + "react/event-loop": "^1.2", + "react/promise": "^3.2 || ^2.6 || ^1.2.1", + "react/stream": "^1.4" }, "require-dev": { - "ext-mbstring": "*", - "phpunit/phpunit": "~4.4" + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36", + "react/async": "^4.3 || ^3.3 || ^2", + "react/promise-stream": "^1.4", + "react/promise-timer": "^1.11" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "React\\Socket\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" }, { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" }, { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" }, { - "name": "Adam Harvey", - "email": "aharvey@php.net" + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" } ], - "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "http://www.github.com/sebastianbergmann/exporter", + "description": "Async, streaming plaintext TCP/IP and secure TLS socket server and client connections for ReactPHP", "keywords": [ - "export", - "exporter" + "Connection", + "Socket", + "async", + "reactphp", + "stream" ], "support": { - "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/master" + "issues": "https://github.com/reactphp/socket/issues", + "source": "https://github.com/reactphp/socket/tree/v1.17.0" }, - "time": "2016-11-19T08:54:04+00:00" + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2025-11-19T20:47:34+00:00" }, { - "name": "sebastian/global-state", - "version": "1.1.1", + "name": "react/stream", + "version": "v1.4.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4" + "url": "https://github.com/reactphp/stream.git", + "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4", - "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4", + "url": "https://api.github.com/repos/reactphp/stream/zipball/1e5b0acb8fe55143b5b426817155190eb6f5b18d", + "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d", "shasum": "" }, "require": { - "php": ">=5.3.3" + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", + "php": ">=5.3.8", + "react/event-loop": "^1.2" }, "require-dev": { - "phpunit/phpunit": "~4.2" - }, - "suggest": { - "ext-uopz": "*" + "clue/stream-filter": "~1.2", + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "React\\Stream\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" } ], - "description": "Snapshotting of global state", - "homepage": "http://www.github.com/sebastianbergmann/global-state", + "description": "Event-driven readable and writable streams for non-blocking I/O in ReactPHP", "keywords": [ - "global state" + "event-driven", + "io", + "non-blocking", + "pipe", + "reactphp", + "readable", + "stream", + "writable" ], "support": { - "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/1.1.1" + "issues": "https://github.com/reactphp/stream/issues", + "source": "https://github.com/reactphp/stream/tree/v1.4.0" }, - "time": "2015-10-12T03:26:01+00:00" + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2024-06-11T12:45:25+00:00" }, { - "name": "sebastian/object-enumerator", - "version": "2.0.1", + "name": "sebastian/cli-parser", + "version": "5.0.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "1311872ac850040a79c3c058bea3e22d0f09cbb7" + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "48a4654fa5e48c1c81214e9930048a572d4b23ca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/1311872ac850040a79c3c058bea3e22d0f09cbb7", - "reference": "1311872ac850040a79c3c058bea3e22d0f09cbb7", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/48a4654fa5e48c1c81214e9930048a572d4b23ca", + "reference": "48a4654fa5e48c1c81214e9930048a572d4b23ca", "shasum": "" }, "require": { - "php": ">=5.6", - "sebastian/recursion-context": "~2.0" + "php": ">=8.4" }, "require-dev": { - "phpunit/phpunit": "~5" + "phpunit/phpunit": "^13.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -2142,41 +8221,68 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Traverses array structures and object graphs to enumerate all referenced objects", - "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", "support": { - "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/master" + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/5.0.0" }, - "time": "2017-02-18T15:18:39+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/cli-parser", + "type": "tidelift" + } + ], + "time": "2026-02-06T04:39:44+00:00" }, { - "name": "sebastian/recursion-context", - "version": "2.0.0", + "name": "sebastian/comparator", + "version": "8.1.2", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "2c3ba150cbec723aa057506e73a8d33bdb286c9a" + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "b3d09f4360ad97dcad8f82d1c047ad16ff38b7e1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/2c3ba150cbec723aa057506e73a8d33bdb286c9a", - "reference": "2c3ba150cbec723aa057506e73a8d33bdb286c9a", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/b3d09f4360ad97dcad8f82d1c047ad16ff38b7e1", + "reference": "b3d09f4360ad97dcad8f82d1c047ad16ff38b7e1", "shasum": "" }, "require": { - "php": ">=5.3.3" + "ext-dom": "*", + "ext-mbstring": "*", + "php": ">=8.4", + "sebastian/diff": "^8.1", + "sebastian/exporter": "^8.0" }, "require-dev": { - "phpunit/phpunit": "~4.4" + "phpunit/phpunit": "^13.0" + }, + "suggest": { + "ext-bcmath": "For comparing BcMath\\Number objects" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-main": "8.1-dev" } }, "autoload": { @@ -2189,48 +8295,80 @@ "BSD-3-Clause" ], "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, { "name": "Jeff Welch", "email": "whatthejeff@gmail.com" }, { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "name": "Volker Dusch", + "email": "github@wallbash.com" }, { - "name": "Adam Harvey", - "email": "aharvey@php.net" + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" } ], - "description": "Provides functionality to recursively process PHP variables", - "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], "support": { - "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/master" + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "security": "https://github.com/sebastianbergmann/comparator/security/policy", + "source": "https://github.com/sebastianbergmann/comparator/tree/8.1.2" }, - "time": "2016-11-19T07:33:16+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator", + "type": "tidelift" + } + ], + "time": "2026-04-14T08:24:42+00:00" }, { - "name": "sebastian/resource-operations", - "version": "1.0.0", + "name": "sebastian/complexity", + "version": "6.0.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52" + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "c5651c795c98093480df79350cb050813fc7a2f3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", - "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/c5651c795c98093480df79350cb050813fc7a2f3", + "reference": "c5651c795c98093480df79350cb050813fc7a2f3", "shasum": "" }, "require": { - "php": ">=5.6.0" + "nikic/php-parser": "^5.0", + "php": ">=8.4" + }, + "require-dev": { + "phpunit/phpunit": "^13.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -2245,38 +8383,62 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Provides a list of PHP built-in functions that operate on resources", - "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", "support": { - "issues": "https://github.com/sebastianbergmann/resource-operations/issues", - "source": "https://github.com/sebastianbergmann/resource-operations/tree/master" + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "security": "https://github.com/sebastianbergmann/complexity/security/policy", + "source": "https://github.com/sebastianbergmann/complexity/tree/6.0.0" }, - "time": "2015-07-28T20:34:47+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/complexity", + "type": "tidelift" + } + ], + "time": "2026-02-06T04:41:32+00:00" }, { - "name": "sebastian/version", - "version": "2.0.1", + "name": "sebastian/diff", + "version": "8.1.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "9c957d730257f49c873f3761674559bd90098a7d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/9c957d730257f49c873f3761674559bd90098a7d", + "reference": "9c957d730257f49c873f3761674559bd90098a7d", "shasum": "" }, "require": { - "php": ">=5.6" + "php": ">=8.4" + }, + "require-dev": { + "phpunit/phpunit": "^13.0", + "symfony/process": "^7.2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-main": "8.1-dev" } }, "autoload": { @@ -2291,898 +8453,870 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" } ], - "description": "Library that helps with managing the version number of Git-hosted PHP projects", - "homepage": "https://github.com/sebastianbergmann/version", + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], "support": { - "issues": "https://github.com/sebastianbergmann/version/issues", - "source": "https://github.com/sebastianbergmann/version/tree/master" + "issues": "https://github.com/sebastianbergmann/diff/issues", + "security": "https://github.com/sebastianbergmann/diff/security/policy", + "source": "https://github.com/sebastianbergmann/diff/tree/8.1.0" }, - "time": "2016-10-03T07:35:21+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/diff", + "type": "tidelift" + } + ], + "time": "2026-04-05T12:02:33+00:00" }, { - "name": "symfony/console", - "version": "v5.4.47", + "name": "sebastian/environment", + "version": "9.3.0", "source": { "type": "git", - "url": "https://github.com/symfony/console.git", - "reference": "c4ba980ca61a9eb18ee6bcc73f28e475852bb1ed" + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "6767059a30e4277ac95ee034809e793528464768" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/c4ba980ca61a9eb18ee6bcc73f28e475852bb1ed", - "reference": "c4ba980ca61a9eb18ee6bcc73f28e475852bb1ed", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/6767059a30e4277ac95ee034809e793528464768", + "reference": "6767059a30e4277ac95ee034809e793528464768", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php73": "^1.9", - "symfony/polyfill-php80": "^1.16", - "symfony/service-contracts": "^1.1|^2|^3", - "symfony/string": "^5.1|^6.0" - }, - "conflict": { - "psr/log": ">=3", - "symfony/dependency-injection": "<4.4", - "symfony/dotenv": "<5.1", - "symfony/event-dispatcher": "<4.4", - "symfony/lock": "<4.4", - "symfony/process": "<4.4" - }, - "provide": { - "psr/log-implementation": "1.0|2.0" + "php": ">=8.4" }, "require-dev": { - "psr/log": "^1|^2", - "symfony/config": "^4.4|^5.0|^6.0", - "symfony/dependency-injection": "^4.4|^5.0|^6.0", - "symfony/event-dispatcher": "^4.4|^5.0|^6.0", - "symfony/lock": "^4.4|^5.0|^6.0", - "symfony/process": "^4.4|^5.0|^6.0", - "symfony/var-dumper": "^4.4|^5.0|^6.0" + "phpunit/phpunit": "^13.0" }, "suggest": { - "psr/log": "For using the console logger", - "symfony/event-dispatcher": "", - "symfony/lock": "", - "symfony/process": "" + "ext-posix": "*" }, "type": "library", + "extra": { + "branch-alias": { + "dev-main": "9.3-dev" + } + }, "autoload": { - "psr-4": { - "Symfony\\Component\\Console\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" + "classmap": [ + "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" } ], - "description": "Eases the creation of beautiful and testable command line interfaces", - "homepage": "https://symfony.com", + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "https://github.com/sebastianbergmann/environment", "keywords": [ - "cli", - "command-line", - "console", - "terminal" + "Xdebug", + "environment", + "hhvm" ], "support": { - "source": "https://github.com/symfony/console/tree/v5.4.47" + "issues": "https://github.com/sebastianbergmann/environment/issues", + "security": "https://github.com/sebastianbergmann/environment/security/policy", + "source": "https://github.com/sebastianbergmann/environment/tree/9.3.0" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" + "url": "https://github.com/sebastianbergmann", + "type": "github" }, { - "url": "https://github.com/fabpot", - "type": "github" + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" }, { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/environment", "type": "tidelift" } ], - "time": "2024-11-06T11:30:55+00:00" + "time": "2026-04-15T12:14:03+00:00" }, { - "name": "symfony/deprecation-contracts", - "version": "v2.5.4", + "name": "sebastian/exporter", + "version": "8.0.2", "source": { "type": "git", - "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "605389f2a7e5625f273b53960dc46aeaf9c62918" + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "9cee180ebe62259e3ed48df2212d1fc8cfd971bb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/605389f2a7e5625f273b53960dc46aeaf9c62918", - "reference": "605389f2a7e5625f273b53960dc46aeaf9c62918", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/9cee180ebe62259e3ed48df2212d1fc8cfd971bb", + "reference": "9cee180ebe62259e3ed48df2212d1fc8cfd971bb", "shasum": "" }, "require": { - "php": ">=7.1" + "ext-mbstring": "*", + "php": ">=8.4", + "sebastian/recursion-context": "^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^13.0" }, "type": "library", "extra": { - "thanks": { - "url": "https://github.com/symfony/contracts", - "name": "symfony/contracts" - }, "branch-alias": { - "dev-main": "2.5-dev" + "dev-main": "8.0-dev" } }, "autoload": { - "files": [ - "function.php" + "classmap": [ + "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" }, { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" } ], - "description": "A generic function and convention to trigger deprecation notices", - "homepage": "https://symfony.com", + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.4" + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "security": "https://github.com/sebastianbergmann/exporter/security/policy", + "source": "https://github.com/sebastianbergmann/exporter/tree/8.0.2" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" + "url": "https://github.com/sebastianbergmann", + "type": "github" }, { - "url": "https://github.com/fabpot", - "type": "github" + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" }, { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter", "type": "tidelift" } ], - "time": "2024-09-25T14:11:13+00:00" + "time": "2026-04-15T12:38:05+00:00" }, { - "name": "symfony/event-dispatcher", - "version": "v5.4.45", + "name": "sebastian/git-state", + "version": "1.0.0", "source": { "type": "git", - "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "72982eb416f61003e9bb6e91f8b3213600dcf9e9" + "url": "https://github.com/sebastianbergmann/git-state.git", + "reference": "792a952e0eba55b6960a48aeceb9f371aad1f76b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/72982eb416f61003e9bb6e91f8b3213600dcf9e9", - "reference": "72982eb416f61003e9bb6e91f8b3213600dcf9e9", + "url": "https://api.github.com/repos/sebastianbergmann/git-state/zipball/792a952e0eba55b6960a48aeceb9f371aad1f76b", + "reference": "792a952e0eba55b6960a48aeceb9f371aad1f76b", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/event-dispatcher-contracts": "^2|^3", - "symfony/polyfill-php80": "^1.16" - }, - "conflict": { - "symfony/dependency-injection": "<4.4" - }, - "provide": { - "psr/event-dispatcher-implementation": "1.0", - "symfony/event-dispatcher-implementation": "2.0" + "php": ">=8.4" }, "require-dev": { - "psr/log": "^1|^2|^3", - "symfony/config": "^4.4|^5.0|^6.0", - "symfony/dependency-injection": "^4.4|^5.0|^6.0", - "symfony/error-handler": "^4.4|^5.0|^6.0", - "symfony/expression-language": "^4.4|^5.0|^6.0", - "symfony/http-foundation": "^4.4|^5.0|^6.0", - "symfony/service-contracts": "^1.1|^2|^3", - "symfony/stopwatch": "^4.4|^5.0|^6.0" - }, - "suggest": { - "symfony/dependency-injection": "", - "symfony/http-kernel": "" + "phpunit/phpunit": "^13.0" }, "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.0-dev" + } + }, "autoload": { - "psr-4": { - "Symfony\\Component\\EventDispatcher\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" + "classmap": [ + "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", - "homepage": "https://symfony.com", + "description": "Library for describing the state of a Git checkout", + "homepage": "https://github.com/sebastianbergmann/git-state", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v5.4.45" + "issues": "https://github.com/sebastianbergmann/git-state/issues", + "security": "https://github.com/sebastianbergmann/git-state/security/policy", + "source": "https://github.com/sebastianbergmann/git-state/tree/1.0.0" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" + "url": "https://github.com/sebastianbergmann", + "type": "github" }, { - "url": "https://github.com/fabpot", - "type": "github" + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" }, { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/git-state", "type": "tidelift" } ], - "time": "2024-09-25T14:11:13+00:00" + "time": "2026-03-21T12:54:28+00:00" }, { - "name": "symfony/event-dispatcher-contracts", - "version": "v2.5.4", + "name": "sebastian/global-state", + "version": "9.0.0", "source": { "type": "git", - "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "e0fe3d79b516eb75126ac6fa4cbf19b79b08c99f" + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "e52e3dc22441e6218c710afe72c3042f8fc41ea7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/e0fe3d79b516eb75126ac6fa4cbf19b79b08c99f", - "reference": "e0fe3d79b516eb75126ac6fa4cbf19b79b08c99f", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/e52e3dc22441e6218c710afe72c3042f8fc41ea7", + "reference": "e52e3dc22441e6218c710afe72c3042f8fc41ea7", "shasum": "" }, "require": { - "php": ">=7.2.5", - "psr/event-dispatcher": "^1" + "php": ">=8.4", + "sebastian/object-reflector": "^6.0", + "sebastian/recursion-context": "^8.0" }, - "suggest": { - "symfony/event-dispatcher-implementation": "" + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^13.0" }, "type": "library", "extra": { - "thanks": { - "url": "https://github.com/symfony/contracts", - "name": "symfony/contracts" - }, "branch-alias": { - "dev-main": "2.5-dev" + "dev-main": "9.0-dev" } }, "autoload": { - "psr-4": { - "Symfony\\Contracts\\EventDispatcher\\": "" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" } ], - "description": "Generic abstractions related to dispatching event", - "homepage": "https://symfony.com", + "description": "Snapshotting of global state", + "homepage": "https://www.github.com/sebastianbergmann/global-state", "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" + "global state" ], "support": { - "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v2.5.4" + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "security": "https://github.com/sebastianbergmann/global-state/security/policy", + "source": "https://github.com/sebastianbergmann/global-state/tree/9.0.0" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" + "url": "https://github.com/sebastianbergmann", + "type": "github" }, { - "url": "https://github.com/fabpot", - "type": "github" + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" }, { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/global-state", "type": "tidelift" } ], - "time": "2024-09-25T14:11:13+00:00" + "time": "2026-02-06T04:45:13+00:00" }, { - "name": "symfony/filesystem", - "version": "v5.4.45", + "name": "sebastian/lines-of-code", + "version": "5.0.0", "source": { "type": "git", - "url": "https://github.com/symfony/filesystem.git", - "reference": "57c8294ed37d4a055b77057827c67f9558c95c54" + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "4f21bb7768e1c997722ccc7efb1d6b5c11bfd471" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/57c8294ed37d4a055b77057827c67f9558c95c54", - "reference": "57c8294ed37d4a055b77057827c67f9558c95c54", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/4f21bb7768e1c997722ccc7efb1d6b5c11bfd471", + "reference": "4f21bb7768e1c997722ccc7efb1d6b5c11bfd471", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-mbstring": "~1.8", - "symfony/polyfill-php80": "^1.16" + "nikic/php-parser": "^5.0", + "php": ">=8.4" }, "require-dev": { - "symfony/process": "^5.4|^6.4" + "phpunit/phpunit": "^13.0" }, "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, "autoload": { - "psr-4": { - "Symfony\\Component\\Filesystem\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" + "classmap": [ + "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Provides basic utilities for the filesystem", - "homepage": "https://symfony.com", + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", "support": { - "source": "https://github.com/symfony/filesystem/tree/v5.4.45" + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/5.0.0" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" + "url": "https://github.com/sebastianbergmann", + "type": "github" }, { - "url": "https://github.com/fabpot", - "type": "github" + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" }, { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/lines-of-code", "type": "tidelift" } ], - "time": "2024-10-22T13:05:35+00:00" + "time": "2026-02-06T04:45:54+00:00" }, { - "name": "symfony/finder", - "version": "v5.4.45", + "name": "sebastian/object-enumerator", + "version": "8.0.0", "source": { "type": "git", - "url": "https://github.com/symfony/finder.git", - "reference": "63741784cd7b9967975eec610b256eed3ede022b" + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "b39ab125fd9a7434b0ecbc4202eebce11a98cfc5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/63741784cd7b9967975eec610b256eed3ede022b", - "reference": "63741784cd7b9967975eec610b256eed3ede022b", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/b39ab125fd9a7434b0ecbc4202eebce11a98cfc5", + "reference": "b39ab125fd9a7434b0ecbc4202eebce11a98cfc5", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-php80": "^1.16" + "php": ">=8.4", + "sebastian/object-reflector": "^6.0", + "sebastian/recursion-context": "^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^13.0" }, "type": "library", + "extra": { + "branch-alias": { + "dev-main": "8.0-dev" + } + }, "autoload": { - "psr-4": { - "Symfony\\Component\\Finder\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" + "classmap": [ + "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" } ], - "description": "Finds files and directories via an intuitive fluent interface", - "homepage": "https://symfony.com", + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", "support": { - "source": "https://github.com/symfony/finder/tree/v5.4.45" + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "security": "https://github.com/sebastianbergmann/object-enumerator/security/policy", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/8.0.0" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" + "url": "https://github.com/sebastianbergmann", + "type": "github" }, { - "url": "https://github.com/fabpot", - "type": "github" + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" }, { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/object-enumerator", "type": "tidelift" } ], - "time": "2024-09-28T13:32:08+00:00" + "time": "2026-02-06T04:46:36+00:00" }, { - "name": "symfony/options-resolver", - "version": "v5.4.45", + "name": "sebastian/object-reflector", + "version": "6.0.0", "source": { "type": "git", - "url": "https://github.com/symfony/options-resolver.git", - "reference": "74e5b6f0db3e8589e6cfd5efb317a1fc2bb52fb6" + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "3ca042c2c60b0eab094f8a1b6a7093f4d4c72200" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/74e5b6f0db3e8589e6cfd5efb317a1fc2bb52fb6", - "reference": "74e5b6f0db3e8589e6cfd5efb317a1fc2bb52fb6", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/3ca042c2c60b0eab094f8a1b6a7093f4d4c72200", + "reference": "3ca042c2c60b0eab094f8a1b6a7093f4d4c72200", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-php73": "~1.0", - "symfony/polyfill-php80": "^1.16" + "php": ">=8.4" + }, + "require-dev": { + "phpunit/phpunit": "^13.0" }, "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, "autoload": { - "psr-4": { - "Symfony\\Component\\OptionsResolver\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" + "classmap": [ + "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" } ], - "description": "Provides an improved replacement for the array_replace PHP function", - "homepage": "https://symfony.com", - "keywords": [ - "config", - "configuration", - "options" - ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", "support": { - "source": "https://github.com/symfony/options-resolver/tree/v5.4.45" + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "security": "https://github.com/sebastianbergmann/object-reflector/security/policy", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/6.0.0" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" + "url": "https://github.com/sebastianbergmann", + "type": "github" }, { - "url": "https://github.com/fabpot", - "type": "github" + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" }, { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/object-reflector", "type": "tidelift" } ], - "time": "2024-09-25T14:11:13+00:00" + "time": "2026-02-06T04:47:13+00:00" }, { - "name": "symfony/polyfill-ctype", - "version": "v1.31.0", + "name": "sebastian/recursion-context", + "version": "8.0.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "74c5af21f6a5833e91767ca068c4d3dfec15317e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", - "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/74c5af21f6a5833e91767ca068c4d3dfec15317e", + "reference": "74c5af21f6a5833e91767ca068c4d3dfec15317e", "shasum": "" }, "require": { - "php": ">=7.2" - }, - "provide": { - "ext-ctype": "*" + "php": ">=8.4" }, - "suggest": { - "ext-ctype": "For best performance" + "require-dev": { + "phpunit/phpunit": "^13.0" }, "type": "library", "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" + "branch-alias": { + "dev-main": "8.0-dev" } }, "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" }, { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" } ], - "description": "Symfony polyfill for ctype functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "ctype", - "polyfill", - "portable" - ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0" + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/8.0.0" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" + "url": "https://github.com/sebastianbergmann", + "type": "github" }, { - "url": "https://github.com/fabpot", - "type": "github" + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" }, { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2026-02-06T04:51:28+00:00" }, { - "name": "symfony/polyfill-intl-grapheme", - "version": "v1.31.0", + "name": "sebastian/type", + "version": "7.0.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe" + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "42412224607bd3931241bbd17f38e0f972f5a916" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", - "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/42412224607bd3931241bbd17f38e0f972f5a916", + "reference": "42412224607bd3931241bbd17f38e0f972f5a916", "shasum": "" }, "require": { - "php": ">=7.2" + "php": ">=8.4" }, - "suggest": { - "ext-intl": "For best performance" + "require-dev": { + "phpunit/phpunit": "^13.0" }, "type": "library", "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" + "branch-alias": { + "dev-main": "7.0-dev" } }, "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Intl\\Grapheme\\": "" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Symfony polyfill for intl's grapheme_* functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "grapheme", - "intl", - "polyfill", - "portable", - "shim" - ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.31.0" + "issues": "https://github.com/sebastianbergmann/type/issues", + "security": "https://github.com/sebastianbergmann/type/security/policy", + "source": "https://github.com/sebastianbergmann/type/tree/7.0.0" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" + "url": "https://github.com/sebastianbergmann", + "type": "github" }, { - "url": "https://github.com/fabpot", - "type": "github" + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" }, { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/type", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2026-02-06T04:52:09+00:00" }, { - "name": "symfony/polyfill-intl-normalizer", - "version": "v1.31.0", + "name": "sebastian/version", + "version": "7.0.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "3833d7255cc303546435cb650316bff708a1c75c" + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "ad37a5552c8e2b88572249fdc19b6da7792e021b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", - "reference": "3833d7255cc303546435cb650316bff708a1c75c", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/ad37a5552c8e2b88572249fdc19b6da7792e021b", + "reference": "ad37a5552c8e2b88572249fdc19b6da7792e021b", "shasum": "" }, "require": { - "php": ">=7.2" - }, - "suggest": { - "ext-intl": "For best performance" + "php": ">=8.4" }, "type": "library", "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" + "branch-alias": { + "dev-main": "7.0-dev" } }, "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Intl\\Normalizer\\": "" - }, "classmap": [ - "Resources/stubs" + "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Symfony polyfill for intl's Normalizer class and related functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "intl", - "normalizer", - "polyfill", - "portable", - "shim" - ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.31.0" + "issues": "https://github.com/sebastianbergmann/version/issues", + "security": "https://github.com/sebastianbergmann/version/security/policy", + "source": "https://github.com/sebastianbergmann/version/tree/7.0.0" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" + "url": "https://github.com/sebastianbergmann", + "type": "github" }, { - "url": "https://github.com/fabpot", - "type": "github" + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" }, { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/version", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2026-02-06T04:52:52+00:00" }, { - "name": "symfony/polyfill-mbstring", - "version": "v1.31.0", + "name": "staabm/side-effects-detector", + "version": "1.0.5", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341" + "url": "https://github.com/staabm/side-effects-detector.git", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341", - "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341", + "url": "https://api.github.com/repos/staabm/side-effects-detector/zipball/d8334211a140ce329c13726d4a715adbddd0a163", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163", "shasum": "" }, "require": { - "php": ">=7.2" - }, - "provide": { - "ext-mbstring": "*" + "ext-tokenizer": "*", + "php": "^7.4 || ^8.0" }, - "suggest": { - "ext-mbstring": "For best performance" + "require-dev": { + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^1.12.6", + "phpunit/phpunit": "^9.6.21", + "symfony/var-dumper": "^5.4.43", + "tomasvotruba/type-coverage": "1.0.0", + "tomasvotruba/unused-public": "1.0.0" }, "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - } + "classmap": [ + "lib/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for the Mbstring extension", - "homepage": "https://symfony.com", + "description": "A static analysis tool to detect side effects in PHP code", "keywords": [ - "compatibility", - "mbstring", - "polyfill", - "portable", - "shim" + "static analysis" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0" + "issues": "https://github.com/staabm/side-effects-detector/issues", + "source": "https://github.com/staabm/side-effects-detector/tree/1.0.5" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", + "url": "https://github.com/staabm", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2024-10-20T05:08:20+00:00" }, { - "name": "symfony/polyfill-php73", - "version": "v1.31.0", + "name": "symfony/event-dispatcher", + "version": "v8.0.9", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php73.git", - "reference": "0f68c03565dcaaf25a890667542e8bd75fe7e5bb" + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "0c3c1a17604c4dbbec4b93fe162c538482096e1f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/0f68c03565dcaaf25a890667542e8bd75fe7e5bb", - "reference": "0f68c03565dcaaf25a890667542e8bd75fe7e5bb", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/0c3c1a17604c4dbbec4b93fe162c538482096e1f", + "reference": "0c3c1a17604c4dbbec4b93fe162c538482096e1f", "shasum": "" }, "require": { - "php": ">=7.2" + "php": ">=8.4", + "symfony/event-dispatcher-contracts": "^2.5|^3" }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } + "conflict": { + "symfony/security-http": "<7.4", + "symfony/service-contracts": "<2.5" + }, + "provide": { + "psr/event-dispatcher-implementation": "1.0", + "symfony/event-dispatcher-implementation": "2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/error-handler": "^7.4|^8.0", + "symfony/expression-language": "^7.4|^8.0", + "symfony/framework-bundle": "^7.4|^8.0", + "symfony/http-foundation": "^7.4|^8.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/stopwatch": "^7.4|^8.0" }, + "type": "library", "autoload": { - "files": [ - "bootstrap.php" - ], "psr-4": { - "Symfony\\Polyfill\\Php73\\": "" + "Symfony\\Component\\EventDispatcher\\": "" }, - "classmap": [ - "Resources/stubs" + "exclude-from-classmap": [ + "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -3191,24 +9325,18 @@ ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", + "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], "support": { - "source": "https://github.com/symfony/polyfill-php73/tree/v1.31.0" + "source": "https://github.com/symfony/event-dispatcher/tree/v8.0.9" }, "funding": [ { @@ -3219,57 +9347,55 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2026-04-18T13:51:42+00:00" }, { - "name": "symfony/polyfill-php80", - "version": "v1.31.0", + "name": "symfony/event-dispatcher-contracts", + "version": "v3.7.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8" + "url": "https://github.com/symfony/event-dispatcher-contracts.git", + "reference": "ccba7060602b7fed0b03c85bf025257f76d9ef32" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", - "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/ccba7060602b7fed0b03c85bf025257f76d9ef32", + "reference": "ccba7060602b7fed0b03c85bf025257f76d9ef32", "shasum": "" }, "require": { - "php": ">=7.2" + "php": ">=8.1", + "psr/event-dispatcher": "^1" }, "type": "library", "extra": { "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php80\\": "" + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" }, - "classmap": [ - "Resources/stubs" - ] + "branch-alias": { + "dev-main": "3.7-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\EventDispatcher\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ - { - "name": "Ion Bazan", - "email": "ion.bazan@gmail.com" - }, { "name": "Nicolas Grekas", "email": "p@tchwork.com" @@ -3279,16 +9405,18 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "description": "Generic abstractions related to dispatching event", "homepage": "https://symfony.com", "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.31.0" + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.7.0" }, "funding": [ { @@ -3299,46 +9427,46 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2026-01-05T13:30:16+00:00" }, { - "name": "symfony/polyfill-php81", - "version": "v1.31.0", + "name": "symfony/filesystem", + "version": "v8.0.9", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php81.git", - "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c" + "url": "https://github.com/symfony/filesystem.git", + "reference": "d1ec4543d5c6c2dac78503c2fae5ea0b3608ce40" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", - "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/d1ec4543d5c6c2dac78503c2fae5ea0b3608ce40", + "reference": "d1ec4543d5c6c2dac78503c2fae5ea0b3608ce40", "shasum": "" }, "require": { - "php": ">=7.2" + "php": ">=8.4", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8" }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } + "require-dev": { + "symfony/process": "^7.4|^8.0" }, + "type": "library", "autoload": { - "files": [ - "bootstrap.php" - ], "psr-4": { - "Symfony\\Polyfill\\Php81\\": "" + "Symfony\\Component\\Filesystem\\": "" }, - "classmap": [ - "Resources/stubs" + "exclude-from-classmap": [ + "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -3347,24 +9475,18 @@ ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", + "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], "support": { - "source": "https://github.com/symfony/polyfill-php81/tree/v1.31.0" + "source": "https://github.com/symfony/filesystem/tree/v8.0.9" }, "funding": [ { @@ -3375,35 +9497,41 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2026-04-18T13:51:42+00:00" }, { - "name": "symfony/process", - "version": "v5.4.47", + "name": "symfony/finder", + "version": "v8.0.8", "source": { "type": "git", - "url": "https://github.com/symfony/process.git", - "reference": "5d1662fb32ebc94f17ddb8d635454a776066733d" + "url": "https://github.com/symfony/finder.git", + "reference": "8da41214757b87d97f181e3d14a4179286151007" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/5d1662fb32ebc94f17ddb8d635454a776066733d", - "reference": "5d1662fb32ebc94f17ddb8d635454a776066733d", + "url": "https://api.github.com/repos/symfony/finder/zipball/8da41214757b87d97f181e3d14a4179286151007", + "reference": "8da41214757b87d97f181e3d14a4179286151007", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/polyfill-php80": "^1.16" + "php": ">=8.4" + }, + "require-dev": { + "symfony/filesystem": "^7.4|^8.0" }, "type": "library", "autoload": { "psr-4": { - "Symfony\\Component\\Process\\": "" + "Symfony\\Component\\Finder\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -3423,10 +9551,10 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Executes commands in sub-processes", + "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v5.4.47" + "source": "https://github.com/symfony/finder/tree/v8.0.8" }, "funding": [ { @@ -3437,52 +9565,43 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-11-06T11:36:42+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { - "name": "symfony/service-contracts", - "version": "v2.5.4", + "name": "symfony/options-resolver", + "version": "v8.0.8", "source": { "type": "git", - "url": "https://github.com/symfony/service-contracts.git", - "reference": "f37b419f7aea2e9abf10abd261832cace12e3300" + "url": "https://github.com/symfony/options-resolver.git", + "reference": "b48bce0a70b914f6953dafbd10474df232ed4de8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f37b419f7aea2e9abf10abd261832cace12e3300", - "reference": "f37b419f7aea2e9abf10abd261832cace12e3300", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/b48bce0a70b914f6953dafbd10474df232ed4de8", + "reference": "b48bce0a70b914f6953dafbd10474df232ed4de8", "shasum": "" }, "require": { - "php": ">=7.2.5", - "psr/container": "^1.1", - "symfony/deprecation-contracts": "^2.1|^3" - }, - "conflict": { - "ext-psr": "<1.1|>=2" - }, - "suggest": { - "symfony/service-implementation": "" + "php": ">=8.4", + "symfony/deprecation-contracts": "^2.5|^3" }, "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/contracts", - "name": "symfony/contracts" - }, - "branch-alias": { - "dev-main": "2.5-dev" - } - }, "autoload": { "psr-4": { - "Symfony\\Contracts\\Service\\": "" - } + "Symfony\\Component\\OptionsResolver\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -3490,26 +9609,23 @@ ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Generic abstractions related to writing services", + "description": "Provides an improved replacement for the array_replace PHP function", "homepage": "https://symfony.com", "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" + "config", + "configuration", + "options" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v2.5.4" + "source": "https://github.com/symfony/options-resolver/tree/v8.0.8" }, "funding": [ { @@ -3520,38 +9636,50 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-25T14:11:13+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { - "name": "symfony/stopwatch", - "version": "v5.4.45", + "name": "symfony/polyfill-php80", + "version": "v1.37.0", "source": { "type": "git", - "url": "https://github.com/symfony/stopwatch.git", - "reference": "fb2c199cf302eb207f8c23e7ee174c1c31a5c004" + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/fb2c199cf302eb207f8c23e7ee174c1c31a5c004", - "reference": "fb2c199cf302eb207f8c23e7ee174c1c31a5c004", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/dfb55726c3a76ea3b6459fcfda1ec2d80a682411", + "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/service-contracts": "^1|^2|^3" + "php": ">=7.2" }, "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, "autoload": { + "files": [ + "bootstrap.php" + ], "psr-4": { - "Symfony\\Component\\Stopwatch\\": "" + "Symfony\\Polyfill\\Php80\\": "" }, - "exclude-from-classmap": [ - "/Tests/" + "classmap": [ + "Resources/stubs" ] }, "notification-url": "https://packagist.org/downloads/", @@ -3560,18 +9688,28 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Provides a way to profile code", + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], "support": { - "source": "https://github.com/symfony/stopwatch/tree/v5.4.45" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.37.0" }, "funding": [ { @@ -3582,54 +9720,50 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-25T14:11:13+00:00" + "time": "2026-04-10T16:19:22+00:00" }, { - "name": "symfony/string", - "version": "v5.4.47", + "name": "symfony/polyfill-php81", + "version": "v1.37.0", "source": { "type": "git", - "url": "https://github.com/symfony/string.git", - "reference": "136ca7d72f72b599f2631aca474a4f8e26719799" + "url": "https://github.com/symfony/polyfill-php81.git", + "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/136ca7d72f72b599f2631aca474a4f8e26719799", - "reference": "136ca7d72f72b599f2631aca474a4f8e26719799", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", + "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-intl-grapheme": "~1.0", - "symfony/polyfill-intl-normalizer": "~1.0", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php80": "~1.15" - }, - "conflict": { - "symfony/translation-contracts": ">=3.0" - }, - "require-dev": { - "symfony/error-handler": "^4.4|^5.0|^6.0", - "symfony/http-client": "^4.4|^5.0|^6.0", - "symfony/translation-contracts": "^1.1|^2", - "symfony/var-exporter": "^4.4|^5.0|^6.0" + "php": ">=7.2" }, "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, "autoload": { "files": [ - "Resources/functions.php" + "bootstrap.php" ], "psr-4": { - "Symfony\\Component\\String\\": "" + "Symfony\\Polyfill\\Php81\\": "" }, - "exclude-from-classmap": [ - "/Tests/" + "classmap": [ + "Resources/stubs" ] }, "notification-url": "https://packagist.org/downloads/", @@ -3646,18 +9780,16 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ - "grapheme", - "i18n", - "string", - "unicode", - "utf-8", - "utf8" + "compatibility", + "polyfill", + "portable", + "shim" ], "support": { - "source": "https://github.com/symfony/string/tree/v5.4.47" + "source": "https://github.com/symfony/polyfill-php81/tree/v1.37.0" }, "funding": [ { @@ -3668,44 +9800,38 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-11-10T20:33:58+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { - "name": "symfony/yaml", - "version": "v4.4.45", + "name": "symfony/process", + "version": "v6.4.33", "source": { "type": "git", - "url": "https://github.com/symfony/yaml.git", - "reference": "aeccc4dc52a9e634f1d1eebeb21eacfdcff1053d" + "url": "https://github.com/symfony/process.git", + "reference": "c46e854e79b52d07666e43924a20cb6dc546644e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/aeccc4dc52a9e634f1d1eebeb21eacfdcff1053d", - "reference": "aeccc4dc52a9e634f1d1eebeb21eacfdcff1053d", + "url": "https://api.github.com/repos/symfony/process/zipball/c46e854e79b52d07666e43924a20cb6dc546644e", + "reference": "c46e854e79b52d07666e43924a20cb6dc546644e", "shasum": "" }, "require": { - "php": ">=7.1.3", - "symfony/polyfill-ctype": "~1.8" - }, - "conflict": { - "symfony/console": "<3.4" - }, - "require-dev": { - "symfony/console": "^3.4|^4.0|^5.0" - }, - "suggest": { - "symfony/console": "For validating YAML files using the lint command" + "php": ">=8.1" }, "type": "library", "autoload": { "psr-4": { - "Symfony\\Component\\Yaml\\": "" + "Symfony\\Component\\Process\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -3725,10 +9851,10 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Loads and dumps YAML files", + "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v4.4.45" + "source": "https://github.com/symfony/process/tree/v6.4.33" }, "funding": [ { @@ -3739,89 +9865,88 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2022-08-02T15:47:23+00:00" + "time": "2026-01-23T16:02:12+00:00" }, { - "name": "webmozart/assert", - "version": "1.11.0", + "name": "theseer/tokenizer", + "version": "2.0.1", "source": { "type": "git", - "url": "https://github.com/webmozarts/assert.git", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" + "url": "https://github.com/theseer/tokenizer.git", + "reference": "7989e43bf381af0eac72e4f0ca5bcbfa81658be4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/7989e43bf381af0eac72e4f0ca5bcbfa81658be4", + "reference": "7989e43bf381af0eac72e4f0ca5bcbfa81658be4", "shasum": "" }, "require": { - "ext-ctype": "*", - "php": "^7.2 || ^8.0" - }, - "conflict": { - "phpstan/phpstan": "<0.12.20", - "vimeo/psalm": "<4.6.1 || 4.6.2" - }, - "require-dev": { - "phpunit/phpunit": "^8.5.13" + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^8.1" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.10-dev" - } - }, "autoload": { - "psr-4": { - "Webmozart\\Assert\\": "src/" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" } ], - "description": "Assertions to validate method input/output with nice error messages.", - "keywords": [ - "assert", - "check", - "validate" - ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { - "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.11.0" + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/2.0.1" }, - "time": "2022-06-03T18:03:27+00:00" + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2025-12-08T11:19:18+00:00" } ], "aliases": [], "minimum-stability": "stable", "stability-flags": { "friendsofphp/php-cs-fixer": 0, - "pcov/clobber": 0, - "phpstan/phpstan": 0 + "laminas/laminas-development-mode": 0, + "mezzio/mezzio-tooling": 0, + "phpstan/phpstan-doctrine": 0, + "phpstan/phpstan-phpunit": 0, + "phpunit/phpunit": 0 }, "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": "~7.2.0", + "php": "^8.4", "ext-curl": "*", "ext-dom": "*", "ext-iconv": "*", - "ext-json": "*", + "ext-intl": "*", "ext-posix": "*" }, "platform-dev": {}, - "plugin-api-version": "2.6.0" + "plugin-api-version": "2.9.0" } diff --git a/config/autoload/dependencies.global.php b/config/autoload/dependencies.global.php new file mode 100644 index 0000000..da73ab8 --- /dev/null +++ b/config/autoload/dependencies.global.php @@ -0,0 +1,36 @@ + [ + // Use 'aliases' to alias a service name to another service. The + // key is the alias name, the value is the service to which it points. + 'aliases' => [ + Doctrine\ORM\EntityManager::class => 'doctrine.entity_manager.orm_default', + ], + // Use 'invokables' for constructor-less services, or services that do + // not require arguments to the constructor. Map a service name to the + // class name. + 'invokables' => [ + Doctrine\ORM\Mapping\UnderscoreNamingStrategy::class => Doctrine\ORM\Mapping\UnderscoreNamingStrategy::class, + Psr\Log\LoggerInterface::class => Application\Service\Logger::class, + ], + // Use 'factories' for services provided by callbacks/factory classes. + 'factories' => [ + 'doctrine.entity_manager.orm_default' => Roave\PsrContainerDoctrine\EntityManagerFactory::class, + Application\Middleware\DetectBrowserLocaleMiddleware::class => Application\Middleware\DetectBrowserLocaleFactory::class, + Application\Middleware\AuthenticationMiddleware::class => Application\Middleware\AuthenticationFactory::class, + Application\View\Helper\GoogleAnalytics::class => Application\View\Helper\GoogleAnalyticsFactory::class, + Doctrine\Migrations\Configuration\Migration\ConfigurationLoader::class => Roave\PsrContainerDoctrine\Migrations\ConfigurationLoaderFactory::class, + Doctrine\Migrations\DependencyFactory::class => Roave\PsrContainerDoctrine\Migrations\DependencyFactoryFactory::class, + Doctrine\DBAL\Logging\Middleware::class => Application\DBAL\Logging\MiddlewareFactory::class, + ], + 'abstract_factories' => [ + Application\Handler\PageHandlerFactory::class, + ], + ], +]; diff --git a/config/autoload/development.local.php.dist b/config/autoload/development.local.php.dist new file mode 100644 index 0000000..edd3efb --- /dev/null +++ b/config/autoload/development.local.php.dist @@ -0,0 +1,15 @@ + [ + 'connection' => [ + 'orm_default' => [ + 'driverClass' => Doctrine\DBAL\Driver\PDO\MySQL\Driver::class, + 'params' => [ + 'host' => 'localhost', + 'dbname' => 'mqueue', + 'user' => 'mqueue', + 'password' => '', + 'port' => 3306, + 'driverOptions' => [ + PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8mb4', + ], + 'defaultTableOptions' => [ + 'charset' => 'utf8mb4', + 'collate' => 'utf8mb4_unicode_520_ci', + ], + ], + ], + ], + 'driver' => [ + 'orm_default' => [ + 'class' => Doctrine\ORM\Mapping\Driver\AttributeDriver::class, + 'cache' => 'array', + 'paths' => ['src/Application/Model'], + ], + ], + 'configuration' => [ + 'orm_default' => [ + 'naming_strategy' => Doctrine\ORM\Mapping\UnderscoreNamingStrategy::class, + 'numeric_functions' => [ + 'now' => DoctrineExtensions\Query\Mysql\Now::class, + 'rand' => DoctrineExtensions\Query\Mysql\Rand::class, + ], + ], + ], + 'types' => [ + 'datetime' => Application\DBAL\Types\ChronosType::class, + 'date' => Application\DBAL\Types\DateType::class, + ], + // migrations configuration + 'migrations' => [ + 'orm_default' => [ + 'table_storage' => [ + 'table_name' => 'doctrine_migration_versions', + ], + 'custom_template' => 'config/migration-template.txt', + 'migrations_paths' => [ + 'Application\Migration' => 'src/Application/Migration', + ], + ], + ], + ], +]; diff --git a/config/autoload/global.php b/config/autoload/global.php new file mode 100644 index 0000000..29d9a93 --- /dev/null +++ b/config/autoload/global.php @@ -0,0 +1,121 @@ + true, + 'googleAnalyticsTrackingCode' => '', + 'templates' => [ + 'paths' => [ + 'app' => ['templates/app'], + 'error' => ['templates/error'], + 'layout' => ['templates/layout'], + ], + 'map' => [ + 'pagination' => 'templates/partial/pagination.phtml', + ], + ], + 'translator' => [ + 'translation_file_patterns' => [ + [ + 'type' => 'gettext', + 'base_dir' => 'data/i18n', + 'pattern' => '%s.mo', + ], + ], + ], + 'navigation' => [ + 'default' => [ + [ + 'label' => ('Activity'), + 'route' => 'activity.index', + + ], + [ + 'label' => ('Movies'), + 'route' => 'movie.index', + 'pages' => [ + [ + 'route' => 'movie.view', + 'visible' => false, + ], + ], + ], + [ + 'label' => ('Add movie'), + 'route' => 'movie.add', + + ], + [ + 'label' => ('Users'), + 'route' => 'user.index', + 'pages' => [ + [ + 'route' => 'user.view', + 'visible' => false, + ], + [ + 'route' => 'user.logout', + 'visible' => false, + ], + [ + 'route' => 'user.login', + 'visible' => false, + ], + [ + 'route' => 'user.new', + 'visible' => false, + ], + ], + ], + [ + 'label' => ('FAQ'), + 'route' => 'faq', + ], + ], + ], + 'view_helpers' => [ + 'aliases' => [ + 'headScript' => Application\View\Helper\HeadScript::class, + 'languageSelector' => Application\View\Helper\LanguageSelector::class, + 'loginState' => Application\View\Helper\LoginState::class, + 'flashMessenger' => Application\View\Helper\FlashMessenger::class, + 'footer' => Application\View\Helper\Footer::class, + 'activity' => Application\View\Helper\Activity::class, + 'alternateFormats' => Application\View\Helper\AlternateFormats::class, + 'urlParams' => Application\View\Helper\UrlParams::class, + 'gravatar' => Application\View\Helper\Gravatar::class, + 'graph' => Application\View\Helper\Graph::class, + 'statusLinks' => Application\View\Helper\StatusLinks::class, + 'statusHelp' => Application\View\Helper\StatusHelp::class, + 'movie' => Application\View\Helper\Movie::class, + 'sort' => Application\View\Helper\Sort::class, + ], + 'invokables' => [ + 'link' => Application\View\Helper\Link::class, + 'rating' => Application\View\Helper\Rating::class, + ], + 'factories' => [ + 'googleAnalytics' => Application\View\Helper\GoogleAnalyticsFactory::class, + 'cacheStamp' => Application\View\Helper\CacheStampFactory::class, + + Application\View\Helper\Activity::class => Application\View\Helper\ActivityFactory::class, + Application\View\Helper\AlternateFormats::class => Application\View\Helper\AlternateFormatsFactory::class, + Application\View\Helper\CacheStamp::class => Application\View\Helper\CacheStampFactory::class, + Application\View\Helper\FlashMessenger::class => Application\View\Helper\FlashMessengerFactory::class, + Application\View\Helper\Footer::class => Application\View\Helper\FooterFactory::class, + Application\View\Helper\GoogleAnalytics::class => Application\View\Helper\GoogleAnalyticsFactory::class, + Application\View\Helper\Graph::class => Application\View\Helper\GraphFactory::class, + Application\View\Helper\Gravatar::class => Application\View\Helper\GravatarFactory::class, + Application\View\Helper\HeadLink::class => Application\View\Helper\HeadLinkFactory::class, + Application\View\Helper\HeadScript::class => Application\View\Helper\HeadScriptFactory::class, + Application\View\Helper\LanguageSelector::class => Application\View\Helper\LanguageSelectorFactory::class, + Application\View\Helper\LoginState::class => Application\View\Helper\LoginStateFactory::class, + Application\View\Helper\Movie::class => Application\View\Helper\MovieFactory::class, + Application\View\Helper\Sort::class => Application\View\Helper\SortFactory::class, + Application\View\Helper\StatusHelp::class => Application\View\Helper\StatusHelpFactory::class, + Application\View\Helper\StatusLinks::class => Application\View\Helper\StatusLinksFactory::class, + Application\View\Helper\UrlParams::class => Application\View\Helper\UrlParamsFactory::class, + ], + ], +]; diff --git a/config/autoload/local.php.dist b/config/autoload/local.php.dist new file mode 100644 index 0000000..db858d7 --- /dev/null +++ b/config/autoload/local.php.dist @@ -0,0 +1,13 @@ + true, + + // Enable debugging; typically used to provide debugging information within templates. + 'debug' => false, + 'mezzio' => [ + // Provide templates for the error handling middleware to use when + // generating responses. + 'error_handler' => [ + 'template_404' => 'error::404', + 'template_error' => 'error::error', + 'layout' => 'layout::error', + ], + ], +]; diff --git a/config/config.php b/config/config.php new file mode 100644 index 0000000..2118b94 --- /dev/null +++ b/config/config.php @@ -0,0 +1,48 @@ + 'data/cache/config-cache.php', +]; + +$aggregator = new ConfigAggregator([ + Mezzio\Tooling\ConfigProvider::class, + Mezzio\LaminasView\ConfigProvider::class, + Mezzio\Router\LaminasRouter\ConfigProvider::class, + Laminas\Router\ConfigProvider::class, + Laminas\HttpHandlerRunner\ConfigProvider::class, + + Laminas\I18n\ConfigProvider::class, + Laminas\Form\ConfigProvider::class, + Mezzio\Session\Ext\ConfigProvider::class, + Mezzio\Session\ConfigProvider::class, + Mezzio\Flash\ConfigProvider::class, + // Include cache configuration + new ArrayProvider($cacheConfig), + Mezzio\Helper\ConfigProvider::class, + Mezzio\ConfigProvider::class, + Mezzio\Router\ConfigProvider::class, + Mimmi20\Mezzio\Navigation\ConfigProvider::class, + Mimmi20\Mezzio\Navigation\LaminasView\ConfigProvider::class, + Laminas\Diactoros\ConfigProvider::class, + + // Load application config in a pre-defined order in such a way that local settings + // overwrite global settings. (Loaded as first to last): + // - `global.php` + // - `*.global.php` + // - `local.php` + // - `*.local.php` + new PhpFileProvider(realpath(__DIR__) . '/autoload/{{,*.}global,{,*.}local}.php'), + + // Load development config if it exists + new PhpFileProvider(realpath(__DIR__) . '/development.config.php'), +], $cacheConfig['config_cache_path']); + +return $aggregator->getMergedConfig(); diff --git a/config/container.php b/config/container.php new file mode 100644 index 0000000..199e8b9 --- /dev/null +++ b/config/container.php @@ -0,0 +1,23 @@ + true, + ConfigAggregator::ENABLE_CACHE => false, +]; diff --git a/config/migration-template.txt b/config/migration-template.txt new file mode 100644 index 0000000..4577d1c --- /dev/null +++ b/config/migration-template.txt @@ -0,0 +1,16 @@ +; + +use Doctrine\DBAL\Schema\Schema; +use Doctrine\Migrations\AbstractMigration; + +class extends AbstractMigration +{ + public function up(Schema $schema): void + { + + } +} diff --git a/config/pipeline.php b/config/pipeline.php new file mode 100644 index 0000000..3f532ea --- /dev/null +++ b/config/pipeline.php @@ -0,0 +1,86 @@ +pipe(ErrorHandler::class); + $app->pipe(ServerUrlMiddleware::class); + + // Pipe more middleware here that you want to execute on every request: + // - bootstrapping + // - pre-conditions + // - modifications to outgoing responses + // + // Piped Middleware may be either callables or service names. Middleware may + // also be passed as an array; each item in the array must resolve to + // middleware eventually (i.e., callable or service name). + // + // Middleware can be attached to specific paths, allowing you to mix and match + // applications under a common domain. The handlers in each middleware + // attached this way will see a URI with the matched path segment removed. + // + // i.e., path of "/api/member/profile" only passes "/member/profile" to $apiMiddleware + // - $app->pipe('/api', $apiMiddleware); + // - $app->pipe('/docs', $apiDocMiddleware); + // - $app->pipe('/files', $filesMiddleware); + + // Register the routing middleware in the middleware pipeline. + // This middleware registers the Mezzio\Router\RouteResult request attribute. + $app->pipe(RouteMiddleware::class); + + // The following handle routing failures for common conditions: + // - HEAD request but no routes answer that method + // - OPTIONS request but no routes answer that method + // - method not allowed + // Order here matters; the MethodNotAllowedMiddleware should be placed + // after the Implicit*Middleware. + $app->pipe(ImplicitHeadMiddleware::class); + $app->pipe(ImplicitOptionsMiddleware::class); + $app->pipe(MethodNotAllowedMiddleware::class); + + // Seed the UrlHelper with the routing results: + $app->pipe(UrlHelperMiddleware::class); + + $app->pipe(SessionMiddleware::class); + $app->pipe(AuthenticationMiddleware::class); + $app->pipe(FlashMessageMiddleware::class); + $app->pipe(DetectBrowserLocaleMiddleware::class); + $app->pipe(NavigationMiddleware::class); + + // Add more middleware here that needs to introspect the routing results; this + // might include: + // + // - route-based authentication + // - route-based validation + // - etc. + + // Register the dispatch middleware in the middleware pipeline + $app->pipe(DispatchMiddleware::class); + + // At this point, if no Response is returned by any middleware, the + // NotFoundHandler kicks in; alternately, you can provide other fallback + // middleware to execute. + $app->pipe(NotFoundHandler::class); +}; diff --git a/config/routes.php b/config/routes.php new file mode 100644 index 0000000..ed3e5d6 --- /dev/null +++ b/config/routes.php @@ -0,0 +1,65 @@ +get('/', App\Handler\HomePageHandler::class, 'home'); + * $app->post('/album', App\Handler\AlbumCreateHandler::class, 'album.create'); + * $app->put('/album/:id', App\Handler\AlbumUpdateHandler::class, 'album.put'); + * $app->patch('/album/:id', App\Handler\AlbumUpdateHandler::class, 'album.patch'); + * $app->delete('/album/:id', App\Handler\AlbumDeleteHandler::class, 'album.delete'); + * + * Or with multiple request methods: + * + * $app->route('/contact', App\Handler\ContactHandler::class, ['GET', 'POST', ...], 'contact'); + * + * Or handling all request methods: + * + * $app->route('/contact', App\Handler\ContactHandler::class)->setName('contact'); + * + * or: + * + * $app->route( + * '/contact', + * App\Handler\ContactHandler::class, + * Mezzio\Router\Route::HTTP_METHOD_ANY, + * 'contact' + * ); + */ + +return static function (Application $app, MiddlewareFactory $factory, ContainerInterface $container): void { + $app->get('/', \Application\Handler\HomePageHandler::class, 'home'); + $app->get('/faq', \Application\Handler\FaqPageHandler::class, 'faq'); + $app->get('/activity', \Application\Handler\ActivityHandler::class, 'activity.index'); + $app->get('/activity/user/:user', \Application\Handler\ActivityHandler::class, 'activity.user'); + $app->get('/activity/movie/:movie', \Application\Handler\ActivityHandler::class, 'activity.movie'); + $app->get('/movie', \Application\Handler\Movie\IndexHandler::class, 'movie.index'); + $app->get('/movie/add', \Application\Handler\Movie\AddHandler::class, 'movie.add'); + $app->get('/movie/view/:id', \Application\Handler\Movie\ViewHandler::class, 'movie.view'); + $app->get('/user', \Application\Handler\User\IndexHandler::class, 'user.index'); + $app->get('/user/new', \Application\Handler\User\NewHandler::class, 'user.new'); + $app->post('/user/new', \Application\Handler\User\NewHandler::class, 'user.new.post'); + $app->get('/user/view/:id', \Application\Handler\User\ViewHandler::class, 'user.view'); + $app->get('/user/login', \Application\Handler\User\LoginHandler::class, 'user.login'); + $app->post('/user/login', \Application\Handler\User\LoginHandler::class, 'user.login.post'); + $app->get('/user/logout', \Application\Handler\User\LogoutHandler::class, 'user.logout'); + $app->get('/status/list/movies/:movies', \Application\Handler\Status\ListHandler::class, 'status.list'); + $app->get('/status/:movie', \Application\Handler\Status\IndexHandler::class, 'status'); + $app->get('/status/:movie/:rating', \Application\Handler\Status\IndexHandler::class, 'status.rating'); + $app->get('/status/graph', \Application\Handler\Status\GraphHandler::class, 'status.graph'); + $app->get('/status/graph/:user', \Application\Handler\Status\GraphHandler::class, 'status.graph.user'); + $app->get('/about', \Application\Handler\AboutPageHandler::class, 'about'); + $app->get('/css/gravatar.css', \Application\Handler\GravatarPageHandler::class, 'css.gravatar'); + $app->get('/js/mqueue-user.js', \Application\Handler\JsMqueueUserHandler::class, 'js.mqueue-user'); + $app->get('/js/remote.js', \Application\Handler\JsRemoteHandler::class, 'js.remote'); +}; diff --git a/library/searchengine/nova/__init__.py b/data/cache/.gitkeep similarity index 100% rename from library/searchengine/nova/__init__.py rename to data/cache/.gitkeep diff --git a/application/localization/fr.mo b/data/i18n/fr.mo similarity index 100% rename from application/localization/fr.mo rename to data/i18n/fr.mo diff --git a/application/localization/fr.po b/data/i18n/fr.po similarity index 100% rename from application/localization/fr.po rename to data/i18n/fr.po diff --git a/application/localization/ko.mo b/data/i18n/ko.mo similarity index 100% rename from application/localization/ko.mo rename to data/i18n/ko.mo diff --git a/application/localization/ko.po b/data/i18n/ko.po similarity index 100% rename from application/localization/ko.po rename to data/i18n/ko.po diff --git a/doc/Doxyfile b/doc/Doxyfile deleted file mode 100644 index feccce2..0000000 --- a/doc/Doxyfile +++ /dev/null @@ -1,1600 +0,0 @@ -# Doxyfile 1.6.3 - -# This file describes the settings to be used by the documentation system -# doxygen (www.doxygen.org) for a project -# -# All text after a hash (#) is considered a comment and will be ignored -# The format is: -# TAG = value [value, ...] -# For lists items can also be appended using: -# TAG += value [value, ...] -# Values that contain spaces should be placed between quotes (" ") - -#--------------------------------------------------------------------------- -# Project related configuration options -#--------------------------------------------------------------------------- - -# This tag specifies the encoding used for all characters in the config file -# that follow. The default is UTF-8 which is also the encoding used for all -# text before the first occurrence of this tag. Doxygen uses libiconv (or the -# iconv built into libc) for the transcoding. See -# http://www.gnu.org/software/libiconv for the list of possible encodings. - -DOXYFILE_ENCODING = UTF-8 - -# The PROJECT_NAME tag is a single word (or a sequence of words surrounded -# by quotes) that should identify the project. - -PROJECT_NAME = mQueue - -# The PROJECT_NUMBER tag can be used to enter a project or revision number. -# This could be handy for archiving the generated documentation or -# if some version control system is used. - -PROJECT_NUMBER = 1.0beta - -# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) -# base path where the generated documentation will be put. -# If a relative path is entered, it will be relative to the location -# where doxygen was started. If left blank the current directory will be used. - -OUTPUT_DIRECTORY = - -# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create -# 4096 sub-directories (in 2 levels) under the output directory of each output -# format and will distribute the generated files over these directories. -# Enabling this option can be useful when feeding doxygen a huge amount of -# source files, where putting all generated files in the same directory would -# otherwise cause performance problems for the file system. - -CREATE_SUBDIRS = NO - -# The OUTPUT_LANGUAGE tag is used to specify the language in which all -# documentation generated by doxygen is written. Doxygen will use this -# information to generate all constant output in the proper language. -# The default language is English, other supported languages are: -# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, -# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, -# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English -# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, -# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, -# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. - -OUTPUT_LANGUAGE = English - -# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will -# include brief member descriptions after the members that are listed in -# the file and class documentation (similar to JavaDoc). -# Set to NO to disable this. - -BRIEF_MEMBER_DESC = YES - -# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend -# the brief description of a member or function before the detailed description. -# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the -# brief descriptions will be completely suppressed. - -REPEAT_BRIEF = YES - -# This tag implements a quasi-intelligent brief description abbreviator -# that is used to form the text in various listings. Each string -# in this list, if found as the leading text of the brief description, will be -# stripped from the text and the result after processing the whole list, is -# used as the annotated text. Otherwise, the brief description is used as-is. -# If left blank, the following values are used ("$name" is automatically -# replaced with the name of the entity): "The $name class" "The $name widget" -# "The $name file" "is" "provides" "specifies" "contains" -# "represents" "a" "an" "the" - -ABBREVIATE_BRIEF = "The $name class" \ - "The $name widget" \ - "The $name file" \ - is \ - provides \ - specifies \ - contains \ - represents \ - a \ - an \ - the - -# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then -# Doxygen will generate a detailed section even if there is only a brief -# description. - -ALWAYS_DETAILED_SEC = NO - -# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all -# inherited members of a class in the documentation of that class as if those -# members were ordinary class members. Constructors, destructors and assignment -# operators of the base classes will not be shown. - -INLINE_INHERITED_MEMB = NO - -# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full -# path before files name in the file list and in the header files. If set -# to NO the shortest path that makes the file name unique will be used. - -FULL_PATH_NAMES = YES - -# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag -# can be used to strip a user-defined part of the path. Stripping is -# only done if one of the specified strings matches the left-hand part of -# the path. The tag can be used to show relative paths in the file list. -# If left blank the directory from which doxygen is run is used as the -# path to strip. - -STRIP_FROM_PATH = - -# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of -# the path mentioned in the documentation of a class, which tells -# the reader which header file to include in order to use a class. -# If left blank only the name of the header file containing the class -# definition is used. Otherwise one should specify the include paths that -# are normally passed to the compiler using the -I flag. - -STRIP_FROM_INC_PATH = - -# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter -# (but less readable) file names. This can be useful is your file systems -# doesn't support long names like on DOS, Mac, or CD-ROM. - -SHORT_NAMES = NO - -# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen -# will interpret the first line (until the first dot) of a JavaDoc-style -# comment as the brief description. If set to NO, the JavaDoc -# comments will behave just like regular Qt-style comments -# (thus requiring an explicit @brief command for a brief description.) - -JAVADOC_AUTOBRIEF = NO - -# If the QT_AUTOBRIEF tag is set to YES then Doxygen will -# interpret the first line (until the first dot) of a Qt-style -# comment as the brief description. If set to NO, the comments -# will behave just like regular Qt-style comments (thus requiring -# an explicit \brief command for a brief description.) - -QT_AUTOBRIEF = NO - -# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen -# treat a multi-line C++ special comment block (i.e. a block of //! or /// -# comments) as a brief description. This used to be the default behaviour. -# The new default is to treat a multi-line C++ comment block as a detailed -# description. Set this tag to YES if you prefer the old behaviour instead. - -MULTILINE_CPP_IS_BRIEF = NO - -# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented -# member inherits the documentation from any documented member that it -# re-implements. - -INHERIT_DOCS = YES - -# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce -# a new page for each member. If set to NO, the documentation of a member will -# be part of the file/class/namespace that contains it. - -SEPARATE_MEMBER_PAGES = NO - -# The TAB_SIZE tag can be used to set the number of spaces in a tab. -# Doxygen uses this value to replace tabs by spaces in code fragments. - -TAB_SIZE = 8 - -# This tag can be used to specify a number of aliases that acts -# as commands in the documentation. An alias has the form "name=value". -# For example adding "sideeffect=\par Side Effects:\n" will allow you to -# put the command \sideeffect (or @sideeffect) in the documentation, which -# will result in a user-defined paragraph with heading "Side Effects:". -# You can put \n's in the value part of an alias to insert newlines. - -ALIASES = - -# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C -# sources only. Doxygen will then generate output that is more tailored for C. -# For instance, some of the names that are used will be different. The list -# of all members will be omitted, etc. - -OPTIMIZE_OUTPUT_FOR_C = YES - -# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java -# sources only. Doxygen will then generate output that is more tailored for -# Java. For instance, namespaces will be presented as packages, qualified -# scopes will look different, etc. - -OPTIMIZE_OUTPUT_JAVA = NO - -# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran -# sources only. Doxygen will then generate output that is more tailored for -# Fortran. - -OPTIMIZE_FOR_FORTRAN = NO - -# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL -# sources. Doxygen will then generate output that is tailored for -# VHDL. - -OPTIMIZE_OUTPUT_VHDL = NO - -# Doxygen selects the parser to use depending on the extension of the files it parses. -# With this tag you can assign which parser to use for a given extension. -# Doxygen has a built-in mapping, but you can override or extend it using this tag. -# The format is ext=language, where ext is a file extension, and language is one of -# the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP, -# Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat -# .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran), -# use: inc=Fortran f=C. Note that for custom extensions you also need to set -# FILE_PATTERNS otherwise the files are not read by doxygen. - -EXTENSION_MAPPING = - -# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want -# to include (a tag file for) the STL sources as input, then you should -# set this tag to YES in order to let doxygen match functions declarations and -# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. -# func(std::string) {}). This also make the inheritance and collaboration -# diagrams that involve STL classes more complete and accurate. - -BUILTIN_STL_SUPPORT = NO - -# If you use Microsoft's C++/CLI language, you should set this option to YES to -# enable parsing support. - -CPP_CLI_SUPPORT = NO - -# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. -# Doxygen will parse them like normal C++ but will assume all classes use public -# instead of private inheritance when no explicit protection keyword is present. - -SIP_SUPPORT = NO - -# For Microsoft's IDL there are propget and propput attributes to indicate getter -# and setter methods for a property. Setting this option to YES (the default) -# will make doxygen to replace the get and set methods by a property in the -# documentation. This will only work if the methods are indeed getting or -# setting a simple type. If this is not the case, or you want to show the -# methods anyway, you should set this option to NO. - -IDL_PROPERTY_SUPPORT = YES - -# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC -# tag is set to YES, then doxygen will reuse the documentation of the first -# member in the group (if any) for the other members of the group. By default -# all members of a group must be documented explicitly. - -DISTRIBUTE_GROUP_DOC = NO - -# Set the SUBGROUPING tag to YES (the default) to allow class member groups of -# the same type (for instance a group of public functions) to be put as a -# subgroup of that type (e.g. under the Public Functions section). Set it to -# NO to prevent subgrouping. Alternatively, this can be done per class using -# the \nosubgrouping command. - -SUBGROUPING = YES - -# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum -# is documented as struct, union, or enum with the name of the typedef. So -# typedef struct TypeS {} TypeT, will appear in the documentation as a struct -# with name TypeT. When disabled the typedef will appear as a member of a file, -# namespace, or class. And the struct will be named TypeS. This can typically -# be useful for C code in case the coding convention dictates that all compound -# types are typedef'ed and only the typedef is referenced, never the tag name. - -TYPEDEF_HIDES_STRUCT = NO - -# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to -# determine which symbols to keep in memory and which to flush to disk. -# When the cache is full, less often used symbols will be written to disk. -# For small to medium size projects (<1000 input files) the default value is -# probably good enough. For larger projects a too small cache size can cause -# doxygen to be busy swapping symbols to and from disk most of the time -# causing a significant performance penality. -# If the system has enough physical memory increasing the cache will improve the -# performance by keeping more symbols in memory. Note that the value works on -# a logarithmic scale so increasing the size by one will rougly double the -# memory usage. The cache size is given by this formula: -# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, -# corresponding to a cache size of 2^16 = 65536 symbols - -SYMBOL_CACHE_SIZE = 0 - -#--------------------------------------------------------------------------- -# Build related configuration options -#--------------------------------------------------------------------------- - -# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in -# documentation are documented, even if no documentation was available. -# Private class members and static file members will be hidden unless -# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES - -EXTRACT_ALL = YES - -# If the EXTRACT_PRIVATE tag is set to YES all private members of a class -# will be included in the documentation. - -EXTRACT_PRIVATE = NO - -# If the EXTRACT_STATIC tag is set to YES all static members of a file -# will be included in the documentation. - -EXTRACT_STATIC = NO - -# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) -# defined locally in source files will be included in the documentation. -# If set to NO only classes defined in header files are included. - -EXTRACT_LOCAL_CLASSES = YES - -# This flag is only useful for Objective-C code. When set to YES local -# methods, which are defined in the implementation section but not in -# the interface are included in the documentation. -# If set to NO (the default) only methods in the interface are included. - -EXTRACT_LOCAL_METHODS = NO - -# If this flag is set to YES, the members of anonymous namespaces will be -# extracted and appear in the documentation as a namespace called -# 'anonymous_namespace{file}', where file will be replaced with the base -# name of the file that contains the anonymous namespace. By default -# anonymous namespace are hidden. - -EXTRACT_ANON_NSPACES = NO - -# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all -# undocumented members of documented classes, files or namespaces. -# If set to NO (the default) these members will be included in the -# various overviews, but no documentation section is generated. -# This option has no effect if EXTRACT_ALL is enabled. - -HIDE_UNDOC_MEMBERS = NO - -# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all -# undocumented classes that are normally visible in the class hierarchy. -# If set to NO (the default) these classes will be included in the various -# overviews. This option has no effect if EXTRACT_ALL is enabled. - -HIDE_UNDOC_CLASSES = NO - -# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all -# friend (class|struct|union) declarations. -# If set to NO (the default) these declarations will be included in the -# documentation. - -HIDE_FRIEND_COMPOUNDS = NO - -# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any -# documentation blocks found inside the body of a function. -# If set to NO (the default) these blocks will be appended to the -# function's detailed documentation block. - -HIDE_IN_BODY_DOCS = NO - -# The INTERNAL_DOCS tag determines if documentation -# that is typed after a \internal command is included. If the tag is set -# to NO (the default) then the documentation will be excluded. -# Set it to YES to include the internal documentation. - -INTERNAL_DOCS = NO - -# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate -# file names in lower-case letters. If set to YES upper-case letters are also -# allowed. This is useful if you have classes or files whose names only differ -# in case and if your file system supports case sensitive file names. Windows -# and Mac users are advised to set this option to NO. - -CASE_SENSE_NAMES = NO - -# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen -# will show members with their full class and namespace scopes in the -# documentation. If set to YES the scope will be hidden. - -HIDE_SCOPE_NAMES = YES - -# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen -# will put a list of the files that are included by a file in the documentation -# of that file. - -SHOW_INCLUDE_FILES = YES - -# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen -# will list include files with double quotes in the documentation -# rather than with sharp brackets. - -FORCE_LOCAL_INCLUDES = NO - -# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] -# is inserted in the documentation for inline members. - -INLINE_INFO = YES - -# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen -# will sort the (detailed) documentation of file and class members -# alphabetically by member name. If set to NO the members will appear in -# declaration order. - -SORT_MEMBER_DOCS = YES - -# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the -# brief documentation of file, namespace and class members alphabetically -# by member name. If set to NO (the default) the members will appear in -# declaration order. - -SORT_BRIEF_DOCS = NO - -# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen -# will sort the (brief and detailed) documentation of class members so that -# constructors and destructors are listed first. If set to NO (the default) -# the constructors will appear in the respective orders defined by -# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. -# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO -# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. - -SORT_MEMBERS_CTORS_1ST = NO - -# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the -# hierarchy of group names into alphabetical order. If set to NO (the default) -# the group names will appear in their defined order. - -SORT_GROUP_NAMES = NO - -# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be -# sorted by fully-qualified names, including namespaces. If set to -# NO (the default), the class list will be sorted only by class name, -# not including the namespace part. -# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. -# Note: This option applies only to the class list, not to the -# alphabetical list. - -SORT_BY_SCOPE_NAME = NO - -# The GENERATE_TODOLIST tag can be used to enable (YES) or -# disable (NO) the todo list. This list is created by putting \todo -# commands in the documentation. - -GENERATE_TODOLIST = YES - -# The GENERATE_TESTLIST tag can be used to enable (YES) or -# disable (NO) the test list. This list is created by putting \test -# commands in the documentation. - -GENERATE_TESTLIST = YES - -# The GENERATE_BUGLIST tag can be used to enable (YES) or -# disable (NO) the bug list. This list is created by putting \bug -# commands in the documentation. - -GENERATE_BUGLIST = YES - -# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or -# disable (NO) the deprecated list. This list is created by putting -# \deprecated commands in the documentation. - -GENERATE_DEPRECATEDLIST= YES - -# The ENABLED_SECTIONS tag can be used to enable conditional -# documentation sections, marked by \if sectionname ... \endif. - -ENABLED_SECTIONS = - -# The MAX_INITIALIZER_LINES tag determines the maximum number of lines -# the initial value of a variable or define consists of for it to appear in -# the documentation. If the initializer consists of more lines than specified -# here it will be hidden. Use a value of 0 to hide initializers completely. -# The appearance of the initializer of individual variables and defines in the -# documentation can be controlled using \showinitializer or \hideinitializer -# command in the documentation regardless of this setting. - -MAX_INITIALIZER_LINES = 30 - -# Set the SHOW_USED_FILES tag to NO to disable the list of files generated -# at the bottom of the documentation of classes and structs. If set to YES the -# list will mention the files that were used to generate the documentation. - -SHOW_USED_FILES = YES - -# If the sources in your project are distributed over multiple directories -# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy -# in the documentation. The default is NO. - -SHOW_DIRECTORIES = NO - -# Set the SHOW_FILES tag to NO to disable the generation of the Files page. -# This will remove the Files entry from the Quick Index and from the -# Folder Tree View (if specified). The default is YES. - -SHOW_FILES = YES - -# Set the SHOW_NAMESPACES tag to NO to disable the generation of the -# Namespaces page. This will remove the Namespaces entry from the Quick Index -# and from the Folder Tree View (if specified). The default is YES. - -SHOW_NAMESPACES = YES - -# The FILE_VERSION_FILTER tag can be used to specify a program or script that -# doxygen should invoke to get the current version for each file (typically from -# the version control system). Doxygen will invoke the program by executing (via -# popen()) the command , where is the value of -# the FILE_VERSION_FILTER tag, and is the name of an input file -# provided by doxygen. Whatever the program writes to standard output -# is used as the file version. See the manual for examples. - -FILE_VERSION_FILTER = - -# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by -# doxygen. The layout file controls the global structure of the generated output files -# in an output format independent way. The create the layout file that represents -# doxygen's defaults, run doxygen with the -l option. You can optionally specify a -# file name after the option, if omitted DoxygenLayout.xml will be used as the name -# of the layout file. - -LAYOUT_FILE = - -#--------------------------------------------------------------------------- -# configuration options related to warning and progress messages -#--------------------------------------------------------------------------- - -# The QUIET tag can be used to turn on/off the messages that are generated -# by doxygen. Possible values are YES and NO. If left blank NO is used. - -QUIET = NO - -# The WARNINGS tag can be used to turn on/off the warning messages that are -# generated by doxygen. Possible values are YES and NO. If left blank -# NO is used. - -WARNINGS = YES - -# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings -# for undocumented members. If EXTRACT_ALL is set to YES then this flag will -# automatically be disabled. - -WARN_IF_UNDOCUMENTED = YES - -# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some -# parameters in a documented function, or documenting parameters that -# don't exist or using markup commands wrongly. - -WARN_IF_DOC_ERROR = YES - -# This WARN_NO_PARAMDOC option can be abled to get warnings for -# functions that are documented, but have no documentation for their parameters -# or return value. If set to NO (the default) doxygen will only warn about -# wrong or incomplete parameter documentation, but not about the absence of -# documentation. - -WARN_NO_PARAMDOC = NO - -# The WARN_FORMAT tag determines the format of the warning messages that -# doxygen can produce. The string should contain the $file, $line, and $text -# tags, which will be replaced by the file and line number from which the -# warning originated and the warning text. Optionally the format may contain -# $version, which will be replaced by the version of the file (if it could -# be obtained via FILE_VERSION_FILTER) - -WARN_FORMAT = "$file:$line: $text" - -# The WARN_LOGFILE tag can be used to specify a file to which warning -# and error messages should be written. If left blank the output is written -# to stderr. - -WARN_LOGFILE = - -#--------------------------------------------------------------------------- -# configuration options related to the input files -#--------------------------------------------------------------------------- - -# The INPUT tag can be used to specify the files and/or directories that contain -# documented source files. You may enter file names like "myfile.cpp" or -# directories like "/usr/src/myproject". Separate the files or directories -# with spaces. - -INPUT = ../ - -# This tag can be used to specify the character encoding of the source files -# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is -# also the default input encoding. Doxygen uses libiconv (or the iconv built -# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for -# the list of possible encodings. - -INPUT_ENCODING = UTF-8 - -# If the value of the INPUT tag contains directories, you can use the -# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank the following patterns are tested: -# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx -# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 - -FILE_PATTERNS = *.c \ - *.cc \ - *.cxx \ - *.cpp \ - *.c++ \ - *.d \ - *.java \ - *.ii \ - *.ixx \ - *.ipp \ - *.i++ \ - *.inl \ - *.h \ - *.hh \ - *.hxx \ - *.hpp \ - *.h++ \ - *.idl \ - *.odl \ - *.cs \ - *.php \ - *.php3 \ - *.inc \ - *.m \ - *.mm \ - *.dox \ - *.py \ - *.f90 \ - *.f \ - *.vhd \ - *.vhdl - -# The RECURSIVE tag can be used to turn specify whether or not subdirectories -# should be searched for input files as well. Possible values are YES and NO. -# If left blank NO is used. - -RECURSIVE = YES - -# The EXCLUDE tag can be used to specify files and/or directories that should -# excluded from the INPUT source files. This way you can easily exclude a -# subdirectory from a directory tree whose root is specified with the INPUT tag. - -EXCLUDE = - -# The EXCLUDE_SYMLINKS tag can be used select whether or not files or -# directories that are symbolic links (a Unix filesystem feature) are excluded -# from the input. - -EXCLUDE_SYMLINKS = NO - -# If the value of the INPUT tag contains directories, you can use the -# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude -# certain files from those directories. Note that the wildcards are matched -# against the file with absolute path, so to exclude all test directories -# for example use the pattern */test/* - -EXCLUDE_PATTERNS = - -# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names -# (namespaces, classes, functions, etc.) that should be excluded from the -# output. The symbol name can be a fully qualified name, a word, or if the -# wildcard * is used, a substring. Examples: ANamespace, AClass, -# AClass::ANamespace, ANamespace::*Test - -EXCLUDE_SYMBOLS = - -# The EXAMPLE_PATH tag can be used to specify one or more files or -# directories that contain example code fragments that are included (see -# the \include command). - -EXAMPLE_PATH = - -# If the value of the EXAMPLE_PATH tag contains directories, you can use the -# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank all files are included. - -EXAMPLE_PATTERNS = * - -# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be -# searched for input files to be used with the \include or \dontinclude -# commands irrespective of the value of the RECURSIVE tag. -# Possible values are YES and NO. If left blank NO is used. - -EXAMPLE_RECURSIVE = NO - -# The IMAGE_PATH tag can be used to specify one or more files or -# directories that contain image that are included in the documentation (see -# the \image command). - -IMAGE_PATH = - -# The INPUT_FILTER tag can be used to specify a program that doxygen should -# invoke to filter for each input file. Doxygen will invoke the filter program -# by executing (via popen()) the command , where -# is the value of the INPUT_FILTER tag, and is the name of an -# input file. Doxygen will then use the output that the filter program writes -# to standard output. If FILTER_PATTERNS is specified, this tag will be -# ignored. - -INPUT_FILTER = - -# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern -# basis. Doxygen will compare the file name with each pattern and apply the -# filter if there is a match. The filters are a list of the form: -# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further -# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER -# is applied to all files. - -FILTER_PATTERNS = - -# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using -# INPUT_FILTER) will be used to filter the input files when producing source -# files to browse (i.e. when SOURCE_BROWSER is set to YES). - -FILTER_SOURCE_FILES = NO - -#--------------------------------------------------------------------------- -# configuration options related to source browsing -#--------------------------------------------------------------------------- - -# If the SOURCE_BROWSER tag is set to YES then a list of source files will -# be generated. Documented entities will be cross-referenced with these sources. -# Note: To get rid of all source code in the generated output, make sure also -# VERBATIM_HEADERS is set to NO. - -SOURCE_BROWSER = YES - -# Setting the INLINE_SOURCES tag to YES will include the body -# of functions and classes directly in the documentation. - -INLINE_SOURCES = NO - -# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct -# doxygen to hide any special comment blocks from generated source code -# fragments. Normal C and C++ comments will always remain visible. - -STRIP_CODE_COMMENTS = YES - -# If the REFERENCED_BY_RELATION tag is set to YES -# then for each documented function all documented -# functions referencing it will be listed. - -REFERENCED_BY_RELATION = YES - -# If the REFERENCES_RELATION tag is set to YES -# then for each documented function all documented entities -# called/used by that function will be listed. - -REFERENCES_RELATION = YES - -# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) -# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from -# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will -# link to the source code. Otherwise they will link to the documentation. - -REFERENCES_LINK_SOURCE = YES - -# If the USE_HTAGS tag is set to YES then the references to source code -# will point to the HTML generated by the htags(1) tool instead of doxygen -# built-in source browser. The htags tool is part of GNU's global source -# tagging system (see http://www.gnu.org/software/global/global.html). You -# will need version 4.8.6 or higher. - -USE_HTAGS = NO - -# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen -# will generate a verbatim copy of the header file for each class for -# which an include is specified. Set to NO to disable this. - -VERBATIM_HEADERS = YES - -#--------------------------------------------------------------------------- -# configuration options related to the alphabetical class index -#--------------------------------------------------------------------------- - -# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index -# of all compounds will be generated. Enable this if the project -# contains a lot of classes, structs, unions or interfaces. - -ALPHABETICAL_INDEX = NO - -# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then -# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns -# in which this list will be split (can be a number in the range [1..20]) - -COLS_IN_ALPHA_INDEX = 5 - -# In case all classes in a project start with a common prefix, all -# classes will be put under the same header in the alphabetical index. -# The IGNORE_PREFIX tag can be used to specify one or more prefixes that -# should be ignored while generating the index headers. - -IGNORE_PREFIX = - -#--------------------------------------------------------------------------- -# configuration options related to the HTML output -#--------------------------------------------------------------------------- - -# If the GENERATE_HTML tag is set to YES (the default) Doxygen will -# generate HTML output. - -GENERATE_HTML = YES - -# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `html' will be used as the default path. - -HTML_OUTPUT = html - -# The HTML_FILE_EXTENSION tag can be used to specify the file extension for -# each generated HTML page (for example: .htm,.php,.asp). If it is left blank -# doxygen will generate files with .html extension. - -HTML_FILE_EXTENSION = .html - -# The HTML_HEADER tag can be used to specify a personal HTML header for -# each generated HTML page. If it is left blank doxygen will generate a -# standard header. - -HTML_HEADER = - -# The HTML_FOOTER tag can be used to specify a personal HTML footer for -# each generated HTML page. If it is left blank doxygen will generate a -# standard footer. - -HTML_FOOTER = - -# The HTML_STYLESHEET tag can be used to specify a user-defined cascading -# style sheet that is used by each HTML page. It can be used to -# fine-tune the look of the HTML output. If the tag is left blank doxygen -# will generate a default style sheet. Note that doxygen will try to copy -# the style sheet file to the HTML output directory, so don't put your own -# stylesheet in the HTML output directory as well, or it will be erased! - -HTML_STYLESHEET = - -# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML -# page will contain the date and time when the page was generated. Setting -# this to NO can help when comparing the output of multiple runs. - -HTML_TIMESTAMP = YES - -# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, -# files or namespaces will be aligned in HTML using tables. If set to -# NO a bullet list will be used. - -HTML_ALIGN_MEMBERS = YES - -# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML -# documentation will contain sections that can be hidden and shown after the -# page has loaded. For this to work a browser that supports -# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox -# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). - -HTML_DYNAMIC_SECTIONS = NO - -# If the GENERATE_DOCSET tag is set to YES, additional index files -# will be generated that can be used as input for Apple's Xcode 3 -# integrated development environment, introduced with OSX 10.5 (Leopard). -# To create a documentation set, doxygen will generate a Makefile in the -# HTML output directory. Running make will produce the docset in that -# directory and running "make install" will install the docset in -# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find -# it at startup. -# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information. - -GENERATE_DOCSET = NO - -# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the -# feed. A documentation feed provides an umbrella under which multiple -# documentation sets from a single provider (such as a company or product suite) -# can be grouped. - -DOCSET_FEEDNAME = "Doxygen generated docs" - -# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that -# should uniquely identify the documentation set bundle. This should be a -# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen -# will append .docset to the name. - -DOCSET_BUNDLE_ID = org.doxygen.Project - -# If the GENERATE_HTMLHELP tag is set to YES, additional index files -# will be generated that can be used as input for tools like the -# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) -# of the generated HTML documentation. - -GENERATE_HTMLHELP = NO - -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can -# be used to specify the file name of the resulting .chm file. You -# can add a path in front of the file if the result should not be -# written to the html output directory. - -CHM_FILE = - -# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can -# be used to specify the location (absolute path including file name) of -# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run -# the HTML help compiler on the generated index.hhp. - -HHC_LOCATION = - -# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag -# controls if a separate .chi index file is generated (YES) or that -# it should be included in the master .chm file (NO). - -GENERATE_CHI = NO - -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING -# is used to encode HtmlHelp index (hhk), content (hhc) and project file -# content. - -CHM_INDEX_ENCODING = - -# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag -# controls whether a binary table of contents is generated (YES) or a -# normal table of contents (NO) in the .chm file. - -BINARY_TOC = NO - -# The TOC_EXPAND flag can be set to YES to add extra items for group members -# to the contents of the HTML help documentation and to the tree view. - -TOC_EXPAND = NO - -# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER -# are set, an additional index file will be generated that can be used as input for -# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated -# HTML documentation. - -GENERATE_QHP = NO - -# If the QHG_LOCATION tag is specified, the QCH_FILE tag can -# be used to specify the file name of the resulting .qch file. -# The path specified is relative to the HTML output folder. - -QCH_FILE = - -# The QHP_NAMESPACE tag specifies the namespace to use when generating -# Qt Help Project output. For more information please see -# http://doc.trolltech.com/qthelpproject.html#namespace - -QHP_NAMESPACE = org.doxygen.Project - -# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating -# Qt Help Project output. For more information please see -# http://doc.trolltech.com/qthelpproject.html#virtual-folders - -QHP_VIRTUAL_FOLDER = doc - -# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add. -# For more information please see -# http://doc.trolltech.com/qthelpproject.html#custom-filters - -QHP_CUST_FILTER_NAME = - -# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see -# Qt Help Project / Custom Filters. - -QHP_CUST_FILTER_ATTRS = - -# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's -# filter section matches. -# Qt Help Project / Filter Attributes. - -QHP_SECT_FILTER_ATTRS = - -# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can -# be used to specify the location of Qt's qhelpgenerator. -# If non-empty doxygen will try to run qhelpgenerator on the generated -# .qhp file. - -QHG_LOCATION = - -# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files -# will be generated, which together with the HTML files, form an Eclipse help -# plugin. To install this plugin and make it available under the help contents -# menu in Eclipse, the contents of the directory containing the HTML and XML -# files needs to be copied into the plugins directory of eclipse. The name of -# the directory within the plugins directory should be the same as -# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before -# the help appears. - -GENERATE_ECLIPSEHELP = NO - -# A unique identifier for the eclipse help plugin. When installing the plugin -# the directory name containing the HTML and XML files should also have -# this name. - -ECLIPSE_DOC_ID = org.doxygen.Project - -# The DISABLE_INDEX tag can be used to turn on/off the condensed index at -# top of each HTML page. The value NO (the default) enables the index and -# the value YES disables it. - -DISABLE_INDEX = NO - -# This tag can be used to set the number of enum values (range [1..20]) -# that doxygen will group on one line in the generated HTML documentation. - -ENUM_VALUES_PER_LINE = 4 - -# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index -# structure should be generated to display hierarchical information. -# If the tag value is set to YES, a side panel will be generated -# containing a tree-like index structure (just like the one that -# is generated for HTML Help). For this to work a browser that supports -# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). -# Windows users are probably better off using the HTML help feature. - -GENERATE_TREEVIEW = NO - -# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, -# and Class Hierarchy pages using a tree view instead of an ordered list. - -USE_INLINE_TREES = NO - -# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be -# used to set the initial width (in pixels) of the frame in which the tree -# is shown. - -TREEVIEW_WIDTH = 250 - -# Use this tag to change the font size of Latex formulas included -# as images in the HTML documentation. The default is 10. Note that -# when you change the font size after a successful doxygen run you need -# to manually remove any form_*.png images from the HTML output directory -# to force them to be regenerated. - -FORMULA_FONTSIZE = 10 - -# When the SEARCHENGINE tag is enabled doxygen will generate a search box -# for the HTML output. The underlying search engine uses javascript -# and DHTML and should work on any modern browser. Note that when using -# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets -# (GENERATE_DOCSET) there is already a search function so this one should -# typically be disabled. For large projects the javascript based search engine -# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. - -SEARCHENGINE = YES - -# When the SERVER_BASED_SEARCH tag is enabled the search engine will be -# implemented using a PHP enabled web server instead of at the web client -# using Javascript. Doxygen will generate the search PHP script and index -# file to put on the web server. The advantage of the server -# based approach is that it scales better to large projects and allows -# full text search. The disadvances is that it is more difficult to setup -# and does not have live searching capabilities. - -SERVER_BASED_SEARCH = NO - -#--------------------------------------------------------------------------- -# configuration options related to the LaTeX output -#--------------------------------------------------------------------------- - -# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will -# generate Latex output. - -GENERATE_LATEX = NO - -# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `latex' will be used as the default path. - -LATEX_OUTPUT = latex - -# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be -# invoked. If left blank `latex' will be used as the default command name. -# Note that when enabling USE_PDFLATEX this option is only used for -# generating bitmaps for formulas in the HTML output, but not in the -# Makefile that is written to the output directory. - -LATEX_CMD_NAME = latex - -# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to -# generate index for LaTeX. If left blank `makeindex' will be used as the -# default command name. - -MAKEINDEX_CMD_NAME = makeindex - -# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact -# LaTeX documents. This may be useful for small projects and may help to -# save some trees in general. - -COMPACT_LATEX = NO - -# The PAPER_TYPE tag can be used to set the paper type that is used -# by the printer. Possible values are: a4, a4wide, letter, legal and -# executive. If left blank a4wide will be used. - -PAPER_TYPE = a4wide - -# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX -# packages that should be included in the LaTeX output. - -EXTRA_PACKAGES = - -# The LATEX_HEADER tag can be used to specify a personal LaTeX header for -# the generated latex document. The header should contain everything until -# the first chapter. If it is left blank doxygen will generate a -# standard header. Notice: only use this tag if you know what you are doing! - -LATEX_HEADER = - -# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated -# is prepared for conversion to pdf (using ps2pdf). The pdf file will -# contain links (just like the HTML output) instead of page references -# This makes the output suitable for online browsing using a pdf viewer. - -PDF_HYPERLINKS = YES - -# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of -# plain latex in the generated Makefile. Set this option to YES to get a -# higher quality PDF documentation. - -USE_PDFLATEX = YES - -# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. -# command to the generated LaTeX files. This will instruct LaTeX to keep -# running if errors occur, instead of asking the user for help. -# This option is also used when generating formulas in HTML. - -LATEX_BATCHMODE = NO - -# If LATEX_HIDE_INDICES is set to YES then doxygen will not -# include the index chapters (such as File Index, Compound Index, etc.) -# in the output. - -LATEX_HIDE_INDICES = NO - -# If LATEX_SOURCE_CODE is set to YES then doxygen will include -# source code with syntax highlighting in the LaTeX output. -# Note that which sources are shown also depends on other settings -# such as SOURCE_BROWSER. - -LATEX_SOURCE_CODE = NO - -#--------------------------------------------------------------------------- -# configuration options related to the RTF output -#--------------------------------------------------------------------------- - -# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output -# The RTF output is optimized for Word 97 and may not look very pretty with -# other RTF readers or editors. - -GENERATE_RTF = NO - -# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `rtf' will be used as the default path. - -RTF_OUTPUT = rtf - -# If the COMPACT_RTF tag is set to YES Doxygen generates more compact -# RTF documents. This may be useful for small projects and may help to -# save some trees in general. - -COMPACT_RTF = NO - -# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated -# will contain hyperlink fields. The RTF file will -# contain links (just like the HTML output) instead of page references. -# This makes the output suitable for online browsing using WORD or other -# programs which support those fields. -# Note: wordpad (write) and others do not support links. - -RTF_HYPERLINKS = NO - -# Load stylesheet definitions from file. Syntax is similar to doxygen's -# config file, i.e. a series of assignments. You only have to provide -# replacements, missing definitions are set to their default value. - -RTF_STYLESHEET_FILE = - -# Set optional variables used in the generation of an rtf document. -# Syntax is similar to doxygen's config file. - -RTF_EXTENSIONS_FILE = - -#--------------------------------------------------------------------------- -# configuration options related to the man page output -#--------------------------------------------------------------------------- - -# If the GENERATE_MAN tag is set to YES (the default) Doxygen will -# generate man pages - -GENERATE_MAN = NO - -# The MAN_OUTPUT tag is used to specify where the man pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `man' will be used as the default path. - -MAN_OUTPUT = man - -# The MAN_EXTENSION tag determines the extension that is added to -# the generated man pages (default is the subroutine's section .3) - -MAN_EXTENSION = .3 - -# If the MAN_LINKS tag is set to YES and Doxygen generates man output, -# then it will generate one additional man file for each entity -# documented in the real man page(s). These additional files -# only source the real man page, but without them the man command -# would be unable to find the correct page. The default is NO. - -MAN_LINKS = NO - -#--------------------------------------------------------------------------- -# configuration options related to the XML output -#--------------------------------------------------------------------------- - -# If the GENERATE_XML tag is set to YES Doxygen will -# generate an XML file that captures the structure of -# the code including all documentation. - -GENERATE_XML = NO - -# The XML_OUTPUT tag is used to specify where the XML pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `xml' will be used as the default path. - -XML_OUTPUT = xml - -# The XML_SCHEMA tag can be used to specify an XML schema, -# which can be used by a validating XML parser to check the -# syntax of the XML files. - -XML_SCHEMA = - -# The XML_DTD tag can be used to specify an XML DTD, -# which can be used by a validating XML parser to check the -# syntax of the XML files. - -XML_DTD = - -# If the XML_PROGRAMLISTING tag is set to YES Doxygen will -# dump the program listings (including syntax highlighting -# and cross-referencing information) to the XML output. Note that -# enabling this will significantly increase the size of the XML output. - -XML_PROGRAMLISTING = YES - -#--------------------------------------------------------------------------- -# configuration options for the AutoGen Definitions output -#--------------------------------------------------------------------------- - -# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will -# generate an AutoGen Definitions (see autogen.sf.net) file -# that captures the structure of the code including all -# documentation. Note that this feature is still experimental -# and incomplete at the moment. - -GENERATE_AUTOGEN_DEF = NO - -#--------------------------------------------------------------------------- -# configuration options related to the Perl module output -#--------------------------------------------------------------------------- - -# If the GENERATE_PERLMOD tag is set to YES Doxygen will -# generate a Perl module file that captures the structure of -# the code including all documentation. Note that this -# feature is still experimental and incomplete at the -# moment. - -GENERATE_PERLMOD = NO - -# If the PERLMOD_LATEX tag is set to YES Doxygen will generate -# the necessary Makefile rules, Perl scripts and LaTeX code to be able -# to generate PDF and DVI output from the Perl module output. - -PERLMOD_LATEX = NO - -# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be -# nicely formatted so it can be parsed by a human reader. This is useful -# if you want to understand what is going on. On the other hand, if this -# tag is set to NO the size of the Perl module output will be much smaller -# and Perl will parse it just the same. - -PERLMOD_PRETTY = YES - -# The names of the make variables in the generated doxyrules.make file -# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. -# This is useful so different doxyrules.make files included by the same -# Makefile don't overwrite each other's variables. - -PERLMOD_MAKEVAR_PREFIX = - -#--------------------------------------------------------------------------- -# Configuration options related to the preprocessor -#--------------------------------------------------------------------------- - -# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will -# evaluate all C-preprocessor directives found in the sources and include -# files. - -ENABLE_PREPROCESSING = YES - -# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro -# names in the source code. If set to NO (the default) only conditional -# compilation will be performed. Macro expansion can be done in a controlled -# way by setting EXPAND_ONLY_PREDEF to YES. - -MACRO_EXPANSION = NO - -# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES -# then the macro expansion is limited to the macros specified with the -# PREDEFINED and EXPAND_AS_DEFINED tags. - -EXPAND_ONLY_PREDEF = NO - -# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files -# in the INCLUDE_PATH (see below) will be search if a #include is found. - -SEARCH_INCLUDES = YES - -# The INCLUDE_PATH tag can be used to specify one or more directories that -# contain include files that are not input files but should be processed by -# the preprocessor. - -INCLUDE_PATH = - -# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard -# patterns (like *.h and *.hpp) to filter out the header-files in the -# directories. If left blank, the patterns specified with FILE_PATTERNS will -# be used. - -INCLUDE_FILE_PATTERNS = - -# The PREDEFINED tag can be used to specify one or more macro names that -# are defined before the preprocessor is started (similar to the -D option of -# gcc). The argument of the tag is a list of macros of the form: name -# or name=definition (no spaces). If the definition and the = are -# omitted =1 is assumed. To prevent a macro definition from being -# undefined via #undef or recursively expanded use the := operator -# instead of the = operator. - -PREDEFINED = - -# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then -# this tag can be used to specify a list of macro names that should be expanded. -# The macro definition that is found in the sources will be used. -# Use the PREDEFINED tag if you want to use a different macro definition. - -EXPAND_AS_DEFINED = - -# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then -# doxygen's preprocessor will remove all function-like macros that are alone -# on a line, have an all uppercase name, and do not end with a semicolon. Such -# function macros are typically used for boiler-plate code, and will confuse -# the parser if not removed. - -SKIP_FUNCTION_MACROS = YES - -#--------------------------------------------------------------------------- -# Configuration::additions related to external references -#--------------------------------------------------------------------------- - -# The TAGFILES option can be used to specify one or more tagfiles. -# Optionally an initial location of the external documentation -# can be added for each tagfile. The format of a tag file without -# this location is as follows: -# TAGFILES = file1 file2 ... -# Adding location for the tag files is done as follows: -# TAGFILES = file1=loc1 "file2 = loc2" ... -# where "loc1" and "loc2" can be relative or absolute paths or -# URLs. If a location is present for each tag, the installdox tool -# does not have to be run to correct the links. -# Note that each tag file must have a unique name -# (where the name does NOT include the path) -# If a tag file is not located in the directory in which doxygen -# is run, you must also specify the path to the tagfile here. - -TAGFILES = - -# When a file name is specified after GENERATE_TAGFILE, doxygen will create -# a tag file that is based on the input files it reads. - -GENERATE_TAGFILE = - -# If the ALLEXTERNALS tag is set to YES all external classes will be listed -# in the class index. If set to NO only the inherited external classes -# will be listed. - -ALLEXTERNALS = NO - -# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed -# in the modules index. If set to NO, only the current project's groups will -# be listed. - -EXTERNAL_GROUPS = YES - -# The PERL_PATH should be the absolute path and name of the perl script -# interpreter (i.e. the result of `which perl'). - -PERL_PATH = /usr/bin/perl - -#--------------------------------------------------------------------------- -# Configuration options related to the dot tool -#--------------------------------------------------------------------------- - -# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will -# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base -# or super classes. Setting the tag to NO turns the diagrams off. Note that -# this option is superseded by the HAVE_DOT option below. This is only a -# fallback. It is recommended to install and use dot, since it yields more -# powerful graphs. - -CLASS_DIAGRAMS = YES - -# You can define message sequence charts within doxygen comments using the \msc -# command. Doxygen will then run the mscgen tool (see -# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the -# documentation. The MSCGEN_PATH tag allows you to specify the directory where -# the mscgen tool resides. If left empty the tool is assumed to be found in the -# default search path. - -MSCGEN_PATH = - -# If set to YES, the inheritance and collaboration graphs will hide -# inheritance and usage relations if the target is undocumented -# or is not a class. - -HIDE_UNDOC_RELATIONS = YES - -# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is -# available from the path. This tool is part of Graphviz, a graph visualization -# toolkit from AT&T and Lucent Bell Labs. The other options in this section -# have no effect if this option is set to NO (the default) - -HAVE_DOT = YES - -# By default doxygen will write a font called FreeSans.ttf to the output -# directory and reference it in all dot files that doxygen generates. This -# font does not include all possible unicode characters however, so when you need -# these (or just want a differently looking font) you can specify the font name -# using DOT_FONTNAME. You need need to make sure dot is able to find the font, -# which can be done by putting it in a standard location or by setting the -# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory -# containing the font. - -DOT_FONTNAME = FreeSans - -# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. -# The default size is 10pt. - -DOT_FONTSIZE = 10 - -# By default doxygen will tell dot to use the output directory to look for the -# FreeSans.ttf font (which doxygen will put there itself). If you specify a -# different font using DOT_FONTNAME you can set the path where dot -# can find it using this tag. - -DOT_FONTPATH = - -# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect inheritance relations. Setting this tag to YES will force the -# the CLASS_DIAGRAMS tag to NO. - -CLASS_GRAPH = YES - -# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect implementation dependencies (inheritance, containment, and -# class references variables) of the class with other documented classes. - -COLLABORATION_GRAPH = YES - -# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for groups, showing the direct groups dependencies - -GROUP_GRAPHS = YES - -# If the UML_LOOK tag is set to YES doxygen will generate inheritance and -# collaboration diagrams in a style similar to the OMG's Unified Modeling -# Language. - -UML_LOOK = NO - -# If set to YES, the inheritance and collaboration graphs will show the -# relations between templates and their instances. - -TEMPLATE_RELATIONS = NO - -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT -# tags are set to YES then doxygen will generate a graph for each documented -# file showing the direct and indirect include dependencies of the file with -# other documented files. - -INCLUDE_GRAPH = YES - -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and -# HAVE_DOT tags are set to YES then doxygen will generate a graph for each -# documented header file showing the documented files that directly or -# indirectly include this file. - -INCLUDED_BY_GRAPH = YES - -# If the CALL_GRAPH and HAVE_DOT options are set to YES then -# doxygen will generate a call dependency graph for every global function -# or class method. Note that enabling this option will significantly increase -# the time of a run. So in most cases it will be better to enable call graphs -# for selected functions only using the \callgraph command. - -CALL_GRAPH = NO - -# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then -# doxygen will generate a caller dependency graph for every global function -# or class method. Note that enabling this option will significantly increase -# the time of a run. So in most cases it will be better to enable caller -# graphs for selected functions only using the \callergraph command. - -CALLER_GRAPH = NO - -# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen -# will graphical hierarchy of all classes instead of a textual one. - -GRAPHICAL_HIERARCHY = YES - -# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES -# then doxygen will show the dependencies a directory has on other directories -# in a graphical way. The dependency relations are determined by the #include -# relations between the files in the directories. - -DIRECTORY_GRAPH = YES - -# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images -# generated by dot. Possible values are png, jpg, or gif -# If left blank png will be used. - -DOT_IMAGE_FORMAT = png - -# The tag DOT_PATH can be used to specify the path where the dot tool can be -# found. If left blank, it is assumed the dot tool can be found in the path. - -DOT_PATH = - -# The DOTFILE_DIRS tag can be used to specify one or more directories that -# contain dot files that are included in the documentation (see the -# \dotfile command). - -DOTFILE_DIRS = - -# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of -# nodes that will be shown in the graph. If the number of nodes in a graph -# becomes larger than this value, doxygen will truncate the graph, which is -# visualized by representing a node as a red box. Note that doxygen if the -# number of direct children of the root node in a graph is already larger than -# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note -# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. - -DOT_GRAPH_MAX_NODES = 50 - -# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the -# graphs generated by dot. A depth value of 3 means that only nodes reachable -# from the root by following a path via at most 3 edges will be shown. Nodes -# that lay further from the root node will be omitted. Note that setting this -# option to 1 or 2 may greatly reduce the computation time needed for large -# code bases. Also note that the size of a graph can be further restricted by -# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. - -MAX_DOT_GRAPH_DEPTH = 0 - -# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent -# background. This is disabled by default, because dot on Windows does not -# seem to support this out of the box. Warning: Depending on the platform used, -# enabling this option may lead to badly anti-aliased labels on the edges of -# a graph (i.e. they become hard to read). - -DOT_TRANSPARENT = NO - -# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output -# files in one run (i.e. multiple -o and -T options on the command line). This -# makes dot run faster, but since only newer versions of dot (>1.8.10) -# support this, this feature is disabled by default. - -DOT_MULTI_TARGETS = NO - -# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will -# generate a legend page explaining the meaning of the various boxes and -# arrows in the dot generated graphs. - -GENERATE_LEGEND = YES - -# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will -# remove the intermediate dot files that are used to generate -# the various graphs. - -DOT_CLEANUP = YES diff --git a/library/searchengine/nova/engines/__init__.py b/library/searchengine/nova/engines/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/library/searchengine/nova/engines/btbit.py b/library/searchengine/nova/engines/btbit.py deleted file mode 100644 index 7d4890c..0000000 --- a/library/searchengine/nova/engines/btbit.py +++ /dev/null @@ -1,128 +0,0 @@ -#VERSION: 1.00 -# AUTHORS: Diego de las Heras (ngosang@hotmail.es) -# CONTRIBUTORS: - -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of the author nor the names of its contributors may be -# used to endorse or promote products derived from this software without -# specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -try: - # python3 - from html.parser import HTMLParser -except ImportError: - # python2 - from HTMLParser import HTMLParser - -#qBt -from novaprinter import prettyPrinter -from helpers import download_file, retrieve_url - - -class btbit(object): - """ Search engine class """ - url = 'https://en.btbit.org' - name = 'BTBit' - - def download_torrent(self, info): - """ Downloader """ - print(download_file(info)) - - class MyHtmlParser(HTMLParser): - """ Parser class """ - def __init__(self, results, url): - HTMLParser.__init__(self) - self.results = results - self.url = url - self.current_item = {} - self.torrent_grabbing = False - self.title_grabbing = False - self.title_array = [] - self.metadata_grabbing = False - self.metadata_array = [] - - def handle_starttag(self, tag, attrs): - if tag == "div": - for attr in attrs: - if attr[1] == "rs": - self.torrent_grabbing = True - if attr[1] == "sbar" and self.torrent_grabbing: - self.metadata_grabbing = True - elif tag == "a": - if self.torrent_grabbing: - params = dict(attrs) - if "href" in params: - link = params["href"] - if link.startswith(self.url + "/bt/"): - self.current_item["desc_link"] = link - self.title_grabbing = True - elif link.startswith("magnet:"): - self.current_item["link"] = link - - def handle_endtag(self, tag): - if tag == "div": - if self.metadata_grabbing: - size_idx = 0 - for meta in self.metadata_array: - size_idx += 1 - if "Size" in meta: - break - self.current_item["size"] = self.metadata_array[size_idx] - self.current_item["name"] = ''.join(self.title_array)[1:-1] - self.current_item["engine_url"] = self.url - self.current_item["seeds"] = -1 - self.current_item["leech"] = -1 - - prettyPrinter(self.current_item) - self.results.append('a') - - # clean up - self.current_item = {} - self.torrent_grabbing = False - self.title_grabbing = False - self.title_array = [] - self.metadata_grabbing = False - self.metadata_array = [] - elif tag == "a": - if self.title_grabbing: - self.title_grabbing = False - - def handle_data(self, data): - if self.title_grabbing: - self.title_array.append(data) - elif self.metadata_grabbing: - self.metadata_array.append(data) - - def search(self, what, cat='all'): - """ Performs search """ - results_list = [] - parser = self.MyHtmlParser(results_list, self.url) - i = 1 - while i < 5: - # "what" is already urlencoded - html = retrieve_url(self.url + '/list/%s/%d-0-0.html' % (what, i)) - parser.feed(html) - if len(results_list) < 1: - break - del results_list[:] - i += 1 - parser.close() diff --git a/library/searchengine/nova/engines/btdb.py b/library/searchengine/nova/engines/btdb.py deleted file mode 100644 index 638c936..0000000 --- a/library/searchengine/nova/engines/btdb.py +++ /dev/null @@ -1,144 +0,0 @@ -#VERSION: 1.20 -# AUTHORS: Charles Worthing -# CONTRIBUTORS: Diego de las Heras (ngosang@hotmail.es) - -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of the author nor the names of its contributors may be -# used to endorse or promote products derived from this software without -# specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -try: - # python3 - from html.parser import HTMLParser -except ImportError: - # python2 - from HTMLParser import HTMLParser - -#qBt -from novaprinter import prettyPrinter -from helpers import download_file, retrieve_url - - -class btdb(object): - """ Search engine class """ - url = 'https://btdb.eu' - name = 'BTDB' - - def download_torrent(self, info): - """ Downloader """ - print(download_file(info)) - - class MyHtmlParser(HTMLParser): - """ Parser class """ - def __init__(self, results, url): - HTMLParser.__init__(self) - self.results = results - self.url = url - self.current_item = {} # One torrent result - self.add_query = True - self.torrent_info_index = 0 # Count of the meta data encountered - self.torrent_info_array = [] - self.meta_data_grabbing = 0 - self.meta_data_array = [] - self.torrent_no_files = 0 - self.torrent_date_added = 0 - self.torrent_popularity = 0 - self.magnet_link = "" - self.desc_link = "" - self.torrent_name = "" - - def handle_starttag(self, tag, attrs): - if tag == "span": - span_dict = dict(attrs) - if "class" in span_dict: - the_class = span_dict["class"] - if the_class == "item-meta-info-value": - self.meta_data_grabbing += 1 - else: - self.meta_data_grabbing = 0 - if tag == "script": - return - if tag == "li": - for attr in attrs: - if attr[1] == "search-ret-item": - self.torrent_info_index = 1 - if tag == "a": - if self.torrent_info_index > 0: - params = dict(attrs) - if "href" in params: - link = params["href"] - if link.startswith("//") and "/torrent/" in link: - self.desc_link = "https:" + link - self.torrent_name = params["title"] - elif link.startswith("magnet:"): - self.magnet_link = link - - def handle_endtag(self, tag): - if tag == "script": - return - if tag == "div": - if self.meta_data_grabbing > 0: - - self.torrent_no_files = self.meta_data_array[2] # Not used - self.torrent_date_added = self.meta_data_array[4] # Not used - self.torrent_popularity = self.meta_data_array[6] # Not used - - self.current_item["size"] = self.meta_data_array[0] - self.current_item["name"] = self.torrent_name - self.current_item["engine_url"] = self.url - self.current_item["link"] = self.magnet_link - self.current_item["desc_link"] = self.desc_link - self.current_item["seeds"] = self.meta_data_array[6] - self.current_item["leech"] = self.meta_data_array[8] - - prettyPrinter(self.current_item) - self.results.append('a') - self.current_item = {} - - self.meta_data_grabbing = 0 - self.meta_data_array = [] - self.magnet_link = "" - self.desc_link = "" - self.torrent_name = "" - - def handle_data(self, data): - if self.torrent_info_index > 0: - self.torrent_info_array.append(data) - self.torrent_info_index += 1 - if self.meta_data_grabbing > 0: - self.meta_data_array.append(data) - self.meta_data_grabbing += 1 - - def search(self, what, cat='all'): - """ Performs search """ - results_list = [] - parser = self.MyHtmlParser(results_list, self.url) - i = 1 - while i < 31: - # "what" is already urlencoded - html = retrieve_url(self.url + '/?search=%s&page=%d&sort=popular' % (what, i)) - parser.feed(html) - if len(results_list) < 1: - break - del results_list[:] - i += 1 - parser.close() diff --git a/library/searchengine/nova/engines/eztv.py b/library/searchengine/nova/engines/eztv.py deleted file mode 100644 index d6fa49e..0000000 --- a/library/searchengine/nova/engines/eztv.py +++ /dev/null @@ -1,79 +0,0 @@ -#VERSION: 1.10 -# AUTHORS: nindogo -# CONTRIBUTORS: Diego de las Heras (ngosang@hotmail.es) - -try: - # python3 - from html.parser import HTMLParser -except ImportError: - # python2 - from HTMLParser import HTMLParser - -# qBt -from novaprinter import prettyPrinter -from helpers import retrieve_url - - -class eztv(object): - name = "EZTV" - url = 'https://eztv.io' - supported_categories = {'all': 'all', 'tv': 'tv'} - - class MyHtmlParser(HTMLParser): - A, TD, TR, TABLE = ('a', 'td', 'tr', 'table') - - """ Sub-class for parsing results """ - def __init__(self, url): - HTMLParser.__init__(self) - self.url = url - - self.in_table_row = False - self.current_item = {} - - def handle_starttag(self, tag, attrs): - params = dict(attrs) - - if (params.get('class') == 'forum_header_border' - and params.get('name') == 'hover'): - self.in_table_row = True - self.current_item = {} - self.current_item['seeds'] = -1 - self.current_item['leech'] = -1 - self.current_item['size'] = -1 - self.current_item['engine_url'] = self.url - - if (tag == self.A - and self.in_table_row and params.get('class') == 'magnet'): - self.current_item['link'] = params.get('href') - - if (tag == self.A - and self.in_table_row and params.get('class') == 'epinfo'): - self.current_item['desc_link'] = self.url + params.get('href') - self.current_item['name'] = params.get('title').split(' (')[0] - - def handle_data(self, data): - data = data.replace(',', '') - if (self.in_table_row - and (data.endswith(' KB') or data.endswith(' MB') or data.endswith(' GB'))): - self.current_item['size'] = data - - elif self.in_table_row and data.isnumeric(): - self.current_item['seeds'] = int(data) - - def handle_endtag(self, tag): - if self.in_table_row and tag == self.TR: - prettyPrinter(self.current_item) - self.in_table_row = False - - def search(self, what, cat='all'): - query = self.url + '/search/' + what.replace('%20', '-') - eztv_html = retrieve_url(query) - - eztv_parser = self.MyHtmlParser(self.url) - eztv_parser.feed(eztv_html) - eztv_parser.close() - - -if __name__ == '__main__': - eztv_se = eztv() - eztv_se.search('Acre', 'all') diff --git a/library/searchengine/nova/engines/jackett.json b/library/searchengine/nova/engines/jackett.json deleted file mode 100644 index 6e46394..0000000 --- a/library/searchengine/nova/engines/jackett.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "api_key": "YOUR_API_KEY_HERE", - "tracker_first": false, - "url": "http://127.0.0.1:9117" -} \ No newline at end of file diff --git a/library/searchengine/nova/engines/jackett.py b/library/searchengine/nova/engines/jackett.py deleted file mode 100644 index aab84cd..0000000 --- a/library/searchengine/nova/engines/jackett.py +++ /dev/null @@ -1,205 +0,0 @@ -#VERSION: 3.02 -# AUTHORS: Diego de las Heras (ngosang@hotmail.es) -# CONTRIBUTORS: ukharley -# hannsen (github.com/hannsen) - -import json -import os -import xml.etree.ElementTree -try: - # python3 - from urllib.parse import urlencode, unquote - from urllib import request as urllib_request - from http.cookiejar import CookieJar -except ImportError: - # python2 - from urllib import urlencode, unquote - import urllib2 as urllib_request - from cookielib import CookieJar - -# qBt -from novaprinter import prettyPrinter -from helpers import download_file - - -############################################################################### -# load configuration from file -CONFIG_FILE = 'jackett.json' -CONFIG_PATH = os.path.join(os.path.dirname(os.path.realpath(__file__)), CONFIG_FILE) -CONFIG_DATA = { - 'api_key': 'YOUR_API_KEY_HERE', # jackett api - 'tracker_first': False, # (False/True) add tracker name to beginning of search result - 'url': 'http://127.0.0.1:9117', # jackett url -} - - -def load_configuration(): - global CONFIG_PATH, CONFIG_DATA - try: - # try to load user data from file - with open(CONFIG_PATH) as f: - CONFIG_DATA = json.load(f) - except ValueError: - # if file exists but it's malformed we load add a flag - CONFIG_DATA['malformed'] = True - except Exception: - # if file doesn't exist, we create it - with open(CONFIG_PATH, 'w') as f: - f.write(json.dumps(CONFIG_DATA, indent=4, sort_keys=True)) - - # do some checks - if any(item not in CONFIG_DATA for item in ['api_key', 'tracker_first', 'url']): - CONFIG_DATA['malformed'] = True - - -load_configuration() -############################################################################### - - -class jackett(object): - name = 'Jackett' - url = CONFIG_DATA['url'] if CONFIG_DATA['url'][-1] != '/' else CONFIG_DATA['url'][:-1] - api_key = CONFIG_DATA['api_key'] - supported_categories = { - 'all': None, - 'anime': ['5070'], - 'books': ['8000'], - 'games': ['1000', '4000'], - 'movies': ['2000'], - 'music': ['3000'], - 'software': ['4000'], - 'tv': ['5000'], - } - - def download_torrent(self, download_url): - # fix for some indexers with magnet link inside .torrent file - if download_url.startswith('magnet:?'): - print(download_url + " " + download_url) - response = self.get_response(download_url) - if response is not None and response.startswith('magnet:?'): - print(response + " " + download_url) - else: - print(download_file(download_url)) - - def search(self, what, cat='all'): - what = unquote(what) - category = self.supported_categories[cat.lower()] - - # check for malformed configuration - if 'malformed' in CONFIG_DATA: - self.handle_error("malformed configuration file", what) - return - - # check api_key - if self.api_key == "YOUR_API_KEY_HERE": - self.handle_error("api key error", what) - return - - # prepare jackett url - params = [ - ('apikey', self.api_key), - ('q', what) - ] - if category is not None: - params.append(('cat', ','.join(category))) - params = urlencode(params) - jacket_url = self.url + "/api/v2.0/indexers/all/results/torznab/api?%s" % params - response = self.get_response(jacket_url) - if response is None: - self.handle_error("connection error", what) - return - - # process search results - response_xml = xml.etree.ElementTree.fromstring(response) - for result in response_xml.find('channel').findall('item'): - res = {} - - title = result.find('title') - if title is not None: - title = title.text - else: - continue - - tracker = result.find('jackettindexer') - tracker = '' if tracker is None else tracker.text - if CONFIG_DATA['tracker_first']: - res['name'] = '[%s] %s' % (tracker, title) - else: - res['name'] = '%s [%s]' % (title, tracker) - - res['link'] = result.find(self.generate_xpath('magneturl')) - if res['link'] is not None: - res['link'] = res['link'].attrib['value'] - else: - res['link'] = result.find('link') - if res['link'] is not None: - res['link'] = res['link'].text - else: - continue - - res['size'] = result.find('size') - res['size'] = -1 if res['size'] is None else (res['size'].text + ' B') - - res['seeds'] = result.find(self.generate_xpath('seeders')) - res['seeds'] = -1 if res['seeds'] is None else int(res['seeds'].attrib['value']) - - res['leech'] = result.find(self.generate_xpath('peers')) - res['leech'] = -1 if res['leech'] is None else int(res['leech'].attrib['value']) - - if res['seeds'] != -1 and res['leech'] != -1: - res['leech'] -= res['seeds'] - - res['desc_link'] = result.find('comments') - if res['desc_link'] is not None: - res['desc_link'] = res['desc_link'].text - else: - res['desc_link'] = result.find('guid') - res['desc_link'] = '' if res['desc_link'] is None else res['desc_link'].text - - # note: engine_url can't be changed, torrent download stops working - res['engine_url'] = self.url - - prettyPrinter(self.escape_pipe(res)) - - def generate_xpath(self, tag): - return './{http://torznab.com/schemas/2015/feed}attr[@name="%s"]' % tag - - # Safety measure until it's fixed in prettyPrinter - def escape_pipe(self, dictionary): - for key in dictionary.keys(): - if isinstance(dictionary[key], str): - dictionary[key] = dictionary[key].replace('|', '%7C') - return dictionary - - def get_response(self, query): - response = None - try: - # we can't use helpers.retrieve_url because of redirects - # we need the cookie processor to handle redirects - opener = urllib_request.build_opener(urllib_request.HTTPCookieProcessor(CookieJar())) - response = opener.open(query).read().decode('utf-8') - except urllib_request.HTTPError as e: - # if the page returns a magnet redirect, used in download_torrent - if e.code == 302: - response = e.url - except Exception: - pass - return response - - def handle_error(self, error_msg, what): - # we need to print the search text to be displayed in qBittorrent when - # 'Torrent names only' is enabled - prettyPrinter({ - 'seeds': -1, - 'size': -1, - 'leech': -1, - 'engine_url': self.url, - 'link': self.url, - 'desc_link': 'https://github.com/qbittorrent/search-plugins/wiki/How-to-configure-Jackett-plugin', # noqa - 'name': "Jackett: %s! Click 'Go to description' button to open help. Configuration file: '%s' Search: '%s'" % (error_msg, CONFIG_PATH, what) # noqa - }) - - -if __name__ == "__main__": - jackett_se = jackett() - jackett_se.search("ubuntu server", 'software') diff --git a/library/searchengine/nova/engines/leetx.py b/library/searchengine/nova/engines/leetx.py deleted file mode 100644 index daa546f..0000000 --- a/library/searchengine/nova/engines/leetx.py +++ /dev/null @@ -1,168 +0,0 @@ -#VERSION: 2.00 -#AUTHORS: Vikas Yadav (https://github.com/v1k45 | http://v1k45.com) -#CONTRIBUTORS: Diego de las Heras (ngosang@hotmail.es) - -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of the author nor the names of its contributors may be -# used to endorse or promote products derived from this software without -# specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -import re -try: - # python3 - from html.parser import HTMLParser -except ImportError: - # python2 - from HTMLParser import HTMLParser - -# qBt -from helpers import retrieve_url -from novaprinter import prettyPrinter - - -class leetx(object): - url = "https://1337x.to" - name = "1337x" - supported_categories = { - 'all': 'All', - 'movies': 'Movies', - 'tv': 'TV', - 'music': 'Music', - 'games': 'Games', - 'anime': 'Anime', - 'software': 'Apps' - } - - class MyHtmlParser(HTMLParser): - A, TABLE, TR, TD, SPAN = ('a', 'table', 'tr', 'td', 'span') - - """ Sub-class for parsing results """ - def __init__(self, results, url): - HTMLParser.__init__(self) - self.results = results - self.url = url - - self.current_result = {} - self.current_item = None - self.inside_table = False - self.inside_row = False - - def handle_starttag(self, tag, attrs): - # are we inside the results table body or not - # if we are not inside the table, no need to process any further - self.inside_table = self.inside_table or tag == self.TABLE - if not self.inside_table: - return - - # convert attrs tuple to dictionary - attrs = dict(attrs) - - # for torrent name and link - link = attrs.get('href', '') - if tag == self.A and link.startswith('/torrent'): - self.current_result['link'] = self.url + link - self.current_result['desc_link'] = self.url + link - self.current_result['engine_url'] = self.url - self.current_item = 'name' - - # to ignore uploader name attached to the torrent size in span tag - if tag == self.SPAN: - self.current_item = None - - # if this is a there can be seeds, leeches or size inside it. - if tag == self.TD: - self.inside_row = True - - # find apporipate data key using class name of td - for item in ['seeds', 'leech', 'size']: - if item in attrs.get('class', ''): - self.current_item = item - break - - def handle_data(self, data): - # if we are not inside the table, no need to process any further - if not self.inside_table: - return - - # do not process data if we are not inside the table body - if self.current_item: - prev_value = self.current_result.get(self.current_item, '') - self.current_result[self.current_item] = prev_value + data - - def handle_endtag(self, tag): - # are we inside the results table body or not - # if we are not inside the table, no need to process any further - if tag == self.TABLE: - self.inside_table = False - if not self.inside_table: - return - - # exiting the table data and maybe moving td or tr element - if self.inside_row and tag == self.TD: - self.inside_row = False - self.current_item = None - - # exiting the tr element, which means all necessary data for a torrent has been - # extracted, we should save it and clean the object's state. - if self.current_result and tag == self.TR: - if 'size' in self.current_result: - self.current_result['size'] = self.current_result['size'].replace(',', '') - prettyPrinter(self.current_result) - self.results.append('a') - self.current_result = {} - self.current_item = None - - def download_torrent(self, download_url): - # since 1337x does not provide torrent links in the search results, - # we will have to fetch the page and extract the magnet link - torrent_page = retrieve_url(download_url) - magnet_match = re.search(r"href\s*\=\s*\"(magnet[^\"]+)\"", torrent_page) - if magnet_match and magnet_match.groups(): - print(magnet_match.groups()[0] + " " + download_url) - else: - raise Exception('Error, please fill a bug report!') - - def search(self, what, cat='all'): - cat = cat.lower() - - # decide which type of search to perform based on category - search_page = "search" if cat == 'all' else 'category-search' - search_url = "{url}/{search_page}/{search_query}/".format( - url=self.url, search_page=search_page, search_query=what) - - # apply search category to url, if any. - if cat != 'all': - search_url += self.supported_categories[cat] + "/" - - # try to get 15 pages (20 * 15 = 300 results) and stop when we don't found results - results_list = [] - parser = self.MyHtmlParser(results_list, self.url) - page = 1 - while page < 16: - # download the page - html = retrieve_url(search_url + str(page) + '/') - parser.feed(html) - if len(results_list) < 1: - break - del results_list[:] - page += 1 - parser.close() diff --git a/library/searchengine/nova/engines/legittorrents.py b/library/searchengine/nova/engines/legittorrents.py deleted file mode 100644 index d9be445..0000000 --- a/library/searchengine/nova/engines/legittorrents.py +++ /dev/null @@ -1,104 +0,0 @@ -#VERSION: 2.03 -# AUTHORS: Christophe Dumez (chris@qbittorrent.org) -# Douman (custparasite@gmx.se) - -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of the author nor the names of its contributors may be -# used to endorse or promote products derived from this software without -# specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -from novaprinter import prettyPrinter -from helpers import retrieve_url, download_file -from HTMLParser import HTMLParser -from re import compile as re_compile - - -class legittorrents(object): - url = 'http://www.legittorrents.info' - name = 'Legit Torrents' - supported_categories = {'all': '0', 'movies': '1', 'tv': '13', - 'music': '2', 'games': '3', 'anime': '5', 'books': '6'} - - def download_torrent(self, info): - print(download_file(info)) - - class MyHtmlParseWithBlackJack(HTMLParser): - """ Parser class """ - def __init__(self, url): - HTMLParser.__init__(self) - self.url = url - self.current_item = None - self.save_item_key = None - - def handle_starttag(self, tag, attrs): - """ Parser's start tag handler """ - if self.current_item: - params = dict(attrs) - if tag == "a": - link = params["href"] - if link.startswith("index") and "title" in params: - # description link - self.current_item["name"] = params["title"][14:] - self.current_item["desc_link"] = "/".join((self.url, link)) - elif link.startswith("download"): - self.current_item["link"] = "/".join((self.url, link)) - elif tag == "td": - if ("width" in params and params["width"] == "30" - and "leech" not in self.current_item): - self.save_item_key = "leech" if "seeds" in self.current_item else "seeds" - - elif tag == "tr": - self.current_item = {} - self.current_item["size"] = "" - self.current_item["engine_url"] = self.url - - def handle_endtag(self, tag): - """ Parser's end tag handler """ - if self.current_item and tag == "tr": - if len(self.current_item) > 4: - prettyPrinter(self.current_item) - self.current_item = None - - def handle_data(self, data): - """ Parser's data handler """ - if self.save_item_key: - self.current_item[self.save_item_key] = data.strip() - self.save_item_key = None - - def search(self, what, cat='all'): - """ Performs search """ - query = "".join((self.url, "/index.php?page=torrents&search=", what, "&category=", - self.supported_categories.get(cat, '0'), "&active=1")) - - get_table = re_compile(r'(?s)(.*)') - data = get_table.search(retrieve_url(query)).group(0) - # extract first ten pages of next results - next_pages = re_compile('(?m)') - next_pages = ["".join((self.url, page)) for page in next_pages.findall(data)[:10]] - - parser = self.MyHtmlParseWithBlackJack(self.url) - parser.feed(data) - parser.close() - - for page in next_pages: - parser.feed(get_table.search(retrieve_url(page)).group(0)) - parser.close() diff --git a/library/searchengine/nova/engines/limetorrents.py b/library/searchengine/nova/engines/limetorrents.py deleted file mode 100644 index a0d3075..0000000 --- a/library/searchengine/nova/engines/limetorrents.py +++ /dev/null @@ -1,127 +0,0 @@ -#VERSION: 4.01 -# AUTHORS: Lima66 -# CONTRIBUTORS: Diego de las Heras (ngosang@hotmail.es) - -import re -try: - # python3 - from html.parser import HTMLParser -except ImportError: - # python2 - from HTMLParser import HTMLParser - -# qBt -from novaprinter import prettyPrinter -from helpers import retrieve_url - -# Fix invalid certificate in Windows -import ssl -ssl._create_default_https_context = ssl._create_unverified_context - - -class limetorrents(object): - url = "https://www.limetorrents.info" - name = "LimeTorrents" - supported_categories = {'all': 'all', - 'anime': 'anime', - 'software': 'applications', - 'games': 'games', - 'movies': 'movies', - 'music': 'music', - 'tv': 'tv'} - - class MyHtmlParser(HTMLParser): - """ Sub-class for parsing results """ - - def error(self, message): - pass - - A, TD, TR, HREF = ('a', 'td', 'tr', 'href') - - def __init__(self, url): - HTMLParser.__init__(self) - self.url = url - self.current_item = {} # dict for found item - self.item_name = None # key's name in current_item dict - self.page_empty = 22000 - self.inside_tr = False - self.findTable = False - self.parser_class = {"tdnormal": "size", # class - "tdseed": "seeds", - "tdleech": "leech"} - - def handle_starttag(self, tag, attrs): - - params = dict(attrs) - if params.get('class') == 'table2': - self.findTable = True - - if tag == self.TR and self.findTable and (params.get('bgcolor') == '#F4F4F4' or params.get('bgcolor') == '#FFFFFF'): # noqa - self.inside_tr = True - self.current_item = {} - if not self.inside_tr: - return - - if self.inside_tr and tag == self.TD: - if "class" in params: - self.item_name = self.parser_class.get(params["class"], None) - if self.item_name: - self.current_item[self.item_name] = -1 - - if self.inside_tr and tag == self.A and self.HREF in params: - link = params["href"] - if link.startswith("http://itorrents.org/torrent/"): - self.current_item["engine_url"] = self.url - self.item_name = "name" - elif link.endswith(".html"): - self.current_item["link"] = self.url + link - self.current_item["desc_link"] = self.url + link - - def handle_data(self, data): - if self.inside_tr and self.item_name: - if self.item_name == 'size' and (data.endswith('MB') or data.endswith('GB')): - self.current_item[self.item_name] = data.strip().replace(',', '') - elif not self.item_name == 'size': - self.current_item[self.item_name] = data.strip().replace(',', '') - - self.item_name = None - - def handle_endtag(self, tag): - if tag == 'table': - self.findTable = False - - if self.inside_tr and tag == self.TR: - self.inside_tr = False - self.item_name = None - array_length = len(self.current_item) - if array_length < 1: - return - prettyPrinter(self.current_item) - self.current_item = {} - - def download_torrent(self, info): - # since limetorrents provides torrent links in itorrent (cloudflare protected), - # we have to fetch the info page and extract the magnet link - info_page = retrieve_url(info) - magnet_match = re.search(r"href\s*\=\s*\"(magnet[^\"]+)\"", info_page) - if magnet_match and magnet_match.groups(): - print(magnet_match.groups()[0] + " " + info) - else: - raise Exception('Error, please fill a bug report!') - - def search(self, query, cat='all'): - """ Performs search """ - query = query.replace("%20", "-") - category = self.supported_categories[cat] - - parser = self.MyHtmlParser(self.url) - page = 1 - while True: - page_url = "{0}/search/{1}/{2}/seeds/{3}".format(self.url, category, query, page) - html = retrieve_url(page_url) - lunghezza_html = len(html) - if page > 6 or lunghezza_html <= parser.page_empty: - return - parser.feed(html) - page += 1 - parser.close() diff --git a/library/searchengine/nova/engines/piratebay.py b/library/searchengine/nova/engines/piratebay.py deleted file mode 100644 index 765cef9..0000000 --- a/library/searchengine/nova/engines/piratebay.py +++ /dev/null @@ -1,191 +0,0 @@ -#VERSION: 2.20 -# AUTHORS: Fabien Devaux (fab@gnux.info) -# CONTRIBUTORS: Christophe Dumez (chris@qbittorrent.org) -# Arthur (custparasite@gmx.se) -# Diego de las Heras (ngosang@hotmail.es) - -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of the author nor the names of its contributors may be -# used to endorse or promote products derived from this software without -# specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -try: - # python3 - from html.parser import HTMLParser -except ImportError: - # python2 - from HTMLParser import HTMLParser - -# qBt -from novaprinter import prettyPrinter -from helpers import download_file, retrieve_url - -# Fix invalid certificate in Windows -import ssl -ssl._create_default_https_context = ssl._create_unverified_context - - -class piratebay(object): - """ Search engine class """ - url = 'https://pirateproxy.live' - name = 'The Pirate Bay' - supported_categories = {'all': '0', 'music': '100', 'movies': '200', - 'games': '400', 'software': '300'} - - def download_torrent(self, info): - """ Downloader """ - print(download_file(info)) - - class MyHtmlParser(HTMLParser): - """ Parser class """ - def __init__(self, results, url): - HTMLParser.__init__(self) - self.results = results - self.url = url - self.current_item = None - self.save_item = None - self.result_table = False # table with results is found - self.result_tbody = False - self.add_query = True - self.result_query = False - - def handle_start_tag_default(self, attrs): - """ Default handler for start tag dispatcher """ - pass - - def handle_start_tag_a(self, attrs): - """ Handler for start tag a """ - params = dict(attrs) - link = params["href"].replace(self.url, '') - if link.startswith("/torrent"): - self.current_item["desc_link"] = "".join((self.url, link)) - self.save_item = "name" - elif link.startswith("magnet"): - self.current_item["link"] = link - # end of the 'name' item - self.current_item['name'] = self.current_item['name'].strip() - self.save_item = None - - def handle_start_tag_font(self, attrs): - """ Handler for start tag font """ - for attr in attrs: - if attr[1] == "detDesc": - self.save_item = "size" - break - - def handle_start_tag_td(self, attrs): - """ Handler for start tag td """ - for attr in attrs: - if attr[1] == "right": - if "seeds" in self.current_item.keys(): - self.save_item = "leech" - else: - self.save_item = "seeds" - break - - def handle_starttag(self, tag, attrs): - """ Parser's start tag handler """ - if self.current_item: - dispatcher = getattr(self, - "_".join(("handle_start_tag", tag)), - self.handle_start_tag_default) - dispatcher(attrs) - - elif self.result_tbody: - if tag == "tr": - self.current_item = {"engine_url": self.url} - - elif tag == "table": - self.result_table = "searchResult" == attrs[0][1] - - elif self.add_query: - if self.result_query and tag == "a": - if len(self.list_searches) < 10: - self.list_searches.append(attrs[0][1]) - else: - self.add_query = False - self.result_query = False - elif tag == "div": - self.result_query = "center" == attrs[0][1] - - def handle_endtag(self, tag): - """ Parser's end tag handler """ - if self.result_tbody: - if tag == "tr": - if 'size' in self.current_item: - # clean up size - temp_data = self.current_item['size'].split() - if "Size" in temp_data: - indx = temp_data.index("Size") - self.current_item['size'] = (temp_data[indx + 1] + " " - + temp_data[indx + 2]) - else: - self.current_item['size'] = -1 - # return result - prettyPrinter(self.current_item) - self.results.append('a') - self.current_item = None - elif tag == "font": - self.save_item = None - elif tag == "table": - self.result_table = self.result_tbody = False - - elif self.result_table: - if tag == "thead": - self.result_tbody = True - elif tag == "table": - self.result_table = self.result_tbody = False - - elif self.add_query and self.result_query: - if tag == "div": - self.add_query = self.result_query = False - - def handle_data(self, data): - """ Parser's data handler """ - if self.save_item: - if (self.save_item == "size" or self.save_item == "name"): - if self.save_item not in self.current_item: - self.current_item[self.save_item] = '' - self.current_item[self.save_item] += " " + data - - else: - self.current_item[self.save_item] = data - self.save_item = None - - def search(self, what, cat='all'): - """ Performs search """ - cat = cat.lower() - # try to get 10 pages (10 * 30 = 300 results) and stop when we don't found results - results_list = [] - parser = self.MyHtmlParser(results_list, self.url) - page = 1 - while page < 11: - # prepare query. 7 is filtering by seeders - page_url = "{0}/search/{1}/{2}/7/{3}".format(self.url, what, page, - self.supported_categories[cat]) - html = retrieve_url(page_url) - parser.feed(html) - if len(results_list) < 1: - break - del results_list[:] - page += 1 - parser.close() diff --git a/library/searchengine/nova/engines/rarbg.py b/library/searchengine/nova/engines/rarbg.py deleted file mode 100644 index 7de3799..0000000 --- a/library/searchengine/nova/engines/rarbg.py +++ /dev/null @@ -1,88 +0,0 @@ -#VERSION: 2.10 -# AUTHORS: b0nk -# CONTRIBUTORS: Diego de las Heras (ngosang@hotmail.es) - -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of the author nor the names of its contributors may be -# used to endorse or promote products derived from this software without -# specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -import json -import time -try: - # python3 - from urllib.parse import urlencode, unquote -except ImportError: - # python2 - from urllib import urlencode, unquote - -# qBt -from novaprinter import prettyPrinter -from helpers import retrieve_url - - -class rarbg(object): - url = 'https://rarbg.to' - name = 'RARBG' - supported_categories = {'all': '1;4;14;15;16;17;21;22;42;18;19;41;27;28;29;30;31;32;40;23;24;25;26;33;34;43;44;45;46;47;48', # noqa - 'movies': 'movies', - 'tv': 'tv', - 'music': '1;23;24;25;26', - 'games': '1;27;28;29;30;31;32;40', - 'software': '1;33;34;43'} - - def search(self, what, cat='all'): - base_url = "https://torrentapi.org/pubapi_v2.php?%s" - app_id = "qbittorrent" - - # get token - params = urlencode({'get_token': 'get_token', 'app_id': app_id}) - response = retrieve_url(base_url % params) - j = json.loads(response) - token = j['token'] - time.sleep(2.1) - - # get response json - what = unquote(what) - category = self.supported_categories[cat] - params = urlencode({'mode': 'search', - 'search_string': what, - 'ranked': 0, - 'category': category, - 'limit': 100, - 'sort': 'seeders', - 'format': 'json_extended', - 'token': token, - 'app_id': 'qbittorrent'}) - response = retrieve_url(base_url % params) - j = json.loads(response) - - # parse results - for result in j['torrent_results']: - res = {'link': result['download'], - 'name': result['title'], - 'size': str(result['size']) + " B", - 'seeds': result['seeders'], - 'leech': result['leechers'], - 'engine_url': self.url, - 'desc_link': result['info_page'] + "&app_id=" + app_id} - prettyPrinter(res) diff --git a/library/searchengine/nova/engines/torlock.py b/library/searchengine/nova/engines/torlock.py deleted file mode 100644 index d71e17c..0000000 --- a/library/searchengine/nova/engines/torlock.py +++ /dev/null @@ -1,101 +0,0 @@ -#VERSION: 2.1 -# AUTHORS: Douman (custparasite@gmx.se) -# CONTRIBUTORS: Diego de las Heras (ngosang@hotmail.es) - -from novaprinter import prettyPrinter -from helpers import retrieve_url, download_file -from re import compile as re_compile -from HTMLParser import HTMLParser - - -class torlock(object): - url = "https://www.torlock.com" - name = "TorLock" - supported_categories = {'all': 'all', - 'anime': 'anime', - 'software': 'software', - 'games': 'game', - 'movies': 'movie', - 'music': 'music', - 'tv': 'television', - 'books': 'ebooks'} - - def download_torrent(self, info): - print(download_file(info)) - - class MyHtmlParser(HTMLParser): - """ Sub-class for parsing results """ - def __init__(self, url): - HTMLParser.__init__(self) - self.url = url - self.article_found = False # true when
    with results is found - self.item_found = False - self.item_bad = False # set to True for malicious links - self.current_item = None # dict for found item - self.item_name = None # key's name in current_item dict - self.parser_class = {"ts": "size", - "tul": "seeds", - "tdl": "leech"} - - def handle_starttag(self, tag, attrs): - params = dict(attrs) - if self.item_found: - if tag == "td": - if "class" in params: - self.item_name = self.parser_class.get(params["class"], None) - if self.item_name: - self.current_item[self.item_name] = "" - - elif self.article_found and tag == "a": - if "href" in params: - link = params["href"] - if link.startswith("/torrent"): - self.current_item["desc_link"] = "".join((self.url, link)) - self.current_item["link"] = "".join((self.url, "/tor/", - link.split('/')[2], ".torrent")) - self.current_item["engine_url"] = self.url - self.item_found = True - self.item_name = "name" - self.current_item["name"] = "" - self.item_bad = "rel" in params and params["rel"] == "nofollow" - - elif tag == "article": - self.article_found = True - self.current_item = {} - - def handle_data(self, data): - if self.item_name: - self.current_item[self.item_name] += data - - def handle_endtag(self, tag): - if tag == "article": - self.article_found = False - elif self.item_name and (tag == "a" or tag == "td"): - self.item_name = None - elif self.item_found and tag == "tr": - self.item_found = False - if not self.item_bad: - prettyPrinter(self.current_item) - self.current_item = {} - - def search(self, query, cat='all'): - """ Performs search """ - query = query.replace("%20", "-") - - parser = self.MyHtmlParser(self.url) - page = "".join((self.url, "/", self.supported_categories[cat], - "/torrents/", query, ".html?sort=seeds&page=1")) - html = retrieve_url(page) - parser.feed(html) - - counter = 1 - additional_pages = re_compile(r"/{0}/torrents/{1}.html\?sort=seeds&page=[0-9]+" - .format(self.supported_categories[cat], query)) - list_searches = additional_pages.findall(html)[:-1] # last link is next(i.e. second) - for page in map(lambda link: "".join((self.url, link)), list_searches): - html = retrieve_url(page) - parser.feed(html) - counter += 1 - if counter > 3: - break - parser.close() diff --git a/library/searchengine/nova/engines/versions.txt b/library/searchengine/nova/engines/versions.txt deleted file mode 100644 index 731b063..0000000 --- a/library/searchengine/nova/engines/versions.txt +++ /dev/null @@ -1,11 +0,0 @@ -btbit: 1.00 -btdb: 1.20 -eztv: 1.10 -jackett: 3.02 -leetx: 2.00 -legittorrents: 2.03 -limetorrents: 4.01 -piratebay: 2.20 -rarbg: 2.10 -torlock: 2.1 -zooqle: 1.13 \ No newline at end of file diff --git a/library/searchengine/nova/engines/zooqle.py b/library/searchengine/nova/engines/zooqle.py deleted file mode 100644 index 39a32ad..0000000 --- a/library/searchengine/nova/engines/zooqle.py +++ /dev/null @@ -1,119 +0,0 @@ -#VERSION: 1.13 -# AUTHORS: Kanishk Singh (https://github.com/ArionMiles/) -# CONTRIBUTORS: affaff (https://github.com/affaff) - -# Copyright (c) 2017 Kanishk Singh - -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: - -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - - -from xml.dom import minidom -from novaprinter import prettyPrinter -from io import StringIO -import gzip - -user_agent = 'Mozilla/5.0 (X11; Linux i686; rv:38.0) Gecko/20100101 Firefox/38.0' -headers = {'User-Agent': user_agent} - -try: - from urllib2 import urlopen, Request, URLError -except ImportError: - from urllib.request import urlopen, Request, URLError - - -def retrieve_url_nodecode(url): - """ Return the content of the url page as a string """ - req = Request(url, headers=headers) - try: - response = urlopen(req) - except URLError as errno: - print(" ".join(("Connection error:", str(errno.reason)))) - print(" ".join(("URL:", url))) - return "" - dat = response.read() - # Check if it is gzipped - if dat[:2] == '\037\213': - # Data is gzip encoded, decode it - compressedstream = StringIO(dat) - gzipper = gzip.GzipFile(fileobj=compressedstream) - extracted_data = gzipper.read() - dat = extracted_data - return dat - return dat - - -class zooqle(object): - """ Search engine class """ - url = 'https://zooqle.com' - name = 'Zooqle' - supported_categories = {'all': 'all', - 'movies': 'Movies', - 'tv': 'TV', - 'music': 'Music', - 'games': 'Games', - 'anime': 'Anime', - 'software': 'Apps', - 'books': 'Books'} - - def search(self, what, cat="all"): - """ Performs search """ - page = 1 - while page < 11: - query = "".join((self.url, "/search?q=", what, - "+category%3A", self.supported_categories[cat], "&fmt=rss")) - if page > 1: - query = query + "&pg=" + str(page) - response = retrieve_url_nodecode(query) - xmldoc = minidom.parseString(response) - itemlist = xmldoc.getElementsByTagName('item') - if len(itemlist) == 0: - return - for item in itemlist: - zooqle_dict = zooqle_dict = {"engine_url": self.url} - zooqle_dict['name'] = (item.getElementsByTagName('title')[0] - .childNodes[0].data) - zooqle_dict["size"] = (item.getElementsByTagName('enclosure')[0] - .attributes['length'].childNodes[0].data) - if zooqle_dict["size"] == '0': - zooqle_dict["link"] = (item.getElementsByTagName('torrent:magnetURI')[0] - .childNodes[0].data) - else: - zooqle_dict["link"] = (item.getElementsByTagName('enclosure')[0] - .attributes['url'].value) - zooqle_dict["desc_link"] = (item.getElementsByTagName('link')[0] - .childNodes[0].data) - zooqle_dict["leech"] = (item.getElementsByTagName('torrent:peers')[0] - .childNodes[0].data) - if not zooqle_dict["leech"].isdigit(): - zooqle_dict["leech"] = '' - zooqle_dict["seeds"] = (item.getElementsByTagName('torrent:seeds')[0] - .childNodes[0].data) - if not zooqle_dict["seeds"].isdigit(): - zooqle_dict["seeds"] = '' - prettyPrinter(zooqle_dict) - totalResultVal = (xmldoc.getElementsByTagName('opensearch:totalResults')[0] - .childNodes[0].data) - startIndex = (xmldoc.getElementsByTagName('opensearch:startIndex')[0] - .childNodes[0].data) - itemsPerPage = (xmldoc.getElementsByTagName('opensearch:itemsPerPage')[0] - .childNodes[0].data) - if (int(startIndex) + int(itemsPerPage)) > int(totalResultVal): - return - page += 1 - return diff --git a/library/searchengine/nova/fix_encoding.py b/library/searchengine/nova/fix_encoding.py deleted file mode 100644 index 7cea2da..0000000 --- a/library/searchengine/nova/fix_encoding.py +++ /dev/null @@ -1,371 +0,0 @@ -# Copyright (c) 2011 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -"""Collection of functions and classes to fix various encoding problems on -multiple platforms with python. -""" - -import codecs -import locale -import os -import sys - - -# Prevents initializing multiple times. -_SYS_ARGV_PROCESSED = False - - -def complain(message): - """If any exception occurs in this file, we'll probably try to print it - on stderr, which makes for frustrating debugging if stderr is directed - to our wrapper. So be paranoid about catching errors and reporting them - to sys.__stderr__, so that the user has a higher chance to see them. - """ - print >> sys.__stderr__, ( - isinstance(message, str) and message or repr(message)) - - -def fix_default_encoding(): - """Forces utf8 solidly on all platforms. - - By default python execution environment is lazy and defaults to ascii - encoding. - - http://uucode.com/blog/2007/03/23/shut-up-you-dummy-7-bit-python/ - """ - if sys.getdefaultencoding() == 'utf-8': - return False - - # Regenerate setdefaultencoding. - reload(sys) - # Module 'sys' has no 'setdefaultencoding' member - # pylint: disable=E1101 - sys.setdefaultencoding('utf-8') - for attr in dir(locale): - if attr[0:3] != 'LC_': - continue - aref = getattr(locale, attr) - try: - locale.setlocale(aref, '') - except locale.Error: - continue - try: - lang = locale.getlocale(aref)[0] - except (TypeError, ValueError): - continue - if lang: - try: - locale.setlocale(aref, (lang, 'UTF-8')) - except locale.Error: - os.environ[attr] = lang + '.UTF-8' - try: - locale.setlocale(locale.LC_ALL, '') - except locale.Error: - pass - return True - - -############################### -# Windows specific - - -def fix_win_sys_argv(encoding): - """Converts sys.argv to 'encoding' encoded string. - - utf-8 is recommended. - - Works around . - """ - global _SYS_ARGV_PROCESSED - if _SYS_ARGV_PROCESSED: - return False - - # These types are available on linux but not Mac. - # pylint: disable=E0611,F0401 - from ctypes import byref, c_int, POINTER, windll, WINFUNCTYPE - from ctypes.wintypes import LPCWSTR, LPWSTR - - # - GetCommandLineW = WINFUNCTYPE(LPWSTR)(('GetCommandLineW', windll.kernel32)) - # - CommandLineToArgvW = WINFUNCTYPE(POINTER(LPWSTR), LPCWSTR, POINTER(c_int))( - ('CommandLineToArgvW', windll.shell32)) - - argc = c_int(0) - argv_unicode = CommandLineToArgvW(GetCommandLineW(), byref(argc)) - argv = [ - argv_unicode[i].encode(encoding, 'replace') - for i in xrange(0, argc.value)] - - if not hasattr(sys, 'frozen'): - # If this is an executable produced by py2exe or bbfreeze, then it - # will have been invoked directly. Otherwise, unicode_argv[0] is the - # Python interpreter, so skip that. - argv = argv[1:] - - # Also skip option arguments to the Python interpreter. - while len(argv) > 0: - arg = argv[0] - if not arg.startswith(u'-') or arg == u'-': - break - argv = argv[1:] - if arg == u'-m': - # sys.argv[0] should really be the absolute path of the - # module source, but never mind. - break - if arg == u'-c': - argv[0] = u'-c' - break - sys.argv = argv - _SYS_ARGV_PROCESSED = True - return True - - -def fix_win_codec(): - """Works around .""" - # - try: - codecs.lookup('cp65001') - return False - except LookupError: - codecs.register( - lambda name: name == 'cp65001' and codecs.lookup('utf-8') or None) - return True - - -class WinUnicodeOutputBase(object): - """Base class to adapt sys.stdout or sys.stderr to behave correctly on - Windows. - - Setting encoding to utf-8 is recommended. - """ - def __init__(self, fileno, name, encoding): - # Corresponding file handle. - self._fileno = fileno - self.encoding = encoding - self.name = name - - self.closed = False - self.softspace = False - self.mode = 'w' - - @staticmethod - def isatty(): - return False - - def close(self): - # Don't really close the handle, that would only cause problems. - self.closed = True - - def fileno(self): - return self._fileno - - def flush(self): - raise NotImplementedError() - - def write(self, text): - raise NotImplementedError() - - def writelines(self, lines): - try: - for line in lines: - self.write(line) - except Exception, e: - complain('%s.writelines: %r' % (self.name, e)) - raise - - -class WinUnicodeConsoleOutput(WinUnicodeOutputBase): - """Output adapter to a Windows Console. - - Understands how to use the win32 console API. - """ - def __init__(self, console_handle, fileno, stream_name, encoding): - super(WinUnicodeConsoleOutput, self).__init__( - fileno, '' % stream_name, encoding) - # Handle to use for WriteConsoleW - self._console_handle = console_handle - - # Loads the necessary function. - # These types are available on linux but not Mac. - # pylint: disable=E0611,F0401 - from ctypes import byref, GetLastError, POINTER, windll, WINFUNCTYPE - from ctypes.wintypes import BOOL, DWORD, HANDLE, LPWSTR - from ctypes.wintypes import LPVOID # pylint: disable=E0611 - - self._DWORD = DWORD - self._byref = byref - - # - self._WriteConsoleW = WINFUNCTYPE( - BOOL, HANDLE, LPWSTR, DWORD, POINTER(DWORD), LPVOID)( - ('WriteConsoleW', windll.kernel32)) - self._GetLastError = GetLastError - - def flush(self): - # No need to flush the console since it's immediate. - pass - - def write(self, text): - try: - if not isinstance(text, unicode): - # Convert to unicode. - text = str(text).decode(self.encoding, 'replace') - remaining = len(text) - while remaining > 0: - n = self._DWORD(0) - # There is a shorter-than-documented limitation on the length of the - # string passed to WriteConsoleW. See - # . - retval = self._WriteConsoleW( - self._console_handle, text, - min(remaining, 10000), - self._byref(n), None) - if retval == 0 or n.value == 0: - raise IOError( - 'WriteConsoleW returned %r, n.value = %r, last error = %r' % ( - retval, n.value, self._GetLastError())) - remaining -= n.value - if not remaining: - break - text = text[n.value:] - except Exception, e: - complain('%s.write: %r' % (self.name, e)) - raise - - -class WinUnicodeOutput(WinUnicodeOutputBase): - """Output adaptor to a file output on Windows. - - If the standard FileWrite function is used, it will be encoded in the current - code page. WriteConsoleW() permits writing any character. - """ - def __init__(self, stream, fileno, encoding): - super(WinUnicodeOutput, self).__init__( - fileno, '' % stream.name, encoding) - # Output stream - self._stream = stream - - # Flush right now. - self.flush() - - def flush(self): - try: - self._stream.flush() - except Exception, e: - complain('%s.flush: %r from %r' % (self.name, e, self._stream)) - raise - - def write(self, text): - try: - if isinstance(text, unicode): - # Replace characters that cannot be printed instead of failing. - text = text.encode(self.encoding, 'replace') - self._stream.write(text) - except Exception, e: - complain('%s.write: %r' % (self.name, e)) - raise - - -def win_handle_is_a_console(handle): - """Returns True if a Windows file handle is a handle to a console.""" - # These types are available on linux but not Mac. - # pylint: disable=E0611,F0401 - from ctypes import byref, POINTER, windll, WINFUNCTYPE - from ctypes.wintypes import BOOL, DWORD, HANDLE - - FILE_TYPE_CHAR = 0x0002 - FILE_TYPE_REMOTE = 0x8000 - INVALID_HANDLE_VALUE = DWORD(-1).value - - # - GetConsoleMode = WINFUNCTYPE(BOOL, HANDLE, POINTER(DWORD))( - ('GetConsoleMode', windll.kernel32)) - # - GetFileType = WINFUNCTYPE(DWORD, DWORD)(('GetFileType', windll.kernel32)) - - # GetStdHandle returns INVALID_HANDLE_VALUE, NULL, or a valid handle. - if handle == INVALID_HANDLE_VALUE or handle is None: - return False - return ( - (GetFileType(handle) & ~FILE_TYPE_REMOTE) == FILE_TYPE_CHAR and - GetConsoleMode(handle, byref(DWORD()))) - - -def win_get_unicode_stream(stream, excepted_fileno, output_handle, encoding): - """Returns a unicode-compatible stream. - - This function will return a direct-Console writing object only if: - - the file number is the expected console file number - - the handle the expected file handle - - the 'real' handle is in fact a handle to a console. - """ - old_fileno = getattr(stream, 'fileno', lambda: None)() - if old_fileno == excepted_fileno: - # These types are available on linux but not Mac. - # pylint: disable=E0611,F0401 - from ctypes import windll, WINFUNCTYPE - from ctypes.wintypes import DWORD, HANDLE - - # - GetStdHandle = WINFUNCTYPE(HANDLE, DWORD)(('GetStdHandle', windll.kernel32)) - - real_output_handle = GetStdHandle(DWORD(output_handle)) - if win_handle_is_a_console(real_output_handle): - # It's a console. - return WinUnicodeConsoleOutput( - real_output_handle, old_fileno, stream.name, encoding) - - # It's something else. Create an auto-encoding stream. - return WinUnicodeOutput(stream, old_fileno, encoding) - - -def fix_win_console(encoding): - """Makes Unicode console output work independently of the current code page. - - This also fixes . - Credit to Michael Kaplan - and - TZOmegaTZIOY - . - """ - if (isinstance(sys.stdout, WinUnicodeOutputBase) or - isinstance(sys.stderr, WinUnicodeOutputBase)): - return False - - try: - # SetConsoleCP and SetConsoleOutputCP could be used to change the code page - # but it's not really useful since the code here is using WriteConsoleW(). - # Also, changing the code page is 'permanent' to the console and needs to be - # reverted manually. - # In practice one needs to set the console font to a TTF font to be able to - # see all the characters but it failed for me in practice. In any case, it - # won't throw any exception when printing, which is the important part. - # -11 and -12 are defined in stdio.h - sys.stdout = win_get_unicode_stream(sys.stdout, 1, -11, encoding) - sys.stderr = win_get_unicode_stream(sys.stderr, 2, -12, encoding) - # TODO(maruel): Do sys.stdin with ReadConsoleW(). Albeit the limitation is - # "It doesn't appear to be possible to read Unicode characters in UTF-8 - # mode" and this appears to be a limitation of cmd.exe. - except Exception, e: - complain('exception %r while fixing up sys.stdout and sys.stderr' % e) - return True - - -def fix_encoding(): - """Fixes various encoding problems on all platforms. - - Should be called at the very beginning of the process. - """ - ret = True - if sys.platform == 'win32': - ret &= fix_win_codec() - - ret &= fix_default_encoding() - - if sys.platform == 'win32': - encoding = sys.getdefaultencoding() - ret &= fix_win_sys_argv(encoding) - ret &= fix_win_console(encoding) - return ret diff --git a/library/searchengine/nova/helpers.py b/library/searchengine/nova/helpers.py deleted file mode 100644 index 409dd95..0000000 --- a/library/searchengine/nova/helpers.py +++ /dev/null @@ -1,122 +0,0 @@ -#VERSION: 1.43 - -# Author: -# Christophe DUMEZ (chris@qbittorrent.org) -# Contributors: -# Diego de las Heras (ngosang@hotmail.es) - -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of the author nor the names of its contributors may be -# used to endorse or promote products derived from this software without -# specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -import gzip -import htmlentitydefs -import os -import re -import socket -import socks -import StringIO -import tempfile -import urllib2 - -# Some sites blocks default python User-agent -user_agent = 'Mozilla/5.0 (X11; Linux i686; rv:38.0) Gecko/20100101 Firefox/38.0' -headers = {'User-Agent': user_agent} -# SOCKS5 Proxy support -if ("sock_proxy" in os.environ) and (len(os.environ["sock_proxy"].strip()) > 0): - proxy_str = os.environ["sock_proxy"].strip() - m = re.match(r"^(?:(?P[^:]+):(?P[^@]+)@)?(?P[^:]+):(?P\w+)$", - proxy_str) - if m is not None: - socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS5, m.group('host'), - int(m.group('port')), True, m.group('username'), m.group('password')) - socket.socket = socks.socksocket - - -def htmlentitydecode(s): - # First convert alpha entities (such as é) - # (Inspired from http://mail.python.org/pipermail/python-list/2007-June/443813.html) - def entity2char(m): - entity = m.group(1) - if entity in htmlentitydefs.name2codepoint: - return unichr(htmlentitydefs.name2codepoint[entity]) - return u" " # Unknown entity: We replace with a space. - t = re.sub(u'&(%s);' % u'|'.join(htmlentitydefs.name2codepoint), entity2char, s) - - # Then convert numerical entities (such as é) - t = re.sub(r'&#(\d+);', lambda x: unichr(int(x.group(1))), t) - - # Then convert hexa entities (such as é) - return re.sub(r'&#x(\w+);', lambda x: unichr(int(x.group(1), 16)), t) - - -def retrieve_url(url): - """ Return the content of the url page as a string """ - req = urllib2.Request(url, headers=headers) - try: - response = urllib2.urlopen(req) - except urllib2.URLError as errno: - print(" ".join(("Connection error:", str(errno.reason)))) - return "" - dat = response.read() - # Check if it is gzipped - if dat[:2] == '\037\213': - # Data is gzip encoded, decode it - compressedstream = StringIO.StringIO(dat) - gzipper = gzip.GzipFile(fileobj=compressedstream) - extracted_data = gzipper.read() - dat = extracted_data - info = response.info() - charset = 'utf-8' - try: - ignore, charset = info['Content-Type'].split('charset=') - except Exception: - pass - dat = dat.decode(charset, 'replace') - dat = htmlentitydecode(dat) - return dat - - -def download_file(url, referer=None): - """ Download file at url and write it to a file, return the path to the file and the url """ - file, path = tempfile.mkstemp() - file = os.fdopen(file, "w") - # Download url - req = urllib2.Request(url, headers=headers) - if referer is not None: - req.add_header('referer', referer) - response = urllib2.urlopen(req) - dat = response.read() - # Check if it is gzipped - if dat[:2] == '\037\213': - # Data is gzip encoded, decode it - compressedstream = StringIO.StringIO(dat) - gzipper = gzip.GzipFile(fileobj=compressedstream) - extracted_data = gzipper.read() - dat = extracted_data - - # Write it to a file - file.write(dat) - file.close() - # return file path - return (path + " " + url) diff --git a/library/searchengine/nova/nova2.py b/library/searchengine/nova/nova2.py deleted file mode 100644 index 1045384..0000000 --- a/library/searchengine/nova/nova2.py +++ /dev/null @@ -1,191 +0,0 @@ -#VERSION: 1.43 - -# Author: -# Fabien Devaux -# Contributors: -# Christophe Dumez (qbittorrent integration) -# Thanks to gab #gcu @ irc.freenode.net (multipage support on PirateBay) -# Thanks to Elias (torrentreactor and isohunt search engines) -# -# Licence: BSD - -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of the author nor the names of its contributors may be -# used to endorse or promote products derived from this software without -# specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -import urllib -from os import path -from glob import glob -from sys import argv -from multiprocessing import Pool, cpu_count -from fix_encoding import fix_encoding - -THREADED = True -try: - MAX_THREADS = cpu_count() -except NotImplementedError: - MAX_THREADS = 1 - -CATEGORIES = {'all', 'movies', 'tv', 'music', 'games', 'anime', 'software', 'pictures', 'books'} - -################################################################################ -# Every engine should have a "search" method taking -# a space-free string as parameter (ex. "family+guy") -# it should call prettyPrinter() with a dict as parameter. -# The keys in the dict must be: link,name,size,seeds,leech,engine_url -# As a convention, try to list results by decreasing number of seeds or similar -################################################################################ - - -def initialize_engines(): - """ Import available engines - - Return list of available engines - """ - supported_engines = [] - - engines = glob(path.join(path.dirname(__file__), 'engines', '*.py')) - for engine in engines: - engi = path.basename(engine).split('.')[0].strip() - if len(engi) == 0 or engi.startswith('_'): - continue - try: - # import engines.[engine] - engine_module = __import__(".".join(("engines", engi))) - # get low-level module - engine_module = getattr(engine_module, engi) - # bind class name - globals()[engi] = getattr(engine_module, engi) - supported_engines.append(engi) - except Exception: - pass - - return supported_engines - - -def engines_to_xml(supported_engines): - """ Generates xml for supported engines """ - tab = " " * 4 - - for short_name in supported_engines: - search_engine = globals()[short_name]() - - supported_categories = "" - if hasattr(search_engine, "supported_categories"): - supported_categories = " ".join((key - for key in search_engine.supported_categories.keys() - if key != "all")) - - yield "".join((tab, "<", short_name, ">\n", - tab, tab, "", search_engine.name, "\n", - tab, tab, "", search_engine.url, "\n", - tab, tab, "", supported_categories, "\n", - tab, "\n")) - - -def displayCapabilities(supported_engines): - """ - Display capabilities in XML format - - - long name - http://example.com - movies music games - - - """ - xml = "".join(("\n", - "".join(engines_to_xml(supported_engines)), - "")) - print(xml) - - -def run_search(engine_list): - """ Run search in engine - - @param engine_list List with engine, query and category - - @retval False if any exceptions occurred - @retval True otherwise - """ - engine, what, cat = engine_list - try: - engine = engine() - # avoid exceptions due to invalid category - if hasattr(engine, 'supported_categories'): - if cat in engine.supported_categories: - engine.search(what, cat) - else: - engine.search(what) - return True - except Exception: - return False - - -def main(args): - fix_encoding() - supported_engines = initialize_engines() - - if not args: - raise SystemExit("./nova2.py [all|engine1[,engine2]*] \n" - "available engines: %s" % (','.join(supported_engines))) - - elif args[0] == "--capabilities": - displayCapabilities(supported_engines) - return - - elif len(args) < 3: - raise SystemExit("./nova2.py [all|engine1[,engine2]*] \n" - "available engines: %s" % (','.join(supported_engines))) - - # get only unique engines with set - engines_list = set(e.lower() for e in args[0].strip().split(',')) - - if 'all' in engines_list: - engines_list = supported_engines - else: - # discard un-supported engines - engines_list = [engine for engine in engines_list - if engine in supported_engines] - - if not engines_list: - # engine list is empty. Nothing to do here - return - - cat = args[1].lower() - - if cat not in CATEGORIES: - raise SystemExit(" - ".join(('Invalid category', cat))) - - what = urllib.quote(' '.join(args[2:])) - - if THREADED: - # child process spawning is controlled min(number of searches, number of cpu) - pool = Pool(min(len(engines_list), MAX_THREADS)) - pool.map(run_search, ([globals()[engine], what, cat] for engine in engines_list)) - else: - map(run_search, ([globals()[engine], what, cat] for engine in engines_list)) - - -if __name__ == "__main__": - main(argv[1:]) diff --git a/library/searchengine/nova/nova2dl.py b/library/searchengine/nova/nova2dl.py deleted file mode 100644 index c04a437..0000000 --- a/library/searchengine/nova/nova2dl.py +++ /dev/null @@ -1,63 +0,0 @@ -#VERSION: 1.22 - -# Author: -# Christophe DUMEZ (chris@qbittorrent.org) - -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of the author nor the names of its contributors may be -# used to endorse or promote products derived from this software without -# specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -import sys -import os -import glob -from helpers import download_file - -supported_engines = dict() - -engines = glob.glob(os.path.join(os.path.dirname(__file__), 'engines', '*.py')) -for engine in engines: - e = engine.split(os.sep)[-1][:-3] - if len(e.strip()) == 0: - continue - if e.startswith('_'): - continue - try: - exec("from engines.%s import %s" % (e, e)) - exec("engine_url = %s.url" % e) - supported_engines[engine_url] = e - except Exception: - pass - -if __name__ == '__main__': - if len(sys.argv) < 3: - raise SystemExit('./nova2dl.py engine_url download_parameter') - engine_url = sys.argv[1].strip() - download_param = sys.argv[2].strip() - if engine_url not in list(supported_engines.keys()): - raise SystemExit('./nova2dl.py: this engine_url was not recognized') - exec("engine = %s()" % supported_engines[engine_url]) - if hasattr(engine, 'download_torrent'): - engine.download_torrent(download_param) - else: - print(download_file(download_param)) - sys.exit(0) diff --git a/library/searchengine/nova/novaprinter.py b/library/searchengine/nova/novaprinter.py deleted file mode 100644 index bfd9615..0000000 --- a/library/searchengine/nova/novaprinter.py +++ /dev/null @@ -1,73 +0,0 @@ -#VERSION: 1.47 - -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of the author nor the names of its contributors may be -# used to endorse or promote products derived from this software without -# specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -import codecs -import sys -from io import open - -# Force UTF-8 printing -sys.stdout = codecs.getwriter('utf-8')(sys.stdout) - - -def prettyPrinter(dictionary): - dictionary['size'] = anySizeToBytes(dictionary['size']) - outtext = "|".join((dictionary["link"], dictionary["name"].replace("|", " "), - str(dictionary["size"]), str(dictionary["seeds"]), - str(dictionary["leech"]), dictionary["engine_url"])) - if 'desc_link' in dictionary: - outtext = "|".join((outtext, dictionary["desc_link"])) - - with open(1, 'w', encoding='utf-8', closefd=False) as utf8_stdout: - utf8_stdout.write(unicode("".join((outtext, "\n")))) - - -def anySizeToBytes(size_string): - """ - Convert a string like '1 KB' to '1024' (bytes) - """ - # separate integer from unit - try: - size, unit = size_string.split() - except Exception: - try: - size = size_string.strip() - unit = ''.join([c for c in size if c.isalpha()]) - if len(unit) > 0: - size = size[:-len(unit)] - except Exception: - return -1 - if len(size) == 0: - return -1 - size = float(size) - if len(unit) == 0: - return int(size) - short_unit = unit.upper()[0] - - # convert - units_dict = {'T': 40, 'G': 30, 'M': 20, 'K': 10} - if short_unit in units_dict: - size = size * 2**units_dict[short_unit] - return int(size) diff --git a/library/searchengine/nova/socks.py b/library/searchengine/nova/socks.py deleted file mode 100644 index 844e8fe..0000000 --- a/library/searchengine/nova/socks.py +++ /dev/null @@ -1,391 +0,0 @@ -"""SocksiPy - Python SOCKS module. -Version 1.01 - -Copyright 2006 Dan-Haim. All rights reserved. -Various fixes by Christophe DUMEZ - 2010 - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: -1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. -3. Neither the name of Dan Haim nor the names of his contributors may be used - to endorse or promote products derived from this software without specific - prior written permission. - -THIS SOFTWARE IS PROVIDED BY DAN HAIM "AS IS" AND ANY EXPRESS OR IMPLIED -WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO -EVENT SHALL DAN HAIM OR HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA -OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT -OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMANGE. - - -This module provides a standard socket-like interface for Python -for tunneling connections through SOCKS proxies. - -""" - -import socket -import struct - -PROXY_TYPE_SOCKS4 = 1 -PROXY_TYPE_SOCKS5 = 2 -PROXY_TYPE_HTTP = 3 - -_defaultproxy = None -_orgsocket = socket.socket - -class ProxyError(Exception): - def __init__(self, value): - self.value = value - def __str__(self): - return repr(self.value) - -class GeneralProxyError(ProxyError): - def __init__(self, value): - self.value = value - def __str__(self): - return repr(self.value) - -class Socks5AuthError(ProxyError): - def __init__(self, value): - self.value = value - def __str__(self): - return repr(self.value) - -class Socks5Error(ProxyError): - def __init__(self, value): - self.value = value - def __str__(self): - return repr(self.value) - -class Socks4Error(ProxyError): - def __init__(self, value): - self.value = value - def __str__(self): - return repr(self.value) - -class HTTPError(ProxyError): - def __init__(self, value): - self.value = value - def __str__(self): - return repr(self.value) - -_generalerrors = ("success", - "invalid data", - "not connected", - "not available", - "bad proxy type", - "bad input") - -_socks5errors = ("succeeded", - "general SOCKS server failure", - "connection not allowed by ruleset", - "Network unreachable", - "Host unreachable", - "Connection refused", - "TTL expired", - "Command not supported", - "Address type not supported", - "Unknown error") - -_socks5autherrors = ("succeeded", - "authentication is required", - "all offered authentication methods were rejected", - "unknown username or invalid password", - "unknown error") - -_socks4errors = ("request granted", - "request rejected or failed", - "request rejected because SOCKS server cannot connect to identd on the client", - "request rejected because the client program and identd report different user-ids", - "unknown error") - -def setdefaultproxy(proxytype=None,addr=None,port=None,rdns=True,username=None,password=None): - """setdefaultproxy(proxytype, addr[, port[, rdns[, username[, password]]]]) - Sets a default proxy which all further socksocket objects will use, - unless explicitly changed. - """ - global _defaultproxy - _defaultproxy = (proxytype,addr,port,rdns,username,password) - -class socksocket(socket.socket): - """socksocket([family[, type[, proto]]]) -> socket object - - Open a SOCKS enabled socket. The parameters are the same as - those of the standard socket init. In order for SOCKS to work, - you must specify family=AF_INET, type=SOCK_STREAM and proto=0. - """ - - def __init__(self, family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0, _sock=None): - _orgsocket.__init__(self,family,type,proto,_sock) - if _defaultproxy != None: - self.__proxy = _defaultproxy - else: - self.__proxy = (None, None, None, None, None, None) - self.__proxysockname = None - self.__proxypeername = None - - def __recvall(self, bytes): - """__recvall(bytes) -> data - Receive EXACTLY the number of bytes requested from the socket. - Blocks until the required number of bytes have been received. - """ - data = "" - while len(data) < bytes: - d = self.recv(bytes-len(data)) - if not d: - raise GeneralProxyError("connection closed unexpectedly") - data = data + d - return data - - def setproxy(self,proxytype=None,addr=None,port=None,rdns=True,username=None,password=None): - """setproxy(proxytype, addr[, port[, rdns[, username[, password]]]]) - Sets the proxy to be used. - proxytype - The type of the proxy to be used. Three types - are supported: PROXY_TYPE_SOCKS4 (including socks4a), - PROXY_TYPE_SOCKS5 and PROXY_TYPE_HTTP - addr - The address of the server (IP or DNS). - port - The port of the server. Defaults to 1080 for SOCKS - servers and 8080 for HTTP proxy servers. - rdns - Should DNS queries be preformed on the remote side - (rather than the local side). The default is True. - Note: This has no effect with SOCKS4 servers. - username - Username to authenticate with to the server. - The default is no authentication. - password - Password to authenticate with to the server. - Only relevant when username is also provided. - """ - self.__proxy = (proxytype,addr,port,rdns,username,password) - - def __negotiatesocks5(self,destaddr,destport): - """__negotiatesocks5(self,destaddr,destport) - Negotiates a connection through a SOCKS5 server. - """ - # First we'll send the authentication packages we support. - if (self.__proxy[4]!=None) and (self.__proxy[5]!=None): - # The username/password details were supplied to the - # setproxy method so we support the USERNAME/PASSWORD - # authentication (in addition to the standard none). - self.sendall("\x05\x02\x00\x02") - else: - # No username/password were entered, therefore we - # only support connections with no authentication. - self.sendall("\x05\x01\x00") - # We'll receive the server's response to determine which - # method was selected - chosenauth = self.__recvall(2) - if chosenauth[0] != "\x05": - self.close() - raise GeneralProxyError((1,_generalerrors[1])) - # Check the chosen authentication method - if chosenauth[1] == "\x00": - # No authentication is required - pass - elif chosenauth[1] == "\x02": - # Okay, we need to perform a basic username/password - # authentication. - self.sendall("\x01" + chr(len(self.__proxy[4])) + self.__proxy[4] + chr(len(self.__proxy[5])) + self.__proxy[5]) - authstat = self.__recvall(2) - if authstat[0] != "\x01": - # Bad response - self.close() - raise GeneralProxyError((1,_generalerrors[1])) - if authstat[1] != "\x00": - # Authentication failed - self.close() - raise Socks5AuthError,((3,_socks5autherrors[3])) - # Authentication succeeded - else: - # Reaching here is always bad - self.close() - if chosenauth[1] == "\xFF": - raise Socks5AuthError((2,_socks5autherrors[2])) - else: - raise GeneralProxyError((1,_generalerrors[1])) - # Now we can request the actual connection - req = "\x05\x01\x00" - # If the given destination address is an IP address, we'll - # use the IPv4 address request even if remote resolving was specified. - try: - ipaddr = socket.inet_aton(destaddr) - req = req + "\x01" + ipaddr - except socket.error: - # Well it's not an IP number, so it's probably a DNS name. - if self.__proxy[3]==True: - # Resolve remotely - ipaddr = None - req = req + "\x03" + chr(len(destaddr)) + destaddr - else: - # Resolve locally - ipaddr = socket.inet_aton(socket.gethostbyname(destaddr)) - req = req + "\x01" + ipaddr - req = req + struct.pack(">H",destport) - self.sendall(req) - # Get the response - resp = self.__recvall(4) - if resp[0] != "\x05": - self.close() - raise GeneralProxyError((1,_generalerrors[1])) - elif resp[1] != "\x00": - # Connection failed - self.close() - if ord(resp[1])<=8: - raise Socks5Error((ord(resp[1]),_generalerrors[ord(resp[1])])) - else: - raise Socks5Error((9,_generalerrors[9])) - # Get the bound address/port - elif resp[3] == "\x01": - boundaddr = self.__recvall(4) - elif resp[3] == "\x03": - resp = resp + self.recv(1) - boundaddr = self.__recvall(ord(resp[4])) - else: - self.close() - raise GeneralProxyError((1,_generalerrors[1])) - boundport = struct.unpack(">H",self.__recvall(2))[0] - self.__proxysockname = (boundaddr,boundport) - if ipaddr != None: - self.__proxypeername = (socket.inet_ntoa(ipaddr),destport) - else: - self.__proxypeername = (destaddr,destport) - - def getproxysockname(self): - """getsockname() -> address info - Returns the bound IP address and port number at the proxy. - """ - return self.__proxysockname - - def getproxypeername(self): - """getproxypeername() -> address info - Returns the IP and port number of the proxy. - """ - return _orgsocket.getpeername(self) - - def getpeername(self): - """getpeername() -> address info - Returns the IP address and port number of the destination - machine (note: getproxypeername returns the proxy) - """ - return self.__proxypeername - - def __negotiatesocks4(self,destaddr,destport): - """__negotiatesocks4(self,destaddr,destport) - Negotiates a connection through a SOCKS4 server. - """ - # Check if the destination address provided is an IP address - rmtrslv = False - try: - ipaddr = socket.inet_aton(destaddr) - except socket.error: - # It's a DNS name. Check where it should be resolved. - if self.__proxy[3]==True: - ipaddr = "\x00\x00\x00\x01" - rmtrslv = True - else: - ipaddr = socket.inet_aton(socket.gethostbyname(destaddr)) - # Construct the request packet - req = "\x04\x01" + struct.pack(">H",destport) + ipaddr - # The username parameter is considered userid for SOCKS4 - if self.__proxy[4] != None: - req = req + self.__proxy[4] - req = req + "\x00" - # DNS name if remote resolving is required - # NOTE: This is actually an extension to the SOCKS4 protocol - # called SOCKS4A and may not be supported in all cases. - if rmtrslv==True: - req = req + destaddr + "\x00" - self.sendall(req) - # Get the response from the server - resp = self.__recvall(8) - if resp[0] != "\x00": - # Bad data - self.close() - raise GeneralProxyError((1,_generalerrors[1])) - if resp[1] != "\x5A": - # Server returned an error - self.close() - if ord(resp[1]) in (91,92,93): - self.close() - raise Socks4Error((ord(resp[1]),_socks4errors[ord(resp[1])-90])) - else: - raise Socks4Error((94,_socks4errors[4])) - # Get the bound address/port - self.__proxysockname = (socket.inet_ntoa(resp[4:]),struct.unpack(">H",resp[2:4])[0]) - if rmtrslv != None: - self.__proxypeername = (socket.inet_ntoa(ipaddr),destport) - else: - self.__proxypeername = (destaddr,destport) - - def __negotiatehttp(self,destaddr,destport): - """__negotiatehttp(self,destaddr,destport) - Negotiates a connection through an HTTP server. - """ - # If we need to resolve locally, we do this now - if self.__proxy[3] == False: - addr = socket.gethostbyname(destaddr) - else: - addr = destaddr - self.sendall("CONNECT " + addr + ":" + str(destport) + " HTTP/1.1\r\n" + "Host: " + destaddr + "\r\n\r\n") - # We read the response until we get the string "\r\n\r\n" - resp = self.recv(1) - while resp.find("\r\n\r\n")==-1: - resp = resp + self.recv(1) - # We just need the first line to check if the connection - # was successful - statusline = resp.splitlines()[0].split(" ",2) - if statusline[0] not in ("HTTP/1.0","HTTP/1.1"): - self.close() - raise GeneralProxyError((1,_generalerrors[1])) - try: - statuscode = int(statusline[1]) - except ValueError: - self.close() - raise GeneralProxyError((1,_generalerrors[1])) - if statuscode != 200: - self.close() - raise HTTPError((statuscode,statusline[2])) - self.__proxysockname = ("0.0.0.0",0) - self.__proxypeername = (addr,destport) - - def connect(self,destpair): - """connect(self,despair) - Connects to the specified destination through a proxy. - destpar - A tuple of the IP/DNS address and the port number. - (identical to socket's connect). - To select the proxy server use setproxy(). - """ - # Do a minimal input check first - if (type(destpair) in (list,tuple)==False) or (len(destpair)<2) or (type(destpair[0])!=str) or (type(destpair[1])!=int): - raise GeneralProxyError((5,_generalerrors[5])) - if self.__proxy[0] == PROXY_TYPE_SOCKS5: - if self.__proxy[2] != None: - portnum = self.__proxy[2] - else: - portnum = 1080 - _orgsocket.connect(self,(self.__proxy[1],portnum)) - self.__negotiatesocks5(destpair[0],destpair[1]) - elif self.__proxy[0] == PROXY_TYPE_SOCKS4: - if self.__proxy[2] != None: - portnum = self.__proxy[2] - else: - portnum = 1080 - _orgsocket.connect(self,(self.__proxy[1],portnum)) - self.__negotiatesocks4(destpair[0],destpair[1]) - elif self.__proxy[0] == PROXY_TYPE_HTTP: - if self.__proxy[2] != None: - portnum = self.__proxy[2] - else: - portnum = 8080 - _orgsocket.connect(self,(self.__proxy[1],portnum)) - self.__negotiatehttp(destpair[0],destpair[1]) - elif self.__proxy[0] == None: - _orgsocket.connect(self,(destpair[0],destpair[1])) - else: - raise GeneralProxyError((4,_generalerrors[4])) diff --git a/old/bin/insert_test_data.php b/old/bin/insert_test_data.php new file mode 100644 index 0000000..62483c0 --- /dev/null +++ b/old/bin/insert_test_data.php @@ -0,0 +1,42 @@ +persist($user1); +$user1->nickname = 'user1'; +$user1->email = 'user1@mail.com'; +$user1->password = sha1('user1'); + +$user2 = new User(); +_em()->persist($user2); +$user2->nickname = 'user2'; +$user2->email = 'user2@mail.com'; +$user2->password = sha1('user2'); + +// Insert movies +$movie1 = new Movie(); +_em()->persist($movie1); +$movie1->setId('0082198'); + +$movie2 = new Movie(); +_em()->persist($movie2); +$movie2->setId('0095016'); + +$movie3 = new Movie(); +_em()->persist($movie3); +$movie3->setId('0096446'); + +_em()->flush(); + +// Insert statuses +$movie1->setStatus($user1, Rating::Favorite); +$movie2->setStatus($user1, Rating::Excellent); +$movie3->setStatus($user1, Rating::Ok); +$movie1->setStatus($user2, Rating::Bad); +$movie2->setStatus($user2, Rating::Need); diff --git a/tests/test_scores.php b/old/tests/test_scores.php similarity index 94% rename from tests/test_scores.php rename to old/tests/test_scores.php index de9c29b..3dabe92 100644 --- a/tests/test_scores.php +++ b/old/tests/test_scores.php @@ -5,7 +5,7 @@ */ require_once __DIR__ . '/../public/index.php'; -$searchEngine = new \mQueue\Service\SearchEngine(); +$searchEngine = new old\application\mQueue\Service\SearchEngine(); // Clean up $destination = __DIR__ . '/data/current/'; diff --git a/package.json b/package.json index a03b6c7..e12f3fe 100644 --- a/package.json +++ b/package.json @@ -2,10 +2,10 @@ "name": "mqueue", "private": true, "dependencies": { - "esbuild": "^0.25.1", - "highcharts": "^12.1.2", - "jquery": "^3.6.0", - "sass": "^1.86.0", + "esbuild": "^0.28.0", + "highcharts": "^12.6.0", + "jquery": "^3.7.1", + "sass": "^1.100.0", "timeago": "^1.6.7" } } diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 7bef2c2..85d8a1e 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,11 +1,43 @@ parameters: ignoreErrors: - - message: "#^Cannot call method current\\(\\) on array\\\\.$#" + message: '#^Call to static method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''Cake\\\\Chronos\\\\Chronos'' and Cake\\Chronos\\Chronos will always evaluate to true\.$#' + identifier: staticMethod.alreadyNarrowedType + count: 3 + path: tests/ApplicationTest/DBAL/Types/ChronosTypeTest.php + + - + message: '#^Call to static method PHPUnit\\Framework\\Assert\:\:assertNull\(\) with null and ''support null values'' will always evaluate to true\.$#' + identifier: staticMethod.alreadyNarrowedType + count: 2 + path: tests/ApplicationTest/DBAL/Types/ChronosTypeTest.php + + - + message: '#^Parameter \#1 \$column of method Application\\DBAL\\Types\\ChronosType\:\:getSQLDeclaration\(\) expects array\, array\ given\.$#' + identifier: argument.type + count: 1 + path: tests/ApplicationTest/DBAL/Types/ChronosTypeTest.php + + - + message: '#^Call to static method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''Cake\\\\Chronos\\\\ChronosDate'' and Cake\\Chronos\\ChronosDate will always evaluate to true\.$#' + identifier: staticMethod.alreadyNarrowedType + count: 2 + path: tests/ApplicationTest/DBAL/Types/DateTypeTest.php + + - + message: '#^Call to static method PHPUnit\\Framework\\Assert\:\:assertNull\(\) with null and ''support null values'' will always evaluate to true\.$#' + identifier: staticMethod.alreadyNarrowedType + count: 2 + path: tests/ApplicationTest/DBAL/Types/DateTypeTest.php + + - + message: '#^Parameter \#1 \$column of method Application\\DBAL\\Types\\DateType\:\:getSQLDeclaration\(\) expects array\, array\ given\.$#' + identifier: argument.type count: 1 - path: application/mQueue/Form/Filters.php + path: tests/ApplicationTest/DBAL/Types/DateTypeTest.php - - message: "#^Method mQueue\\\\View\\\\Helper\\\\HeadScript\\:\\:__call\\(\\) should return mQueue\\\\View\\\\Helper\\\\HeadScript but returns Zend_View_Helper_HeadScript\\.$#" + message: '#^Call to static method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''Application\\\\Handler\\\\HomePageHandler'' and Application\\Handler\\HomePageHandler will always evaluate to true\.$#' + identifier: staticMethod.alreadyNarrowedType count: 1 - path: application/mQueue/View/Helper/HeadScript.php + path: tests/ApplicationTest/Handler/PageHandlerFactoryTest.php diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 2d7ec96..502068e 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -1,19 +1,18 @@ parameters: level: 5 paths: - - application/mQueue/ + - bin/ + - src/ - tests/ - bootstrapFiles: - - tests/bootstrap.php + doctrine: + repositoryClass: Application\Repository\AbstractRepository + objectManagerLoader: tests/object-manager.php ignoreErrors: - - '~Result of method Zend_View_Helper_HeadLink\:\:__call\(\) \(void\) is used\.~' - - '~^Access to an undefined property mQueue\\(Model|Form)\\.*~' - - '~^Access to an undefined property mQueueTest\\.*~' - - # Tolerate loose typing in magic Zend things - - '~^Access to an undefined property (Zend_Controller_Action_Helper_Abstract|Zend_Db_Table_Row_Abstract|Zend_View|Zend_View_Interface|Zend_Form|Zend_Navigation_Page|Zend_Session_Namespace|Application\\Form\\JQuery\\SubForm)\:\:.*\.$~' - - '~^Call to an undefined method (Zend_View|Zend_View_Interface|Zend_Controller_Action_Helper_Abstract|Zend_Form_Decorator_Abstract|Zend_Form_Element)\:\:.*\(\)\.$~' - - '~but returns (Zend_Db_Table_Row_Abstract|Zend_Db_Table_Rowset_Abstract)(\|null)?\.$~' + - identifier: class.extendsFinalByPhpDoc includes: - phpstan-baseline.neon + - vendor/phpstan/phpstan-doctrine/extension.neon + - vendor/phpstan/phpstan-doctrine/rules.neon + - vendor/phpstan/phpstan-phpunit/extension.neon + - vendor/phpstan/phpstan-phpunit/rules.neon diff --git a/phpunit.xml b/phpunit.xml deleted file mode 100644 index b780734..0000000 --- a/phpunit.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - ./tests/mQueueTest - - - - - - ./application/ - - ./application/ - - - - - - - - - - diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 0000000..fdabb39 --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,26 @@ + + + + + ./tests + + + + + + src + + + diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..f12009f --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,545 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + esbuild: + specifier: ^0.28.0 + version: 0.28.0 + highcharts: + specifier: ^12.6.0 + version: 12.6.0 + jquery: + specifier: ^3.7.1 + version: 3.7.1 + sass: + specifier: ^1.100.0 + version: 1.100.0 + timeago: + specifier: ^1.6.7 + version: 1.6.7 + +packages: + + '@esbuild/aix-ppc64@0.28.0': + resolution: {integrity: sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.28.0': + resolution: {integrity: sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.28.0': + resolution: {integrity: sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.28.0': + resolution: {integrity: sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.28.0': + resolution: {integrity: sha512-0T+A9WZm+bZ84nZBtk1ckYsOvyA3x7e2Acj1KdVfV4/2tdG4fzUp91YHx+GArWLtwqp77pBXVCPn2We7Letr0Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.28.0': + resolution: {integrity: sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.28.0': + resolution: {integrity: sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.28.0': + resolution: {integrity: sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.28.0': + resolution: {integrity: sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.28.0': + resolution: {integrity: sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.28.0': + resolution: {integrity: sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.28.0': + resolution: {integrity: sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.28.0': + resolution: {integrity: sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.28.0': + resolution: {integrity: sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.28.0': + resolution: {integrity: sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.28.0': + resolution: {integrity: sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.28.0': + resolution: {integrity: sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.28.0': + resolution: {integrity: sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.28.0': + resolution: {integrity: sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.28.0': + resolution: {integrity: sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.28.0': + resolution: {integrity: sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.28.0': + resolution: {integrity: sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.28.0': + resolution: {integrity: sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.28.0': + resolution: {integrity: sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.28.0': + resolution: {integrity: sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.28.0': + resolution: {integrity: sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@parcel/watcher-android-arm64@2.5.6': + resolution: {integrity: sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [android] + + '@parcel/watcher-darwin-arm64@2.5.6': + resolution: {integrity: sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [darwin] + + '@parcel/watcher-darwin-x64@2.5.6': + resolution: {integrity: sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [darwin] + + '@parcel/watcher-freebsd-x64@2.5.6': + resolution: {integrity: sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [freebsd] + + '@parcel/watcher-linux-arm-glibc@2.5.6': + resolution: {integrity: sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@parcel/watcher-linux-arm-musl@2.5.6': + resolution: {integrity: sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + libc: [musl] + + '@parcel/watcher-linux-arm64-glibc@2.5.6': + resolution: {integrity: sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@parcel/watcher-linux-arm64-musl@2.5.6': + resolution: {integrity: sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@parcel/watcher-linux-x64-glibc@2.5.6': + resolution: {integrity: sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@parcel/watcher-linux-x64-musl@2.5.6': + resolution: {integrity: sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@parcel/watcher-win32-arm64@2.5.6': + resolution: {integrity: sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [win32] + + '@parcel/watcher-win32-ia32@2.5.6': + resolution: {integrity: sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==} + engines: {node: '>= 10.0.0'} + cpu: [ia32] + os: [win32] + + '@parcel/watcher-win32-x64@2.5.6': + resolution: {integrity: sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [win32] + + '@parcel/watcher@2.5.6': + resolution: {integrity: sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==} + engines: {node: '>= 10.0.0'} + + chokidar@5.0.0: + resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} + engines: {node: '>= 20.19.0'} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + esbuild@0.28.0: + resolution: {integrity: sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw==} + engines: {node: '>=18'} + hasBin: true + + highcharts@12.6.0: + resolution: {integrity: sha512-zWwoXECOakJT2huBMqIZOjDNLLgyZFCcfZqTj2g6cvYOGxjF3nvooC+rIaF5aKiharluyGKNflfRNrzOAIjdRg==} + peerDependencies: + jspdf: ^4.1.0 + svg2pdf.js: ^2.7.0 + peerDependenciesMeta: + jspdf: + optional: true + svg2pdf.js: + optional: true + + immutable@5.1.5: + resolution: {integrity: sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A==} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + jquery@3.7.1: + resolution: {integrity: sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==} + + node-addon-api@7.1.1: + resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} + + picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} + engines: {node: '>=12'} + + readdirp@5.0.0: + resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==} + engines: {node: '>= 20.19.0'} + + sass@1.100.0: + resolution: {integrity: sha512-B5j0rYMlinhhOo9tjQebMVVn0TfyXAF+wB3b2ggZUuJ/is/Y+7+JGjirAMxHZ9Z3hIP98NPfamlAkBHa1lAaXQ==} + engines: {node: '>=20.19.0'} + hasBin: true + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + timeago@1.6.7: + resolution: {integrity: sha512-FikcjN98+ij0siKH4VO4dZ358PR3oDDq4Vdl1+sN9gWz1/+JXGr3uZbUShYH/hL7bMhcTpPbplJU5Tej4b4jbQ==} + +snapshots: + + '@esbuild/aix-ppc64@0.28.0': + optional: true + + '@esbuild/android-arm64@0.28.0': + optional: true + + '@esbuild/android-arm@0.28.0': + optional: true + + '@esbuild/android-x64@0.28.0': + optional: true + + '@esbuild/darwin-arm64@0.28.0': + optional: true + + '@esbuild/darwin-x64@0.28.0': + optional: true + + '@esbuild/freebsd-arm64@0.28.0': + optional: true + + '@esbuild/freebsd-x64@0.28.0': + optional: true + + '@esbuild/linux-arm64@0.28.0': + optional: true + + '@esbuild/linux-arm@0.28.0': + optional: true + + '@esbuild/linux-ia32@0.28.0': + optional: true + + '@esbuild/linux-loong64@0.28.0': + optional: true + + '@esbuild/linux-mips64el@0.28.0': + optional: true + + '@esbuild/linux-ppc64@0.28.0': + optional: true + + '@esbuild/linux-riscv64@0.28.0': + optional: true + + '@esbuild/linux-s390x@0.28.0': + optional: true + + '@esbuild/linux-x64@0.28.0': + optional: true + + '@esbuild/netbsd-arm64@0.28.0': + optional: true + + '@esbuild/netbsd-x64@0.28.0': + optional: true + + '@esbuild/openbsd-arm64@0.28.0': + optional: true + + '@esbuild/openbsd-x64@0.28.0': + optional: true + + '@esbuild/openharmony-arm64@0.28.0': + optional: true + + '@esbuild/sunos-x64@0.28.0': + optional: true + + '@esbuild/win32-arm64@0.28.0': + optional: true + + '@esbuild/win32-ia32@0.28.0': + optional: true + + '@esbuild/win32-x64@0.28.0': + optional: true + + '@parcel/watcher-android-arm64@2.5.6': + optional: true + + '@parcel/watcher-darwin-arm64@2.5.6': + optional: true + + '@parcel/watcher-darwin-x64@2.5.6': + optional: true + + '@parcel/watcher-freebsd-x64@2.5.6': + optional: true + + '@parcel/watcher-linux-arm-glibc@2.5.6': + optional: true + + '@parcel/watcher-linux-arm-musl@2.5.6': + optional: true + + '@parcel/watcher-linux-arm64-glibc@2.5.6': + optional: true + + '@parcel/watcher-linux-arm64-musl@2.5.6': + optional: true + + '@parcel/watcher-linux-x64-glibc@2.5.6': + optional: true + + '@parcel/watcher-linux-x64-musl@2.5.6': + optional: true + + '@parcel/watcher-win32-arm64@2.5.6': + optional: true + + '@parcel/watcher-win32-ia32@2.5.6': + optional: true + + '@parcel/watcher-win32-x64@2.5.6': + optional: true + + '@parcel/watcher@2.5.6': + dependencies: + detect-libc: 2.1.2 + is-glob: 4.0.3 + node-addon-api: 7.1.1 + picomatch: 4.0.4 + optionalDependencies: + '@parcel/watcher-android-arm64': 2.5.6 + '@parcel/watcher-darwin-arm64': 2.5.6 + '@parcel/watcher-darwin-x64': 2.5.6 + '@parcel/watcher-freebsd-x64': 2.5.6 + '@parcel/watcher-linux-arm-glibc': 2.5.6 + '@parcel/watcher-linux-arm-musl': 2.5.6 + '@parcel/watcher-linux-arm64-glibc': 2.5.6 + '@parcel/watcher-linux-arm64-musl': 2.5.6 + '@parcel/watcher-linux-x64-glibc': 2.5.6 + '@parcel/watcher-linux-x64-musl': 2.5.6 + '@parcel/watcher-win32-arm64': 2.5.6 + '@parcel/watcher-win32-ia32': 2.5.6 + '@parcel/watcher-win32-x64': 2.5.6 + optional: true + + chokidar@5.0.0: + dependencies: + readdirp: 5.0.0 + + detect-libc@2.1.2: + optional: true + + esbuild@0.28.0: + optionalDependencies: + '@esbuild/aix-ppc64': 0.28.0 + '@esbuild/android-arm': 0.28.0 + '@esbuild/android-arm64': 0.28.0 + '@esbuild/android-x64': 0.28.0 + '@esbuild/darwin-arm64': 0.28.0 + '@esbuild/darwin-x64': 0.28.0 + '@esbuild/freebsd-arm64': 0.28.0 + '@esbuild/freebsd-x64': 0.28.0 + '@esbuild/linux-arm': 0.28.0 + '@esbuild/linux-arm64': 0.28.0 + '@esbuild/linux-ia32': 0.28.0 + '@esbuild/linux-loong64': 0.28.0 + '@esbuild/linux-mips64el': 0.28.0 + '@esbuild/linux-ppc64': 0.28.0 + '@esbuild/linux-riscv64': 0.28.0 + '@esbuild/linux-s390x': 0.28.0 + '@esbuild/linux-x64': 0.28.0 + '@esbuild/netbsd-arm64': 0.28.0 + '@esbuild/netbsd-x64': 0.28.0 + '@esbuild/openbsd-arm64': 0.28.0 + '@esbuild/openbsd-x64': 0.28.0 + '@esbuild/openharmony-arm64': 0.28.0 + '@esbuild/sunos-x64': 0.28.0 + '@esbuild/win32-arm64': 0.28.0 + '@esbuild/win32-ia32': 0.28.0 + '@esbuild/win32-x64': 0.28.0 + + highcharts@12.6.0: {} + + immutable@5.1.5: {} + + is-extglob@2.1.1: + optional: true + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + optional: true + + jquery@3.7.1: {} + + node-addon-api@7.1.1: + optional: true + + picomatch@4.0.4: + optional: true + + readdirp@5.0.0: {} + + sass@1.100.0: + dependencies: + chokidar: 5.0.0 + immutable: 5.1.5 + source-map-js: 1.2.1 + optionalDependencies: + '@parcel/watcher': 2.5.6 + + source-map-js@1.2.1: {} + + timeago@1.6.7: + dependencies: + jquery: 3.7.1 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..f588aaa --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,3 @@ +allowBuilds: + '@parcel/watcher': false + esbuild: false diff --git a/public/.htaccess b/public/.htaccess deleted file mode 100644 index b3e4114..0000000 --- a/public/.htaccess +++ /dev/null @@ -1,13 +0,0 @@ -RewriteEngine On -RewriteCond %{REQUEST_FILENAME} -s [OR] -RewriteCond %{REQUEST_FILENAME} -l [OR] -RewriteCond %{REQUEST_FILENAME} -d -RewriteRule ^.*$ - [NC,L] -RewriteRule ^.*$ index.php [NC,L] - -# Cache control for 1 week - - - Header set Cache-Control "max-age=604800" - - diff --git a/public/images/browser_ie.svg b/public/images/browser_ie.svg deleted file mode 100644 index d218cd5..0000000 --- a/public/images/browser_ie.svg +++ /dev/null @@ -1,474 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/public/images/browser_opera.gif b/public/images/browser_opera.gif deleted file mode 100644 index bf67d2089bd2a06a34463686aaf20f116abac2eb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1032 zcmZ?wbhEHb zo((cGy9EXJ%E@grG+Y-DusS||QBLma{DRd51t<9VPYMW}1tLMgOM-$og@o=23m?$c zJ)y6E%*g1piOG2rlgm0fcl7iw7#m+QHN9$TcEiHruC?_85s_!2qA$h7UW<#rk&t*J zDfwPX>Z6?eYb)zdO3I(r)IaO!elaxqVr=r&-2AJR)i-;GbKc&k0|Ku2_*@DJy&4dB zCouSKNa*S0BDGi1_B<@YU7ro0s>upx|#Ip+7>yendt8h>CfclJcai z?0Z(u&xE8O>6yRs3x5<9|0*v1QC2a1!i23`wrt(H^;k>G@z&Ox&CU0^y6#PwcyHRY z>nm5@TDj`>`t`pWoBp)4{pjlcF>TuKX)}J!U+`@G`q!H_z1qC_)&Bjzmo5LhblIO} z%m1xe_jkjlyLax~zkB!ffdj9PAOCao*q;+8KVG=__5S_8H*bD@`0(rV=YJnO{PphL zulMi&y?pua{rg{^KK=gu`S;ha|37~G_vzE0&!7K(|Nigi&wsyv|NHaj-`~Ig|NZ;_ z|3AYhAQS>RAO)bjz`*gA!Itw4%gX=-`vxAzS1KD8E_En57dRGE8{%Evqv z-CCxk+%!_3wA`=aP{G1a%NEX7@LY4{<&_oO)3hx(OrIDxpAb;)`lVnQ^yJv0w$7A4 zFCHG|o2ls3aAU%i0wG>m%ZQ4Lmzt0FNzIT-Yg(w}%B2>%XVIjhs=xGI;?a88XV543W!LBT)1q&-^{?M=JQF!{SX6FkVWM! z5m$x=<^q~03jD+*8l(j diff --git a/public/images/browser_safari.gif b/public/images/browser_safari.gif deleted file mode 100644 index c0bb73dd10c6e546fe13cbb7a768e3e0852c0f4f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1044 zcmZ?wbhEHb6krfw_|5 z=k)Z>E!o|hGyC`DOx#>GVSVYOJ!RAOS4`esJ!MDroZU{H2Yvew`c6INH~UQB%(GF` zjytWn>#*it!1C)6i!a5jxE8kYuG5w$Zd;$EPCA@D^K{1iv-z`5m(4y?G2>9x+_P!x zZ)L8#S+?X%+1A@R`yLnXd04&s!Qxon75M{e8WC{r&w- z&C^;tCpC4?=$Cg4wfX&73u7_MEwM=FXot zZ(ifXC5@BUHB4RKI(bF=lvOR$H_n^CVE%%I)0XdBxNy;;#fz6LUABDr@@30c?Ap0= z=k7gw_U>z(eW-2qnf^rwyB3}7TzsZ$`T5@E=et%MYhQPzY5VQTOAk(3e|gIKD@*p= z*s|x?{{07L9J<$d?8UR^ zFJHZW{rb(DPoF-0`}Pf-I)*u+_>+Z^f#E-c4#=ONJi);6k>Nk53`f?L1I-+)^SqwE zn!2WiIelhRbL(MI-6}iPl&kDKb8UJ!uHO8RaNaSbAcn)~%ZZ5#or(?_d{8zBlQ#63 zrDJ*J#j)u;#>aFTzP{(5ZfQ6{BXEkR^He_lH!dAt`g zH}G@5sF;US)dvH|B=!j+&N`eLsu#Ft2Ke=eXbL`Q7x590;>nr1fLqgd^0I(SDqc>E hq3n^G3LDxct2<<{$S}+^n9$6?%#-r6#6W?;8UP(ttzG~C diff --git a/public/index.php b/public/index.php index 2921f3b..9ccc417 100644 --- a/public/index.php +++ b/public/index.php @@ -1,36 +1,38 @@ bootstrap(); - -// we only run the application if this file were NOT included (otherwise, the file was included to access misc functions) -if (realpath(__FILE__) == realpath($_SERVER['SCRIPT_FILENAME'])) { - $application->run(); -} +chdir(dirname(__DIR__)); +require 'vendor/autoload.php'; + +/** + * Self-called anonymous function that creates its own scope and keeps the global namespace clean. + */ +(function (): void { + /** @var Psr\Container\ContainerInterface $container */ + $container = require 'config/container.php'; + + global $app; + /** @var Mezzio\Application $app */ + $app = $container->get(Mezzio\Application::class); + $factory = $container->get(Mezzio\MiddlewareFactory::class); + + // Execute programmatic/declarative middleware pipeline and routing + // configuration statements + (require 'config/pipeline.php')($app, $factory, $container); + (require 'config/routes.php')($app, $factory, $container); + + // we only run the application if this file was NOT included (otherwise, the file was included to access misc functions) + if (realpath(__FILE__) === realpath($_SERVER['SCRIPT_FILENAME'])) { + $app->run(); + } +})(); diff --git a/src/Application/DBAL/Logging/MiddlewareFactory.php b/src/Application/DBAL/Logging/MiddlewareFactory.php new file mode 100644 index 0000000..0f473ce --- /dev/null +++ b/src/Application/DBAL/Logging/MiddlewareFactory.php @@ -0,0 +1,41 @@ + [ + * 'configuration' => [ + * 'orm_default' => [ + * // Log all SQL queries from Doctrine (to logs/all.log) + * 'middlewares' => [\Doctrine\DBAL\Logging\Middleware::class], + * ], + * ], + * ], + * ]; + * ``` + */ +class MiddlewareFactory implements FactoryInterface +{ + /** + * @param string $requestedName + */ + public function __invoke(ContainerInterface $container, $requestedName, ?array $options = null): Middleware + { + $logger = $container->get(LoggerInterface::class); + + return new Middleware($logger); + } +} diff --git a/src/Application/DBAL/Types/ChronosType.php b/src/Application/DBAL/Types/ChronosType.php new file mode 100644 index 0000000..478f487 --- /dev/null +++ b/src/Application/DBAL/Types/ChronosType.php @@ -0,0 +1,58 @@ +getDateTimeTypeDeclarationSQL($column); + } + + /** + * @return ($value is null ? null : string) + */ + public function convertToDatabaseValue(mixed $value, AbstractPlatform $platform): ?string + { + if ($value === null) { + return $value; + } + + if ($value instanceof DateTimeInterface) { + return $value->format($platform->getDateTimeFormatString()); + } + + throw InvalidType::new($value, self::class, ['null', Chronos::class]); + } + + /** + * @return ($value is null ? null : Chronos) + */ + public function convertToPHPValue(mixed $value, AbstractPlatform $platform): ?Chronos + { + if ($value === null || $value instanceof Chronos) { + return $value; + } + + if (!is_string($value) && !$value instanceof DateTimeInterface) { + throw InvalidFormat::new( + (string) $value, + self::class, + $platform->getDateTimeFormatString(), + ); + } + + $val = new Chronos($value); + + return $val; + } +} diff --git a/src/Application/DBAL/Types/DateType.php b/src/Application/DBAL/Types/DateType.php new file mode 100644 index 0000000..f143ae9 --- /dev/null +++ b/src/Application/DBAL/Types/DateType.php @@ -0,0 +1,57 @@ +getDateTypeDeclarationSQL($column); + } + + /** + * @return ($value is null ? null : string) + */ + public function convertToDatabaseValue(mixed $value, AbstractPlatform $platform): ?string + { + if ($value === null) { + return $value; + } + + if ($value instanceof ChronosDate) { + return $value->format($platform->getDateFormatString()); + } + + throw InvalidType::new($value, self::class, ['null', ChronosDate::class]); + } + + /** + * @return ($value is null ? null : ChronosDate) + */ + public function convertToPHPValue(mixed $value, AbstractPlatform $platform): ?ChronosDate + { + if ($value === null || $value instanceof ChronosDate) { + return $value; + } + + if (!is_string($value)) { + throw InvalidFormat::new( + (string) $value, + self::class, + $platform->getDateFormatString(), + ); + } + + $val = new ChronosDate($value); + + return $val; + } +} diff --git a/src/Application/Enum/Rating.php b/src/Application/Enum/Rating.php new file mode 100644 index 0000000..5dad76c --- /dev/null +++ b/src/Application/Enum/Rating.php @@ -0,0 +1,51 @@ + _tr('Not rated'), + self::Need => _tr('Need'), + self::Bad => _tr('Bad'), + self::Ok => _tr('Ok'), + self::Excellent => _tr('Excellent'), + self::Favorite => _tr('Favorite'), + }; + } + + /** + * @return list + */ + public static function possibleChoices(bool $withNothing = false): array + { + return [ + ...($withNothing ? [self::Nothing] : []), + self::Need, + self::Bad, + self::Ok, + self::Excellent, + self::Favorite, + ]; + } + + /** + * @return list + */ + public static function possibleValues(bool $withNothing = false): array + { + return array_map(fn (Rating $rating) => $rating->value, self::possibleChoices()); + } +} diff --git a/src/Application/Form/Filter.php b/src/Application/Form/Filter.php new file mode 100644 index 0000000..e1b60e1 --- /dev/null +++ b/src/Application/Form/Filter.php @@ -0,0 +1,115 @@ + 'GET', + ]; + + public function __construct( + string $name, + private readonly bool $withExtraField, + ) + { + parent::__construct($name); + + $users = []; + if (\Application\Model\User::getCurrent()) { + $users = [0 => _tr('<< me >>')]; + } + + foreach (_em()->getRepository(\Application\Model\User::class)->getAll() as $user) { + $users[$user->id] = $user->nickname; + } + + // Add the user element + $user = new Element\Select('user'); + $user->setAttribute('required', true); + $user->setAttribute('class', 'filterUser'); + $user->setValueOptions($users); + $this->add($user); + + // Add the condition element + $condition = new Element\Select('condition'); + $condition->setAttribute('required', true); + $condition->setValueOptions([ + 'is' => _tr('rating is'), + 'isnot' => _tr('rating is not'), + ]); + $this->add($condition); + + $statuses = []; + $helper = new \Application\View\Helper\Rating(); + foreach (Rating::possibleChoices() as $rating) { + $statuses[$rating->value] = $helper($rating); + } + $statuses = $statuses + [Rating::Nothing->value => _tr('nothing')]; + + // Add the status element + $status = new Element\MultiCheckbox('status'); + $status->setAttribute('class', 'filterStatus'); + $status->setValueOptions($statuses); + $status->setLabelOption('disable_html_escape', true); + $this->add($status); + + if ($this->withExtraField) { + // Add the title element + $title = new Element\Text('title'); + $title->setAttribute('autofocus', true); + $title->setAttribute('placeholder', _tr('title')); + $this->add($title); + + // Add the withSource element + $withSource = new Element\Checkbox('withSource'); + $withSource->setAttribute('autofocus', true); + $withSource->setAttribute('class', 'inversed-checkbox'); + $withSource->setLabel(_tr('With source')); + $this->add($withSource); + } + } + + public function getInputFilterSpecification(): array + { + $userInput = new Input('user'); + $userInput->setRequired(true); + $userInput->getFilterChain()->attach(new ToInt()); + $userInput->getValidatorChain()->attach(new Validate\User()); + + $conditionInput = new Input('condition'); + $conditionInput->setRequired(true); + + $statusInput = new Input('status'); + $statusInput->getFilterChain()->attach(new ToInt()); + + $result = [ + $userInput, + $conditionInput, + $statusInput, + ]; + + if ($this->withExtraField) { + $titleInput = new Input('title'); + $titleInput->getFilterChain()->attach(new StringTrim()); + $titleInput->setRequired(false); + $titleInput->setAllowEmpty(true); + + $withSourceInput = new Input('withSource'); + $withSourceInput->setAllowEmpty(true); + + $result[] = $titleInput; + $result[] = $withSourceInput; + } + + return $result; + } +} diff --git a/src/Application/Form/Filters.php b/src/Application/Form/Filters.php new file mode 100644 index 0000000..826ae84 --- /dev/null +++ b/src/Application/Form/Filters.php @@ -0,0 +1,160 @@ + 'GET', + ]; + + public function __construct() + { + parent::__construct(); + $this->setName('filters'); + + // Add the submit button + $submit = new Element\Submit('submit'); + $submit->setValue(_tr('Apply')); + $this->add($submit); + + $image = new Element\Image('addFilter'); + $image->setValue(_tr('Apply')); + $image->setAttribute('class', 'addFilter'); + $image->setAttribute('src', '/images/add.png'); + $this->add($image); + } + + /** + * Overrides isValid to dynamically generate subforms which will be used for validation. + */ + public function setData(?iterable $data): self + { + $data = $this->createSubForms($data); + + parent::setData($data); + + return $this; + } + + /** + * Override getValues() to replace special '0' value with current user. + */ + public function getData(int $flag = FormInterface::VALUES_NORMALIZED): array + { + $data = parent::getData($flag) ?? []; + foreach ($data as $key => &$value) { + if (!$this->isFilter($key)) { + continue; + } + + if ($value['user'] === 0) { + $value['user'] = \Application\Model\User::getCurrent()?->id; + + } + } + + return $data; + } + + /** + * Create actual filter as subforms according to filter values given. + * It will at least create one subform. It may add a default subform + * if 'addFilter_x' value is given. + * + * @param array $defaults values of form + * + * @return array $defaults modified values with additional filter + */ + private function createSubForms(iterable $defaults): array + { + // Find out the highest filter number + $max = 0; + foreach (array_keys($defaults) as $key) { + if (preg_match('/^filter(\d+)$/', $key, $m)) { + if ($m[1] > $max) { + $max = $m[1]; + } + } + } + + // If we specifically asked to add a filter or if there is none, then add a new filter with default value + if (isset($defaults['addFilter_x']) || $max === 0) { + + $defaults['filter' . ($max + 1)] = [ + 'user' => \Application\Model\User::getCurrent() ? 0 : _em()->getRepository(\Application\Model\User::class)->findOneBy([])->id, + 'condition' => 'is', + 'status' => Rating::possibleValues(), + 'withSource' => false, + ]; + } + + // Create all filters + $first = true; + foreach ($defaults as $key => &$value) { + if ($this->isFilter($key)) { + $subform = new Filter($key, $first); + $this->add($subform, ['priority' => 1]); + $first = false; + + // No status choice, means "nothing" choice + $status = $value['status'] ?? []; + if (!$status) { + $value['status'] = [0]; + } + } + } + + return $defaults; + } + + /** + * Returns values as readable text for end-user. + */ + public function getValuesText(): string + { + $data = $this->getData(); + $text = []; + foreach ($data as $key => $value) { + if (!$this->isFilter($key)) { + continue; + } + $text[] = $this->getValuesTextOneFilter($value); + } + + return implode(' + ', $text); + } + + private function getValuesTextOneFilter(array $values): string + { + $text = ''; + + if (@$values['title']) { + $text = _tr('title') . ':"' . $values['title'] . '" + '; + } + + $id = $values['user']; + if (!$id) { + return ''; + } + $user = _em()->getRepository(\Application\Model\User::class)->findOneById($id); + + $statusLabels = []; + foreach ($values['status'] as $status) { + $statusLabels[] = Rating::from($status)->name(); + } + $text .= $user->nickname . ':' . implode('+', $statusLabels); + + return $text; + } + + private function isFilter(string $key): bool + { + return preg_match('~^filter\d+$~', $key); + } +} diff --git a/src/Application/Form/Login.php b/src/Application/Form/Login.php new file mode 100644 index 0000000..73729cf --- /dev/null +++ b/src/Application/Form/Login.php @@ -0,0 +1,44 @@ +setAttribute('class', 'grid'); + + // Add the email + $email = new Element\Email('email'); + $email->setLabel(_tr('Email:')); + $email->setAttribute('required', true); + $email->setAttribute('autofocus', true); + $this->add($email); + + // Add the password + $password = new Element\Password('password'); + $password->setLabel(_tr('Password:')); + $password->setAttribute('required', true); + $this->add($password); + + $passwordInput = new Input('password'); + $passwordInput->setRequired(true); + + // Add the submit button + $submit = new Element\Submit('submit'); + $submit->setValue(_tr('Login')); + $this->add($submit); + + // Add referrer to redirect after login + $referrer = new Element\Hidden('referrer'); + $this->add($referrer); + + $inputFilter = $this->getInputFilter(); + $inputFilter->add($passwordInput); + } +} diff --git a/src/Application/Form/Movie.php b/src/Application/Form/Movie.php new file mode 100644 index 0000000..d2ca7a3 --- /dev/null +++ b/src/Application/Form/Movie.php @@ -0,0 +1,40 @@ + 'GET', + ]; + + public function __construct() + { + parent::__construct(); + $this->setAttribute('class', 'grid'); + + // Add the ID element + $id = new Element\Text('id'); + $id->setLabel(_tr('IMDb url or id:')); + $id->setAttribute('required', true); + $id->setAttribute('autofocus', true); + $this->add($id); + + $idInput = new Input('id'); + $idInput->setRequired(true); + $idInput->getValidatorChain()->attach(new Regex('/(\\d{7,})/')); + + // Add the submit button + $submit = new Element\Submit('submit'); + $submit->setValue(_tr('Add movie')); + $this->add($submit); + + $inputFilter = $this->getInputFilter(); + $inputFilter->add($idInput); + } +} diff --git a/src/Application/Form/QuickSearch.php b/src/Application/Form/QuickSearch.php new file mode 100644 index 0000000..c69ebf4 --- /dev/null +++ b/src/Application/Form/QuickSearch.php @@ -0,0 +1,31 @@ + 'GET', + ]; + + public function __construct() + { + parent::__construct(); + $this->setName('quickSearch'); + + $this->setAttribute('action', '/movie'); + + // Add the comment element + $search = new Element\Text('search'); + $search->setAttribute('placeholder', _tr('search movie…')); + $this->add($search); + + // Add the submit button + $submit = new Element\Submit('searchSubmit'); + $submit->setValue(_tr('Search')); + $this->add($submit); + } +} diff --git a/src/Application/Form/User.php b/src/Application/Form/User.php new file mode 100644 index 0000000..ee2abde --- /dev/null +++ b/src/Application/Form/User.php @@ -0,0 +1,60 @@ +setAttribute('class', 'grid'); + + // Add the nickname element + $nickname = new Element\Text('nickname'); + $nickname->setLabel(_tr('Nickname:')); + $nickname->setAttribute('required', true); + $nickname->setAttribute('autofocus', true); + $this->add($nickname); + + $nicknameInput = new Input('nickname'); + $nicknameInput->setRequired(true); + $nicknameInput->getFilterChain()->attach(new StringTrim()); + $nicknameInput->getValidatorChain()->attach(new NoRecordExists('nickname')); + + // Add the email element + $email = new Element\Email('email'); + $email->setLabel(_tr('Email:')); + $email->setAttribute('required', true); + $this->add($email); + + $emailInput = new Input('email'); + $emailInput->setRequired(true); + $emailInput->getFilterChain()->attach(new StringTrim()); + $emailInput->getValidatorChain()->attach(new NoRecordExists('email')); + + // Add the password ail element + $password = new Element\Password('password'); + $password->setLabel(_tr('Password:')); + $password->setAttribute('required', true); + $this->add($password); + + $passwordInput = new Input('password'); + $passwordInput->setRequired(true); + + // Add the submit button + $submit = new Element\Submit('submit'); + $submit->setValue(_tr('Subscribe')); + $this->add($submit); + + $inputFilter = $this->getInputFilter(); + $inputFilter->add($nicknameInput); + $inputFilter->add($emailInput); + $inputFilter->add($passwordInput); + } +} diff --git a/src/Application/Form/Validate/User.php b/src/Application/Form/Validate/User.php new file mode 100644 index 0000000..bdf9e6d --- /dev/null +++ b/src/Application/Form/Validate/User.php @@ -0,0 +1,43 @@ + Message templates + */ + protected $messageTemplates = [ + self::ERROR_RECORD_FOUND => 'A record matching the input was found', + ]; + + /** + * Returns true if and only if $value contains a valid User ID. + * + * @param int $value + */ + public function isValid($value): bool + { + $valid = ($value === 0) || $this->query($value); + if (!$valid) { + $this->error(self::ERROR_RECORD_FOUND); + } + + return $valid; + } + + private function query($value): bool + { + return (bool) _em()->getConnection()->fetchOne('SELECT COUNT(*) FROM user WHERE id = :value', ['value' => $value]); + } +} diff --git a/src/Application/Handler/AboutPageHandler.php b/src/Application/Handler/AboutPageHandler.php new file mode 100644 index 0000000..edecc63 --- /dev/null +++ b/src/Application/Handler/AboutPageHandler.php @@ -0,0 +1,17 @@ +template->render('app::about')); + } +} diff --git a/src/Application/Handler/ActivityHandler.php b/src/Application/Handler/ActivityHandler.php new file mode 100644 index 0000000..c46baa7 --- /dev/null +++ b/src/Application/Handler/ActivityHandler.php @@ -0,0 +1,56 @@ +getAttribute(RouteResult::class); + $userId = $routeResult->getMatchedParams()['user'] ?? null; + $movieId = $routeResult->getMatchedParams()['movie'] ?? null; + + // By default, we show overall activity + $item = null; + $title = _tr('Overall activity'); + + // Try to show user's activity + if ($userId) { + $item = _em()->getRepository(User::class)->findOneById($userId); + if ($item) { + $title = _tr('Activity for %nickname%', ['nickname' => $item->nickname]); + } + } + + // Try to show movie's activity + if ($movieId) { + $item = _em()->getRepository(Movie::class)->findOneById($movieId); + if ($item) { + $title = _tr('Activity for %title%', ['title' => $item->title]); + } + } + + $activity = PaginatorFactory::create( + $request, + _em()->getRepository(Status::class)->getActivityQuery($item), + ); + + $data = [ + 'title' => $title, + 'activity' => $activity, + ]; + + return $this->htmlOrRss($request, 'app::activity', $data); + } +} diff --git a/src/Application/Handler/FaqPageHandler.php b/src/Application/Handler/FaqPageHandler.php new file mode 100644 index 0000000..ca28741 --- /dev/null +++ b/src/Application/Handler/FaqPageHandler.php @@ -0,0 +1,17 @@ +template->render('app::faq')); + } +} diff --git a/src/Application/Handler/GravatarPageHandler.php b/src/Application/Handler/GravatarPageHandler.php new file mode 100644 index 0000000..77f2f5c --- /dev/null +++ b/src/Application/Handler/GravatarPageHandler.php @@ -0,0 +1,21 @@ + _em()->getRepository(User::class)->getAll(), + 'layout' => false, + ]; + + return new HtmlResponse($this->template->render('app::gravatar-css', $data), headers: ['content-type' => 'text/css']); + } +} diff --git a/src/Application/Handler/HomePageHandler.php b/src/Application/Handler/HomePageHandler.php new file mode 100644 index 0000000..e5a469c --- /dev/null +++ b/src/Application/Handler/HomePageHandler.php @@ -0,0 +1,23 @@ +js('app::mqueue-user-js'); + } +} diff --git a/src/Application/Handler/JsRemoteHandler.php b/src/Application/Handler/JsRemoteHandler.php new file mode 100644 index 0000000..fe1f8ca --- /dev/null +++ b/src/Application/Handler/JsRemoteHandler.php @@ -0,0 +1,20 @@ +get('config'); + $data = [ + 'minimize' => (bool) $config['minimize'], + ]; + + return $this->js('app::remote-js', $data); + } +} diff --git a/src/Application/Handler/Movie/AddHandler.php b/src/Application/Handler/Movie/AddHandler.php new file mode 100644 index 0000000..be57436 --- /dev/null +++ b/src/Application/Handler/Movie/AddHandler.php @@ -0,0 +1,35 @@ +getQueryParams()['id'] ?? null) { + $form->setData($request->getQueryParams()); + if ($form->isValid()) { + $values = $form->getData(); + $movie = _em()->getRepository(Movie::class)->getOrCreate((int) Movie::extractId($values['id'])); + + $movies[] = $movie; + } + } + + $data = [ + 'form' => $form, + 'movies' => $movies, + ]; + + return new HtmlResponse($this->template->render('app::movie/add', $data)); + } +} diff --git a/src/Application/Handler/Movie/IndexHandler.php b/src/Application/Handler/Movie/IndexHandler.php new file mode 100644 index 0000000..4e9bb70 --- /dev/null +++ b/src/Application/Handler/Movie/IndexHandler.php @@ -0,0 +1,112 @@ +getRepository(User::class)->count()) { + $this->error('At least one user must exist to access this page'); + } + + $form = new Filters(); + + // Detect if at least one filter was submitted + $submitted = false; + foreach ($request->getQueryParams() as $key => $filter) { + if (preg_match('/^filter\d+$/', $key)) { + $submitted = true; + } + } + + $search = $request->getQueryParams()['search'] ?? null; + + // If was submitted, try to validate values + if ($submitted) { + $form->setData($request->getQueryParams()); + if (!$form->isValid()) { + $this->flashMessages($request)->flash('warning', _tr('Filter is invalid.')); + $form->setData([]); + } + } // If we submitted a quicksearch, set default values to search with any status + elseif ($search) { + $form->setData([ + 'filter1' => [ + 'user' => User::getCurrent() ? 0 : _em()->getRepository(User::class)->getAll()[0]->id, + 'condition' => 'is', + 'status' => Rating::possibleValues(true), + 'title' => $search, + ], + ]); + } // Otherwise clear the filter + else { + $form->setData([]); + } + + // Gather users selected in filters + $users = []; + $form->isValid(); + $filters = $form->getData(); + foreach ($filters as $key => $filter) { + if (!preg_match('/^filter\d+$/', $key)) { + continue; + } + + $id = $filter['user']; + if ($id) { + $users[$id] = _em()->getRepository(User::class)->findOneById($id); + } + } + + // If we output rss, we force sorting by date + if (($request->getQueryParams()['format'] ?? null) === 'rss') { + $queryParamsForSorting = [ + 'sort' => $filters['filter1']['withSource'] ? 'date_search' : 'date', + 'sortOrder' => 'desc', + ]; + } else { + $queryParamsForSorting = $request->getQueryParams(); + } + + $allowedSortingKey = ['movie.title', 'date', 'movie.dateSearch']; + $usersCount = count($users); + for ($i = 0; $i < $usersCount; ++$i) { + $allowedSortingKey[] = "status$i.rating"; + } + $sorting = new Sorting($queryParamsForSorting, $allowedSortingKey); + + $permanentParams = $form->getData(); + unset($permanentParams['addFilter']); + $permanentParams[$sorting->keyParamName] = $sorting->selectedKey; + $permanentParams[$sorting->orderParamName] = $sorting->selectedOrder; + + // Set up the paginator: Apply pagination only if there is no special context (so it is normal html rendering) + $paginator = PaginatorFactory::create( + $request, + _em()->getRepository(Movie::class)->getFilteredQuery($filters, $sorting), + ); + + $data = [ + 'formFilter' => $form, + 'users' => $users, + 'paginator' => $paginator, + 'permanentParams' => $permanentParams, + 'filterName' => $form->getValuesText(), + 'sorting' => $sorting, + ]; + + return $this->htmlOrRss($request, 'app::movie/index', $data); + } +} diff --git a/src/Application/Handler/Movie/ViewHandler.php b/src/Application/Handler/Movie/ViewHandler.php new file mode 100644 index 0000000..0a09d13 --- /dev/null +++ b/src/Application/Handler/Movie/ViewHandler.php @@ -0,0 +1,45 @@ +getAttribute(RouteResult::class); + $id = Movie::extractId($routeResult->getMatchedParams()['id'] ?? null); + + $movie = null; + if ($id) { + $movie = _em()->getRepository(Movie::class)->findOneById($id); + } + + if (!$movie) { + return $this->error(_tr('Movie not found')); + } + + $activity = PaginatorFactory::create( + $request, + _em()->getRepository(Status::class)->getActivityQuery($movie), + ); + + $data = [ + 'movie' => $movie, + 'users' => _em()->getRepository(User::class)->getAll(), + 'movieActivity' => $activity, + ]; + + return new HtmlResponse($this->template->render('app::movie/view', $data)); + } +} diff --git a/src/Application/Handler/PageHandler.php b/src/Application/Handler/PageHandler.php new file mode 100644 index 0000000..f68dfc9 --- /dev/null +++ b/src/Application/Handler/PageHandler.php @@ -0,0 +1,81 @@ +getQueryParams()['format'] ?? null) === 'rss') { + return new XmlResponse( + $this->template->render( + $name . '-rss', + [ + ...$data, + 'layout' => false, + ], + ), + headers: ['content-type' => 'application/rss+xml; charset=utf-8'], + ); + + } + + return new HtmlResponse($this->template->render($name, $data)); + } + + protected function js(string $name, array $data = []): ResponseInterface + { + return new TextResponse( + $this->template->render( + $name, + [ + ...$data, + 'layout' => false, + ], + ), + headers: ['content-type' => 'application/javascript; charset=utf-8'], + ); + } + + protected function error(string $message, int $status = 400): ResponseInterface + { + return new HtmlResponse($this->template->render('error::error', [ + 'status' => $status, + 'message' => $message, + + ])); + } + + protected function flashMessages(ServerRequestInterface $request): FlashMessagesInterface + { + return $request->getAttribute(FlashMessageMiddleware::FLASH_ATTRIBUTE); + } +} diff --git a/src/Application/Handler/PageHandlerFactory.php b/src/Application/Handler/PageHandlerFactory.php new file mode 100644 index 0000000..2780a02 --- /dev/null +++ b/src/Application/Handler/PageHandlerFactory.php @@ -0,0 +1,44 @@ + $requestedName + * + * @return T + */ + public function __invoke(ContainerInterface $container, $requestedName, ?array $options = null): PageHandler + { + $router = $container->get(RouterInterface::class); + $template = $container->get(TemplateRendererInterface::class); + $helpers = $container->get(HelperPluginManager::class); + + return new $requestedName( + $router, + $template, + $helpers->get(ServerUrlHelper::class), + $helpers->get(UrlHelper::class), + $helpers->get(StatusLinks::class), + ); + } +} diff --git a/src/Application/Handler/Status/GraphHandler.php b/src/Application/Handler/Status/GraphHandler.php new file mode 100644 index 0000000..98a9c58 --- /dev/null +++ b/src/Application/Handler/Status/GraphHandler.php @@ -0,0 +1,58 @@ +getQueryParams()['percent'] ?? false); + /** @var RouteResult $routeResult */ + $routeResult = $request->getAttribute(RouteResult::class); + $id = $routeResult->getMatchedParams()['user'] ?? null; + + $user = _em()->getRepository(User::class)->findOneById($id); + $data = _em()->getRepository(Status::class)->getGraph($user, $percent); + $chart = [ + 'chart' => [ + 'zoomType' => 'x', + ], + 'title' => [ + 'text' => '', + ], + 'xAxis' => [ + 'type' => 'datetime', + ], + 'yAxis' => [ + 'title' => [ + 'text' => 'Movies', + ], + 'min' => 0, + ], + 'series' => $data, + ]; + + if ($percent) { + $chart['chart']['type'] = 'area'; + $chart['yAxis']['title']['text'] = $chart['yAxis']['title']['text'] . ' [%]'; + $chart['plotOptions'] = [ + 'area' => [ + 'stacking' => 'percent', + 'marker' => [ + 'enabled' => false, + ], + ], + ]; + } + + return new JsonResponse($chart); + } +} diff --git a/src/Application/Handler/Status/IndexHandler.php b/src/Application/Handler/Status/IndexHandler.php new file mode 100644 index 0000000..6e70486 --- /dev/null +++ b/src/Application/Handler/Status/IndexHandler.php @@ -0,0 +1,56 @@ +getAttribute(RouteResult::class); + $idMovie = Movie::extractId($routeResult->getMatchedParams()['movie'] ?? null); + + if (!$idMovie) { + return $this->error('No valid movie specified.'); + } + + // If new rating is specified, and we are logged in, save it and create movie if needed + $rating = $routeResult->getMatchedParams()['rating'] ?? null; + if ($rating !== null && User::getCurrent()) { + $movie = _em()->getRepository(Movie::class)->getOrCreate((int) $idMovie); + + $status = $movie->setStatus(User::getCurrent(), Rating::from((int) $rating)); + _em()->flush(); + } else { + $status = _em()->getRepository(Status::class)->getOneByMovieAndUser($idMovie, User::getCurrent()); + } + + $jsonCallback = $request->getQueryParams()['jsoncallback'] ?? null; + if ($jsonCallback) { + return new JsonCallbackResponse( + $jsonCallback, + [ + 'status' => ($this->statusLinks)($status), + 'id' => $status->getUniqueId(), + ], + ); + } + + $data = [ + 'status' => $status, + ]; + + return new HtmlResponse($this->template->render('app::status/index', $data)); + } +} diff --git a/src/Application/Handler/Status/ListHandler.php b/src/Application/Handler/Status/ListHandler.php new file mode 100644 index 0000000..5dc13ca --- /dev/null +++ b/src/Application/Handler/Status/ListHandler.php @@ -0,0 +1,47 @@ +getAttribute(RouteResult::class); + + $idMovies = []; + foreach (explode(',', $routeResult->getMatchedParams()['movies'] ?? '') as $idMovie) { + $idMovie = Movie::extractId($idMovie); + if ($idMovie) { + $idMovies[] = $idMovie; + } + } + + $statuses = _em()->getRepository(Status::class)->getAllByMoviesAndUser($idMovies, User::getCurrent()); + + $json = []; + foreach ($statuses as $s) { + $html = ($this->statusLinks)($s); + $json[$s->getUniqueId()] = $html; + } + + $jsonCallback = $request->getQueryParams()['jsoncallback'] ?? 'jsonp'; + + return new JsonCallbackResponse( + $jsonCallback, + [ + 'status' => $json, + ], + ); + } +} diff --git a/src/Application/Handler/User/IndexHandler.php b/src/Application/Handler/User/IndexHandler.php new file mode 100644 index 0000000..7eb7f3f --- /dev/null +++ b/src/Application/Handler/User/IndexHandler.php @@ -0,0 +1,21 @@ + _em()->getRepository(User::class)->getAll(), + ]; + + return new HtmlResponse($this->template->render('app::user/index', $data)); + } +} diff --git a/src/Application/Handler/User/LoginHandler.php b/src/Application/Handler/User/LoginHandler.php new file mode 100644 index 0000000..fd3386d --- /dev/null +++ b/src/Application/Handler/User/LoginHandler.php @@ -0,0 +1,67 @@ +getMethod() === 'POST') { + + $form->setData($request->getParsedBody()); + if ($form->isValid()) { + $values = $form->getData(); + + /** @var null|User $user */ + $user = _em()->getRepository(User::class)->getByEmailAndPassword($values['email'], $values['password']); + if ($user) { + User::setCurrent($user); + /** @var \Mezzio\Session\SessionInterface $session */ + $session = $request->getAttribute(SessionMiddleware::SESSION_ATTRIBUTE); + $session->set('user', $user->id); + + $this->flashMessages($request)->flash('notice', _tr('Logged in.')); + + // If we have a valid referer to one page of ours (except login or logout), redirect to it + $referrer = $values['referrer'] ?? ''; + $loginUrl = ($this->serverUrl)(($this->url)('user.login')); + $logoutUrl = ($this->serverUrl)(($this->url)('user.logout')); + if (str_starts_with($referrer, ($this->serverUrl)('/')) + && !str_starts_with($referrer, $loginUrl) + && !str_starts_with($referrer, $logoutUrl)) { + + return new RedirectResponse($referrer); + } + + return new RedirectResponse('/movie'); + } + + $this->flashMessages($request)->flash('error', _tr('Login failed.')); + $currentUrl = (string) $request->getUri(); + + return new RedirectResponse($currentUrl); + } + } else { + $form->setData([ + 'referrer' => $request->getServerParams()['HTTP_REFERER'] ?? '', + ]); + } + + $data = [ + 'form' => $form, + ]; + + return new HtmlResponse($this->template->render('app::user/login', $data)); + } +} diff --git a/src/Application/Handler/User/LogoutHandler.php b/src/Application/Handler/User/LogoutHandler.php new file mode 100644 index 0000000..4991cc0 --- /dev/null +++ b/src/Application/Handler/User/LogoutHandler.php @@ -0,0 +1,24 @@ +getAttribute(SessionMiddleware::SESSION_ATTRIBUTE); + $session->clear(); + + User::setCurrent(null); + + return new HtmlResponse($this->template->render('app::user/logout')); + } +} diff --git a/src/Application/Handler/User/NewHandler.php b/src/Application/Handler/User/NewHandler.php new file mode 100644 index 0000000..9c0dc47 --- /dev/null +++ b/src/Application/Handler/User/NewHandler.php @@ -0,0 +1,43 @@ +getAttribute(SessionMiddleware::SESSION_ATTRIBUTE); + + $form = new \Application\Form\User(); + + if ($request->getMethod() === 'POST') { + $form->setData($request->getParsedBody()); + if ($form->isValid()) { + $values = $form->getData(); + $user = _em()->getRepository(User::class)->insertUser($values); + User::setCurrent($user); + + $session->set('user', $user->id); + + $this->flashMessages($request)->flash('notice', _tr('Subscription complete.')); + + return new RedirectResponse(($this->url)('movie.index')); + } + } + + $data = [ + 'form' => $form, + ]; + + return new HtmlResponse($this->template->render('app::user/login', $data)); + } +} diff --git a/src/Application/Handler/User/ViewHandler.php b/src/Application/Handler/User/ViewHandler.php new file mode 100644 index 0000000..89ca63e --- /dev/null +++ b/src/Application/Handler/User/ViewHandler.php @@ -0,0 +1,41 @@ +getAttribute(RouteResult::class); + $id = $routeResult->getMatchedParams()['id'] ?? null; + + $user = null; + if ($id) { + $user = _em()->getRepository(User::class)->findOneById($id); + } + + if (!$user) { + return $this->error(_tr('User not found')); + } + + $activity = PaginatorFactory::create( + $request, + _em()->getRepository(Status::class)->getActivityQuery($user), + ); + $data = [ + 'user' => $user, + 'userActivity' => $activity, + ]; + + return $this->htmlOrRss($request, 'app::user/view', $data); + } +} diff --git a/src/Application/Middleware/AuthenticationFactory.php b/src/Application/Middleware/AuthenticationFactory.php new file mode 100644 index 0000000..0810b8f --- /dev/null +++ b/src/Application/Middleware/AuthenticationFactory.php @@ -0,0 +1,19 @@ +get(EntityManager::class); + + return new AuthenticationMiddleware($entityManager->getRepository(User::class)); + } +} diff --git a/src/Application/Middleware/AuthenticationMiddleware.php b/src/Application/Middleware/AuthenticationMiddleware.php new file mode 100644 index 0000000..8fa0903 --- /dev/null +++ b/src/Application/Middleware/AuthenticationMiddleware.php @@ -0,0 +1,46 @@ +getAttribute(SessionMiddleware::SESSION_ATTRIBUTE); + + if ($session->has('user')) { + $user = $this->userRepository->findOneById($session->get('user')); + + if ($user) { + User::setCurrent($user); + } + + // If we were supposed to be logged in, but the user is not available anymore, that means the user + // was forcibly logged out (likely deleted), so we clear his entire session + if (!User::getCurrent()) { + $session->clear(); + } + } + + return $handler->handle($request); + } +} diff --git a/src/Application/Middleware/DetectBrowserLocaleFactory.php b/src/Application/Middleware/DetectBrowserLocaleFactory.php new file mode 100644 index 0000000..548d2f2 --- /dev/null +++ b/src/Application/Middleware/DetectBrowserLocaleFactory.php @@ -0,0 +1,18 @@ +get(TranslatorInterface::class); + + return new DetectBrowserLocaleMiddleware($translator); + } +} diff --git a/src/Application/Middleware/DetectBrowserLocaleMiddleware.php b/src/Application/Middleware/DetectBrowserLocaleMiddleware.php new file mode 100644 index 0000000..d70f3d2 --- /dev/null +++ b/src/Application/Middleware/DetectBrowserLocaleMiddleware.php @@ -0,0 +1,64 @@ +getAttribute(SessionMiddleware::SESSION_ATTRIBUTE); + + $requested = $request->getQueryParams()['lang'] ?? null; + + $locale = $this->pick( + $requested, // Language switch by user + $session->get('lang'), // Memorized choice + Locale::acceptFromHttp($request->getHeaderLine('accept-language')), // If nothing else, read browser configuration + ); + + if ($requested === $locale) { + $session->set('lang', $locale); + } + + $this->translator->setLocale($locale); + Locale::setDefault($locale); + + return $handler->handle($request); + } + + private function pick(null|false|string ...$choices): string + { + $supported = ['en', 'fr', 'ko']; + + foreach ($choices as $choice) { + if (!$choice) { + continue; + } + + $valid = Locale::lookup($supported, $choice); + if ($valid) { + return $valid; + } + } + + return reset($supported); + } +} diff --git a/src/Application/Migration/Version20260412140844.php b/src/Application/Migration/Version20260412140844.php new file mode 100644 index 0000000..e4c5b9d --- /dev/null +++ b/src/Application/Migration/Version20260412140844.php @@ -0,0 +1,31 @@ +addSql('DROP TABLE setting'); + $this->addSql('ALTER TABLE movie CHANGE dateUpdate date_update DATETIME NOT NULL, CHANGE searchCount search_count SMALLINT UNSIGNED DEFAULT 0 NOT NULL, CHANGE id id INT AUTO_INCREMENT NOT NULL, CHANGE title title VARCHAR(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, CHANGE source source VARCHAR(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, ADD start_year SMALLINT UNSIGNED DEFAULT NULL, CHANGE dateSearch date_search DATETIME DEFAULT NULL'); + $this->addSql('ALTER TABLE status DROP FOREIGN KEY `status_ibfk_1`'); + $this->addSql('UPDATE movie SET start_year = YEAR(dateRelease);'); + $this->addSql('ALTER TABLE movie DROP dateRelease;'); + $this->addSql('ALTER TABLE status DROP FOREIGN KEY `status_ibfk_2`'); + $this->addSql('DROP INDEX idMovie ON status'); + $this->addSql('DROP INDEX idUser ON status'); + $this->addSql('ALTER TABLE status CHANGE dateUpdate date_update DATETIME NOT NULL, CHANGE isLatest is_latest TINYINT DEFAULT 0 NOT NULL, CHANGE idMovie movie_id INT NOT NULL, CHANGE idUser user_id INT NOT NULL, CHANGE rating rating INT UNSIGNED DEFAULT 0 NOT NULL'); + $this->addSql('ALTER TABLE status ADD CONSTRAINT FK_7B00651C8F93B6FC FOREIGN KEY (movie_id) REFERENCES movie (id) ON DELETE CASCADE'); + $this->addSql('ALTER TABLE status ADD CONSTRAINT FK_7B00651CA76ED395 FOREIGN KEY (user_id) REFERENCES user (id) ON DELETE CASCADE'); + $this->addSql('CREATE INDEX IDX_7B00651C8F93B6FC ON status (movie_id)'); + $this->addSql('CREATE INDEX IDX_7B00651CA76ED395 ON status (user_id)'); + $this->addSql('ALTER TABLE user CHANGE dateUpdate date_update DATETIME NOT NULL, CHANGE nickname nickname VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, CHANGE email email VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, CHANGE password password VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL'); + $this->addSql('ALTER TABLE user RENAME INDEX nickname TO UNIQ_8D93D649A188FE64'); + $this->addSql('ALTER TABLE user RENAME INDEX email TO UNIQ_8D93D649E7927C74'); + } +} diff --git a/src/Application/Model/AbstractModel.php b/src/Application/Model/AbstractModel.php new file mode 100644 index 0000000..cd3f974 --- /dev/null +++ b/src/Application/Model/AbstractModel.php @@ -0,0 +1,34 @@ +dateUpdate = Chronos::now(); + } + + /** + * Automatically called by Doctrine when the object is created/updated. + */ + #[ORM\PrePersist] + #[ORM\PreUpdate] + public function timestamp(): void + { + $this->dateUpdate = Chronos::now(); + } +} diff --git a/src/Application/Model/Movie.php b/src/Application/Model/Movie.php new file mode 100644 index 0000000..5f75048 --- /dev/null +++ b/src/Application/Model/Movie.php @@ -0,0 +1,186 @@ +title) { + $this->fetchData(); + } + + return $this->title; + } + } + + #[ORM\Column(type: 'smallint', nullable: true, options: ['unsigned' => true])] + public ?int $startYear; + + #[ORM\Column(type: 'datetime', nullable: true)] + public ?Chronos $dateSearch = null; + + #[ORM\Column(type: 'smallint', options: ['unsigned' => true, 'default' => 0])] + public int $searchCount = 0; + + #[ORM\Column(type: 'smallint', options: ['unsigned' => true, 'default' => 0])] + public int $identity = 0; + + #[ORM\Column(type: 'smallint', options: ['unsigned' => true, 'default' => 0])] + public int $quality = 0; + + #[ORM\Column(type: 'smallint', options: ['unsigned' => true, 'default' => 0])] + public int $score = 0; + + #[ORM\Column(type: 'string', length: 1024, nullable: true)] + public ?string $source = null; + + /** + * Fetch data from IMDb and store in database (possibly overwriting). + */ + public function fetchData(): void + { + $couldNotFetch = '[title not available, could not fetch from IMDb]'; + + $data = $this->fetchJson(); + $title = @$data['primaryTitle']; + + // Extract title + if ($title) { + $this->title = $title; + } else { + $this->title = $couldNotFetch; + + return; // If there is not even title give up everything + } + + // Extract release date + $startYear = @$data['startYear']; + if ($startYear) { + $this->startYear = $startYear; + $this->title .= " ($startYear)"; + } else { + $this->startYear = null; + } + + $this->dateUpdate = Chronos::now(); + _em()->flush(); + } + + private function fetchJson(): ?array + { + $url = 'https://api.imdbapi.dev/titles/tt' . self::paddedId($this->id); + $ch = curl_init($url); + curl_setopt($ch, CURLOPT_HTTPHEADER, ['Accept-Language: en-US,en;q=0.8']); + curl_setopt($ch, CURLOPT_HEADER, false); + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + + $file = curl_exec($ch); + curl_close($ch); + if (!$file) { + return null; + } + + return json_decode($file, JSON_OBJECT_AS_ARRAY | JSON_THROW_ON_ERROR); + } + + /** + * Sets the ID for the movie from any string containing a valid ID. + */ + public function setId(int $id): void + { + $extractedId = self::extractId($id); + if (!$extractedId) { + throw new Exception(sprintf('Invalid Id for movie. Given "%1$s", extracted "%2$s"', $id, $extractedId)); + } + + $this->id = (int) $extractedId; + } + + /** + * Returns the IMDb url for the movie. + */ + public function getImdbUrl(): string + { + return 'https://www.imdb.com/title/tt' . self::paddedId($this->id) . '/'; + } + + /** + * Returns the status for this movie and the specified user. + */ + public function getStatus(?User $user = null): Status + { + return _em()->getRepository(Status::class)->getOneByMovieAndUser($this->id, $user); + } + + /** + * Set the status for the specified user. + */ + public function setStatus(User $user, Rating $rating): Status + { + $status = _em()->getRepository(Status::class)->set($this, $user, $rating); + + return $status; + } + + /** + * Set the source for the movie if any. In any case record the search date and count. + */ + public function setSource(array|false $source): void + { + $this->dateSearch = Chronos::now(); + ++$this->searchCount; + if ($source && @$source['score']) { + $this->identity = $source['identity']; + $this->quality = $source['quality']; + $this->score = $source['score']; + $this->source = $source['link']; + } + } +} diff --git a/src/Application/Model/Status.php b/src/Application/Model/Status.php new file mode 100644 index 0000000..8a4819f --- /dev/null +++ b/src/Application/Model/Status.php @@ -0,0 +1,41 @@ + true, 'default' => Rating::Nothing])] + public Rating $rating = Rating::Nothing; + + #[ORM\Column(type: 'boolean', options: ['default' => false])] + public bool $isLatest = false; + + #[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')] + #[ORM\ManyToOne(targetEntity: Movie::class)] + public Movie $movie; + + #[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')] + #[ORM\ManyToOne(targetEntity: User::class)] + public User $user; + + /** + * Returns the unique ID for this status to be used in HTML. + */ + public function getUniqueId(): string + { + return Movie::paddedId($this->movie->id) . '_' . (isset($this->user) ? $this->user->id : ''); + } + + public function getName(): string + { + return $this->rating->name(); + } +} diff --git a/src/Application/Model/User.php b/src/Application/Model/User.php new file mode 100644 index 0000000..e184665 --- /dev/null +++ b/src/Application/Model/User.php @@ -0,0 +1,51 @@ +getRepository(Status::class)->getStatistics($this); + } +} diff --git a/src/Application/Paginator/PaginatorFactory.php b/src/Application/Paginator/PaginatorFactory.php new file mode 100644 index 0000000..2bf0fea --- /dev/null +++ b/src/Application/Paginator/PaginatorFactory.php @@ -0,0 +1,53 @@ +getAttribute(SessionInterface::class); + $perPage = $session->get('perPage', 25); + + $userPerPage = $request->getQueryParams()['perPage'] ?? null; + if ($userPerPage) { + if ($perPage !== $userPerPage) { + $session->set('perPage', $perPage); + } + + $perPage = $userPerPage; + } + + // If we export to csv or rss, override perPage parameter + $format = $request->getQueryParams()['format'] ?? null; + switch ($format) { + case 'csv': + $perPage = -1; + + break; + case 'rss': + $perPage = 200; + + break; + } + + // Defines the paginator + $adapter = new QueryAdapter($query); + $paginator = new Paginator($adapter); + + $paginator->setCurrentPageNumber($request->getQueryParams()['page'] ?? 0); + $paginator->setItemCountPerPage($perPage); + + return $paginator; + } +} diff --git a/src/Application/Paginator/QueryAdapter.php b/src/Application/Paginator/QueryAdapter.php new file mode 100644 index 0000000..63e9a38 --- /dev/null +++ b/src/Application/Paginator/QueryAdapter.php @@ -0,0 +1,20 @@ +getQuery()->setFirstResult($offset); + $this->getQuery()->setMaxResults($itemCountPerPage); + + return $this->getQuery()->getResult(); + } +} diff --git a/src/Application/Repository/AbstractRepository.php b/src/Application/Repository/AbstractRepository.php new file mode 100644 index 0000000..0c67827 --- /dev/null +++ b/src/Application/Repository/AbstractRepository.php @@ -0,0 +1,14 @@ + + * + * @method null|T findOneById(integer $id) + */ +abstract class AbstractRepository extends EntityRepository {} diff --git a/src/Application/Repository/MovieRepository.php b/src/Application/Repository/MovieRepository.php new file mode 100644 index 0000000..4e3af5e --- /dev/null +++ b/src/Application/Repository/MovieRepository.php @@ -0,0 +1,181 @@ + + */ +class MovieRepository extends AbstractRepository +{ + public function getOrCreate(int $id): Movie + { + $movie = $this->findOneById($id); + + if (!$movie) { + $movie = new Movie(); + $movie->setId($id); + + // Exceptionally allow manually set ID + $metadata = _em()->getClassMetaData(Movie::class); + $metadata->setIdGenerator(new AssignedGenerator()); + $metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_NONE); + + _em()->persist($movie); + $movie->title = ''; + _em()->flush(); + } + + return $movie; + } + + /** + * Returns movies for search. + * + * @return Movie[] + */ + public function getAllForSearch(): array + { + $qb = $this->createQueryBuilder('movie') + ->innerJoin(Status::class, 'status', Join::WITH, 'movie.id = status.movie') + ->andWhere('status.isLatest = 1') + ->andWhere('status.rating = :rating') + ->andWhere('movie.source IS NULL') + ->andWhere("movie.dateSearch IS NULL OR movie.dateSearch < DATE_SUB(NOW(), movie.searchCount, 'MONTH')") // Search for same movie with an incrementally longer interval (1 month, 2 month, 3 month, etc.) + ->andWhere('movie.startYear IS NOT NULL') // Movie must be released ... + ->andWhere("movie.startYear < DATE_SUB(NOW(), 1, 'MONTH')") // ...at least released one month ago, or longer + ->addGroupBy('movie.id') + ->addOrderBy('COUNT(movie.id)', 'DESC') // First, order by popularity, so get the most needed first + ->addOrderBy('RAND()', 'ASC') // Then, randomize a little bit so we don't always look for the same movies + ->setParameter('rating', Rating::Need) + ->setMaxResults(5); + + return $qb->getQuery()->getResult(); + } + + /** + * Returns movies for data fetching. + * + * @return Movie[] + */ + public function getAllForFetching(?int $limit = null): array + { + $qb = $this->createQueryBuilder('movie') + ->andWhere("movie.dateUpdate IS NULL OR movie.dateUpdate < DATE_SUB(NOW(), 1, 'MONTH')") // Don't fetch data for movies, more than once a month + ->addOrderBy('RAND()'); // Randomize order, so we don't watch only old movies + + if ($limit) { + $qb->setMaxResults($limit); + } + + return $qb->getQuery()->getResult(); + } + + /** + * Returns a query filtered according to parameters. This query may be used with paginator. + */ + public function getFilteredQuery(array $filters, Sorting $sorting): Query + { + $select = $this->createQueryBuilder('movie'); + + $i = 0; + $maxDate = ''; + $filtersDone = []; + foreach ($filters as $key => $filter) { + if (!($filter['user'] ?? false)) { + continue; + } + + $filterUniqueId = $filter['user']; + if (!preg_match('/^filter\d+$/', $key) || in_array($filterUniqueId, $filtersDone, true)) { + continue; + } + + $filtersDone[] = $filterUniqueId; + $showNoRating = in_array(0, $filter['status'], true); + $alias = 'status' . $i++; + $allowNull = ' OR ' . $alias . '.user IS NULL'; + if ($filter['condition'] === 'is') { + $condition = ''; + $allowNull = $showNoRating ? $allowNull : ''; + } else { + $condition = 'NOT '; + $allowNull = !$showNoRating ? $allowNull : ''; + } + + $paramUser = 'user' . $i; + $select->leftJoin(Status::class, $alias, Join::WITH, '(movie.id = ' . $alias . '.movie AND ' . $alias . ".user = :$paramUser)" . $allowNull); + $select->setParameter($paramUser, $filter['user']); + + $select->andWhere($alias . '.isLatest = 1 OR ' . $alias . '.isLatest IS NULL'); + + // Filter by status + $paramRating = 'rating' . $i; + $select->andWhere($alias . '.rating ' . $condition . "IN (:$paramRating)" . $allowNull); + $select->setParameter($paramRating, $filter['status']); + + // Filter by title + $title = $filter['title'] ?? null; + if ($title) { + $id = Movie::extractId($title) ?? -1; + $titles = explode(' ', mb_trim($title)); + foreach ($titles as $y => $part) { + if (!$part) { + continue; + } + + $paramSearch = 'search' . $i . '_' . $y; + $paramId = 'id' . $i . '_' . $y; + $select->andWhere("movie.title LIKE :$paramSearch OR movie.id = :$paramId"); + $select->setParameter($paramSearch, '%' . $part . '%'); + $select->setParameter($paramId, $id); + } + } + + // Filter by presence of source + if ($filter['withSource'] ?? null) { + $select->andWhere('movie.source IS NOT NULL'); + } + + if ($maxDate) { + $maxDate = "IF($alias.dateUpdate IS NULL OR $alias.dateUpdate < $maxDate, $maxDate, $alias.dateUpdate)"; + } else { + $maxDate = "$alias.dateUpdate"; + } + } + + $key = $sorting->validKey; + if ($key === 'date') { + $key = $maxDate ?: 'movie.title'; + } + + $select->addOrderBy($key, $sorting->validOrder); + + return $select->getQuery(); + } + + /** + * Delete obsolete sources for all movies. + * An obsolete source is either a source older than 3 months, or + * a source which is not needed anymore (nobody need the movie anymore). + */ + public function deleteObsoleteSources(): void + { + $db = $this->getEntityManager()->getConnection(); + $update = 'UPDATE movie SET date_update = date_update, date_search = NULL, search_count = 0, identity = 0, quality = 0, score = 0, source = NULL'; + + // Delete sources older than 6 months + $db->executeStatement($update . ' WHERE `source` IS NOT NULL AND `date_search` < DATE_SUB(NOW(), INTERVAL 6 MONTH)'); + + // Delete non-needed sources + $db->executeStatement($update . ' WHERE `movie`.`id` NOT IN (SELECT `status`.`movie_id` FROM `status` WHERE `rating` = :rating AND `is_latest`)', ['rating' => Rating::Need->value]); + } +} diff --git a/src/Application/Repository/StatusRepository.php b/src/Application/Repository/StatusRepository.php new file mode 100644 index 0000000..3950ee0 --- /dev/null +++ b/src/Application/Repository/StatusRepository.php @@ -0,0 +1,263 @@ + + */ +class StatusRepository extends AbstractRepository +{ + /** + * Define the status for a movie-user tuple. If an existing status exists and + * is very recent, it will be updated, otherwise a new status will be created. + * + * IMPORTANT: This is the only allowed way to modify status. + */ + public function set(Movie $movie, User $user, Rating $rating): Status + { + $connection = $this->getEntityManager()->getConnection(); + $connection->beginTransaction(); + + // Find out if a very recent status exists to be replaced, so user can change their mind "quickly" + $timeLimit = Chronos::now()->subMinutes(5)->toDateTimeString(); + $qb = $this->createQueryBuilder('status') + ->andWhere('status.user = :user') + ->andWhere('status.movie = :movie') + ->andWhere('status.dateUpdate > :timeLimit') + ->setParameter('user', $user->id) + ->setParameter('movie', $movie->id) + ->setParameter('timeLimit', $timeLimit); + + $status = $qb->getQuery()->getOneOrNullResult(); + + // Otherwise, create a brand new one and set all existing one as "old" + if (!$status) { + $status = new Status(); + $this->getEntityManager()->persist($status); + $status->user = $user; + $status->movie = $movie; + $status->isLatest = true; + + // Here we must set dateUpdate to itself to avoid auto-update of the timestamp field by MySql + $connection->executeStatement( + 'UPDATE `status` SET is_latest = 0, date_update = date_update WHERE user_id = :user AND movie_id = :movie', + [ + 'user' => $user->id, + 'movie' => $movie->id, + ], + ); + } + + $status->rating = $rating; + + _em()->flush(); + + $connection->commit(); + + return $status; + } + + /** + * Find a status by its user and movie. If not found it will be created (but not saved). + */ + public function getOneByMovieAndUser(int|string $id, ?User $user): Status + { + $statuses = $this->getAllByMoviesAndUser([$id], $user); + + return reset($statuses); + } + + /** + * Returns an array of Status containing all statuses for specified ids + * (if they don't exist in database, they will be created with default values but not saved). + * + * @param (int|string)[] $idMovies + * + * @return Status[] + */ + public function getAllByMoviesAndUser(array $idMovies, ?User $user): array + { + $statuses = []; + if (!count($idMovies)) { + return $statuses; + } + + // Do not hit database if we know there won't be any result anyway + if ($user) { + $qb = $this->createQueryBuilder('status') + ->andWhere('status.user = :user') + ->setParameter('user', $user) + ->andWhere('status.movie IN (:movies)') + ->setParameter('movies', $idMovies) + ->andWhere('status.isLatest = 1'); + + /** @var Status[] $records */ + $records = $qb->getQuery()->getResult(); + + foreach ($records as $record) { + $statuses[$record->movie->id] = $record; + } + } + + // Fill non-existing statuses in databases + foreach ($idMovies as $id) { + if (!array_key_exists($id, $statuses)) { + $status = new Status(); + if ($user) { + $status->user = $user; + } + $status->movie = $this->getEntityManager()->getReference(Movie::class, $id); + $statuses[$id] = $status; + } + } + + return $statuses; + } + + /** + * Build statistic for the given user. + * + * @return array statistics + */ + public function getStatistics(User $user): array + { + $sql = <<getEntityManager()->getConnection()->fetchAllAssociative($sql, ['user' => $user->id]); + + // Set all count to 0 + $result = [ + 'total' => 0, + 'rated' => 0, + Rating::Nothing->value => 0, + ]; + foreach (Rating::possibleChoices() as $rating) { + $result[$rating->value] = 0; + } + + // Fetch real counts + foreach ($records as $row) { + $result[$row['rating']] = $row['count']; + if ((int) $row['rating'] !== Rating::Nothing->value) { + $result['rated'] += $row['count']; + } + $result['total'] += $row['count']; + } + + return $result; + } + + /** + * Build statistic for the given user. + * + * @return array statistics + */ + public function getGraph(?User $user, bool $percent): array + { + $qb = $this->getEntityManager()->getConnection()->createQueryBuilder() + ->select('status.rating, status.user_id, status.movie_id, status.date_update') + ->from('status') + ->addOrderBy('status.date_update'); + + if ($user) { + $qb->andWhere('status.user_id = :user') + ->setParameter('user', $user->id); + } + + $records = $qb->fetchAllAssociative(); + + // Set all count to 0 + $cumulatedStatuses = [Rating::Nothing->value => 0]; + $graphData = []; + foreach (Rating::possibleChoices() as $rating) { + $cumulatedStatuses[$rating->value] = 0; + $graphData[$rating->value] = []; + } + + // Fetch real counts + $lastStatuses = []; + foreach ($records as $row) { + $rating = $row['rating']; + $userId = $row['user_id']; + $movieId = $row['movie_id']; + + // Add new status + ++$cumulatedStatuses[$rating]; + $changed = [$rating]; + + // Substract old status + if (isset($lastStatuses[$userId][$movieId])) { + --$cumulatedStatuses[$lastStatuses[$userId][$movieId]]; + $changed[] = $lastStatuses[$userId][$movieId]; + } + $lastStatuses[$userId][$movieId] = $rating; + + $time = new Chronos($row['date_update']); + $time->setTimezone(new DateTimeZone('GMT')); + $epoch = (int) $time->format('U') * 1000; + + // If we are in percent mode, we need all status for each timestamp + if ($percent) { + $changed = Rating::possibleValues(); + } + + // Keep for the graph only the changed values (and overwrite previous value if it happened at exactly the same time) + foreach ($changed as $val) { + $graphData[$val][$epoch] = [ + $epoch, + $cumulatedStatuses[$val], + ]; + } + } + + // Format everything in a more output friendly way + $result = []; + foreach (Rating::possibleChoices() as $rating) { + $result[] = [ + 'name' => $rating->name(), + 'data' => array_values($graphData[$rating->value]), + ]; + } + + return $result; + } + + /** + * Returns the query to get activity for either the whole system, or a specific user, or a specific movie. + */ + public function getActivityQuery(null|Movie|User $item): Query + { + $qb = $this->createQueryBuilder('status') + ->addSelect('movie') + ->addSelect('user') + ->innerJoin('status.movie', 'movie', Join::WITH) + ->innerJoin('status.user', 'user', Join::WITH) + ->addOrderBy('status.dateUpdate', 'DESC') + ->addOrderBy('status.id', 'ASC'); + + if ($item instanceof User) { + $qb->andWhere('status.user = :user') + ->setParameter('user', $item); + } elseif ($item instanceof Movie) { + $qb->andWhere('status.movie = :movie') + ->setParameter('movie', $item); + } + + return $qb->getQuery(); + } +} diff --git a/src/Application/Repository/UserRepository.php b/src/Application/Repository/UserRepository.php new file mode 100644 index 0000000..f289295 --- /dev/null +++ b/src/Application/Repository/UserRepository.php @@ -0,0 +1,53 @@ + + */ +class UserRepository extends AbstractRepository +{ + /** + * Create and save a new user. + * + * @param array{nickname: string, email: string, password: string} $values + */ + public function insertUser(array $values): User + { + $user = new User(); + $this->getEntityManager()->persist($user); + + $user->nickname = $values['nickname']; + $user->email = $values['email']; + $user->password = sha1($values['password']); + + $this->getEntityManager()->flush(); + + return $user; + } + + /** + * Finds a user by its email and password (not hashed). + */ + public function getByEmailAndPassword(string $email, string $password): ?User + { + return $this->findOneBy([ + 'email' => $email, + 'password' => sha1($password), + ]); + } + + /** + * Finds all users. + * + * @return User[] + */ + public function getAll(): array + { + return $this->createQueryBuilder('user') + ->addOrderBy('user.nickname') + ->getQuery()->getResult(); + } +} diff --git a/src/Application/Response/JsonCallbackResponse.php b/src/Application/Response/JsonCallbackResponse.php new file mode 100644 index 0000000..049da41 --- /dev/null +++ b/src/Application/Response/JsonCallbackResponse.php @@ -0,0 +1,38 @@ + $jsonCallback, + ...$data, + ], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR) + . ')'; + + $body = $this->createBody($body); + + parent::__construct($body, $status, ['content-type' => 'application/javascript; charset=utf-8']); + } + + private function createBody(string $json): Stream + { + $body = new Stream('php://temp', 'wb+'); + $body->write($json); + $body->rewind(); + + return $body; + } +} diff --git a/src/Application/Service/Logger.php b/src/Application/Service/Logger.php new file mode 100644 index 0000000..3c47393 --- /dev/null +++ b/src/Application/Service/Logger.php @@ -0,0 +1,59 @@ +file = fopen('logs/all.log', 'a+b'); + if (!$this->file) { + throw new Exception('Cannot open log file'); + } + } + + public function __destruct() + { + fclose($this->file); + $this->file = false; + } + + public function log($level, Stringable|string $message, array $context = []): void + { + if (!$this->file) { + return; + } + + foreach ($context as $k => $v) { + $placeholder = '{' . $k . '}'; + if (!str_contains($message, $placeholder)) { + continue; + } + + $message = str_replace($placeholder, $this->serialize($v), $message); + unset($context[$k]); + } + + $line = '[' . Chronos::now()->toIso8601String() . '] ' . $level . ': ' . $message . ($context ? ' ' . $this->serialize($context) : ''); + fwrite($this->file, $line . PHP_EOL); + } + + private function serialize(mixed $v): string + { + if (is_string($v)) { + return $v; + } + + return json_encode($v); + } +} diff --git a/src/Application/Service/Processor.php b/src/Application/Service/Processor.php new file mode 100644 index 0000000..5c31456 --- /dev/null +++ b/src/Application/Service/Processor.php @@ -0,0 +1,78 @@ +getImdbUrl() . "\t"; + flush(); + + $callback($movie); + + echo "\n"; + + sleep($seconds); + } + + return $total; + } + + /** + * Search source for movies with release date. + */ + public function searchSource(): void + { + $movies = _em()->getRepository(Movie::class)->getAllForSearch(); + $searcher = function (Movie $movie): void { + $searchEngine = new SearchEngine(); + + $movie->fetchData(); // Refresh movie data to be sure we have latest available title + $title = $movie->title; + $data = $searchEngine->search($title); + $scores = $searchEngine->computeScores($title, $data); + + $best = @reset($scores); + $movie->setSource($best); + _em()->flush(); + + echo $movie->source ?: '[source not found]'; + }; + + // 5 minutes pause between search, not to stress third-party servers + $total = $this->process($searcher, $movies, 5 * 60); + + echo $total . " movie sources updated in database\n"; + } + + /** + * Fetch movie data to update title and release date for those that need it. + * + * @param int $seconds seconds to sleep between each movie + */ + public function fetchMovieData(?int $limit, int $seconds = 0): void + { + $movies = _em()->getRepository(Movie::class)->getAllForFetching($limit); + $fetcher = function (Movie $movie): void { + $movie->fetchData(); + echo $movie->title; + }; + + $total = $this->process($fetcher, $movies, $seconds); + + echo $total . " movies updated in database\n"; + } +} diff --git a/application/mQueue/Service/SearchEngine.php b/src/Application/Service/SearchEngine.php similarity index 83% rename from application/mQueue/Service/SearchEngine.php rename to src/Application/Service/SearchEngine.php index 8775cb7..bc63b2e 100644 --- a/application/mQueue/Service/SearchEngine.php +++ b/src/Application/Service/SearchEngine.php @@ -1,6 +1,6 @@ &1'); - preg_match('/\\d+\\.\\d+\\.\\d+/', $cmd, $matches); - $version = $matches[0]; - if (version_compare($version, '3.0.0', '>=')) { - return 'python ' . APPLICATION_PATH . '/../library/searchengine/nova3/nova2.py'; - } - - return 'python ' . APPLICATION_PATH . '/../library/searchengine/nova/nova2.py'; + return 'python library/searchengine/nova3/nova2.py'; } /** @@ -33,7 +24,7 @@ protected function getNovaCmd() * * @return string the stdout of the command */ - protected function execute($cmd, $timeout) + protected function execute(string $cmd, int $timeout): string { $maximumTime = time() + $timeout; $stdout = null; @@ -42,7 +33,7 @@ protected function execute($cmd, $timeout) $process = proc_open( $cmd, [['pipe', 'r'], ['pipe', 'w'], ['pipe', 'w']], - $pipes + $pipes, ); $status = []; @@ -82,19 +73,15 @@ protected function execute($cmd, $timeout) /** * Parse string content and return an array of unique sources. - * - * @param string $content - * - * @return array */ - public function parse($content) + public function parse(string $content): array { $data = []; $keys = ['link', 'name', 'size', 'seeds', 'leech', 'engine_url', 'page']; $duplicates = []; - foreach (explode(PHP_EOL, trim($content)) as $line) { - $values = explode('|', trim($line)); - if (count($keys) == count($values)) { + foreach (explode(PHP_EOL, mb_trim($content)) as $line) { + $values = explode('|', mb_trim($line)); + if (count($keys) === count($values)) { $source = array_combine($keys, $values); $duplicateKey = $source['name'] . $source['size']; if (!array_key_exists($duplicateKey, $duplicates)) { @@ -109,12 +96,8 @@ public function parse($content) /** * Search for the given title and return an array of sources. - * - * @param string $title - * - * @return array sources */ - public function search($title) + public function search(string $title): array { $cmd = $this->getNovaCmd() . ' all movies ' . escapeshellarg(str_replace(' ', '+', $title)) . ' 2>&1'; echo $cmd . PHP_EOL; @@ -128,12 +111,8 @@ public function search($title) /** * Returns the given name simplified as much as possible. - * - * @param string $name - * - * @return string */ - protected function cleanName($name) + protected function cleanName(string $name): string { // Insert space before uppercase letters $name = preg_replace('/([A-Z])/', ' \1', $name); @@ -156,19 +135,15 @@ protected function cleanName($name) // Keep only a single space character $name = preg_replace('/[[:space:]]+/', ' ', $name); - $name = trim($name); + $name = mb_trim($name); return $name; } /** * Compute scores of all sources according to the title searched. - * - * @param string $title - * - * @return array */ - public function computeScores($title, array $sources) + public function computeScores(string $title, array $sources): array { $rules = [ // @see http://en.wikipedia.org/wiki/Pirated_movie_release_types @@ -179,8 +154,8 @@ public function computeScores($title, array $sources) '/\b(maxspeed|axxo|dimension|fxg)\b/i' => 50, // Well known teams '/\b(swesub|nlt-release)\b/i' => -30, // Avoid teams specialized in foreign versions '/\b(french|fra|truefrench|italian|ita|russian|german)\b/i' => -30, // Avoid dubbed language - '/\b1080p\b/i' => 20, - '/\b720p\b/i' => 30, // Favor 720p instead of 1080p because of the filesize and better "compatibility" for low powered computer + '/\b1080p\b/i' => 30, // Favor 1080p + '/\b720p\b/i' => 20, '/\b(x264|xvid)\b/i' => 20, // Good formats '/\b(uncut|unrated|extended|director\'s cut|director cut)\b/i' => 20, // Director's cut version is supposedly better ]; @@ -240,13 +215,13 @@ public function computeScores($title, array $sources) // Sort by score, then seeds, then leech usort($sources, function ($source, $other) { - if ($other['score'] != $source['score']) { + if ($other['score'] !== $source['score']) { return $other['score'] - $source['score']; } - if ($other['seeds'] != $source['seeds']) { + if ($other['seeds'] !== $source['seeds']) { return $other['seeds'] - $source['seeds']; } - if ($other['leech'] != $source['leech']) { + if ($other['leech'] !== $source['leech']) { return $other['leech'] - $source['leech']; } diff --git a/src/Application/Service/Sorting.php b/src/Application/Service/Sorting.php new file mode 100644 index 0000000..dd34518 --- /dev/null +++ b/src/Application/Service/Sorting.php @@ -0,0 +1,42 @@ +keyParamName = 'sort'; + $this->orderParamName = $this->keyParamName . 'Order'; + $key = $queryParams[$this->keyParamName] ?? ''; + $order = $queryParams[$this->orderParamName] ?? ''; + + $this->selectedKey = $key; + $this->selectedOrder = $order; + + $validKey = $this->selectedKey; + if (!in_array($validKey, $allowedKeys, true)) { + $validKey = reset($allowedKeys); + } + + $this->validKey = $validKey; + $this->validOrder = strcasecmp($order, 'desc') === 0 ? 'DESC' : 'ASC'; + } +} diff --git a/src/Application/Validator/NoRecordExists.php b/src/Application/Validator/NoRecordExists.php new file mode 100644 index 0000000..58efe1a --- /dev/null +++ b/src/Application/Validator/NoRecordExists.php @@ -0,0 +1,55 @@ + Message templates + */ + protected $messageTemplates = [ + self::ERROR_RECORD_FOUND => 'A record matching the input was found', + ]; + + /** + * @param "email"|"nickname" $field + */ + public function __construct( + private readonly string $field, + ) + { + parent::__construct(); + } + + /** + * @param mixed $value + */ + public function isValid($value): bool + { + $valid = true; + $this->setValue($value); + + $result = $this->query($value); + if ($result) { + $valid = false; + $this->error(self::ERROR_RECORD_FOUND); + } + + return $valid; + } + + private function query($value): bool + { + return (bool) _em()->getConnection()->fetchOne("SELECT COUNT(*) FROM user WHERE $this->field = :value", ['value' => $value]); + } +} diff --git a/src/Application/View/Helper/Activity.php b/src/Application/View/Helper/Activity.php new file mode 100644 index 0000000..ce529f9 --- /dev/null +++ b/src/Application/View/Helper/Activity.php @@ -0,0 +1,68 @@ + $activity + * @param string[] $hiddenColumns optionally hidden columns + */ + public function __invoke(Paginator $activity, array $hiddenColumns = []): string + { + $columns = ['date', 'user', 'status', 'movie']; + $columns = array_diff($columns, $hiddenColumns); + + $result = '
    '; + + /** @var Status $status */ + foreach ($activity as $status) { + $user = $status->user; + $movie = $status->movie; + + $result .= '
    '; + + if (in_array('user', $columns, true)) { + $result .= ''; + } + + $result .= '
    '; + + if (in_array('movie', $columns, true)) { + $result .= '
    ' . ($this->movie)($movie) . '
    '; + } + if (in_array('status', $columns, true)) { + $result .= '' . ($this->statusLinks)($status) . ' '; + } + + if (in_array('date', $columns, true)) { + $result .= ''; + } + + $result .= '
    '; + + $result .= '
    '; + } + + if ($activity->count() === 0) { + $result .= '

    ' . _tr('There is no activity to show.') . '

    '; + } + + return $result . '

    '; + } +} diff --git a/src/Application/View/Helper/ActivityFactory.php b/src/Application/View/Helper/ActivityFactory.php new file mode 100644 index 0000000..d7caec7 --- /dev/null +++ b/src/Application/View/Helper/ActivityFactory.php @@ -0,0 +1,25 @@ +get(HelperPluginManager::class); + + return new Activity( + $helpers->get(Gravatar::class), + $helpers->get(Movie::class), + $helpers->get(StatusLinks::class), + $helpers->get(UrlHelper::class), + ); + } +} diff --git a/application/mQueue/View/Helper/AlternateFormats.php b/src/Application/View/Helper/AlternateFormats.php similarity index 53% rename from application/mQueue/View/Helper/AlternateFormats.php rename to src/Application/View/Helper/AlternateFormats.php index 6a67e57..bc3767b 100644 --- a/application/mQueue/View/Helper/AlternateFormats.php +++ b/src/Application/View/Helper/AlternateFormats.php @@ -1,12 +1,10 @@ [ @@ -17,33 +15,33 @@ class AlternateFormats extends Zend_View_Helper_Abstract ], ]; + public function __construct( + private readonly HeadLink $headLink, + ) {} + /** * Returns a string of HTML links for end-user, and also append 'alternate' links to HTML header. - * - * @param string $title - * - * @return string */ - public function alternateFormats(array $formats, $title = null) + public function __invoke(array $formats, ?string $title = ''): string { $formatLinks = []; foreach ($formats as $format => $url) { // Inject format and locale parameters - if (mb_strpos($url, '?') === false) { - $url .= '?'; - } else { + if (str_contains($url, '?')) { $url .= '&'; + } else { + $url .= '?'; } - $url .= 'format=' . $format . '&lang=' . Zend_Registry::get(Zend_Locale::class)->getLanguage(); + $url .= 'format=' . $format . '&lang=' . mb_substr(Locale::getDefault(), 0, 2); $formatLinks[] = '' . self::$supportedFormats[$format]['name'] . ''; if ($title && isset(self::$supportedFormats[$format]['mime'])) { - $this->view->headLink()->appendAlternate($url, self::$supportedFormats[$format]['mime'], $title); + ($this->headLink)()->appendAlternate($url, self::$supportedFormats[$format]['mime'], $title); } } - $result = '

    ' . $this->view->translate('Also available in:') . ' ' . implode(' | ', $formatLinks) . '

    '; + $result = '

    ' . _tr('Also available in:') . ' ' . implode(' | ', $formatLinks) . '

    '; return $result; } diff --git a/src/Application/View/Helper/AlternateFormatsFactory.php b/src/Application/View/Helper/AlternateFormatsFactory.php new file mode 100644 index 0000000..5c821d7 --- /dev/null +++ b/src/Application/View/Helper/AlternateFormatsFactory.php @@ -0,0 +1,21 @@ +get(HelperPluginManager::class); + + return new AlternateFormats( + $helpers->get(HeadLink::class), + ); + } +} diff --git a/src/Application/View/Helper/CacheStamp.php b/src/Application/View/Helper/CacheStamp.php new file mode 100644 index 0000000..6337629 --- /dev/null +++ b/src/Application/View/Helper/CacheStamp.php @@ -0,0 +1,29 @@ +minimize) { + $fileName = str_replace('/js/min/', '/js/', $fileName); + } + + $fullPath = 'public/' . $fileName; + if (is_file($fullPath)) { + $fileName = $fileName . '?' . filemtime($fullPath); + } + + return $fileName; + } +} diff --git a/src/Application/View/Helper/CacheStampFactory.php b/src/Application/View/Helper/CacheStampFactory.php new file mode 100644 index 0000000..0a3e234 --- /dev/null +++ b/src/Application/View/Helper/CacheStampFactory.php @@ -0,0 +1,19 @@ +get('config'); + + return new CacheStamp($config['minimize']); + } +} diff --git a/src/Application/View/Helper/FlashMessenger.php b/src/Application/View/Helper/FlashMessenger.php new file mode 100644 index 0000000..257ddb8 --- /dev/null +++ b/src/Application/View/Helper/FlashMessenger.php @@ -0,0 +1,33 @@ +getFlashes() as $level => $message) { + $result .= '
    ' . ($this->escapeHtml)($message) . '
    '; + } + + return $result; + } +} diff --git a/src/Application/View/Helper/FlashMessengerFactory.php b/src/Application/View/Helper/FlashMessengerFactory.php new file mode 100644 index 0000000..0e5438b --- /dev/null +++ b/src/Application/View/Helper/FlashMessengerFactory.php @@ -0,0 +1,24 @@ +get(HelperPluginManager::class); + + return new FlashMessenger( + $helpers->get(EscapeHtml::class), + $helpers->get(EscapeHtmlAttr::class), + ); + } +} diff --git a/src/Application/View/Helper/Footer.php b/src/Application/View/Helper/Footer.php new file mode 100644 index 0000000..de555a1 --- /dev/null +++ b/src/Application/View/Helper/Footer.php @@ -0,0 +1,20 @@ +url)('about') . '">' . _tr('about mQueue') . ' '; + } +} diff --git a/src/Application/View/Helper/FooterFactory.php b/src/Application/View/Helper/FooterFactory.php new file mode 100644 index 0000000..913546c --- /dev/null +++ b/src/Application/View/Helper/FooterFactory.php @@ -0,0 +1,22 @@ +get(HelperPluginManager::class); + + return new Footer( + $helpers->get(UrlHelper::class), + ); + } +} diff --git a/src/Application/View/Helper/GoogleAnalytics.php b/src/Application/View/Helper/GoogleAnalytics.php new file mode 100644 index 0000000..10be026 --- /dev/null +++ b/src/Application/View/Helper/GoogleAnalytics.php @@ -0,0 +1,33 @@ +trackingCode) { + return ''; + } + + $result = << + + STRING; + + return $result; + } +} diff --git a/src/Application/View/Helper/GoogleAnalyticsFactory.php b/src/Application/View/Helper/GoogleAnalyticsFactory.php new file mode 100644 index 0000000..cc24f4d --- /dev/null +++ b/src/Application/View/Helper/GoogleAnalyticsFactory.php @@ -0,0 +1,18 @@ +get('config'); + + return new GoogleAnalytics($config['googleAnalyticsTrackingCode']); + } +} diff --git a/src/Application/View/Helper/Graph.php b/src/Application/View/Helper/Graph.php new file mode 100644 index 0000000..a8161de --- /dev/null +++ b/src/Application/View/Helper/Graph.php @@ -0,0 +1,51 @@ +url)('status.graph.user', ['user' => $user->id]); + } else { + $url = ($this->url)('status.graph'); + } + + $js = <<headScript)() + ->appendFile('/js/min/highcharts.js') + ->appendScript($js); + + return $html; + } +} diff --git a/src/Application/View/Helper/GraphFactory.php b/src/Application/View/Helper/GraphFactory.php new file mode 100644 index 0000000..d666351 --- /dev/null +++ b/src/Application/View/Helper/GraphFactory.php @@ -0,0 +1,23 @@ +get(HelperPluginManager::class); + + return new Graph( + $helpers->get(HeadScript::class), + $helpers->get(UrlHelper::class), + ); + } +} diff --git a/src/Application/View/Helper/Gravatar.php b/src/Application/View/Helper/Gravatar.php new file mode 100644 index 0000000..379e245 --- /dev/null +++ b/src/Application/View/Helper/Gravatar.php @@ -0,0 +1,29 @@ + 'gravatar user_' . $user->id . ' ' . $size, + 'title' => $user->nickname, + ]; + $result = 'htmlAttributes)($attribs) . '">'; + + return $result; + } +} diff --git a/src/Application/View/Helper/GravatarFactory.php b/src/Application/View/Helper/GravatarFactory.php new file mode 100644 index 0000000..90ccca2 --- /dev/null +++ b/src/Application/View/Helper/GravatarFactory.php @@ -0,0 +1,22 @@ +get(HelperPluginManager::class); + + return new Gravatar( + $helpers->get(HtmlAttributes::class), + ); + } +} diff --git a/src/Application/View/Helper/HeadLink.php b/src/Application/View/Helper/HeadLink.php new file mode 100644 index 0000000..a970f11 --- /dev/null +++ b/src/Application/View/Helper/HeadLink.php @@ -0,0 +1,26 @@ +cacheStamp)($args[0]); + } + + return parent::__call($method, $args); + } +} diff --git a/src/Application/View/Helper/HeadLinkFactory.php b/src/Application/View/Helper/HeadLinkFactory.php new file mode 100644 index 0000000..680370e --- /dev/null +++ b/src/Application/View/Helper/HeadLinkFactory.php @@ -0,0 +1,21 @@ +get(HelperPluginManager::class); + + return new HeadLink( + $helpers->get(CacheStamp::class), + ); + } +} diff --git a/src/Application/View/Helper/HeadScript.php b/src/Application/View/Helper/HeadScript.php new file mode 100644 index 0000000..e2101ac --- /dev/null +++ b/src/Application/View/Helper/HeadScript.php @@ -0,0 +1,44 @@ +includeDirectory($file); + } else { + $file = ($this->cacheStamp)(str_replace('public/', '', $file)); + $this->prependFile($file); + } + } + } + + /** + * Support timestamp, compilation and concatenation of folder. + * Compiled and concatenated files must pre-exist (compiled by external tools). + */ + public function prependDirectory(string $fileName, string $directory): self + { + if ($this->minimize) { + $this->prependFile(($this->cacheStamp)($fileName)); + } else { + $this->includeDirectory('public' . $directory); + } + + return $this; + } +} diff --git a/src/Application/View/Helper/HeadScriptFactory.php b/src/Application/View/Helper/HeadScriptFactory.php new file mode 100644 index 0000000..6559c22 --- /dev/null +++ b/src/Application/View/Helper/HeadScriptFactory.php @@ -0,0 +1,23 @@ +get('config'); + $helpers = $container->get(HelperPluginManager::class); + + return new HeadScript( + $config['minimize'], + $helpers->get(CacheStamp::class), + ); + } +} diff --git a/application/mQueue/View/Helper/LanguageSelector.php b/src/Application/View/Helper/LanguageSelector.php similarity index 63% rename from application/mQueue/View/Helper/LanguageSelector.php rename to src/Application/View/Helper/LanguageSelector.php index 4649630..dde7f90 100644 --- a/application/mQueue/View/Helper/LanguageSelector.php +++ b/src/Application/View/Helper/LanguageSelector.php @@ -1,18 +1,19 @@ 'English', 'ko' => '한국어', @@ -23,7 +24,7 @@ public function languageSelector() $params = $_GET; foreach ($languages as $val => $name) { $params['lang'] = $val; - $result .= '' . $name . ' '; + $result .= '' . $name . ' '; } $result .= ''; diff --git a/src/Application/View/Helper/LanguageSelectorFactory.php b/src/Application/View/Helper/LanguageSelectorFactory.php new file mode 100644 index 0000000..b1d6fb7 --- /dev/null +++ b/src/Application/View/Helper/LanguageSelectorFactory.php @@ -0,0 +1,21 @@ +get(HelperPluginManager::class); + + return new LanguageSelector( + $helpers->get(UrlParams::class), + ); + } +} diff --git a/application/mQueue/View/Helper/Link.php b/src/Application/View/Helper/Link.php similarity index 58% rename from application/mQueue/View/Helper/Link.php rename to src/Application/View/Helper/Link.php index 1cc7530..95422c4 100644 --- a/application/mQueue/View/Helper/Link.php +++ b/src/Application/View/Helper/Link.php @@ -1,17 +1,13 @@ getImdbUrl(); $result = ' ' . $url . ''; diff --git a/src/Application/View/Helper/LoginState.php b/src/Application/View/Helper/LoginState.php new file mode 100644 index 0000000..cb13958 --- /dev/null +++ b/src/Application/View/Helper/LoginState.php @@ -0,0 +1,35 @@ +'; + + $user = User::getCurrent(); + if ($user) { + $result .= '' . ($this->gravatar)($user) . ' ' . ($this->escapeHtml)($user->nickname) . ' '; + $result .= '' . _tr('Logout') . ' '; + } else { + $result .= ' ' . _tr('Subscribe') . ' '; + $result .= '' . _tr('Login') . ''; + } + + return $result . ''; + } +} diff --git a/src/Application/View/Helper/LoginStateFactory.php b/src/Application/View/Helper/LoginStateFactory.php new file mode 100644 index 0000000..a2ae9f6 --- /dev/null +++ b/src/Application/View/Helper/LoginStateFactory.php @@ -0,0 +1,25 @@ +get(HelperPluginManager::class); + + return new LoginState( + $helpers->get(EscapeHtml::class), + $helpers->get(Gravatar::class), + $helpers->get(UrlHelper::class), + ); + } +} diff --git a/src/Application/View/Helper/Movie.php b/src/Application/View/Helper/Movie.php new file mode 100644 index 0000000..61034ef --- /dev/null +++ b/src/Application/View/Helper/Movie.php @@ -0,0 +1,37 @@ +link)($movie); + + $user = User::getCurrent(); + if ($user) { + $status = $movie->getStatus($user); + $title = _tr('Your rating is : %status%', ['status' => $status->getName()]); + } else { + $title = _tr('You are not logged in'); + } + + $movieUrl = ($this->url)('movie.view', ['id' => $movie->id]); + $result .= ' ' . ($this->escapeHtml)($movie->title) . ''; + + return $result; + } +} diff --git a/src/Application/View/Helper/MovieFactory.php b/src/Application/View/Helper/MovieFactory.php new file mode 100644 index 0000000..7d8253f --- /dev/null +++ b/src/Application/View/Helper/MovieFactory.php @@ -0,0 +1,25 @@ +get(HelperPluginManager::class); + + return new Movie( + $helpers->get(EscapeHtml::class), + $helpers->get(Link::class), + $helpers->get(UrlHelper::class), + ); + } +} diff --git a/src/Application/View/Helper/Rating.php b/src/Application/View/Helper/Rating.php new file mode 100644 index 0000000..114fbe5 --- /dev/null +++ b/src/Application/View/Helper/Rating.php @@ -0,0 +1,16 @@ +value . '" title="' . $rating->name() . '">' . $rating->name() . ''; + + return $result; + } +} diff --git a/src/Application/View/Helper/Sort.php b/src/Application/View/Helper/Sort.php new file mode 100644 index 0000000..4ee1593 --- /dev/null +++ b/src/Application/View/Helper/Sort.php @@ -0,0 +1,37 @@ + 'asc', 'asc' => 'desc']; + $selectedSortOrder = $sorting->selectedOrder; + if (!in_array($sorting->selectedOrder, $orders, true)) { + $selectedSortOrder = reset($orders); + } + + $url = ($this->urlParams)(array_merge($additionalParameters, ['sort' => $column, 'sortOrder' => $orders[$selectedSortOrder]])); + $result = ' $label])) . '" href="' . $url . '">' . ($this->escapeHtml)($label) . ''; + + return $result; + } +} diff --git a/src/Application/View/Helper/SortFactory.php b/src/Application/View/Helper/SortFactory.php new file mode 100644 index 0000000..ea82b83 --- /dev/null +++ b/src/Application/View/Helper/SortFactory.php @@ -0,0 +1,25 @@ +get(HelperPluginManager::class); + + return new Sort( + $helpers->get(EscapeHtml::class), + $helpers->get(EscapeHtmlAttr::class), + $helpers->get(UrlParams::class), + ); + } +} diff --git a/src/Application/View/Helper/StatusHelp.php b/src/Application/View/Helper/StatusHelp.php new file mode 100644 index 0000000..f0d91c7 --- /dev/null +++ b/src/Application/View/Helper/StatusHelp.php @@ -0,0 +1,26 @@ +'; + $result .= '
  • ' . ($this->rating)(\Application\Enum\Rating::Need) . ': ' . _tr('I want to see this movie') . '
  • '; + $result .= '
  • ' . ($this->rating)(\Application\Enum\Rating::Bad) . ': ' . _tr('Boring movie, I wasted my time') . '
  • '; + $result .= '
  • ' . ($this->rating)(\Application\Enum\Rating::Ok) . ': ' . _tr('Enjoyable movie (most movies)') . '
  • '; + $result .= '
  • ' . ($this->rating)(\Application\Enum\Rating::Excellent) . ': ' . _tr('Excellent, I would watch it twice') . '
  • '; + $result .= '
  • ' . ($this->rating)(\Application\Enum\Rating::Favorite) . ': ' . _tr('Incredibly awesome, the kind of movie you must watch many times regularly') . '
  • '; + $result .= ''; + + return $result; + } +} diff --git a/src/Application/View/Helper/StatusHelpFactory.php b/src/Application/View/Helper/StatusHelpFactory.php new file mode 100644 index 0000000..8dcee77 --- /dev/null +++ b/src/Application/View/Helper/StatusHelpFactory.php @@ -0,0 +1,21 @@ +get(HelperPluginManager::class); + + return new StatusHelp( + $helpers->get(Rating::class), + ); + } +} diff --git a/src/Application/View/Helper/StatusLinks.php b/src/Application/View/Helper/StatusLinks.php new file mode 100644 index 0000000..cedfd42 --- /dev/null +++ b/src/Application/View/Helper/StatusLinks.php @@ -0,0 +1,45 @@ +getUniqueId() . '">'; + $user = User::getCurrent(); + + // Deactivate links if no logged user + if ($user) { + $tag = 'a'; + } else { + $tag = 'span'; + } + + foreach (\Application\Enum\Rating::possibleChoices() as $rating) { + $class = $rating->value . ($status->rating === $rating ? ' current' : ''); + $url = ($this->serverUrl)() . ($this->url)('status.rating', [ + 'movie' => $status->movie->id, + 'rating' => ($rating === $status->rating && $user && $user === $status->user) ? 0 : $rating->value, + ]); + + $result .= '<' . $tag . ' class="mqueue_status mqueue_status_' . $class . '"' . ($tag === 'a' ? ' href="' . $url . '"' : '') . ' title="' . $rating->name() . '">' . $rating->name() . ''; + } + $result .= ''; + + return $result; + } +} diff --git a/src/Application/View/Helper/StatusLinksFactory.php b/src/Application/View/Helper/StatusLinksFactory.php new file mode 100644 index 0000000..4ae8603 --- /dev/null +++ b/src/Application/View/Helper/StatusLinksFactory.php @@ -0,0 +1,24 @@ +get(HelperPluginManager::class); + + return new StatusLinks( + $helpers->get(ServerUrl::class), + $helpers->get(UrlHelper::class), + ); + } +} diff --git a/application/mQueue/View/Helper/UrlParams.php b/src/Application/View/Helper/UrlParams.php similarity index 60% rename from application/mQueue/View/Helper/UrlParams.php rename to src/Application/View/Helper/UrlParams.php index f220289..ab2f6aa 100644 --- a/application/mQueue/View/Helper/UrlParams.php +++ b/src/Application/View/Helper/UrlParams.php @@ -1,34 +1,33 @@ escapeParams = $escapeParams; - return $this->view->serverUrl() . $this->view->url() . '?' . $this->flatten($params); + return ($this->url)() . '?' . $this->flatten($params); } /** * Flatten a recursive array in GET parameters (same as HTML form send GET request). - * - * @param string $result - * @param string $previousName */ - private function flatten(array $params, $result = '', $previousName = null): string + private function flatten(array $params, string $result = '', string $previousName = ''): string { foreach ($params as $key => $value) { if ($previousName) { @@ -44,7 +43,7 @@ private function flatten(array $params, $result = '', $previousName = null): str $result .= $this->escapeParams ? '&' : '&'; } - $result .= $name . '=' . urlencode($value); + $result .= $name . '=' . urlencode($value ?? ''); } } diff --git a/src/Application/View/Helper/UrlParamsFactory.php b/src/Application/View/Helper/UrlParamsFactory.php new file mode 100644 index 0000000..d73be31 --- /dev/null +++ b/src/Application/View/Helper/UrlParamsFactory.php @@ -0,0 +1,22 @@ +get(HelperPluginManager::class); + + return new UrlParams( + $helpers->get(UrlHelper::class), + ); + } +} diff --git a/src/functions.php b/src/functions.php new file mode 100644 index 0000000..e337d7c --- /dev/null +++ b/src/functions.php @@ -0,0 +1,67 @@ +get(EntityManager::class); +} + +/** + * Dump all arguments. + */ +function v(): void +{ + var_dump(func_get_args()); +} + +/** + * Dump all arguments and die. + */ +function w(): never +{ + $isHtml = (PHP_SAPI !== 'cli'); + echo "\n_________________________________________________________________________________________________________________________" . ($isHtml ? '
    ' : '') . "\n"; + var_dump(func_get_args()); + echo "\n" . ($isHtml ? '
    ' : '') . '_________________________________________________________________________________________________________________________' . ($isHtml ? '
    ' : '') . "\n";
    +    debug_print_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
    +    echo '' . ($isHtml ? '
    ' : '') . '_________________________________________________________________________________________________________________________' . ($isHtml ? '
    ' : '') . "\n"; + exit("script aborted on purpose.\n"); +} + +/** + * Translate given message in current language. + * + * If replacements are given, they will be replaced after translation: + * + * ``` + * _tr('Hello %my-name%', ['my-name' => 'John']); // Bonjour John + * ``` + * + * @param array $replacements + */ +function _tr(string $message, array $replacements = []): string +{ + global $container; + + $translator = $container->get(TranslatorInterface::class); + $translation = $translator->translate($message); + if (!$replacements) { + return $translation; + } + + $finalReplacements = []; + foreach ($replacements as $key => $value) { + $finalReplacements['%' . $key . '%'] = $value; + } + + return strtr($translation, $finalReplacements); +} diff --git a/application/sass/_handheld.scss b/src/sass/_handheld.scss similarity index 100% rename from application/sass/_handheld.scss rename to src/sass/_handheld.scss diff --git a/application/sass/_local.scss b/src/sass/_local.scss similarity index 83% rename from application/sass/_local.scss rename to src/sass/_local.scss index de6c5f9..ebc463c 100644 --- a/application/sass/_local.scss +++ b/src/sass/_local.scss @@ -1,10 +1,12 @@ @use "variables"; + :root { --font-color: #595959; } a { text-decoration: none; + &:hover { text-decoration: underline; } @@ -28,17 +30,16 @@ body { margin: 0 auto; border: 6px solid variables.$color1; border-radius: 10px; - -moz-border-radius: 10px; - -webkit-border-radius: 10px; padding-bottom: 2em; } #header { background-image: repeating-linear-gradient(-45deg, variables.$color1, variables.$color1 15px, variables.$color2 15px, variables.$color2 30px); - background-image: -webkit-repeating-linear-gradient(-45deg, variables.$color1, variables.$color1 15px, variables.$color2 15px, variables.$color2 30px); background-color: variables.$color2; + h1 { margin: 0px; + a { background: url("../images/logo.svg") no-repeat scroll 0 0; display: block; @@ -47,11 +48,13 @@ body { position: relative; top: -14px; width: 256px; + span { display: none; } } } + .tagline { color: white; font-size: 1.2em; @@ -70,25 +73,27 @@ body { #header-navigation { border-radius: 5px; - -moz-border-radius: 5px; - -webkit-border-radius: 5px; padding: 5px; // Repeat the logo slightly over the links background: #163066 url("../images/logo.svg") no-repeat scroll -39px -149px; + a { color: #afc9ff; padding: 5px; font-weight: bold; text-decoration: none; + border-radius: 5px; + &:hover { - border-radius: 5px; - -moz-border-radius: 5px; - -webkit-border-radius: 5px; color: #163066; - background-color: #afc9ff; + background-color: #afc9ff !important; } } + + .active a { + background-color: rgb(175 201 255 / 0.25); + } } ul.navigation { @@ -96,6 +101,7 @@ ul.navigation { list-style: none outside none; margin: 0; padding: 0; + li { float: left; } @@ -110,6 +116,7 @@ ul.navigation { margin-top: 1em; text-align: center; font-size: 0.8em; + a { color: var(--font-color); text-decoration: none; @@ -138,31 +145,35 @@ ul.navigation { font-size: 0.8em; } -dl.filter { - dd, dt { - display: inline; - margin: 0px; - line-height: 2em; - } - dd { +div.filter { + margin-bottom: 2em; + + select { margin-right: 1em; } + + input[type='text'] { + margin: 0 1em; + } + } #fieldset-filters { #submit-label { display: none; } - #submit-element { + + [name="submit"] { float: right; margin-left: 1em; } } -dl.filter { - #filter1-title-element input { +#filters { + input[name='filter1[title]'] { width: 10em; } + #filter1-withSource-element { margin-right: auto; } @@ -176,6 +187,7 @@ dl.filter { .filter { margin-top: 0em; } + .buttons { margin-top: 0em; margin-bottom: 0em; @@ -186,7 +198,7 @@ ol ol { list-style-type: lower-alpha; } -#quickSearch { +form[name=quickSearch] { float: right; font-size: 0.8em; font-weight: bold; @@ -202,6 +214,7 @@ ol ol { margin-right: 1em; margin-top: 0.5em; clear: both; + a { color: white; opacity: 0.8; @@ -220,8 +233,6 @@ span.gravatar.big { background: #FFFFCC; border: 3px solid #FFCC33; border-radius: 5px; - -moz-border-radius: 5px; - -webkit-border-radius: 5px; } ul.statusHelp { @@ -231,14 +242,13 @@ ul.statusHelp { } input { - &[type='text'], &[type='password'] { + &[type='text'], &[type='password'], &[type='email'] { width: 20em; } - &[type='text'] { - &[id='id'], &[id='url'] { - width: 40em; - max-width: 100%; // Don't overflow small screens - } + + &[type='text'][name='id'] { + width: 40em; + max-width: 100%; // Don't overflow small screens } } @@ -250,6 +260,7 @@ tr { > th { background-color: white; } + &:hover { background-color: #EFEFEF; } @@ -257,12 +268,15 @@ tr { td { padding-right: 1em; + &.dateUpdate, &.user, &.rating { white-space: nowrap; } + &.movie { overflow: hidden; } + &.number { text-align: right; } @@ -270,13 +284,16 @@ td { a.sort { color: var(--font-color); + &.asc:after, &.desc:after { position: absolute; padding-left: 0.5em; } + &.asc:after { content: 'â–´'; } + &.desc:after { content: 'â–¾'; } @@ -295,18 +312,11 @@ a.sort { list-style-image: url("../images/browser_firefox.svg"); } -.browser_safari { - list-style-image: url("../images/browser_safari.gif"); -} - -.browser_ie { - list-style-image: url("../images/browser_ie.svg"); -} - a.imdb { &.hideUrl span { display: none; } + display: inline-block; min-height: 16px; padding-left: 16px; @@ -322,22 +332,23 @@ div.flashmessenger { background: 8px 50% no-repeat; border: 3px solid; border-radius: 5px; - -moz-border-radius: 5px; - -webkit-border-radius: 5px; padding: 0.5em 0.5em 0.5em 2.7em; margin-top: 0.5em; + &.notice { background-image: url("../images/flashmessenger-notice.png"); background-color: #dfffdf; border-color: #9fcf9f; color: #005f00; } + &.warning { background-image: url("../images/flashmessenger-warning.png"); background-color: #FFEBC1; border-color: #FDBF3B; color: #A6750C; } + &.error { background-image: url("../images/flashmessenger-error.png"); background-color: #ffe3e3; @@ -347,16 +358,18 @@ div.flashmessenger { } } -#quickSearch { - #search { +form[name=quickSearch] { + [name=search] { opacity: 0.8; width: 10em; border: 2px solid variables.$color1; + &:hover, &:focus { opacity: 1; } } - #searchSubmit { + + [name=searchSubmit] { display: none; } } @@ -371,6 +384,7 @@ a.rss { margin-bottom: 1em; display: flex; } + .user { margin-right: 0.5em; } @@ -380,3 +394,30 @@ a.rss { font-size: 0.5em; font-weight: bold; } + +form.grid { + --margin: 2em; + + > label { + display: block; + margin-bottom: 1em; + + > input { + display: block; + margin-left: var(--margin); + } + } + + > input { + margin-left: var(--margin); + } +} + +label:has(> input.inversed-checkbox) { + display: inline-flex; + flex-direction: row-reverse; +} + +.input-error { + border: 3px solid #dd0000; +} diff --git a/application/sass/_print.scss b/src/sass/_print.scss similarity index 100% rename from application/sass/_print.scss rename to src/sass/_print.scss diff --git a/application/sass/_status.scss b/src/sass/_status.scss similarity index 100% rename from application/sass/_status.scss rename to src/sass/_status.scss diff --git a/application/sass/_variables.scss b/src/sass/_variables.scss similarity index 100% rename from application/sass/_variables.scss rename to src/sass/_variables.scss diff --git a/application/sass/application.scss b/src/sass/application.scss similarity index 100% rename from application/sass/application.scss rename to src/sass/application.scss diff --git a/application/sass/both.scss b/src/sass/both.scss similarity index 100% rename from application/sass/both.scss rename to src/sass/both.scss diff --git a/templates/app/about.phtml b/templates/app/about.phtml new file mode 100644 index 0000000..66a824d --- /dev/null +++ b/templates/app/about.phtml @@ -0,0 +1,6 @@ +headTitle(_tr('About')); ?> +

    translate('About mQueue'); ?>

    +

    translate('This project started as a personal need in 2009. It is written in PHP and was an opportunity to explore Zend Framework, later migrated to Mezzio, and jQuery. It also uses FatCow\'s icon set.'); ?> +

    +

    translate('Source code is freely available on GitHub.'); ?> +

    diff --git a/templates/app/activity-rss.phtml b/templates/app/activity-rss.phtml new file mode 100644 index 0000000..2099d6a --- /dev/null +++ b/templates/app/activity-rss.phtml @@ -0,0 +1,49 @@ +setType('rss'); +$feed->setTitle('mQueue - ' . $this->title); +$feed->setLink($this->serverUrl($this->url())); +$feed->setFeedLink($this->serverUrl($this->url(queryParams: ['format' => 'rss', 'lang' => mb_substr(Locale::getDefault(), 0, 2)])), 'rss'); +$feed->setDateModified(); +$feed->setDescription($this->title); + +// Cycle through the rankings, creating an array storing +// each, and push the array onto the $entries array +/** @var Status $status */ +foreach ($this->activity as $status) { + $user = $status->user; + $movie = $status->movie; + + $text = '

    ' . $this->gravatar($user) . ' ' . $this->escapeHtml($user->nickname) . ' ' + . '' . $this->escapeHtml($movie->title) . ' ' + . $status->getName() . '

    ' . $movie->getImdbUrl() . '

    '; + + $title = $user->nickname . ' - ' . $movie->title . ' - ' . $status->getName(); + + $entry = $feed->createEntry(); + $feed->addEntry($entry); + + $entry->setTitle($title); + $entry->setLink($this->serverUrl($this->url('movie.view', ['id' => $movie->id]))); + $entry->setDescription($title); + $entry->setContent($text); + $entry->setDateModified($status->dateUpdate); + $entry->addAuthor(['name' => 'mQueue']); + + if ($movie->source) { + $entry->setEnclosure([ + 'uri' => $movie->source, + 'type' => 'application/x-bittorrent', + 'length' => 0, // Because unknown + ]); + } +} + +echo $feed->export('rss'); diff --git a/templates/app/activity.phtml b/templates/app/activity.phtml new file mode 100644 index 0000000..d3efc94 --- /dev/null +++ b/templates/app/activity.phtml @@ -0,0 +1,9 @@ +headTitle($this->title); ?> +

    escapeHtml($this->title); ?>

    +activity($this->activity); ?> +alternateFormats([ + 'rss' => $this->serverUrl($this->url()), +], $this->title === $this->translate('Overall activity') ? null : 'mQueue - ' . $this->title); // Don't give title to avoid adding a duplicate alternate link to overall activity +?> +paginationControl($this->activity, null, null, ['permanentParams' => []]); ?> diff --git a/application/views/scripts/faq/index.phtml b/templates/app/faq.phtml similarity index 65% rename from application/views/scripts/faq/index.phtml rename to templates/app/faq.phtml index bdb3a3e..bcec61b 100644 --- a/application/views/scripts/faq/index.phtml +++ b/templates/app/faq.phtml @@ -4,16 +4,14 @@
    translate('How does this work ?'); ?>
    translate('To get the most of this website, you should:'); ?>
      -
    1. translate('Create an account'); ?>
    2. +
    3. translate('Create an account'); ?>
      1. translate('For Chrome, skip to next step'); ?>
      2. -
      3. translate('For Firefox, install Greasemonkey'); ?>
      4. -
      5. translate('For Safari, SIMBL and GreaseKit should work'); ?>
      6. -
      7. translate('For IE, Greasemonkey for IE may hopefully help'); ?>
      8. +
      9. translate('For Firefox, install Greasemonkey'); ?>
    4. -
    5. translate('Install the user script'); ?>
    6. +
    7. translate('Install the user script'); ?>
    8. translate('Surf on www.imdb.com and rate movies by clicking icons'); ?>
    @@ -28,7 +26,7 @@
    translate('You cannot. It is meant to be a very simple, fast and efficient system. No extra bloating options.'); ?>
    translate('How to add a picture to my account ?'); ?>
    -
    translate('Sign up for www.gravatar.com with the same email address and your picture will show up here.'); ?>
    +
    translate('Sign up for www.gravatar.com with the same email address and your picture will show up here.'); ?>
    translate('What about privacy ?'); ?>
    translate('Everything is publicly available for anyone. The only exceptions are emails and passwords which will never be published.'); ?>
    diff --git a/application/views/scripts/css/gravatar-css.phtml b/templates/app/gravatar-css.phtml similarity index 82% rename from application/views/scripts/css/gravatar-css.phtml rename to templates/app/gravatar-css.phtml index ae04c36..70f5afa 100644 --- a/application/views/scripts/css/gravatar-css.phtml +++ b/templates/app/gravatar-css.phtml @@ -10,7 +10,7 @@ foreach ($sizes as $name => $size) { echo 'span.gravatar.' . $name . '{width: ' . $size . 'px; height: ' . $size . 'px;}' . PHP_EOL; } foreach ($this->users as $user) { - $urlBase = 'https://secure.gravatar.com/avatar/' . md5(trim(mb_strtolower($user->email))) . '.jpg?default=identicon'; + $urlBase = 'https://secure.gravatar.com/avatar/' . md5(mb_trim(mb_strtolower($user->email))) . '.jpg?default=identicon'; foreach ($sizes as $name => $size) { $url = $urlBase . '&size=' . $size; ?> span.gravatar.user_id; ?>. {background-image: url();} diff --git a/templates/app/movie/add.phtml b/templates/app/movie/add.phtml new file mode 100644 index 0000000..f18321a --- /dev/null +++ b/templates/app/movie/add.phtml @@ -0,0 +1,39 @@ +headTitle(_tr('Add movie')); ?> +

    +form($this->form); ?> +movies): ?> +

    + + + + + + + movies) === 0): ?> + + + + movies as $entry): ?> + + + + + +
    + '' . _tr('FAQ') . '']); ?> +
    statusLinks($entry->getStatus(Application\Model\User::getCurrent())); ?> + movie($entry); ?> +
    + +
    statusHelp(); ?>
    + + + +
    + '' . _tr('FAQ') . '', + ]); + ?> +
    + diff --git a/templates/app/movie/index-rss.phtml b/templates/app/movie/index-rss.phtml new file mode 100644 index 0000000..7e5e113 --- /dev/null +++ b/templates/app/movie/index-rss.phtml @@ -0,0 +1,50 @@ +setType('rss'); +$feed->setTitle('mQueue - ' . $this->formFilter->getValuesText()); +$feed->setLink($this->serverUrl($this->urlParams($this->permanentParams))); +$feed->setFeedLink($this->serverUrl($this->url(queryParams: ['format' => 'rss', 'lang' => mb_substr(Locale::getDefault(), 0, 2)])), 'rss'); +$feed->setDateModified(); +$feed->setDescription($this->formFilter->getValuesText()); + +// Cycle through the rankings, creating an array storing +// each, and push the array onto the $entries array + +foreach ($this->paginator as $movie) { + $date = $movie->dateUpdate; + $text = '

    ' . $movie->getImdbUrl() . '

    '; + + foreach ($this->users as $user) { + $status = $movie->getStatus($user); + $text .= '

    ' . $this->gravatar($user) . ' ' . $this->escapeHtml($user->nickname) . ' ' + . $status->getName() . '

    '; + } + + $title = $movie->title; + + $entry = $feed->createEntry(); + $feed->addEntry($entry); + + $entry->setTitle($title); + $entry->setLink($this->serverUrl($this->url('movie.view', ['id' => $movie->id]))); + $entry->setDescription($title); + $entry->setContent($text); + $entry->setDateModified($date->getTimestamp()); + $entry->addAuthor(['name' => 'mQueue']); + + if ($movie->source) { + $entry->setEnclosure([ + 'uri' => $movie->source, + 'type' => 'application/x-bittorrent', + 'length' => 0, // Because unknown + ]); + } +} + +echo $feed->export('rss'); diff --git a/application/views/scripts/movie/index.csv.phtml b/templates/app/movie/index.csv.phtml similarity index 100% rename from application/views/scripts/movie/index.csv.phtml rename to templates/app/movie/index.csv.phtml diff --git a/templates/app/movie/index.phtml b/templates/app/movie/index.phtml new file mode 100644 index 0000000..7a58f32 --- /dev/null +++ b/templates/app/movie/index.phtml @@ -0,0 +1,68 @@ +headTitle(_tr('Movies')); +?> +

    +form($this->formFilter); // Weird but required to have correct name input below?> +form()->openTag($this->formFilter); ?> +
    + + formFilter as $element): + if ($element instanceof FieldsetInterface):?> +
    + formRow($subElement); + endforeach; ?> +
    + formRow($element); + endif; + endforeach; +?> +
    +form()->closeTag(); ?> + + + users as $user): + ?> + + + + + paginator->getTotalItemCount() === 0): ?> + + + + paginator as $entry): ?> + + users as $user): ?> + + + + + +
    sort($user->nickname, 'status' . $i++, $this->sorting, $this->permanentParams); ?>sort(_tr('Title'), 'title', $this->sorting, $this->permanentParams); ?>
    + +
    statusLinks($entry->getStatus($user)) ?>movie($entry); ?>
    +alternateFormats([ + 'rss' => $this->urlParams($this->permanentParams, false), + 'csv' => $this->urlParams($this->permanentParams, false), +], 'mQueue - ' . $this->filterName); +?> +paginationControl($this->paginator, null, null, ['permanentParams' => $this->permanentParams]); +?> + +
    + paginator->getTotalItemCount() !== 0): ?>statusHelp(); ?> '' . _tr('FAQ') . '', + ]); + ?> +
    diff --git a/templates/app/movie/view.phtml b/templates/app/movie/view.phtml new file mode 100644 index 0000000..94258cc --- /dev/null +++ b/templates/app/movie/view.phtml @@ -0,0 +1,27 @@ +headTitle($this->movie->title); ?> +

    escapeHtml($this->movie->title); ?>

    link($this->movie, true) ?>

    +

    + + + + + + users as $user) { + echo ''; + echo ''; + echo ''; + + echo ''; + } +?> +
    ' . $this->gravatar($user) . ' ' . $this->escapeHtml($user->nickname) . '' . $this->statusLinks($this->movie->getStatus($user)) . '

    +activity($this->movieActivity, ['movie']); ?> +alternateFormats([ + 'rss' => $this->url('movie.index', ['movie' => $this->movie->id]), +], 'mQueue - ' . _tr('Activity for %s', [$this->movie->title])); +?> + +paginationControl($this->movieActivity); ?> diff --git a/application/views/scripts/js/mqueue-user-js.phtml b/templates/app/mqueue-user-js.phtml similarity index 69% rename from application/views/scripts/js/mqueue-user-js.phtml rename to templates/app/mqueue-user-js.phtml index c535c36..7cdc87b 100644 --- a/application/views/scripts/js/mqueue-user-js.phtml +++ b/templates/app/mqueue-user-js.phtml @@ -1,8 +1,8 @@ // ==UserScript== -// @name mQueue - Injects buttons to rate IMDb movies on serverUrl() . $this->baseUrl(''); ?> +// @name mQueue - Injects buttons to rate IMDb movies on serverUrl('/') . PHP_EOL; ?> // @namespace mQueue // @include * -// @exclude serverUrl() . $this->baseUrl(''); ?>* +// @exclude serverUrl('/'); ?>* // ==/UserScript== /*global document, XPathResult */ @@ -11,6 +11,6 @@ if (document.evaluate('count(//a[contains(@href, "title/tt")])', document, null, XPathResult.NUMBER_TYPE, null).numberValue > 0) { const script = document.createElement('script'); - script.src = 'serverUrl() . $this->baseUrl('js/remote.js'); ?>'; + script.src = 'serverUrl('/js/remote.js'); ?>'; document.getElementsByTagName('head')[0].appendChild(script); } diff --git a/application/views/scripts/js/remote-js.phtml b/templates/app/remote-js.phtml similarity index 59% rename from application/views/scripts/js/remote-js.phtml rename to templates/app/remote-js.phtml index dd65676..0ced89f 100644 --- a/application/views/scripts/js/remote-js.phtml +++ b/templates/app/remote-js.phtml @@ -1,4 +1,4 @@ -/*global $, document, injectStatus */ +minimize ? 'min/' : ''); ?>/*global $, document, injectStatus */ (function(){ (function($){ @@ -7,7 +7,7 @@ $(document).ready(function() { $("head").append(''); -mqueue.monitorLinks('serverUrl() . $this->baseUrl('') ?>'); +mqueue.monitorLinks('serverUrl() ?>'); }); })(jQuery.noConflict(true)); })(); diff --git a/application/views/scripts/status/index.phtml b/templates/app/status/index.phtml similarity index 100% rename from application/views/scripts/status/index.phtml rename to templates/app/status/index.phtml diff --git a/templates/app/user/index.phtml b/templates/app/user/index.phtml new file mode 100644 index 0000000..02d5f96 --- /dev/null +++ b/templates/app/user/index.phtml @@ -0,0 +1,33 @@ +headTitle(_tr('Users')); ?> +

    translate('Users list'); ?>

    + + '; +echo ''; +foreach (Rating::possibleChoices() as $rating) { + echo ''; +} +echo ''; +echo ''; +echo ''; + +// Table data +foreach ($this->users as $user) { + echo ''; + echo ''; + $stats = $user->getStatistics(); + foreach (Rating::possibleChoices() as $rating) { + echo ''; + } + echo ''; + echo ''; + echo ''; +} +?> +
    ' . $this->translate('User') . '' . $rating->name() . '' . $this->translate('Rated') . '' . $this->translate('Not rated') . '
    ' . $this->gravatar($user) . ' ' . $this->escapeHtml($user->nickname) . '' . $stats[$rating->value] . '' . $stats['rated'] . '' . $stats[Rating::Nothing->value] . '
    + +

    translate('Timeline'); ?>

    +graph(); ?> diff --git a/application/views/scripts/user/login.phtml b/templates/app/user/login.phtml similarity index 69% rename from application/views/scripts/user/login.phtml rename to templates/app/user/login.phtml index e1e9a71..931e1a0 100644 --- a/application/views/scripts/user/login.phtml +++ b/templates/app/user/login.phtml @@ -1,3 +1,3 @@ headTitle(_tr('Login')); ?>

    translate('Login'); ?>

    -form; ?> \ No newline at end of file +form($this->form); ?> diff --git a/application/views/scripts/user/logout.phtml b/templates/app/user/logout.phtml similarity index 100% rename from application/views/scripts/user/logout.phtml rename to templates/app/user/logout.phtml diff --git a/application/views/scripts/user/new.phtml b/templates/app/user/new.phtml similarity index 72% rename from application/views/scripts/user/new.phtml rename to templates/app/user/new.phtml index effd13c..fa52895 100644 --- a/application/views/scripts/user/new.phtml +++ b/templates/app/user/new.phtml @@ -1,3 +1,3 @@ headTitle(_tr('Subscribe')); ?>

    translate('Create new user'); ?>

    -form; ?> \ No newline at end of file +form($this->form); ?> diff --git a/templates/app/user/view.phtml b/templates/app/user/view.phtml new file mode 100644 index 0000000..eb53b23 --- /dev/null +++ b/templates/app/user/view.phtml @@ -0,0 +1,40 @@ +headTitle($this->user->nickname); ?> +gravatar($this->user, 'big'); ?> +

    escapeHtml($this->user->nickname); ?>

    translate('Statistics'); ?>

    + + + '; +foreach (Rating::possibleChoices() as $rating) { + echo ''; +} +echo ''; +echo ''; +echo ''; + +$stats = $this->user->getStatistics(); +echo ''; +foreach (Rating::possibleChoices() as $rating) { + echo ''; +} +echo ''; +echo ''; +echo ''; +?> +
    ' . $rating->name() . '' . $this->translate('Rated') . '' . $this->translate('Not rated') . '
    ' . $stats[$rating->value] . '' . $stats['rated'] . '' . $stats[Rating::Nothing->value] . '
    + +

    translate('Timeline'); ?>

    +graph($this->user); ?> + +

    translate('Activity'); ?>

    +activity($this->userActivity, ['user']); ?> + +alternateFormats([ + 'rss' => $this->url('activity.user', ['user' => $this->user->id]), +], 'mQueue - ' . _tr('Activity for %s', [$this->user->nickname])); +?> +paginationControl($this->userActivity); ?> diff --git a/templates/error/404.phtml b/templates/error/404.phtml new file mode 100644 index 0000000..fe545ac --- /dev/null +++ b/templates/error/404.phtml @@ -0,0 +1,4 @@ +headTitle('404 Not Found'); ?> + +

    Oops!

    +

    404 Not Found error

    diff --git a/templates/error/error.phtml b/templates/error/error.phtml new file mode 100644 index 0000000..3908449 --- /dev/null +++ b/templates/error/error.phtml @@ -0,0 +1,10 @@ +headTitle(sprintf('%d %s', $this->status, $this->message ?: $this->reason)); ?> + +

    Oops!

    +message): ?> +

    escapeHtml($this->message) ?>

    +

    escapeHtml(sprintf('%d %s', $this->status, $this->reason)) ?>

    + + error);?> + + diff --git a/templates/layout/default.phtml b/templates/layout/default.phtml new file mode 100644 index 0000000..fdd17a0 --- /dev/null +++ b/templates/layout/default.phtml @@ -0,0 +1,61 @@ + + + + + + + + googleAnalytics(); +echo $this->headTitle('Application')->setSeparator(' - '); +echo $this->headLink([ + 'rel' => 'icon', + 'href' => $this->serverUrl('/images/favicon.png'), + 'PREPEND', +]) + ->appendStylesheet('/css/application.css', 'all') + ->appendStylesheet($this->serverUrl('/css/gravatar.css'), 'all') + ->prependAlternate($this->serverUrl($this->url('activity.index', [], ['format' => 'rss', 'lang' => $lang])), 'application/rss+xml', $this->translate('Application - Overall activity')); + +// Set localization file for timeago +$path = '/js/min/jquery.timeago.' . $lang . '.js'; +if (is_file('public' . $path)) { + $this->headScript()->prependFile($path); +} + +echo $this->headScript() + ->prependDirectory( + '/js/min/application.js', + '/js/application', + ); +?> + + +
    + +
    + flashMessenger(); ?> + layout()->content; ?> +
    +
    + + + diff --git a/templates/layout/error.phtml b/templates/layout/error.phtml new file mode 100644 index 0000000..dc79e29 --- /dev/null +++ b/templates/layout/error.phtml @@ -0,0 +1,5 @@ +isError = true; + +require 'default.phtml'; diff --git a/application/views/scripts/pagination.phtml b/templates/partial/pagination.phtml similarity index 72% rename from application/views/scripts/pagination.phtml rename to templates/partial/pagination.phtml index 8cf9ad5..f698a31 100644 --- a/application/views/scripts/pagination.phtml +++ b/templates/partial/pagination.phtml @@ -12,7 +12,7 @@ if (!is_array($this->permanentParams)) { pagesInRange as $page): ?> - current): ?> + current): ?> @@ -30,18 +30,18 @@ if (!is_array($this->permanentParams)) { itemCountPerPage == $p) { - echo $p; - } else { - echo '' . $p . ''; - } +$perPage = [25, 50, 100, 200]; +foreach ($perPage as $p) { + if ($this->itemCountPerPage === $p) { + echo $p; + } else { + echo '' . $p . ''; + } - if ($p != end($perPage)) { - echo ', '; - } - } - ?> + if ($p !== end($perPage)) { + echo ', '; + } +} +?>

    diff --git a/tests/ApplicationTest/DBAL/Types/ChronosTypeTest.php b/tests/ApplicationTest/DBAL/Types/ChronosTypeTest.php new file mode 100644 index 0000000..1e40876 --- /dev/null +++ b/tests/ApplicationTest/DBAL/Types/ChronosTypeTest.php @@ -0,0 +1,71 @@ +type = new ChronosType(); + $this->platform = new MySQLPlatform(); + } + + public function testConvertToDatabaseValue(): void + { + self::assertSame('DATETIME', $this->type->getSqlDeclaration(['foo'], $this->platform)); + + $actual = $this->type->convertToDatabaseValue(new Chronos('2016-01-01 15:58:59'), $this->platform); + self::assertSame('2016-01-01 15:58:59', $actual, 'support Chronos'); + + $actual = $this->type->convertToDatabaseValue(new DateTimeImmutable('2016-01-01 15:58:59'), $this->platform); + self::assertSame('2016-01-01 15:58:59', $actual, 'support DateTimeImmutable'); + + self::assertNull($this->type->convertToDatabaseValue(null, $this->platform), 'support null values'); + } + + public function testConvertToPHPValue(): void + { + $actualPhp = $this->type->convertToPHPValue('2016-01-01 15:58:59', $this->platform); + self::assertInstanceOf(Chronos::class, $actualPhp); + self::assertSame('2016-01-01 15:58:59', $actualPhp->__toString(), 'support string'); + + $actualPhp = $this->type->convertToPHPValue(new Chronos('2016-01-01 15:58:59'), $this->platform); + self::assertInstanceOf(Chronos::class, $actualPhp); + self::assertSame('2016-01-01 15:58:59', $actualPhp->__toString(), 'support Chronos'); + + $actualPhp = $this->type->convertToPHPValue(new DateTimeImmutable('2016-01-01 15:58:59'), $this->platform); + self::assertInstanceOf(Chronos::class, $actualPhp); + self::assertSame('2016-01-01 15:58:59', $actualPhp->__toString(), 'support DateTimeImmutable'); + + self::assertNull($this->type->convertToPHPValue(null, $this->platform), 'support null values'); + } + + public function testConvertToPHPValueThrowsWithInvalidValue(): void + { + $this->expectException(InvalidFormat::class); + + $this->type->convertToPHPValue(123, $this->platform); + } + + public function testConvertToDatabaseValueThrowsWithInvalidValue(): void + { + $this->expectException(InvalidType::class); + + $this->type->convertToDatabaseValue(123, $this->platform); + } +} diff --git a/tests/ApplicationTest/DBAL/Types/DateTypeTest.php b/tests/ApplicationTest/DBAL/Types/DateTypeTest.php new file mode 100644 index 0000000..0bc2a8f --- /dev/null +++ b/tests/ApplicationTest/DBAL/Types/DateTypeTest.php @@ -0,0 +1,63 @@ +type = new DateType(); + $this->platform = new MySQLPlatform(); + } + + public function testConvertToDatabaseValue(): void + { + self::assertSame('DATE', $this->type->getSqlDeclaration(['foo'], $this->platform)); + + $actual = $this->type->convertToDatabaseValue(new ChronosDate('2016-01-01'), $this->platform); + self::assertSame('2016-01-01', $actual, 'support Chronos'); + + self::assertNull($this->type->convertToDatabaseValue(null, $this->platform), 'support null values'); + } + + public function testConvertToPHPValue(): void + { + $actualPhp = $this->type->convertToPHPValue('2022-12-31', $this->platform); + self::assertInstanceOf(ChronosDate::class, $actualPhp); + self::assertSame('2022-12-31', $actualPhp->__toString(), 'support string'); + + $actualPhp = $this->type->convertToPHPValue(new ChronosDate('2022-12-31'), $this->platform); + self::assertInstanceOf(ChronosDate::class, $actualPhp); + self::assertSame('2022-12-31', $actualPhp->__toString(), 'support ChronosDate'); + + self::assertNull($this->type->convertToPHPValue(null, $this->platform), 'support null values'); + } + + public function testConvertToPHPValueThrowsWithInvalidValue(): void + { + $this->expectException(InvalidFormat::class); + + $this->type->convertToPHPValue(123, $this->platform); + } + + public function testConvertToDatabaseValueThrowsWithInvalidValue(): void + { + $this->expectException(InvalidType::class); + + $this->type->convertToDatabaseValue(123, $this->platform); + } +} diff --git a/tests/ApplicationTest/Handler/AboutPageHandlerTest.php b/tests/ApplicationTest/Handler/AboutPageHandlerTest.php new file mode 100644 index 0000000..f9829dc --- /dev/null +++ b/tests/ApplicationTest/Handler/AboutPageHandlerTest.php @@ -0,0 +1,17 @@ +handle(new ServerRequest(uri: '/about')); + + self::assertInstanceOf(HtmlResponse::class, $response); + self::assertStringContainsString('

    This project started as a personal need', $response->getBody()); + } +} diff --git a/tests/ApplicationTest/Handler/AbstractHandler.php b/tests/ApplicationTest/Handler/AbstractHandler.php new file mode 100644 index 0000000..ccac8eb --- /dev/null +++ b/tests/ApplicationTest/Handler/AbstractHandler.php @@ -0,0 +1,27 @@ +clear(); + + parent::tearDown(); + } + + protected function handle(ServerRequestInterface $request): ResponseInterface + { + // @var \Mezzio\Application $app + global $app; + + return $app->handle($request); + } +} diff --git a/tests/ApplicationTest/Handler/ActivityHandlerTest.php b/tests/ApplicationTest/Handler/ActivityHandlerTest.php new file mode 100644 index 0000000..064c964 --- /dev/null +++ b/tests/ApplicationTest/Handler/ActivityHandlerTest.php @@ -0,0 +1,42 @@ +handle(new ServerRequest(uri: '/activity')); + + self::assertInstanceOf(HtmlResponse::class, $response); + self::assertStringContainsString('

    Overall activity', $response->getBody()); + } + + public function testUser(): void + { + $response = $this->handle(new ServerRequest(uri: '/activity/user/1001')); + + self::assertInstanceOf(HtmlResponse::class, $response); + self::assertStringContainsString('

    Activity for user1', $response->getBody()); + } + + public function testMovie(): void + { + $response = $this->handle(new ServerRequest(uri: '/activity/movie/96446')); + + self::assertInstanceOf(HtmlResponse::class, $response); + self::assertStringContainsString('

    Activity for Willow', $response->getBody()); + } + + public function testRss(): void + { + $response = $this->handle(new ServerRequest(uri: '/activity', queryParams: ['format' => 'rss'])); + + self::assertInstanceOf(XmlResponse::class, $response); + self::assertStringContainsString('mQueue - Overall activity', $response->getBody()); + } +} diff --git a/tests/ApplicationTest/Handler/FaqPageHandlerTest.php b/tests/ApplicationTest/Handler/FaqPageHandlerTest.php new file mode 100644 index 0000000..a7a3174 --- /dev/null +++ b/tests/ApplicationTest/Handler/FaqPageHandlerTest.php @@ -0,0 +1,18 @@ +handle(new ServerRequest(uri: '/faq')); + + self::assertInstanceOf(HtmlResponse::class, $response); + self::assertStringContainsString('Create an account', $response->getBody()); + self::assertStringContainsString('I want to see this movie', $response->getBody()); + } +} diff --git a/tests/ApplicationTest/Handler/GravatarPageHandlerTest.php b/tests/ApplicationTest/Handler/GravatarPageHandlerTest.php new file mode 100644 index 0000000..6665800 --- /dev/null +++ b/tests/ApplicationTest/Handler/GravatarPageHandlerTest.php @@ -0,0 +1,19 @@ +handle(new ServerRequest(uri: '/css/gravatar.css')); + + self::assertInstanceOf(HtmlResponse::class, $response); + self::assertSame(['text/css'], $response->getHeader('content-type')); + self::assertStringContainsString('span.gravatar', $response->getBody()); + self::assertStringContainsString('span.gravatar.big', $response->getBody()); + } +} diff --git a/tests/ApplicationTest/Handler/HomePageHandlerTest.php b/tests/ApplicationTest/Handler/HomePageHandlerTest.php new file mode 100644 index 0000000..6de0041 --- /dev/null +++ b/tests/ApplicationTest/Handler/HomePageHandlerTest.php @@ -0,0 +1,31 @@ +handle(new ServerRequest(uri: '/')); + + self::assertInstanceOf(RedirectResponse::class, $response); + self::assertSame(['/activity'], $response->getHeader('location')); + } + + public function testWithUser(): void + { + $this->setCurrentUser('user1'); + $response = $this->handle(new ServerRequest(uri: '/')); + + self::assertInstanceOf(RedirectResponse::class, $response); + self::assertSame(['/movie'], $response->getHeader('location')); + } +} diff --git a/tests/ApplicationTest/Handler/JsMqueueUserHandlerTest.php b/tests/ApplicationTest/Handler/JsMqueueUserHandlerTest.php new file mode 100644 index 0000000..5ad5f47 --- /dev/null +++ b/tests/ApplicationTest/Handler/JsMqueueUserHandlerTest.php @@ -0,0 +1,18 @@ +handle(new ServerRequest(uri: '/js/mqueue-user.js')); + + self::assertInstanceOf(TextResponse::class, $response); + self::assertSame(['application/javascript; charset=utf-8'], $response->getHeader('content-type')); + self::assertStringContainsString('document.evaluate', $response->getBody()); + } +} diff --git a/tests/ApplicationTest/Handler/JsRemoteHandlerTest.php b/tests/ApplicationTest/Handler/JsRemoteHandlerTest.php new file mode 100644 index 0000000..7bc4b89 --- /dev/null +++ b/tests/ApplicationTest/Handler/JsRemoteHandlerTest.php @@ -0,0 +1,19 @@ +handle(new ServerRequest(uri: '/js/remote.js')); + + self::assertInstanceOf(TextResponse::class, $response); + self::assertSame(['application/javascript; charset=utf-8'], $response->getHeader('content-type')); + self::assertStringContainsString('jquery', $response->getBody()); + self::assertStringContainsString('mqueue', $response->getBody()); + } +} diff --git a/tests/ApplicationTest/Handler/Movie/AddHandlerTest.php b/tests/ApplicationTest/Handler/Movie/AddHandlerTest.php new file mode 100644 index 0000000..e82d054 --- /dev/null +++ b/tests/ApplicationTest/Handler/Movie/AddHandlerTest.php @@ -0,0 +1,27 @@ +handle(new ServerRequest(uri: '/movie/add')); + + self::assertInstanceOf(HtmlResponse::class, $response); + self::assertStringContainsString('IMDb url or id', $response->getBody()); + self::assertStringContainsString('learn how to add', $response->getBody()); + } + + public function testAddSubmitted(): void + { + $response = $this->handle(new ServerRequest(uri: '/movie/add', queryParams: ['id' => 'https://www.imdb.com/title/tt0096446/'])); + + self::assertInstanceOf(HtmlResponse::class, $response); + self::assertStringContainsString('Willow', $response->getBody()); + } +} diff --git a/tests/ApplicationTest/Handler/Movie/IndexHandlerTest.php b/tests/ApplicationTest/Handler/Movie/IndexHandlerTest.php new file mode 100644 index 0000000..f2d5a13 --- /dev/null +++ b/tests/ApplicationTest/Handler/Movie/IndexHandlerTest.php @@ -0,0 +1,65 @@ +handle(new ServerRequest(uri: '/movie')); + + self::assertInstanceOf(HtmlResponse::class, $response); + self::assertStringContainsString('Filter', $response->getBody()); + self::assertStringContainsString('Title', $response->getBody()); + } + + public function testIndexFiltered(): void + { + $response = $this->handle(new ServerRequest(uri: '/movie', queryParams: [ + 'filter1' => [ + 'user' => 1001, + 'condition' => 'is', + 'status' => [0, 1, 2, 3, 4, 5], + 'title' => '', + 'withSource' => false, + ], + ])); + + self::assertInstanceOf(HtmlResponse::class, $response); + self::assertStringContainsString('Filter', $response->getBody()); + self::assertStringContainsString('Title', $response->getBody()); + } + + public function testIndexSearch(): void + { + $response = $this->handle(new ServerRequest(uri: '/movie', queryParams: ['search' => 'will'])); + + self::assertInstanceOf(HtmlResponse::class, $response); + self::assertStringContainsString('Willow', $response->getBody()); + } + + public function testIndexRss(): void + { + $response = $this->handle(new ServerRequest(uri: '/movie', queryParams: ['format' => 'rss'])); + + self::assertInstanceOf(XmlResponse::class, $response); + self::assertStringContainsString('mQueue - user1:Need+Bad+Ok+Excellent+Favorite', $response->getBody()); + } + + public function testNoUsers(): void + { + _em()->getConnection()->delete('user'); + $response = $this->handle(new ServerRequest(uri: '/movie')); + + self::assertInstanceOf(HtmlResponse::class, $response); + self::assertStringContainsString('At least one user must exist to access this page', $response->getBody()); + } +} diff --git a/tests/ApplicationTest/Handler/Movie/ViewHandlerTest.php b/tests/ApplicationTest/Handler/Movie/ViewHandlerTest.php new file mode 100644 index 0000000..006c669 --- /dev/null +++ b/tests/ApplicationTest/Handler/Movie/ViewHandlerTest.php @@ -0,0 +1,26 @@ +handle(new ServerRequest(uri: '/movie/view/96446')); + + self::assertInstanceOf(HtmlResponse::class, $response); + self::assertStringContainsString('

    Willow

    ', $response->getBody()); + } + + public function testNotFound(): void + { + $response = $this->handle(new ServerRequest(uri: '/movie/view/123')); + + self::assertInstanceOf(HtmlResponse::class, $response); + self::assertStringContainsString('Movie not found', $response->getBody()); + } +} diff --git a/tests/ApplicationTest/Handler/PageHandlerFactoryTest.php b/tests/ApplicationTest/Handler/PageHandlerFactoryTest.php new file mode 100644 index 0000000..5a1b1ca --- /dev/null +++ b/tests/ApplicationTest/Handler/PageHandlerFactoryTest.php @@ -0,0 +1,23 @@ +canCreate($container, HomePageHandler::class)); + + $homePage = $factory($container, HomePageHandler::class); + self::assertInstanceOf(HomePageHandler::class, $homePage); + } +} diff --git a/tests/ApplicationTest/Handler/Status/GraphHandlerTest.php b/tests/ApplicationTest/Handler/Status/GraphHandlerTest.php new file mode 100644 index 0000000..2cc3783 --- /dev/null +++ b/tests/ApplicationTest/Handler/Status/GraphHandlerTest.php @@ -0,0 +1,31 @@ +handle(new ServerRequest(uri: '/status/graph')); + self::assertInstanceOf(JsonResponse::class, $response); + + $body = (string) $response->getBody(); + $chart = json_decode($body, true, JSON_THROW_ON_ERROR); + self::assertArrayHasKey('chart', $chart); + } + + public function testPercent(): void + { + $response = $this->handle(new ServerRequest(uri: '/status/graph', queryParams: ['percent' => true])); + self::assertInstanceOf(JsonResponse::class, $response); + + $body = (string) $response->getBody(); + $chart = json_decode($body, true, JSON_THROW_ON_ERROR); + self::assertArrayHasKey('chart', $chart); + self::assertStringContainsString(' [%]', $chart['yAxis']['title']['text']); + } +} diff --git a/tests/ApplicationTest/Handler/Status/IndexHandlerTest.php b/tests/ApplicationTest/Handler/Status/IndexHandlerTest.php new file mode 100644 index 0000000..93c0b39 --- /dev/null +++ b/tests/ApplicationTest/Handler/Status/IndexHandlerTest.php @@ -0,0 +1,51 @@ +handle(new ServerRequest(uri: '/status/invalid-id')); + + $body = (string) $response->getBody(); + self::assertInstanceOf(HtmlResponse::class, $response); + self::assertStringContainsString('No valid movie specified', $body); + } + + public function testHtml(): void + { + $response = $this->handle(new ServerRequest(uri: '/status/96446')); + + $body = (string) $response->getBody(); + self::assertInstanceOf(HtmlResponse::class, $response); + self::assertStringContainsString('Current status', $body); + } + + public function testNonExistingMovie(): void + { + $response = $this->handle(new ServerRequest(uri: '/status/96446', queryParams: ['jsoncallback' => 'myCallback'])); + + $body = (string) $response->getBody(); + self::assertInstanceOf(JsonCallbackResponse::class, $response); + self::assertStringContainsString('myCallback(', $body); + } + + public function testSetStatus(): void + { + $this->setCurrentUser('user1'); + $response = $this->handle(new ServerRequest(uri: '/status/96446/3')); + + $body = (string) $response->getBody(); + self::assertInstanceOf(HtmlResponse::class, $response); + self::assertStringContainsString('mqueue_status mqueue_status_3 current', $body); + } +} diff --git a/tests/ApplicationTest/Handler/Status/ListHandlerTest.php b/tests/ApplicationTest/Handler/Status/ListHandlerTest.php new file mode 100644 index 0000000..b7d9454 --- /dev/null +++ b/tests/ApplicationTest/Handler/Status/ListHandlerTest.php @@ -0,0 +1,38 @@ +handle(new ServerRequest(uri: '/status/list/movies/1234567')); + self::assertInstanceOf(JsonCallbackResponse::class, $response); + + $body = (string) $response->getBody(); + self::assertStringContainsString('Need', $body); + self::assertStringContainsString('Bad', $body); + self::assertStringContainsString('Ok', $body); + self::assertStringContainsString('Excellent', $body); + self::assertStringContainsString('Favorite', $body); + } + + public function testBasicBiggerID(): void + { + // Can view any movie status with bigger ID (even non-existing movies) + $response = $this->handle(new ServerRequest(uri: '/status/list/movies/1234567890')); + self::assertInstanceOf(JsonCallbackResponse::class, $response); + + $body = (string) $response->getBody(); + self::assertStringContainsString('Need', $body); + self::assertStringContainsString('Bad', $body); + self::assertStringContainsString('Ok', $body); + self::assertStringContainsString('Excellent', $body); + self::assertStringContainsString('Favorite', $body); + } +} diff --git a/tests/ApplicationTest/Handler/User/IndexHandlerTest.php b/tests/ApplicationTest/Handler/User/IndexHandlerTest.php new file mode 100644 index 0000000..61ff8a4 --- /dev/null +++ b/tests/ApplicationTest/Handler/User/IndexHandlerTest.php @@ -0,0 +1,18 @@ +handle(new ServerRequest(uri: '/user')); + + self::assertInstanceOf(HtmlResponse::class, $response); + self::assertStringContainsString('

    Users list', $response->getBody()); + } +} diff --git a/tests/ApplicationTest/Handler/User/LoginHandlerTest.php b/tests/ApplicationTest/Handler/User/LoginHandlerTest.php new file mode 100644 index 0000000..416cb5e --- /dev/null +++ b/tests/ApplicationTest/Handler/User/LoginHandlerTest.php @@ -0,0 +1,80 @@ +handle(new ServerRequest(uri: '/user/login', serverParams: ['HTTP_REFERER' => 'https://mqueue.lan/movie/add'])); + + self::assertInstanceOf(HtmlResponse::class, $response); + self::assertStringContainsString('"https://mqueue.lan/movie/add"', $response->getBody()); + } + + public function testLoginAndLogout(): void + { + self::assertNull(User::getCurrent(), 'at first we are not logged in'); + + $response = $this->handle(new ServerRequest(uri: '/user/login') + ->withMethod('POST') + ->withParsedBody([ + 'email' => 'user1@example.com', + 'password' => 'user1', + ])); + + self::assertInstanceOf(RedirectResponse::class, $response); + self::assertSame(['/movie'], $response->getHeader('location'), 'successful subscription redirect to movie list'); + self::assertNotNull(User::getCurrent(), 'after login, we are login'); + self::assertSame('user1', User::getCurrent()->nickname); + + $response = $this->handle(new ServerRequest(uri: '/user/logout')); + + self::assertInstanceOf(HtmlResponse::class, $response); + self::assertStringContainsString('You successfully log out', $response->getBody()); + self::assertNull(User::getCurrent(), 'after logged out, we are logged out'); + } + + public function testFailedLogin(): void + { + self::assertNull(User::getCurrent(), 'at first we are not logged in'); + + $response = $this->handle(new ServerRequest(uri: '/user/login') + ->withMethod('POST') + ->withParsedBody([ + 'email' => 'user1@example.com', + 'password' => 'invalid', + ])); + + self::assertInstanceOf(RedirectResponse::class, $response); + self::assertSame(['/user/login'], $response->getHeader('location'), 'successful subscription redirect to movie list'); + self::assertNull(User::getCurrent(), 'after login, we are login'); + } + + public function testRedirect(): void + { + self::assertNull(User::getCurrent(), 'at first we are not logged in'); + + $response = $this->handle(new ServerRequest(uri: '/user/login', serverParams: ['HTTP_REFERER' => 'https://mqueue.lan/movie/add']) + ->withMethod('POST') + ->withParsedBody([ + 'email' => 'user1@example.com', + 'password' => 'user1', + 'referrer' => '/movie/add', + ])); + + self::assertInstanceOf(RedirectResponse::class, $response); + self::assertSame(['/movie/add'], $response->getHeader('location'), 'successful subscription redirect to REFERER'); + } +} diff --git a/tests/ApplicationTest/Handler/User/NewHandlerTest.php b/tests/ApplicationTest/Handler/User/NewHandlerTest.php new file mode 100644 index 0000000..93f6bfa --- /dev/null +++ b/tests/ApplicationTest/Handler/User/NewHandlerTest.php @@ -0,0 +1,43 @@ +handle(new ServerRequest(uri: '/user/new')); + + self::assertInstanceOf(HtmlResponse::class, $response); + self::assertStringContainsString('Password:', $response->getBody()); + } + + public function testNew(): void + { + self::assertNull(User::getCurrent(), 'at first we are not logged in'); + + $response = $this->handle(new ServerRequest(uri: '/user/new') + ->withMethod('POST') + ->withParsedBody([ + 'nickname' => 'new test user', + 'email' => 'new_valid@email.org', + 'password' => 'superpassword', + ])); + + self::assertInstanceOf(RedirectResponse::class, $response); + self::assertSame(['/movie'], $response->getHeader('location'), 'successful subscription redirect to movie list'); + self::assertNotNull(User::getCurrent(), 'after subscription, we are automatically logged in'); + self::assertSame('new test user', User::getCurrent()->nickname); + } +} diff --git a/tests/ApplicationTest/Handler/User/ViewHandlerTest.php b/tests/ApplicationTest/Handler/User/ViewHandlerTest.php new file mode 100644 index 0000000..b98db8c --- /dev/null +++ b/tests/ApplicationTest/Handler/User/ViewHandlerTest.php @@ -0,0 +1,29 @@ +handle(new ServerRequest(uri: '/user/view/1001')); + + self::assertInstanceOf(HtmlResponse::class, $response); + self::assertStringContainsString('

    user1

    ', $response->getBody()); + } + + public function testNotFound(): void + { + $response = $this->handle(new ServerRequest(uri: '/user/view/123')); + + self::assertInstanceOf(HtmlResponse::class, $response); + self::assertStringContainsString('User not found', $response->getBody()); + } +} diff --git a/tests/ApplicationTest/InMemoryContainer.php b/tests/ApplicationTest/InMemoryContainer.php new file mode 100644 index 0000000..a21cb64 --- /dev/null +++ b/tests/ApplicationTest/InMemoryContainer.php @@ -0,0 +1,53 @@ + + */ + public array $services = []; + + public function setService(string $name, mixed $service): void + { + $this->services[$name] = $service; + } + + /** + * @param string $id + * + * @return mixed + */ + public function get($id) + { + if (!$this->has($id)) { + throw new RuntimeException(sprintf('Service not found "%s"', $id)); + } + + return $this->services[$id]; + } + + /** + * @param string $id + * + * @return bool + */ + public function has($id) + { + return array_key_exists($id, $this->services); + } +} diff --git a/tests/ApplicationTest/Middleware/AuthenticationMiddlewareTest.php b/tests/ApplicationTest/Middleware/AuthenticationMiddlewareTest.php new file mode 100644 index 0000000..a4377b1 --- /dev/null +++ b/tests/ApplicationTest/Middleware/AuthenticationMiddlewareTest.php @@ -0,0 +1,90 @@ +process(false, null); + + self::assertFalse($session->has('user')); + self::assertTrue($session->has('other')); + self::assertNull(User::getCurrent()); + } + + public function testUserNotFound(): void + { + $user = null; + $session = $this->process(true, $user); + + self::assertFalse($session->has('user')); + self::assertFalse($session->has('other')); + self::assertNull(User::getCurrent()); + } + + public function testUserNoLimit(): void + { + $user = new User(); + $session = $this->process(true, $user); + + self::assertTrue($session->has('user')); + self::assertTrue($session->has('other')); + self::assertSame($user, User::getCurrent()); + } + + private function process(bool $userInSession, ?User $user): SessionInterface + { + User::setCurrent(null); + + $userRepository = new class($user) extends UserRepository { + public function __construct( + private readonly ?User $user, + ) {} + + public function findOneById(int $id): ?User + { + return $this->user; + } + }; + + $session = new Session(['other' => 'foo']); + if ($userInSession) { + $session->set('user', 123); + } + $request = new ServerRequest(); + $request = $request->withAttribute(SessionMiddleware::SESSION_ATTRIBUTE, $session); + + $response = new Response(); + $handler = new class($response) implements RequestHandlerInterface { + public function __construct( + private readonly ResponseInterface $response, + ) {} + + public function handle(ServerRequestInterface $request): ResponseInterface + { + return $this->response; + } + }; + + $middleware = new AuthenticationMiddleware($userRepository); + $middleware->process($request, $handler); + + return $session; + } +} diff --git a/tests/ApplicationTest/Middleware/DetectBrowserLocaleMiddlewareTest.php b/tests/ApplicationTest/Middleware/DetectBrowserLocaleMiddlewareTest.php new file mode 100644 index 0000000..90c5f65 --- /dev/null +++ b/tests/ApplicationTest/Middleware/DetectBrowserLocaleMiddlewareTest.php @@ -0,0 +1,48 @@ +createMock(Translator::class); + $translator->expects(self::once()) + ->method('setLocale') + ->with($expected); + + $session = $this->createMock(SessionInterface::class); + $session->expects(self::once()) + ->method('get') + ->willReturn($sessionValue); + + $request = new ServerRequest(); + $request = $request + ->withQueryParams(['lang' => $preferred]) + ->withAttribute(SessionMiddleware::SESSION_ATTRIBUTE, $session); + + $middleware = new DetectBrowserLocaleMiddleware($translator); + $middleware->process($request, self::createStub(RequestHandlerInterface::class)); + } + + public static function providerProcess(): iterable + { + yield ['fr', null, 'fr']; + yield ['en', null, 'en']; + yield ['foo', null, 'en']; + yield [null, null, 'en']; + yield [null, 'ko', 'ko']; + yield ['fr', 'ko', 'fr']; + yield 'https://bugs.php.net/bug.php?id=81383 should be workaround-ed' => [str_repeat('a', 157), null, 'en']; + } +} diff --git a/tests/ApplicationTest/Model/MovieTest.php b/tests/ApplicationTest/Model/MovieTest.php new file mode 100644 index 0000000..105b01b --- /dev/null +++ b/tests/ApplicationTest/Model/MovieTest.php @@ -0,0 +1,92 @@ +id); + $movie->setId(12); + self::assertSame(12, $movie->id); + } + + public function testGetImdbUrl(): void + { + $movie = new Movie(); + $movie->setId(12); + self::assertSame('https://www.imdb.com/title/tt0000012/', $movie->getImdbUrl()); + } + + #[DataProvider('providerSetSource')] + public function testSetSource(array|false $source, array $expected): void + { + $movie = new Movie(); + $movie->setSource($source); + self::assertSame( + $expected, + [ + 'searchCount' => $movie->searchCount, + 'identity' => $movie->identity, + 'quality' => $movie->quality, + 'score' => $movie->score, + 'source' => $movie->source, + ], + ); + } + + public static function providerSetSource(): iterable + { + yield [ + [ + 'identity' => 10, + 'quality' => 20, + 'score' => 0, + 'link' => 'https://example.com', + ], + [ + 'searchCount' => 1, + 'identity' => 0, + 'quality' => 0, + 'score' => 0, + 'source' => null, + ], + ]; + + yield [ + [ + 'identity' => 10, + 'quality' => 20, + 'score' => 30, + 'link' => 'https://example.com', + ], + [ + 'searchCount' => 1, + 'identity' => 10, + 'quality' => 20, + 'score' => 30, + 'source' => 'https://example.com', + ], + ]; + } +} diff --git a/tests/ApplicationTest/Repository/MovieRepositoryTest.php b/tests/ApplicationTest/Repository/MovieRepositoryTest.php new file mode 100644 index 0000000..c856e50 --- /dev/null +++ b/tests/ApplicationTest/Repository/MovieRepositoryTest.php @@ -0,0 +1,178 @@ +traitSetupWithTransaction(); + $this->repository = _em()->getRepository(Movie::class); + } + + public function testGetOrCreate(): void + { + $newMovie = $this->repository->getOrCreate(1); + self::assertSame('https://www.imdb.com/title/tt0000001/', $newMovie->getImdbUrl()); + + $existingMovie = $this->repository->getOrCreate(96446); + self::assertSame('Willow', $existingMovie->title); + } + + public function testGetAllForSearch(): void + { + $actual = $this->repository->getAllForSearch(); + self::assertSame([96446], $this->toIds($actual)); + } + + public function testGetAllForFetching(): void + { + $actual = $this->repository->getAllForFetching(); + $ids = $this->toIds($actual); + sort($ids); + self::assertSame([96446, 103064, 28650488], $ids); + + $actual = $this->repository->getAllForFetching(200); + $ids = $this->toIds($actual); + sort($ids); + self::assertSame([96446, 103064, 28650488], $ids); + } + + #[DataProvider('providerGetFilteredQuery')] + public function testGetFilteredQuery(array $filters, Sorting $sorting, array $expected): void + { + $actual = $this->repository->getFilteredQuery($filters, $sorting)->getResult(); + self::assertSame($expected, $this->toIds($actual)); + } + + public static function providerGetFilteredQuery(): iterable + { + $sorting1 = new Sorting([], ['movie.title']); + $sorting2 = new Sorting([], ['date']); + $sorting3 = new Sorting([], ['movie.dateSearch']); + $sorting4 = new Sorting([], ['status0.rating']); + + yield 'sort by title' => [ + [], + $sorting1, + [103064, 28650488, 96446], + ]; + + yield 'sort by date' => [ + [], + $sorting2, + [103064, 28650488, 96446], + ]; + + yield 'sort by search date' => [ + [], + $sorting3, + [96446, 28650488, 103064], + ]; + + yield 'filter by status' => [ + [ + 'filter1' => [ + 'user' => 1001, + 'condition' => 'is', + 'status' => [1, 5], + 'title' => '', + 'withSource' => false, + ], + ], + $sorting1, + [96446], + ]; + + yield 'sort by status' => [ + [ + 'filter1' => [ + 'user' => 1001, + 'condition' => 'is', + 'status' => [0, 1, 2, 3, 4, 5], + 'title' => '', + 'withSource' => false, + ]], + $sorting4, + [28650488, 103064, 96446], + ]; + + yield 'filter by NOT status' => [ + [ + 'filter1' => [ + 'user' => 1001, + 'condition' => 'isnot', + 'status' => [1, 5], + 'title' => '', + 'withSource' => false, + ], + ], + $sorting1, + [103064, 28650488], + ]; + + yield 'filter by title' => [ + [ + 'filter1' => [ + 'user' => 1001, + 'condition' => 'is', + 'status' => [0, 1, 2, 3, 4, 5], + 'title' => 'will', + 'withSource' => false, + ], + ], + $sorting1, + [96446], + ]; + + yield 'filter by source' => [ + [ + 'filter1' => [ + 'user' => 1001, + 'condition' => 'is', + 'status' => [0, 1, 2, 3, 4, 5], + 'title' => '', + 'withSource' => true, + ], + ], + $sorting1, + [103064], + ]; + + yield 'filter by multiple users' => [ + [ + 'filter1' => [ + 'user' => 1002, + 'condition' => 'is', + 'status' => [1], + ], + 'filter2' => [ + 'user' => 1003, + 'condition' => 'is', + 'status' => [1], + ], + ], + $sorting1, + [96446], + ]; + } + + public function testDeleteObsoleteSources(): void + { + $this->repository->deleteObsoleteSources(); + self::assertSame(0, $this->getEntityManager()->getConnection()->fetchOne('SELECT COUNT(*) FROM movie WHERE source IS NOT NULL')); + } +} diff --git a/tests/ApplicationTest/Repository/StatusRepositoryTest.php b/tests/ApplicationTest/Repository/StatusRepositoryTest.php new file mode 100644 index 0000000..e7ada5c --- /dev/null +++ b/tests/ApplicationTest/Repository/StatusRepositoryTest.php @@ -0,0 +1,339 @@ +traitSetupWithTransaction(); + $this->repository = _em()->getRepository(Status::class); + } + + public function testGetStatistics(): void + { + $user = $this->getEntityManager()->getReference(User::class, 1001); + $actual = $this->repository->getStatistics($user); + + self::assertSame([ + 'total' => 3, + 'rated' => 1, + 0 => 2, + 1 => 0, + 2 => 0, + 3 => 0, + 4 => 0, + 5 => 1, + ], $actual); + } + + public function testGetActivityQuery(): void + { + $user = $this->getEntityManager()->getReference(User::class, 1001); + $movie = $this->getEntityManager()->getReference(Movie::class, 96446); + + $actual = $this->repository->getActivityQuery($user)->getResult(); + self::assertSame([ + 2004, + 2003, + 2002, + 2001, + ], $this->toIds($actual)); + + $actual = $this->repository->getActivityQuery($movie)->getResult(); + self::assertSame([ + 2002, + 2005, + 2007, + 2001, + ], $this->toIds($actual)); + + $actual = $this->repository->getActivityQuery(null)->getResult(); + self::assertSame([ + 2004, + 2003, + 2002, + 2005, + 2006, + 2007, + 2001, + ], $this->toIds($actual)); + + } + + public function testSet(): void + { + $user = $this->getEntityManager()->getReference(User::class, 1001); + $movie = $this->getEntityManager()->getReference(Movie::class, 96446); + + $status = $this->repository->set($movie, $user, Rating::Bad); + self::assertSame($movie, $status->movie); + self::assertSame($user, $status->user); + self::assertSame(Rating::Bad, $status->rating); + self::assertNotNull($status->id); + } + + public function testGetAllByMoviesAndUser(): void + { + $user = $this->getEntityManager()->getReference(User::class, 1001); + $actual = $this->repository->getAllByMoviesAndUser([], null); + self::assertSame([], $actual); + + $actual = $this->repository->getAllByMoviesAndUser([96446, 123], null); + self::assertSame([ + 96446 => null, + 123 => null, + ], $this->toIds($actual)); + + $actual = $this->repository->getAllByMoviesAndUser([96446, 123], $user); + self::assertSame([ + 96446 => 2002, + 123 => null, + ], $this->toIds($actual)); + } + + public function testGetOneByMovieAndUser(): void + { + $user = $this->getEntityManager()->getReference(User::class, 1001); + + $status = $this->repository->getOneByMovieAndUser(96446, $user); + self::assertSame(96446, $status->movie->id); + self::assertSame($user, $status->user); + self::assertSame(Rating::Favorite, $status->rating); + self::assertNotNull($status->id); + + $status = $this->repository->getOneByMovieAndUser(123, $user); + self::assertSame(123, $status->movie->id); + self::assertSame($user, $status->user); + self::assertSame(Rating::Nothing, $status->rating); + self::assertNull($status->id); + } + + public function testGetGraph(): void + { + $user = $this->getEntityManager()->getReference(User::class, 1001); + $actual = $this->repository->getGraph(null, false); + self::assertSame([ + [ + 'name' => 'Need', + 'data' => [ + [ + 1577890800000, + 2, + ], + ], + ], + [ + 'name' => 'Bad', + 'data' => [ + [ + 1577977200000, + 1, + ], + [ + 1578063600000, + 0, + ], + ], + ], + [ + 'name' => 'Ok', + 'data' => [ + [ + 1577804400000, + 1, + ], + [ + 1577890800000, + 0, + ], + ], + ], + [ + 'name' => 'Excellent', + 'data' => [], + ], + [ + 'name' => 'Favorite', + 'data' => [ + [ + 1577890800000, + 2, + ], + ], + ], + ], $actual); + + $actual = $this->repository->getGraph($user, false); + self::assertSame([ + [ + 'name' => 'Need', + 'data' => [], + ], + [ + 'name' => 'Bad', + 'data' => [ + [ + 1577977200000, + 1, + ], + [ + 1578063600000, + 0, + ], + ], + ], + [ + 'name' => 'Ok', + 'data' => [ + [ + 1577804400000, + 1, + ], + [ + 1577890800000, + 0, + ], + ], + ], + [ + 'name' => 'Excellent', + 'data' => [], + ], + [ + 'name' => 'Favorite', + 'data' => [ + [ + 1577890800000, + 1, + ], + ], + ], + ], $actual); + + $actual = $this->repository->getGraph($user, true); + self::assertSame([ + [ + 'name' => 'Need', + 'data' => [ + [ + 1577804400000, + 0, + ], + [ + 1577890800000, + 0, + ], + [ + 1577977200000, + 0, + ], + [ + 1578063600000, + 0, + ], + ], + ], + [ + 'name' => 'Bad', + 'data' => [ + [ + 1577804400000, + 0, + ], + [ + 1577890800000, + 0, + ], + [ + 1577977200000, + 1, + ], + [ + 1578063600000, + 0, + ], + ], + ], + [ + 'name' => 'Ok', + 'data' => [ + [ + 1577804400000, + 1, + ], + [ + 1577890800000, + 0, + ], + [ + 1577977200000, + 0, + ], + [ + 1578063600000, + 0, + ], + ], + ], + [ + 'name' => 'Excellent', + 'data' => [ + [ + 1577804400000, + 0, + ], + [ + 1577890800000, + 0, + ], + [ + 1577977200000, + 0, + ], + [ + 1578063600000, + 0, + ], + ], + ], + [ + 'name' => 'Favorite', + 'data' => [ + [ + 1577804400000, + 0, + ], + [ + 1577890800000, + 1, + ], + [ + 1577977200000, + 1, + ], + [ + 1578063600000, + 1, + ], + ], + ], + ], $actual); + } +} diff --git a/tests/ApplicationTest/Repository/UserRepositoryTest.php b/tests/ApplicationTest/Repository/UserRepositoryTest.php new file mode 100644 index 0000000..db281cb --- /dev/null +++ b/tests/ApplicationTest/Repository/UserRepositoryTest.php @@ -0,0 +1,60 @@ +traitSetupWithTransaction(); + $this->repository = _em()->getRepository(User::class); + } + + public function testInsertUser(): void + { + $user = $this->repository->insertUser([ + 'nickname' => 'newuser', + 'email' => 'newuser@example.com', + 'password' => 'mypassword', + ]); + + self::assertSame('newuser', $user->nickname); + self::assertSame('newuser@example.com', $user->email); + self::assertSame('91dfd9ddb4198affc5c194cd8ce6d338fde470e2', $user->password); + self::assertNotNull($user->id); + } + + public function testGetByEmailAndPassword(): void + { + $user = $this->repository->getByEmailAndPassword('user1@example.com', 'user1'); + self::assertSame('user1', $user?->nickname); + + $user = $this->repository->getByEmailAndPassword('user1@example.com', 'invalid password'); + self::assertNull($user); + + $user = $this->repository->getByEmailAndPassword('invalid email@example.com', 'user1'); + self::assertNull($user); + + $user = $this->repository->getByEmailAndPassword('user2@example.com', 'user1'); + self::assertNull($user); + + $user = $this->repository->getByEmailAndPassword('user2@example.com', 'user2'); + self::assertSame('user2', $user?->nickname); + } + + public function testGetAll(): void + { + self::assertCount(3, $this->repository->getAll()); + } +} diff --git a/tests/ApplicationTest/Traits/TestWithTransactionAndUser.php b/tests/ApplicationTest/Traits/TestWithTransactionAndUser.php new file mode 100644 index 0000000..65b3e15 --- /dev/null +++ b/tests/ApplicationTest/Traits/TestWithTransactionAndUser.php @@ -0,0 +1,72 @@ +id; + } + + return $models; + } + + /** + * Start transaction. + */ + protected function setUp(): void + { + $this->getEntityManager()->clear(); + $this->getEntityManager()->beginTransaction(); + + User::setCurrent(null); + } + + /** + * Cancel transaction, to undo all changes made. + */ + protected function tearDown(): void + { + User::setCurrent(null); + + $this->getEntityManager()->rollback(); + $this->getEntityManager()->clear(); + $this->getEntityManager()->getConnection()->close(); + + parent::tearDown(); + } + + /** + * @return ($email is 'anonymous' ? null : User) + */ + protected function setCurrentUser(string $email): ?User + { + $user = null; + if ($email !== 'anonymous') { + $email = $email . '@example.com'; + $userRepository = $this->getEntityManager()->getRepository(User::class); + $user = $userRepository->findOneByEmail($email); + self::assertNotNull($user, 'given email must exist in test DB: ' . $email); + } + + User::setCurrent($user); + + return $user; + } +} diff --git a/tests/bootstrap.php b/tests/bootstrap.php deleted file mode 100644 index 48de151..0000000 --- a/tests/bootstrap.php +++ /dev/null @@ -1,8 +0,0 @@ - 'index', 'controller' => 'about', 'module' => 'default']; - $url = $this->url($this->urlizeOptions($params)); - $this->dispatch($url); - - // assertions - $this->assertModule($params['module']); - $this->assertController($params['controller']); - $this->assertAction($params['action']); - - $this->assertQueryContentContains('p', 'This project started as a personal need'); - } -} diff --git a/tests/mQueueTest/Controller/AbstractControllerTestCase.php b/tests/mQueueTest/Controller/AbstractControllerTestCase.php deleted file mode 100644 index 270dcee..0000000 --- a/tests/mQueueTest/Controller/AbstractControllerTestCase.php +++ /dev/null @@ -1,85 +0,0 @@ - 'test user', - 'email' => 'valid@email.org', - 'password' => 'superpassword', - ]; - - protected $movieData = [ - 'id' => '0096446', - 'title' => 'Willow (1988)', - ]; - - protected function setUp(): void - { - $this->bootstrap = new Zend_Application( - APPLICATION_ENV, - [ - 'config' => [ - APPLICATION_PATH . '/configs/application.ini', - ], - ] - ); - - $this->testUser = \mQueue\Model\UserMapper::findEmailPassword($this->userData['email'], $this->userData['password']); - if (!$this->testUser) { - $this->testUser = \mQueue\Model\UserMapper::insertUser($this->userData); - } - - $movie = \mQueue\Model\MovieMapper::find($this->movieData['id']); - if (!$movie) { - \mQueue\Model\MovieMapper::getDbTable()->createRow($this->movieData)->save(); - } - - parent::setUp(); - } - - public function loginUser($login, $password): void - { - $this->request->setMethod('POST') - ->setPost([ - 'login' => $login, - 'password' => $password, - ]); - $this->dispatch('/user/login'); - - $this->resetRequest() - ->resetResponse(); - - $this->request->setPost([]); - } - - /** - * Assert against plain text search; content should contain needle. - * - * @param string $needle needle that should be contained in content - * @param string $message - */ - public function assertContentContains($needle, $message = ''): void - { - $this->_incrementAssertionCount(); - $content = $this->response->outputBody(); - if (mb_strpos($content, $needle) === false) { - $failure = sprintf('Failed asserting needle DENOTED BY %s DOES NOT EXIST', $needle); - if (!empty($message)) { - $failure = $message . "\n" . $failure; - } - - throw new Zend_Test_PHPUnit_Constraint_Exception($failure); - } - } -} diff --git a/tests/mQueueTest/Controller/ActivityControllerTest.php b/tests/mQueueTest/Controller/ActivityControllerTest.php deleted file mode 100644 index 3a03e7e..0000000 --- a/tests/mQueueTest/Controller/ActivityControllerTest.php +++ /dev/null @@ -1,49 +0,0 @@ -assertModule($params['module']); - $this->assertController($params['controller']); - $this->assertAction($params['action']); - } - - public function testIndexAction(): void - { - $params = ['action' => 'index', 'controller' => 'activity', 'module' => 'default']; - $url = $this->url($this->urlizeOptions($params)); - $this->dispatch($url); - - // assertions - $this->assertCommonThings($params); - - $this->assertQueryContentContains('h2', 'Overall activity'); - } - - public function testUserAction(): void - { - $params = ['action' => 'index', 'controller' => 'activity', 'module' => 'default', 'user' => $this->testUser->id]; - $url = $this->url($this->urlizeOptions($params)); - $this->dispatch($url); - - // assertions - $this->assertCommonThings($params); - - $this->assertQueryContentContains('h2', 'Activity for'); - } - - public function testMovieAction(): void - { - $params = ['action' => 'index', 'controller' => 'activity', 'module' => 'default', 'movie' => $this->movieData['id']]; - $url = $this->url($this->urlizeOptions($params)); - $this->dispatch($url); - - // assertions - $this->assertCommonThings($params); - - $this->assertQueryContentContains('h2', 'Activity for ' . $this->movieData['title']); - } -} diff --git a/tests/mQueueTest/Controller/CssControllerTest.php b/tests/mQueueTest/Controller/CssControllerTest.php deleted file mode 100644 index 7da4156..0000000 --- a/tests/mQueueTest/Controller/CssControllerTest.php +++ /dev/null @@ -1,22 +0,0 @@ - 'gravatar.css', 'controller' => 'css', 'module' => 'default']; - $url = $this->url($this->urlizeOptions($params)); - $this->dispatch($url); - - // assertions - $this->assertModule($params['module']); - $this->assertController($params['controller']); - $this->assertAction($params['action']); - - $this->assertHeaderContains('Content-Type', 'text/css'); - $this->assertContentContains('span.gravatar'); - $this->assertContentContains('span.gravatar.big'); - } -} diff --git a/tests/mQueueTest/Controller/FaqControllerTest.php b/tests/mQueueTest/Controller/FaqControllerTest.php deleted file mode 100644 index 7bfcac8..0000000 --- a/tests/mQueueTest/Controller/FaqControllerTest.php +++ /dev/null @@ -1,21 +0,0 @@ - 'index', 'controller' => 'faq', 'module' => 'default']; - $url = $this->url($this->urlizeOptions($params)); - $this->dispatch($url); - - // assertions - $this->assertModule($params['module']); - $this->assertController($params['controller']); - $this->assertAction($params['action']); - - $this->assertQueryContentContains('li', 'Create an account'); - $this->assertQueryContentContains('ul.statusHelp li', 'I want to see this movie'); - } -} diff --git a/tests/mQueueTest/Controller/IndexControllerTest.php b/tests/mQueueTest/Controller/IndexControllerTest.php deleted file mode 100644 index a2fd7af..0000000 --- a/tests/mQueueTest/Controller/IndexControllerTest.php +++ /dev/null @@ -1,20 +0,0 @@ - 'index', 'controller' => 'index', 'module' => 'default']; - $url = $this->url($this->urlizeOptions($params)); - $this->dispatch($url); - - // assertions - $this->assertModule($params['module']); - $this->assertController($params['controller']); - $this->assertAction($params['action']); - - $this->assertRedirectTo('/activity'); - } -} diff --git a/tests/mQueueTest/Controller/JsControllerTest.php b/tests/mQueueTest/Controller/JsControllerTest.php deleted file mode 100644 index 1e15686..0000000 --- a/tests/mQueueTest/Controller/JsControllerTest.php +++ /dev/null @@ -1,37 +0,0 @@ - 'remote.js', 'controller' => 'js', 'module' => 'default']; - $url = $this->url($this->urlizeOptions($params)); - $this->dispatch($url); - - // assertions - $this->assertModule($params['module']); - $this->assertController($params['controller']); - $this->assertAction($params['action']); - - $this->assertHeaderContains('Content-Type', 'application/javascript'); - $this->assertContentContains('jquery'); - $this->assertContentContains('mqueue'); - } - - public function testMqueueUserJsAction(): void - { - $params = ['action' => 'mqueue-user.js', 'controller' => 'js', 'module' => 'default']; - $url = $this->url($this->urlizeOptions($params)); - $this->dispatch($url); - - // assertions - $this->assertModule($params['module']); - $this->assertController($params['controller']); - $this->assertAction($params['action']); - - $this->assertHeaderContains('Content-Type', 'application/javascript'); - $this->assertContentContains('document.evaluate'); - } -} diff --git a/tests/mQueueTest/Controller/MovieControllerTest.php b/tests/mQueueTest/Controller/MovieControllerTest.php deleted file mode 100644 index 28883bf..0000000 --- a/tests/mQueueTest/Controller/MovieControllerTest.php +++ /dev/null @@ -1,36 +0,0 @@ - 'index', 'controller' => 'movie', 'module' => 'default']; - $url = $this->url($this->urlizeOptions($params)); - $this->dispatch($url); - - // assertions - $this->assertModule($params['module']); - $this->assertController($params['controller']); - $this->assertAction($params['action']); - - $this->assertQueryContentContains('legend', 'Filter'); - $this->assertQueryContentContains('th a', 'Title'); - } - - public function testAddAction(): void - { - $params = ['action' => 'add', 'controller' => 'movie', 'module' => 'default']; - $url = $this->url($this->urlizeOptions($params)); - $this->dispatch($url); - - // assertions - $this->assertModule($params['module']); - $this->assertController($params['controller']); - $this->assertAction($params['action']); - - $this->assertQueryContentContains('label', 'IMDb url or id'); - $this->assertQueryContentContains('.tips', 'learn how to add'); - } -} diff --git a/tests/mQueueTest/Controller/StatusControllerTest.php b/tests/mQueueTest/Controller/StatusControllerTest.php deleted file mode 100644 index 8763594..0000000 --- a/tests/mQueueTest/Controller/StatusControllerTest.php +++ /dev/null @@ -1,50 +0,0 @@ - 'index', 'controller' => 'status', 'module' => 'default']; - $url = $this->url($this->urlizeOptions($params)); - $this->dispatch($url); - - // assertions - $this->assertModule($params['module']); - $this->assertController('error'); - $this->assertAction('error'); - - $this->assertQueryContentContains('p', 'no valid movie specified'); - - // Can view any movie status (even non-existing movies) - $urlStatusShort = $url . '/1234567'; - $this->dispatch($urlStatusShort); - - // assertions - $this->assertModule($params['module']); - $this->assertController($params['controller']); - $this->assertAction($params['action']); - - $this->assertQueryContentContains('.mqueue_status.mqueue_status_1', 'Need'); - $this->assertQueryContentContains('.mqueue_status.mqueue_status_2', 'Bad'); - $this->assertQueryContentContains('.mqueue_status.mqueue_status_3', 'Ok'); - $this->assertQueryContentContains('.mqueue_status.mqueue_status_4', 'Excellent'); - $this->assertQueryContentContains('.mqueue_status.mqueue_status_5', 'Favorite'); - - // Can view any movie status with bigger ID (even non-existing movies) - $urlStatusLong = $url . '/1234567890'; - $this->dispatch($urlStatusLong); - - // assertions - $this->assertModule($params['module']); - $this->assertController($params['controller']); - $this->assertAction($params['action']); - - $this->assertQueryContentContains('.mqueue_status.mqueue_status_1', 'Need'); - $this->assertQueryContentContains('.mqueue_status.mqueue_status_2', 'Bad'); - $this->assertQueryContentContains('.mqueue_status.mqueue_status_3', 'Ok'); - $this->assertQueryContentContains('.mqueue_status.mqueue_status_4', 'Excellent'); - $this->assertQueryContentContains('.mqueue_status.mqueue_status_5', 'Favorite'); - } -} diff --git a/tests/mQueueTest/Controller/UserControllerTest.php b/tests/mQueueTest/Controller/UserControllerTest.php deleted file mode 100644 index 7646b3b..0000000 --- a/tests/mQueueTest/Controller/UserControllerTest.php +++ /dev/null @@ -1,119 +0,0 @@ - 'new test user', - 'email' => 'new_valid@email.org', - 'password' => 'superpassword', - ]; - - protected function tearDown(): void - { - $user = \mQueue\Model\UserMapper::findEmailPassword($this->newUserData['email'], $this->newUserData['password']); - if ($user) { - $user->delete(); - } - - parent::tearDown(); - } - - public function testIndexAction(): void - { - $params = ['action' => 'index', 'controller' => 'user', 'module' => 'default']; - $url = $this->url($this->urlizeOptions($params)); - $this->dispatch($url); - - // assertions - $this->assertModule($params['module']); - $this->assertController($params['controller']); - $this->assertAction($params['action']); - - $this->assertQueryContentContains('h2', 'Users list'); - } - - public function testNewAction(): void - { - // First, query to display form - $params = ['action' => 'new', 'controller' => 'user', 'module' => 'default']; - $url = $this->url($this->urlizeOptions($params)); - $this->dispatch($url); - - // assertions - $this->assertModule($params['module']); - $this->assertController($params['controller']); - $this->assertAction($params['action']); - - $this->assertQueryContentContains('h2', 'Create new user'); - $this->assertQueryContentContains('form', 'Subscribe'); - - // Find out csrf value to be re-used in POST - $captcha = null; - $captchaId = null; - $csrf = null; - foreach ($_SESSION as $key => $q) { - if (preg_match('~^Zend_Form_Captcha_(.*)$~', $key, $m)) { - $captcha = $q['word']; - $captchaId = $m[1]; - } elseif ($key === 'Zend_Form_Element_Hash_salt_csrf') { - $csrf = $q['hash']; - } - } - - // Reset everything - $this->resetRequest(); - $this->resetResponse(); - - // Prepare POST query - $this->newUserData['captcha'] = [ - 'id' => $captchaId, - 'input' => $captcha, - ]; - $this->newUserData['csrf'] = $csrf; - $this->request->setMethod('POST') - ->setPost($this->newUserData); - - // Subscribe new test user - $this->dispatch($url); - - $this->assertRedirectTo('/movie', 'successful subscription redirect to movie list'); - } - - public function testLoginAction(): void - { - self::assertNull(\mQueue\Model\User::getCurrent(), 'at first we are not logged in'); - - // Create test user - $this->testNewAction(); - - self::assertNotNull(\mQueue\Model\User::getCurrent(), 'after subscription, we are automatically logged in'); - - // Reset everything - $this->resetRequest(); - $this->resetResponse(); - - $params = ['action' => 'logout', 'controller' => 'user', 'module' => 'default']; - $url = $this->url($this->urlizeOptions($params)); - $this->dispatch($url); - - self::assertNull(\mQueue\Model\User::getCurrent(), 'after logged out, we are logged out'); - - // Reset everything - $this->resetRequest(); - $this->resetResponse(); - - // Prepare POST query - $this->request->setMethod('POST') - ->setPost($this->newUserData); - - $params = ['action' => 'login', 'controller' => 'user', 'module' => 'default']; - $url = $this->url($this->urlizeOptions($params)); - $this->dispatch($url); - - self::assertNotNull(\mQueue\Model\User::getCurrent(), 'after login, we are login'); - } -} diff --git a/tests/mQueueTest/bootstrap.php b/tests/mQueueTest/bootstrap.php deleted file mode 100644 index e69de29..0000000 diff --git a/tests/object-manager.php b/tests/object-manager.php new file mode 100644 index 0000000..69e98f6 --- /dev/null +++ b/tests/object-manager.php @@ -0,0 +1,7 @@ +=1.5.0 <4.0", jquery@^3.6.0: - version "3.7.1" - resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.7.1.tgz#083ef98927c9a6a74d05a6af02806566d16274de" - integrity sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg== - -micromatch@^4.0.5: - version "4.0.8" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" - integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== - dependencies: - braces "^3.0.3" - picomatch "^2.3.1" - -node-addon-api@^7.0.0: - version "7.1.1" - resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-7.1.1.tgz#1aba6693b0f255258a049d621329329322aad558" - integrity sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ== - -picomatch@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" - integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== - -readdirp@^4.0.1: - version "4.1.2" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-4.1.2.tgz#eb85801435fbf2a7ee58f19e0921b068fc69948d" - integrity sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg== - -sass@^1.86.0: - version "1.86.0" - resolved "https://registry.yarnpkg.com/sass/-/sass-1.86.0.tgz#f49464fb6237a903a93f4e8760ef6e37a5030114" - integrity sha512-zV8vGUld/+mP4KbMLJMX7TyGCuUp7hnkOScgCMsWuHtns8CWBoz+vmEhoGMXsaJrbUP8gj+F1dLvVe79sK8UdA== - dependencies: - chokidar "^4.0.0" - immutable "^5.0.2" - source-map-js ">=0.6.2 <2.0.0" - optionalDependencies: - "@parcel/watcher" "^2.4.1" - -"source-map-js@>=0.6.2 <2.0.0": - version "1.2.1" - resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" - integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== - -timeago@^1.6.7: - version "1.6.7" - resolved "https://registry.yarnpkg.com/timeago/-/timeago-1.6.7.tgz#afd467c29a911e697fc22a81888c7c3022783cb5" - integrity sha512-FikcjN98+ij0siKH4VO4dZ358PR3oDDq4Vdl1+sN9gWz1/+JXGr3uZbUShYH/hL7bMhcTpPbplJU5Tej4b4jbQ== - dependencies: - jquery ">=1.5.0 <4.0" - -to-regex-range@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" - integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== - dependencies: - is-number "^7.0.0" From a7e0bfcd7acfb4f372c14eeaab352546ef1ae5c5 Mon Sep 17 00:00:00 2001 From: Adrien Crivelli Date: Mon, 29 Jun 2026 08:55:59 +0200 Subject: [PATCH 02/12] Fix CI --- .github/workflows/main.yml | 35 ++++++---- bin/load-test-data.sh | 7 ++ bin/migrate.sql | 68 +++++++++++++++---- config/autoload/local.php.dist | 13 ++++ .../Migration/Version20260412140844.php | 31 --------- .../Migration/Version20260629062614.php | 20 ++++++ 6 files changed, 116 insertions(+), 58 deletions(-) create mode 100755 bin/load-test-data.sh delete mode 100644 src/Application/Migration/Version20260412140844.php create mode 100644 src/Application/Migration/Version20260629062614.php diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 41f5dad..3054005 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -6,7 +6,7 @@ jobs: services: mariadb: - image: mariadb:10.3 + image: mariadb:10.11 ports: - 3306:3306 env: @@ -15,14 +15,27 @@ jobs: options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=5 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 + with: + persist-credentials: false + + - name: Install mariadb-client + run: | + sudo apt update + sudo apt install mariadb-client + + - uses: pnpm/action-setup@v6 + - uses: actions/setup-node@v6 + with: + node-version: '22' + cache: 'pnpm' + - uses: shivammathur/setup-php@v2 with: - php-version: '7.2' - coverage: pcov + php-version: '8.4' tools: cs2pr - - uses: actions/cache@v4 + - uses: actions/cache@v5 with: path: ~/.cache/composer key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} @@ -35,17 +48,11 @@ jobs: run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" # Prepare our app - - run: mysql --protocol=tcp --user=root --execute='SET GLOBAL sql_mode = "";' - - run: cp application/configs/application.sample.ini application/configs/application.ini + - run: cp config/autoload/local.php.dist config/autoload/local.php - run: ./bin/build.sh + - run: ./bin/load-test-data.sh # Lint and tests - run: ./vendor/bin/php-cs-fixer fix --format=checkstyle | cs2pr - run: ./vendor/bin/phpstan analyse - - run: ./vendor/bin/pcov clobber - - run: ./vendor/bin/phpunit --coverage-clover coverage-clover.xml - - - name: Upload code coverage - run: | - curl -LO https://scrutinizer-ci.com/ocular.phar - php ocular.phar code-coverage:upload --format=php-clover coverage-clover.xml + - run: ./vendor/bin/phpunit diff --git a/bin/load-test-data.sh b/bin/load-test-data.sh new file mode 100755 index 0000000..3ff00cf --- /dev/null +++ b/bin/load-test-data.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +set -xe + +sudo mariadb mqueue -e 'DROP DATABASE IF EXISTS mqueue; CREATE DATABASE mqueue;' +./bin/doctrine migrations:migrate --no-interaction +cat ./tests/data/fixture.sql | sudo mariadb mqueue diff --git a/bin/migrate.sql b/bin/migrate.sql index 0d4a202..1d05e8c 100644 --- a/bin/migrate.sql +++ b/bin/migrate.sql @@ -1,17 +1,59 @@ -DROP TABLE setting; -DROP TABLE setting; -ALTER TABLE movie CHANGE dateUpdate date_update DATETIME NOT NULL, CHANGE searchCount search_count SMALLINT UNSIGNED DEFAULT 0 NOT NULL, CHANGE id id INT AUTO_INCREMENT NOT NULL, CHANGE title title VARCHAR(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci NOT NULL, CHANGE source source VARCHAR(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci DEFAULT NULL, ADD start_year SMALLINT UNSIGNED DEFAULT NULL AFTER title, ADD dateSearch date_search DATETIME DEFAULT NULL; -UPDATE movie SET start_year = YEAR(releaseDate); -ALTER TABLE movie DROP dateRelease; -ALTER TABLE status DROP FOREIGN KEY `status_ibfk_1`; -ALTER TABLE status DROP FOREIGN KEY `status_ibfk_2`; +ALTER DATABASE mqueue COLLATE = 'utf8mb4_unicode_520_ci'; +DROP TABLE IF EXISTS setting; + +ALTER TABLE movie + CHANGE dateUpdate date_update DATETIME NOT NULL, + CHANGE searchCount search_count SMALLINT UNSIGNED DEFAULT 0 NOT NULL, + CHANGE id id INT AUTO_INCREMENT NOT NULL, + CHANGE title title VARCHAR(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci NOT NULL, + CHANGE source source VARCHAR(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci DEFAULT NULL, + ADD start_year SMALLINT UNSIGNED DEFAULT NULL AFTER title, + CHANGE dateSearch date_search DATETIME DEFAULT NULL, + COLLATE = 'utf8mb4_unicode_520_ci'; + +UPDATE movie +SET start_year = YEAR(dateRelease); +ALTER TABLE movie + DROP dateRelease; + +ALTER TABLE status + DROP FOREIGN KEY `status_ibfk_1`, + DROP FOREIGN KEY `status_ibfk_2`; + DROP INDEX idMovie ON status; DROP INDEX idUser ON status; -ALTER TABLE status CHANGE dateUpdate date_update DATETIME NOT NULL, CHANGE isLatest is_latest TINYINT DEFAULT 0 NOT NULL, CHANGE idMovie movie_id INT NOT NULL, CHANGE idUser user_id INT NOT NULL, CHANGE rating rating INT UNSIGNED DEFAULT 0 NOT NULL; -ALTER TABLE status ADD CONSTRAINT FK_7B00651C8F93B6FC FOREIGN KEY (movie_id) REFERENCES movie (id) ON DELETE CASCADE; -ALTER TABLE status ADD CONSTRAINT FK_7B00651CA76ED395 FOREIGN KEY (user_id) REFERENCES user (id) ON DELETE CASCADE; + +ALTER TABLE status + CHANGE dateUpdate date_update DATETIME NOT NULL, + CHANGE isLatest is_latest TINYINT DEFAULT 0 NOT NULL, + CHANGE idMovie movie_id INT NOT NULL, + CHANGE idUser user_id INT NOT NULL, + CHANGE rating rating INT UNSIGNED DEFAULT 0 NOT NULL, + ADD CONSTRAINT FK_7B00651C8F93B6FC FOREIGN KEY (movie_id) REFERENCES movie (id) ON DELETE CASCADE, + ADD CONSTRAINT FK_7B00651CA76ED395 FOREIGN KEY (user_id) REFERENCES user (id) ON DELETE CASCADE, + COLLATE = 'utf8mb4_unicode_520_ci'; + + CREATE INDEX IDX_7B00651C8F93B6FC ON status (movie_id); CREATE INDEX IDX_7B00651CA76ED395 ON status (user_id); -ALTER TABLE user CHANGE dateUpdate date_update DATETIME NOT NULL, CHANGE nickname nickname VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci NOT NULL, CHANGE email email VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci NOT NULL, CHANGE password password VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci NOT NULL; -ALTER TABLE user RENAME INDEX nickname TO UNIQ_8D93D649A188FE64; -ALTER TABLE user RENAME INDEX email TO UNIQ_8D93D649E7927C74; +ALTER TABLE user + CHANGE dateUpdate date_update DATETIME NOT NULL, + CHANGE nickname nickname VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci NOT NULL, + CHANGE email email VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci NOT NULL, + CHANGE password password VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci NOT NULL, + RENAME INDEX nickname TO UNIQ_8D93D649A188FE64, RENAME INDEX email TO UNIQ_8D93D649E7927C74, + COLLATE = 'utf8mb4_unicode_520_ci'; + + +CREATE TABLE `doctrine_migration_versions` +( + `version` VARCHAR(255) NOT NULL, + `executed_at` DATETIME DEFAULT NULL, + `execution_time` INT(11) DEFAULT NULL, + PRIMARY KEY (`version`) +) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_unicode_520_ci; + +INSERT INTO doctrine_migration_versions (version, executed_at, execution_time) +VALUES ('Application\\Migration\\Version20260629062614', NOW(), 0); diff --git a/config/autoload/local.php.dist b/config/autoload/local.php.dist index db858d7..ce6d4a2 100644 --- a/config/autoload/local.php.dist +++ b/config/autoload/local.php.dist @@ -10,4 +10,17 @@ declare(strict_types=1); return [ + 'doctrine' => [ + 'connection' => [ + 'orm_default' => [ + 'params' => [ + 'host' => getenv('CI') ? '127.0.0.1' : 'localhost', // On GitHub Actions we want to connect via tcp, but locally we prefer socket + 'dbname' => 'mqueue', + 'user' => 'root', + 'password' => '', + 'port' => 3306, + ], + ], + ], + ], ]; diff --git a/src/Application/Migration/Version20260412140844.php b/src/Application/Migration/Version20260412140844.php deleted file mode 100644 index e4c5b9d..0000000 --- a/src/Application/Migration/Version20260412140844.php +++ /dev/null @@ -1,31 +0,0 @@ -addSql('DROP TABLE setting'); - $this->addSql('ALTER TABLE movie CHANGE dateUpdate date_update DATETIME NOT NULL, CHANGE searchCount search_count SMALLINT UNSIGNED DEFAULT 0 NOT NULL, CHANGE id id INT AUTO_INCREMENT NOT NULL, CHANGE title title VARCHAR(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, CHANGE source source VARCHAR(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, ADD start_year SMALLINT UNSIGNED DEFAULT NULL, CHANGE dateSearch date_search DATETIME DEFAULT NULL'); - $this->addSql('ALTER TABLE status DROP FOREIGN KEY `status_ibfk_1`'); - $this->addSql('UPDATE movie SET start_year = YEAR(dateRelease);'); - $this->addSql('ALTER TABLE movie DROP dateRelease;'); - $this->addSql('ALTER TABLE status DROP FOREIGN KEY `status_ibfk_2`'); - $this->addSql('DROP INDEX idMovie ON status'); - $this->addSql('DROP INDEX idUser ON status'); - $this->addSql('ALTER TABLE status CHANGE dateUpdate date_update DATETIME NOT NULL, CHANGE isLatest is_latest TINYINT DEFAULT 0 NOT NULL, CHANGE idMovie movie_id INT NOT NULL, CHANGE idUser user_id INT NOT NULL, CHANGE rating rating INT UNSIGNED DEFAULT 0 NOT NULL'); - $this->addSql('ALTER TABLE status ADD CONSTRAINT FK_7B00651C8F93B6FC FOREIGN KEY (movie_id) REFERENCES movie (id) ON DELETE CASCADE'); - $this->addSql('ALTER TABLE status ADD CONSTRAINT FK_7B00651CA76ED395 FOREIGN KEY (user_id) REFERENCES user (id) ON DELETE CASCADE'); - $this->addSql('CREATE INDEX IDX_7B00651C8F93B6FC ON status (movie_id)'); - $this->addSql('CREATE INDEX IDX_7B00651CA76ED395 ON status (user_id)'); - $this->addSql('ALTER TABLE user CHANGE dateUpdate date_update DATETIME NOT NULL, CHANGE nickname nickname VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, CHANGE email email VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, CHANGE password password VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL'); - $this->addSql('ALTER TABLE user RENAME INDEX nickname TO UNIQ_8D93D649A188FE64'); - $this->addSql('ALTER TABLE user RENAME INDEX email TO UNIQ_8D93D649E7927C74'); - } -} diff --git a/src/Application/Migration/Version20260629062614.php b/src/Application/Migration/Version20260629062614.php new file mode 100644 index 0000000..40a523f --- /dev/null +++ b/src/Application/Migration/Version20260629062614.php @@ -0,0 +1,20 @@ +addSql('CREATE TABLE movie (id INT AUTO_INCREMENT NOT NULL, date_update DATETIME NOT NULL, title VARCHAR(512) NOT NULL, start_year SMALLINT UNSIGNED DEFAULT NULL, date_search DATETIME DEFAULT NULL, search_count SMALLINT UNSIGNED DEFAULT 0 NOT NULL, identity SMALLINT UNSIGNED DEFAULT 0 NOT NULL, quality SMALLINT UNSIGNED DEFAULT 0 NOT NULL, score SMALLINT UNSIGNED DEFAULT 0 NOT NULL, source VARCHAR(1024) DEFAULT NULL, PRIMARY KEY (id)) DEFAULT CHARACTER SET utf8mb4'); + $this->addSql('CREATE TABLE status (id INT AUTO_INCREMENT NOT NULL, date_update DATETIME NOT NULL, rating INT UNSIGNED DEFAULT 0 NOT NULL, is_latest TINYINT DEFAULT 0 NOT NULL, movie_id INT NOT NULL, user_id INT NOT NULL, INDEX IDX_7B00651C8F93B6FC (movie_id), INDEX IDX_7B00651CA76ED395 (user_id), PRIMARY KEY (id)) DEFAULT CHARACTER SET utf8mb4'); + $this->addSql('CREATE TABLE user (id INT AUTO_INCREMENT NOT NULL, date_update DATETIME NOT NULL, nickname VARCHAR(255) NOT NULL, email VARCHAR(255) NOT NULL, password VARCHAR(255) NOT NULL, UNIQUE INDEX UNIQ_8D93D649A188FE64 (nickname), UNIQUE INDEX UNIQ_8D93D649E7927C74 (email), PRIMARY KEY (id)) DEFAULT CHARACTER SET utf8mb4'); + $this->addSql('ALTER TABLE status ADD CONSTRAINT FK_7B00651C8F93B6FC FOREIGN KEY (movie_id) REFERENCES movie (id) ON DELETE CASCADE'); + $this->addSql('ALTER TABLE status ADD CONSTRAINT FK_7B00651CA76ED395 FOREIGN KEY (user_id) REFERENCES user (id) ON DELETE CASCADE'); + } +} From 4037eba998882aad9f138ac5194a573484c648be Mon Sep 17 00:00:00 2001 From: Adrien Crivelli Date: Mon, 29 Jun 2026 08:58:55 +0200 Subject: [PATCH 03/12] Specify pnpm version --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index e12f3fe..fbc68dc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,7 @@ { "name": "mqueue", "private": true, + "packageManager": "pnpm@11.9.0", "dependencies": { "esbuild": "^0.28.0", "highcharts": "^12.6.0", From e3627c58f87e8fa2ba3b0552253339b5269466b3 Mon Sep 17 00:00:00 2001 From: Adrien Crivelli Date: Mon, 29 Jun 2026 09:01:13 +0200 Subject: [PATCH 04/12] Use doctrine to update database --- bin/build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/build.sh b/bin/build.sh index 3287f19..b2fc8b1 100755 --- a/bin/build.sh +++ b/bin/build.sh @@ -17,7 +17,7 @@ echo "Updating all PHP dependencies via composer..." $COMPOSER install --classmap-authoritative --ignore-platform-reqs echo "Updating database..." -$PHP ./bin/update_database.php +$PHP ./bin/doctrine migrations:migrate --no-interaction echo "Compiling CSS..." ./node_modules/.bin/sass --style=compressed --no-source-map src/sass/:public/css/ From 80efc24c183cd0a45ab6e1228b9a64bd2c242923 Mon Sep 17 00:00:00 2001 From: Adrien Crivelli Date: Mon, 29 Jun 2026 09:10:39 +0200 Subject: [PATCH 05/12] Can load test data on GitHub Actions --- bin/load-test-data.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bin/load-test-data.sh b/bin/load-test-data.sh index 3ff00cf..584c44d 100755 --- a/bin/load-test-data.sh +++ b/bin/load-test-data.sh @@ -2,6 +2,9 @@ set -xe -sudo mariadb mqueue -e 'DROP DATABASE IF EXISTS mqueue; CREATE DATABASE mqueue;' +# On GitHub Actions we want to connect via tcp, but locally we prefer socket +HOST=$([ -n "$CI" ] && echo '127.0.0.1' || echo 'localhost') + +sudo mariadb -h $HOST -e 'DROP DATABASE IF EXISTS mqueue; CREATE DATABASE mqueue;' ./bin/doctrine migrations:migrate --no-interaction cat ./tests/data/fixture.sql | sudo mariadb mqueue From 273bde2a1e388eef6d7806784d3989d2fcaa0812 Mon Sep 17 00:00:00 2001 From: Adrien Crivelli Date: Mon, 29 Jun 2026 09:17:36 +0200 Subject: [PATCH 06/12] Can load test data on GitHub Actions (bis) --- bin/load-test-data.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/load-test-data.sh b/bin/load-test-data.sh index 584c44d..426007a 100755 --- a/bin/load-test-data.sh +++ b/bin/load-test-data.sh @@ -7,4 +7,4 @@ HOST=$([ -n "$CI" ] && echo '127.0.0.1' || echo 'localhost') sudo mariadb -h $HOST -e 'DROP DATABASE IF EXISTS mqueue; CREATE DATABASE mqueue;' ./bin/doctrine migrations:migrate --no-interaction -cat ./tests/data/fixture.sql | sudo mariadb mqueue +cat ./tests/data/fixture.sql | sudo mariadb mqueue -h $HOST From c90ee2b6c1d34f0ed3f1fbabf6b3bb4ee8b2188e Mon Sep 17 00:00:00 2001 From: Adrien Crivelli Date: Mon, 29 Jun 2026 09:21:37 +0200 Subject: [PATCH 07/12] Happy Stan --- src/Application/Model/Movie.php | 2 +- tests/ApplicationTest/Handler/Status/GraphHandlerTest.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Application/Model/Movie.php b/src/Application/Model/Movie.php index 5f75048..ecbe0cc 100644 --- a/src/Application/Model/Movie.php +++ b/src/Application/Model/Movie.php @@ -127,7 +127,7 @@ private function fetchJson(): ?array return null; } - return json_decode($file, JSON_OBJECT_AS_ARRAY | JSON_THROW_ON_ERROR); + return json_decode($file, true, flags: JSON_THROW_ON_ERROR); } /** diff --git a/tests/ApplicationTest/Handler/Status/GraphHandlerTest.php b/tests/ApplicationTest/Handler/Status/GraphHandlerTest.php index 2cc3783..7c3979d 100644 --- a/tests/ApplicationTest/Handler/Status/GraphHandlerTest.php +++ b/tests/ApplicationTest/Handler/Status/GraphHandlerTest.php @@ -14,7 +14,7 @@ public function testBasic(): void self::assertInstanceOf(JsonResponse::class, $response); $body = (string) $response->getBody(); - $chart = json_decode($body, true, JSON_THROW_ON_ERROR); + $chart = json_decode($body, true, flags: JSON_THROW_ON_ERROR); self::assertArrayHasKey('chart', $chart); } @@ -24,7 +24,7 @@ public function testPercent(): void self::assertInstanceOf(JsonResponse::class, $response); $body = (string) $response->getBody(); - $chart = json_decode($body, true, JSON_THROW_ON_ERROR); + $chart = json_decode($body, true, flags: JSON_THROW_ON_ERROR); self::assertArrayHasKey('chart', $chart); self::assertStringContainsString(' [%]', $chart['yAxis']['title']['text']); } From f1d7132977d2ee91f376ca9055c6034d1d388364 Mon Sep 17 00:00:00 2001 From: Adrien Crivelli Date: Tue, 30 Jun 2026 08:40:57 +0200 Subject: [PATCH 08/12] DB is strict --- config/autoload/doctrine.global.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/autoload/doctrine.global.php b/config/autoload/doctrine.global.php index c5b095e..ac4cdf7 100644 --- a/config/autoload/doctrine.global.php +++ b/config/autoload/doctrine.global.php @@ -14,7 +14,7 @@ 'password' => '', 'port' => 3306, 'driverOptions' => [ - PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8mb4', + PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4; SET sql_mode = 'STRICT_TRANS_TABLES';", ], 'defaultTableOptions' => [ 'charset' => 'utf8mb4', From c50746ed04c4fbd29aebe5fa845f6e8269063711 Mon Sep 17 00:00:00 2001 From: Adrien Crivelli Date: Tue, 30 Jun 2026 20:51:13 +0200 Subject: [PATCH 09/12] Chronos is immutable --- src/Application/Repository/StatusRepository.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Application/Repository/StatusRepository.php b/src/Application/Repository/StatusRepository.php index 3950ee0..5dd47d9 100644 --- a/src/Application/Repository/StatusRepository.php +++ b/src/Application/Repository/StatusRepository.php @@ -208,7 +208,7 @@ public function getGraph(?User $user, bool $percent): array $lastStatuses[$userId][$movieId] = $rating; $time = new Chronos($row['date_update']); - $time->setTimezone(new DateTimeZone('GMT')); + $time = $time->setTimezone(new DateTimeZone('GMT')); $epoch = (int) $time->format('U') * 1000; // If we are in percent mode, we need all status for each timestamp From 28d0bd5a6ee456a50ee40aba22ddf4e1e6658fa7 Mon Sep 17 00:00:00 2001 From: Adrien Crivelli Date: Tue, 30 Jun 2026 21:46:47 +0200 Subject: [PATCH 10/12] DB is same timezone as PHP --- .../Repository/StatusRepositoryTest.php | 62 +++++++++---------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/tests/ApplicationTest/Repository/StatusRepositoryTest.php b/tests/ApplicationTest/Repository/StatusRepositoryTest.php index e7ada5c..a8116c7 100644 --- a/tests/ApplicationTest/Repository/StatusRepositoryTest.php +++ b/tests/ApplicationTest/Repository/StatusRepositoryTest.php @@ -134,7 +134,7 @@ public function testGetGraph(): void 'name' => 'Need', 'data' => [ [ - 1577890800000, + 1577919600000, 2, ], ], @@ -143,11 +143,11 @@ public function testGetGraph(): void 'name' => 'Bad', 'data' => [ [ - 1577977200000, + 1578006000000, 1, ], [ - 1578063600000, + 1578092400000, 0, ], ], @@ -156,11 +156,11 @@ public function testGetGraph(): void 'name' => 'Ok', 'data' => [ [ - 1577804400000, + 1577833200000, 1, ], [ - 1577890800000, + 1577919600000, 0, ], ], @@ -173,7 +173,7 @@ public function testGetGraph(): void 'name' => 'Favorite', 'data' => [ [ - 1577890800000, + 1577919600000, 2, ], ], @@ -190,11 +190,11 @@ public function testGetGraph(): void 'name' => 'Bad', 'data' => [ [ - 1577977200000, + 1578006000000, 1, ], [ - 1578063600000, + 1578092400000, 0, ], ], @@ -203,11 +203,11 @@ public function testGetGraph(): void 'name' => 'Ok', 'data' => [ [ - 1577804400000, + 1577833200000, 1, ], [ - 1577890800000, + 1577919600000, 0, ], ], @@ -220,7 +220,7 @@ public function testGetGraph(): void 'name' => 'Favorite', 'data' => [ [ - 1577890800000, + 1577919600000, 1, ], ], @@ -233,19 +233,19 @@ public function testGetGraph(): void 'name' => 'Need', 'data' => [ [ - 1577804400000, + 1577833200000, 0, ], [ - 1577890800000, + 1577919600000, 0, ], [ - 1577977200000, + 1578006000000, 0, ], [ - 1578063600000, + 1578092400000, 0, ], ], @@ -254,19 +254,19 @@ public function testGetGraph(): void 'name' => 'Bad', 'data' => [ [ - 1577804400000, + 1577833200000, 0, ], [ - 1577890800000, + 1577919600000, 0, ], [ - 1577977200000, + 1578006000000, 1, ], [ - 1578063600000, + 1578092400000, 0, ], ], @@ -275,19 +275,19 @@ public function testGetGraph(): void 'name' => 'Ok', 'data' => [ [ - 1577804400000, + 1577833200000, 1, ], [ - 1577890800000, + 1577919600000, 0, ], [ - 1577977200000, + 1578006000000, 0, ], [ - 1578063600000, + 1578092400000, 0, ], ], @@ -296,19 +296,19 @@ public function testGetGraph(): void 'name' => 'Excellent', 'data' => [ [ - 1577804400000, + 1577833200000, 0, ], [ - 1577890800000, + 1577919600000, 0, ], [ - 1577977200000, + 1578006000000, 0, ], [ - 1578063600000, + 1578092400000, 0, ], ], @@ -317,19 +317,19 @@ public function testGetGraph(): void 'name' => 'Favorite', 'data' => [ [ - 1577804400000, + 1577833200000, 0, ], [ - 1577890800000, + 1577919600000, 1, ], [ - 1577977200000, + 1578006000000, 1, ], [ - 1578063600000, + 1578092400000, 1, ], ], From 90ac76b97b8ee5260675e91afe4cd652cf547326 Mon Sep 17 00:00:00 2001 From: Adrien Crivelli Date: Wed, 1 Jul 2026 08:21:00 +0200 Subject: [PATCH 11/12] Tests are timezone independent --- .../Repository/StatusRepositoryTest.php | 167 +++++------------- 1 file changed, 42 insertions(+), 125 deletions(-) diff --git a/tests/ApplicationTest/Repository/StatusRepositoryTest.php b/tests/ApplicationTest/Repository/StatusRepositoryTest.php index a8116c7..5702b81 100644 --- a/tests/ApplicationTest/Repository/StatusRepositoryTest.php +++ b/tests/ApplicationTest/Repository/StatusRepositoryTest.php @@ -10,6 +10,7 @@ use Application\Model\User; use Application\Repository\StatusRepository; use ApplicationTest\Traits\TestWithTransactionAndUser; +use Cake\Chronos\Chronos; use PHPUnit\Framework\TestCase; class StatusRepositoryTest extends TestCase @@ -127,42 +128,32 @@ public function testGetOneByMovieAndUser(): void public function testGetGraph(): void { + $d1 = $this->epoch('2020-01-01'); + $d2 = $this->epoch('2020-01-02'); + $d3 = $this->epoch('2020-01-03'); + $d4 = $this->epoch('2020-01-04'); + $user = $this->getEntityManager()->getReference(User::class, 1001); $actual = $this->repository->getGraph(null, false); self::assertSame([ [ 'name' => 'Need', 'data' => [ - [ - 1577919600000, - 2, - ], + [$d2, 2], ], ], [ 'name' => 'Bad', 'data' => [ - [ - 1578006000000, - 1, - ], - [ - 1578092400000, - 0, - ], + [$d3, 1], + [$d4, 0], ], ], [ 'name' => 'Ok', 'data' => [ - [ - 1577833200000, - 1, - ], - [ - 1577919600000, - 0, - ], + [$d1, 1], + [$d2, 0], ], ], [ @@ -172,10 +163,7 @@ public function testGetGraph(): void [ 'name' => 'Favorite', 'data' => [ - [ - 1577919600000, - 2, - ], + [$d2, 2], ], ], ], $actual); @@ -189,27 +177,15 @@ public function testGetGraph(): void [ 'name' => 'Bad', 'data' => [ - [ - 1578006000000, - 1, - ], - [ - 1578092400000, - 0, - ], + [$d3, 1], + [$d4, 0], ], ], [ 'name' => 'Ok', 'data' => [ - [ - 1577833200000, - 1, - ], - [ - 1577919600000, - 0, - ], + [$d1, 1], + [$d2, 0], ], ], [ @@ -219,10 +195,7 @@ public function testGetGraph(): void [ 'name' => 'Favorite', 'data' => [ - [ - 1577919600000, - 1, - ], + [$d2, 1], ], ], ], $actual); @@ -232,108 +205,52 @@ public function testGetGraph(): void [ 'name' => 'Need', 'data' => [ - [ - 1577833200000, - 0, - ], - [ - 1577919600000, - 0, - ], - [ - 1578006000000, - 0, - ], - [ - 1578092400000, - 0, - ], + [$d1, 0], + [$d2, 0], + [$d3, 0], + [$d4, 0], ], ], [ 'name' => 'Bad', 'data' => [ - [ - 1577833200000, - 0, - ], - [ - 1577919600000, - 0, - ], - [ - 1578006000000, - 1, - ], - [ - 1578092400000, - 0, - ], - ], + [$d1, 0], + [$d2, 0], + [$d3, 1], + [$d4, 0], ], ], [ 'name' => 'Ok', 'data' => [ - [ - 1577833200000, - 1, - ], - [ - 1577919600000, - 0, - ], - [ - 1578006000000, - 0, - ], - [ - 1578092400000, - 0, - ], + [$d1, 1], + [$d2, 0], + [$d3, 0], + [$d4, 0], ], ], [ 'name' => 'Excellent', 'data' => [ - [ - 1577833200000, - 0, - ], - [ - 1577919600000, - 0, - ], - [ - 1578006000000, - 0, - ], - [ - 1578092400000, - 0, - ], + [$d1, 0], + [$d2, 0], + [$d3, 0], + [$d4, 0], ], ], [ 'name' => 'Favorite', 'data' => [ - [ - 1577833200000, - 0, - ], - [ - 1577919600000, - 1, - ], - [ - 1578006000000, - 1, - ], - [ - 1578092400000, - 1, - ], + [$d1, 0], + [$d2, 1], + [$d3, 1], + [$d4, 1], ], ], ], $actual); } + + private function epoch(string $s): int + { + return new Chronos($s)->timestamp * 1000; + } } From fc07d45385394d09174e7dfb5f7636871489ae3f Mon Sep 17 00:00:00 2001 From: Adrien Crivelli Date: Wed, 1 Jul 2026 09:21:47 +0200 Subject: [PATCH 12/12] Update all PHP deps --- .php-cs-fixer.dist.php | 1 + bin/pre-commit.sh | 2 +- composer.json | 12 +- composer.lock | 1050 ++++++++++++++++++++++++---------------- 4 files changed, 639 insertions(+), 426 deletions(-) diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php index be75a23..ae67dc2 100644 --- a/.php-cs-fixer.dist.php +++ b/.php-cs-fixer.dist.php @@ -15,6 +15,7 @@ ->setRiskyAllowed(true) ->setFinder($finder) ->setCacheFile(sys_get_temp_dir() . '/php-cs-fixer' . preg_replace('~\W~', '-', __DIR__)) + ->setUnsupportedPhpVersionAllowed(true) ->setRules([ 'align_multiline_comment' => true, 'array_indentation' => true, diff --git a/bin/pre-commit.sh b/bin/pre-commit.sh index 31dcfa8..3660077 100755 --- a/bin/pre-commit.sh +++ b/bin/pre-commit.sh @@ -14,7 +14,7 @@ if [ "$files" != "" ]; then done # Run php-cs-fixer validation before commit - echo "$files" | PHP_CS_FIXER_IGNORE_ENV=1 xargs php8.4 ./vendor/bin/php-cs-fixer fix --diff --config .php-cs-fixer.dist.php + echo "$files" | xargs php8.4 ./vendor/bin/php-cs-fixer fix --diff --config .php-cs-fixer.dist.php if [ $? -ne 0 ]; then pass=false fi diff --git a/composer.json b/composer.json index 71f845c..6de5bd8 100644 --- a/composer.json +++ b/composer.json @@ -17,12 +17,12 @@ "development-status": "laminas-development-mode status", "mezzio": "laminas --ansi", "check": [ - "PHP_CS_FIXER_IGNORE_ENV=1 php-cs-fixer fix --ansi --dry-run --diff", + "php-cs-fixer fix --ansi --dry-run --diff", "phpstan analyse --ansi", "@test" ], "fix": [ - "PHP_CS_FIXER_IGNORE_ENV=1 php-cs-fixer fix --ansi" + "php-cs-fixer fix --ansi" ], "clear-config-cache": "php bin/clear-config-cache.php", "cs-check": "phpcs", @@ -61,15 +61,15 @@ "doctrine/migrations": "^3.9", "doctrine/orm": "^3.6", "laminas/laminas-captcha": "^2.20", - "laminas/laminas-config-aggregator": "^1.19", + "laminas/laminas-config-aggregator": "^1.20", "laminas/laminas-diactoros": "^3.8", "laminas/laminas-feed": "^2.26", "laminas/laminas-form": "^3.24", - "laminas/laminas-i18n": "^2.32", + "laminas/laminas-i18n": "^2.33", "laminas/laminas-paginator": "^2.22", "laminas/laminas-servicemanager": "^3.24", "laminas/laminas-stdlib": "^3.21", - "mezzio/mezzio": "^3.23", + "mezzio/mezzio": "^3.28", "mezzio/mezzio-flash": "^1.10", "mezzio/mezzio-helpers": "^5.20", "mezzio/mezzio-laminasrouter": "^3.12", @@ -77,7 +77,7 @@ "mezzio/mezzio-session": "^1.17", "mezzio/mezzio-session-ext": "^1.21", "mimmi20/mezzio-navigation-laminasviewrenderer": "^7.0", - "roave/psr-container-doctrine": "^6.1" + "roave/psr-container-doctrine": "^6.2" }, "require-dev": { "friendsofphp/php-cs-fixer": "@stable", diff --git a/composer.lock b/composer.lock index b77d789..561e2ab 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "12b81c549f6749061ddc21ef9cef9328", + "content-hash": "387f30b91314b682e113374c2df00692", "packages": [ { "name": "beberlei/doctrineextensions", @@ -70,26 +70,26 @@ }, { "name": "brick/varexporter", - "version": "0.6.0", + "version": "0.7.0", "source": { "type": "git", "url": "https://github.com/brick/varexporter.git", - "reference": "af98bfc2b702a312abbcaff37656dbe419cec5bc" + "reference": "b3a50b8f630a9ed5015ea3e1f00479af261ed80d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/brick/varexporter/zipball/af98bfc2b702a312abbcaff37656dbe419cec5bc", - "reference": "af98bfc2b702a312abbcaff37656dbe419cec5bc", + "url": "https://api.github.com/repos/brick/varexporter/zipball/b3a50b8f630a9ed5015ea3e1f00479af261ed80d", + "reference": "b3a50b8f630a9ed5015ea3e1f00479af261ed80d", "shasum": "" }, "require": { "nikic/php-parser": "^5.0", - "php": "^8.1" + "php": "^8.2" }, "require-dev": { "php-coveralls/php-coveralls": "^2.2", - "phpunit/phpunit": "^10.5", - "vimeo/psalm": "6.8.4" + "phpunit/phpunit": "^11.0", + "vimeo/psalm": "6.14.3" }, "type": "library", "autoload": { @@ -107,7 +107,7 @@ ], "support": { "issues": "https://github.com/brick/varexporter/issues", - "source": "https://github.com/brick/varexporter/tree/0.6.0" + "source": "https://github.com/brick/varexporter/tree/0.7.0" }, "funding": [ { @@ -115,7 +115,7 @@ "type": "github" } ], - "time": "2025-02-20T17:42:39+00:00" + "time": "2026-01-06T22:56:00+00:00" }, { "name": "cakephp/chronos", @@ -1001,16 +1001,16 @@ }, { "name": "doctrine/orm", - "version": "3.6.4", + "version": "3.6.7", "source": { "type": "git", "url": "https://github.com/doctrine/orm.git", - "reference": "156f3b5a984e7eaa72d440bb6de1d3b6f8d2d6fd" + "reference": "bc217c0e19c3a9eadfa67697143b87c9ba01272c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/orm/zipball/156f3b5a984e7eaa72d440bb6de1d3b6f8d2d6fd", - "reference": "156f3b5a984e7eaa72d440bb6de1d3b6f8d2d6fd", + "url": "https://api.github.com/repos/doctrine/orm/zipball/bc217c0e19c3a9eadfa67697143b87c9ba01272c", + "reference": "bc217c0e19c3a9eadfa67697143b87c9ba01272c", "shasum": "" }, "require": { @@ -1083,9 +1083,9 @@ ], "support": { "issues": "https://github.com/doctrine/orm/issues", - "source": "https://github.com/doctrine/orm/tree/3.6.4" + "source": "https://github.com/doctrine/orm/tree/3.6.7" }, - "time": "2026-05-07T07:04:34+00:00" + "time": "2026-05-25T16:45:47+00:00" }, { "name": "doctrine/persistence", @@ -1306,20 +1306,20 @@ }, { "name": "laminas/laminas-config-aggregator", - "version": "1.19.0", + "version": "1.20.0", "source": { "type": "git", "url": "https://github.com/laminas/laminas-config-aggregator.git", - "reference": "612343ce135c340fc667da3615e50d865a86b4d9" + "reference": "75331b17a9ba72c5fa8679988ef08a588ddd86af" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-config-aggregator/zipball/612343ce135c340fc667da3615e50d865a86b4d9", - "reference": "612343ce135c340fc667da3615e50d865a86b4d9", + "url": "https://api.github.com/repos/laminas/laminas-config-aggregator/zipball/75331b17a9ba72c5fa8679988ef08a588ddd86af", + "reference": "75331b17a9ba72c5fa8679988ef08a588ddd86af", "shasum": "" }, "require": { - "brick/varexporter": "^0.5.0 || ^0.4.0 || ^0.6.0", + "brick/varexporter": "^0.5.0 || ^0.4.0 || ^0.6.0 || ^0.7.0", "laminas/laminas-stdlib": "^3.18.0", "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", "webimpress/safe-writer": "^2.2.0" @@ -1370,7 +1370,7 @@ "type": "community_bridge" } ], - "time": "2025-10-14T19:57:01+00:00" + "time": "2026-06-01T15:37:55+00:00" }, { "name": "laminas/laminas-diactoros", @@ -1985,16 +1985,16 @@ }, { "name": "laminas/laminas-hydrator", - "version": "4.18.1", + "version": "4.19.0", "source": { "type": "git", "url": "https://github.com/laminas/laminas-hydrator.git", - "reference": "93a8ea33dccd2639ab8f07fbbfbc40911774aa9b" + "reference": "bf6c980013f70b1141aadef979cf8b66241fff4f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-hydrator/zipball/93a8ea33dccd2639ab8f07fbbfbc40911774aa9b", - "reference": "93a8ea33dccd2639ab8f07fbbfbc40911774aa9b", + "url": "https://api.github.com/repos/laminas/laminas-hydrator/zipball/bf6c980013f70b1141aadef979cf8b66241fff4f", + "reference": "bf6c980013f70b1141aadef979cf8b66241fff4f", "shasum": "" }, "require": { @@ -2058,20 +2058,20 @@ "type": "community_bridge" } ], - "time": "2026-05-04T08:00:40+00:00" + "time": "2026-05-13T22:31:51+00:00" }, { "name": "laminas/laminas-i18n", - "version": "2.32.1", + "version": "2.33.0", "source": { "type": "git", "url": "https://github.com/laminas/laminas-i18n.git", - "reference": "8e71b40318f0df6253329e837188e1d77cf83aea" + "reference": "477501f286fba6e78f57d36c8533d05d15f6da8d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-i18n/zipball/8e71b40318f0df6253329e837188e1d77cf83aea", - "reference": "8e71b40318f0df6253329e837188e1d77cf83aea", + "url": "https://api.github.com/repos/laminas/laminas-i18n/zipball/477501f286fba6e78f57d36c8533d05d15f6da8d", + "reference": "477501f286fba6e78f57d36c8533d05d15f6da8d", "shasum": "" }, "require": { @@ -2098,6 +2098,7 @@ "laminas/laminas-filter": "^2.42", "laminas/laminas-validator": "^2.65.0", "laminas/laminas-view": "^2.44", + "phpbench/phpbench": "^1.6", "phpunit/phpunit": "^11.5.46", "psalm/plugin-phpunit": "^0.19.5", "vimeo/psalm": "^6.14.2" @@ -2147,7 +2148,7 @@ "type": "community_bridge" } ], - "time": "2025-12-15T14:23:40+00:00" + "time": "2026-05-18T16:10:02+00:00" }, { "name": "laminas/laminas-inputfilter", @@ -2549,16 +2550,16 @@ }, { "name": "laminas/laminas-router", - "version": "3.18.0", + "version": "3.19.0", "source": { "type": "git", "url": "https://github.com/laminas/laminas-router.git", - "reference": "e9a3dcf704cfc2ea47da6c2275409c2187034f8f" + "reference": "c1d572280573afefad9285cd50e1cda678de726e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-router/zipball/e9a3dcf704cfc2ea47da6c2275409c2187034f8f", - "reference": "e9a3dcf704cfc2ea47da6c2275409c2187034f8f", + "url": "https://api.github.com/repos/laminas/laminas-router/zipball/c1d572280573afefad9285cd50e1cda678de726e", + "reference": "c1d572280573afefad9285cd50e1cda678de726e", "shasum": "" }, "require": { @@ -2614,11 +2615,11 @@ }, "funding": [ { - "url": "https://funding.communitybridge.org/projects/laminas-project", - "type": "community_bridge" + "url": "https://crowdfunding.linuxfoundation.org/initiatives/laminas-project", + "type": "custom" } ], - "time": "2026-03-02T14:54:55+00:00" + "time": "2026-06-26T14:41:47+00:00" }, { "name": "laminas/laminas-servicemanager", @@ -2712,16 +2713,16 @@ }, { "name": "laminas/laminas-session", - "version": "2.26.0", + "version": "2.27.0", "source": { "type": "git", "url": "https://github.com/laminas/laminas-session.git", - "reference": "1c4f1a80ccc6403fff85bb13fb928da79dd1636e" + "reference": "4b80f42ebada8efbf8e124ecb20c6fbaf725b513" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-session/zipball/1c4f1a80ccc6403fff85bb13fb928da79dd1636e", - "reference": "1c4f1a80ccc6403fff85bb13fb928da79dd1636e", + "url": "https://api.github.com/repos/laminas/laminas-session/zipball/4b80f42ebada8efbf8e124ecb20c6fbaf725b513", + "reference": "4b80f42ebada8efbf8e124ecb20c6fbaf725b513", "shasum": "" }, "require": { @@ -2793,7 +2794,7 @@ "type": "community_bridge" } ], - "time": "2025-11-14T12:52:38+00:00" + "time": "2026-05-20T11:04:31+00:00" }, { "name": "laminas/laminas-stdlib", @@ -2856,22 +2857,23 @@ }, { "name": "laminas/laminas-stratigility", - "version": "3.14.1", + "version": "4.3.1", "source": { "type": "git", "url": "https://github.com/laminas/laminas-stratigility.git", - "reference": "01368bf7b0be76a5f15d0727607da8253d451225" + "reference": "32be07e85cff5a3ba97ba14ff57ead04eade4051" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-stratigility/zipball/01368bf7b0be76a5f15d0727607da8253d451225", - "reference": "01368bf7b0be76a5f15d0727607da8253d451225", + "url": "https://api.github.com/repos/laminas/laminas-stratigility/zipball/32be07e85cff5a3ba97ba14ff57ead04eade4051", + "reference": "32be07e85cff5a3ba97ba14ff57ead04eade4051", "shasum": "" }, "require": { "fig/http-message-util": "^1.1", "laminas/laminas-escaper": "^2.10.0", "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", + "psr/http-factory": "^1.0.2", "psr/http-message": "^1.0 || ^2.0", "psr/http-server-middleware": "^1.0.2" }, @@ -2879,14 +2881,15 @@ "zendframework/zend-stratigility": "*" }, "require-dev": { - "laminas/laminas-coding-standard": "^3.1.0", + "laminas/laminas-coding-standard": "~3.1.0", "laminas/laminas-diactoros": "^2.25 || ^3.8.0", - "phpunit/phpunit": "^10.5.58", - "psalm/plugin-phpunit": "^0.19.0", - "vimeo/psalm": "^5.26.1" + "phpunit/phpunit": "^11.5.42", + "psalm/plugin-phpunit": "^0.19.5", + "vimeo/psalm": "^6.13.1" }, "suggest": { - "psr/http-message-implementation": "Please install a psr/http-message-implementation to consume Stratigility; e.g., laminas/laminas-diactoros" + "psr/http-factory-implementation": "Please install a psr/http-factory implementation to consume Stratigility; e.g., laminas/laminas-diactoros", + "psr/http-message-implementation": "Please install a psr/http-message implementation to consume Stratigility; e.g., laminas/laminas-diactoros" }, "type": "library", "autoload": { @@ -2894,11 +2897,7 @@ "src/functions/double-pass-middleware.php", "src/functions/host.php", "src/functions/middleware.php", - "src/functions/path.php", - "src/functions/double-pass-middleware.legacy.php", - "src/functions/host.legacy.php", - "src/functions/middleware.legacy.php", - "src/functions/path.legacy.php" + "src/functions/path.php" ], "psr-4": { "Laminas\\Stratigility\\": "src/" @@ -2915,6 +2914,7 @@ "laminas", "middleware", "psr-15", + "psr-17", "psr-7" ], "support": { @@ -2931,7 +2931,7 @@ "type": "community_bridge" } ], - "time": "2026-05-03T17:52:33+00:00" + "time": "2026-05-03T18:04:42+00:00" }, { "name": "laminas/laminas-text", @@ -3294,30 +3294,30 @@ }, { "name": "mezzio/mezzio", - "version": "3.23.2", + "version": "3.28.1", "source": { "type": "git", "url": "https://github.com/mezzio/mezzio.git", - "reference": "988d39687683c9ae70d213c68c75c89965caad30" + "reference": "b67f3f4a090d444e94e1ef3c49085b8e313ad501" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mezzio/mezzio/zipball/988d39687683c9ae70d213c68c75c89965caad30", - "reference": "988d39687683c9ae70d213c68c75c89965caad30", + "url": "https://api.github.com/repos/mezzio/mezzio/zipball/b67f3f4a090d444e94e1ef3c49085b8e313ad501", + "reference": "b67f3f4a090d444e94e1ef3c49085b8e313ad501", "shasum": "" }, "require": { "fig/http-message-util": "^1.1.5", "laminas/laminas-httphandlerrunner": "^2.1", - "laminas/laminas-stratigility": "^3.5", - "mezzio/mezzio-router": "^3.15.0", - "mezzio/mezzio-template": "^2.2", + "laminas/laminas-stratigility": "^4.3", + "mezzio/mezzio-router": "^3.15.0 || ^4.0.0", + "mezzio/mezzio-template": "^2.2 || ^3.0", "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", "psr/container": "^1.0||^2.0", "psr/http-factory": "^1.0", "psr/http-message": "^1.0.1 || ^2.0.0", "psr/http-server-middleware": "^1.0", - "webmozart/assert": "^1.11.0" + "webmozart/assert": "^1.11.0 || ^2.0.0" }, "conflict": { "container-interop/container-interop": "<1.2.0", @@ -3332,10 +3332,10 @@ "filp/whoops": "^2.18.4", "laminas/laminas-coding-standard": "^3.1.0", "laminas/laminas-diactoros": "^3.8.0", - "laminas/laminas-servicemanager": "^3.23.1", + "laminas/laminas-servicemanager": "^3.24.0", "mezzio/mezzio-fastroute": "^3.14", "mezzio/mezzio-laminasrouter": "^3.12", - "phpunit/phpunit": "^11.5.42", + "phpunit/phpunit": "^11.5.44", "psalm/plugin-phpunit": "^0.19.5", "vimeo/psalm": "^6.13.1" }, @@ -3396,7 +3396,7 @@ "type": "community_bridge" } ], - "time": "2025-10-22T10:56:04+00:00" + "time": "2026-06-10T09:37:33+00:00" }, { "name": "mezzio/mezzio-authentication", @@ -4168,16 +4168,16 @@ }, { "name": "mimmi20/mezzio-navigation", - "version": "5.0.5", + "version": "5.0.6", "source": { "type": "git", "url": "https://github.com/mimmi20/mezzio-navigation.git", - "reference": "19fb80664c3fb3d019174a96ad2b39f356961021" + "reference": "341cb57023d46ceb21461368afb2443d85c8dbe2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mimmi20/mezzio-navigation/zipball/19fb80664c3fb3d019174a96ad2b39f356961021", - "reference": "19fb80664c3fb3d019174a96ad2b39f356961021", + "url": "https://api.github.com/repos/mimmi20/mezzio-navigation/zipball/341cb57023d46ceb21461368afb2443d85c8dbe2", + "reference": "341cb57023d46ceb21461368afb2443d85c8dbe2", "shasum": "" }, "require": { @@ -4185,7 +4185,7 @@ "laminas/laminas-servicemanager": "^3.23.0", "laminas/laminas-stdlib": "^3.21.0", "mimmi20/mezzio-generic-authorization": "^4.0.0", - "php": "~8.3.0 || ~8.4.0 || ~8.5.0", + "php": "~8.3.0 || ~8.4.0 || ~8.5.0 || ~8.6.0", "psr/container": "^1.1.2", "psr/http-message": "^2.0", "psr/http-server-handler": "^1.0.2", @@ -4196,29 +4196,31 @@ "mezzio/mezzio-authorization": "*" }, "require-dev": { + "ergebnis/composer-normalize": "^2.52.0", "ext-ctype": "*", "ext-dom": "*", "ext-simplexml": "*", "ext-tokenizer": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "infection/infection": "^0.31.7", + "infection/infection": "^0.33.2", + "jbelien/phpstan-sarif-formatter": "^1.2.0", "mezzio/mezzio-helpers": "^5.20.0", "mezzio/mezzio-router": "^3.18.0", "mimmi20/coding-standard": "^6.2.5", - "nikic/php-parser": "^5.6.2", + "nikic/php-parser": "^5.7.0", "phpstan/extension-installer": "^1.4.3", - "phpstan/phpstan": "^2.1.31", - "phpstan/phpstan-deprecation-rules": "^2.0.3", - "phpunit/phpunit": "^12.4.1", - "rector/rector": "^2.2.4", - "rector/type-perfect": "^2.1.1", - "shipmonk/composer-dependency-analyser": "^1.8.3", + "phpstan/phpstan": "^2.2.2", + "phpstan/phpstan-deprecation-rules": "^2.0.4", + "phpunit/phpunit": "^12.5.4", + "rector/rector": "^2.4.5", + "rector/type-perfect": "^2.1.4", + "shipmonk/composer-dependency-analyser": "^1.8.4", "symfony/process": "^7.3.4", - "symplify/phpstan-rules": "^14.8.0", - "tomasvotruba/cognitive-complexity": "^1.0.0", - "tomasvotruba/type-coverage": "^2.0.2", - "tomasvotruba/unused-public": "^2.0.1" + "symplify/phpstan-rules": "^14.10.0", + "tomasvotruba/cognitive-complexity": "^1.1.1", + "tomasvotruba/type-coverage": "^2.2.1", + "tomasvotruba/unused-public": "^2.2.0" }, "suggest": { "laminas/laminas-config": "to provide page configuration (optional, as arrays and Traversables are also allowed)", @@ -4230,6 +4232,10 @@ "extra": { "laminas": { "config-provider": "Mimmi20\\Mezzio\\Navigation\\ConfigProvider" + }, + "composer-normalize": { + "indent-size": 2, + "indent-style": "space" } }, "autoload": { @@ -4258,7 +4264,7 @@ "issues": "https://github.com/mimmi20/mezzio-navigation/issues", "source": "https://github.com/mimmi20/mezzio-navigation" }, - "time": "2025-11-10T17:14:22+00:00" + "time": "2026-06-14T06:38:01+00:00" }, { "name": "mimmi20/mezzio-navigation-laminasviewrenderer", @@ -4848,24 +4854,24 @@ }, { "name": "roave/psr-container-doctrine", - "version": "6.1.0", + "version": "6.2.0", "source": { "type": "git", "url": "https://github.com/Roave/psr-container-doctrine.git", - "reference": "fee8378c681942f994a2d042ffe084b7cf51de5e" + "reference": "08e441312170b589fbae730cfd05636b4555abd5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/psr-container-doctrine/zipball/fee8378c681942f994a2d042ffe084b7cf51de5e", - "reference": "fee8378c681942f994a2d042ffe084b7cf51de5e", + "url": "https://api.github.com/repos/Roave/psr-container-doctrine/zipball/08e441312170b589fbae730cfd05636b4555abd5", + "reference": "08e441312170b589fbae730cfd05636b4555abd5", "shasum": "" }, "require": { "doctrine/common": "^3.5.0", - "doctrine/dbal": "^4.3.4", - "doctrine/event-manager": "^2.0.1", - "doctrine/migrations": "^3.9.4", - "doctrine/orm": "^3.5.2", + "doctrine/dbal": "^4.4.2", + "doctrine/event-manager": "^2.1.1", + "doctrine/migrations": "^3.9.6", + "doctrine/orm": "^3.6.2", "doctrine/persistence": "^3.4.0 || ^4.1.1", "php": "~8.4.0 || ~8.5.0", "psr/cache": "^2.0.0 || ^3.0.0", @@ -4873,10 +4879,13 @@ }, "require-dev": { "doctrine/coding-standard": "^14.0.0", - "phpunit/phpunit": "^12.4.1", + "illuminate/container": "^13.9.0", + "laminas/laminas-servicemanager": "^4.5.0", + "phpunit/phpunit": "^13.1.10", "psalm/plugin-phpunit": "^0.19.5", - "symfony/yaml": "^7.3.3", - "vimeo/psalm": "^6.13.1" + "symfony/cache": "^8.0.10", + "symfony/yaml": "^8.0.11", + "vimeo/psalm": "^6.15.1" }, "type": "library", "autoload": { @@ -4903,29 +4912,35 @@ "homepage": "https://github.com/Roave/psr-container-doctrine", "support": { "issues": "https://github.com/Roave/psr-container-doctrine/issues", - "source": "https://github.com/Roave/psr-container-doctrine/tree/6.1.0" + "source": "https://github.com/Roave/psr-container-doctrine/tree/6.2.0" }, - "time": "2025-10-23T13:28:05+00:00" + "time": "2026-05-15T15:05:33+00:00" }, { "name": "symfony/console", - "version": "v8.0.9", + "version": "v8.1.1", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "7113778e2e91f4709cb3194a75dfa9c0d028d94d" + "reference": "b711a8ab808b6c074c6b8caef70d0fd8d6b6d07d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/7113778e2e91f4709cb3194a75dfa9c0d028d94d", - "reference": "7113778e2e91f4709cb3194a75dfa9c0d028d94d", + "url": "https://api.github.com/repos/symfony/console/zipball/b711a8ab808b6c074c6b8caef70d0fd8d6b6d07d", + "reference": "b711a8ab808b6c074c6b8caef70d0fd8d6b6d07d", "shasum": "" }, "require": { - "php": ">=8.4", + "php": ">=8.4.1", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "^1.0", + "symfony/polyfill-php85": "^1.32", "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^7.4|^8.0" + "symfony/string": "^7.4.6|^8.0.6" + }, + "conflict": { + "symfony/dependency-injection": "<8.1", + "symfony/event-dispatcher": "<8.1" }, "provide": { "psr/log-implementation": "1.0|2.0|3.0" @@ -4933,14 +4948,18 @@ "require-dev": { "psr/log": "^1|^2|^3", "symfony/config": "^7.4|^8.0", - "symfony/dependency-injection": "^7.4|^8.0", - "symfony/event-dispatcher": "^7.4|^8.0", + "symfony/dependency-injection": "^8.1", + "symfony/event-dispatcher": "^8.1", + "symfony/filesystem": "^7.4|^8.0", "symfony/http-foundation": "^7.4|^8.0", "symfony/http-kernel": "^7.4|^8.0", "symfony/lock": "^7.4|^8.0", "symfony/messenger": "^7.4|^8.0", + "symfony/mime": "^7.4|^8.0", "symfony/process": "^7.4|^8.0", "symfony/stopwatch": "^7.4|^8.0", + "symfony/uid": "^7.4|^8.0", + "symfony/validator": "^7.4|^8.0", "symfony/var-dumper": "^7.4|^8.0" }, "type": "library", @@ -4975,7 +4994,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v8.0.9" + "source": "https://github.com/symfony/console/tree/v8.1.1" }, "funding": [ { @@ -4995,20 +5014,20 @@ "type": "tidelift" } ], - "time": "2026-04-29T15:02:55+00:00" + "time": "2026-06-16T12:55:20+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v3.7.0", + "version": "v3.7.1", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "50f59d1f3ca46d41ac911f97a78626b6756af35b" + "reference": "f3202fa1b5097b0af062dc978b32ecf63404e31d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/50f59d1f3ca46d41ac911f97a78626b6756af35b", - "reference": "50f59d1f3ca46d41ac911f97a78626b6756af35b", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/f3202fa1b5097b0af062dc978b32ecf63404e31d", + "reference": "f3202fa1b5097b0af062dc978b32ecf63404e31d", "shasum": "" }, "require": { @@ -5046,7 +5065,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.7.0" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.7.1" }, "funding": [ { @@ -5066,7 +5085,7 @@ "type": "tidelift" } ], - "time": "2026-04-13T15:52:40+00:00" + "time": "2026-06-05T06:23:12+00:00" }, { "name": "symfony/polyfill-ctype", @@ -5151,18 +5170,105 @@ ], "time": "2026-04-10T16:19:22+00:00" }, + { + "name": "symfony/polyfill-deepclone", + "version": "v1.40.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-deepclone.git", + "reference": "dca4ccba5f360070b574414dce4c1e7a559844fa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-deepclone/zipball/dca4ccba5f360070b574414dce4c1e7a559844fa", + "reference": "dca4ccba5f360070b574414dce4c1e7a559844fa", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "provide": { + "ext-deepclone": "*" + }, + "suggest": { + "ext-deepclone": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\DeepClone\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the deepclone extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "deepclone", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-deepclone/tree/v1.40.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-06-12T07:27:17+00:00" + }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.37.0", + "version": "v1.38.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "4864388bfbd3001ce88e234fab652acd91fdc57e" + "reference": "e9247d281d694a5120554d9afaf54e070e88a603" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/4864388bfbd3001ce88e234fab652acd91fdc57e", - "reference": "4864388bfbd3001ce88e234fab652acd91fdc57e", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/e9247d281d694a5120554d9afaf54e070e88a603", + "reference": "e9247d281d694a5120554d9afaf54e070e88a603", "shasum": "" }, "require": { @@ -5211,7 +5317,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.37.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.38.1" }, "funding": [ { @@ -5231,20 +5337,20 @@ "type": "tidelift" } ], - "time": "2026-04-26T13:13:48+00:00" + "time": "2026-05-26T05:58:03+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.37.0", + "version": "v1.38.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "3833d7255cc303546435cb650316bff708a1c75c" + "reference": "2d446c214bdbe5b71bde5011b060a05fece3ae6b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", - "reference": "3833d7255cc303546435cb650316bff708a1c75c", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/2d446c214bdbe5b71bde5011b060a05fece3ae6b", + "reference": "2d446c214bdbe5b71bde5011b060a05fece3ae6b", "shasum": "" }, "require": { @@ -5296,7 +5402,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.37.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.38.0" }, "funding": [ { @@ -5316,20 +5422,20 @@ "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2026-05-25T13:48:31+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.37.0", + "version": "v1.38.2", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "6a21eb99c6973357967f6ce3708cd55a6bec6315" + "reference": "d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6a21eb99c6973357967f6ce3708cd55a6bec6315", - "reference": "6a21eb99c6973357967f6ce3708cd55a6bec6315", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6", + "reference": "d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6", "shasum": "" }, "require": { @@ -5381,7 +5487,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.37.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.38.2" }, "funding": [ { @@ -5401,20 +5507,20 @@ "type": "tidelift" } ], - "time": "2026-04-10T17:25:58+00:00" + "time": "2026-05-27T06:59:30+00:00" }, { "name": "symfony/polyfill-php84", - "version": "v1.37.0", + "version": "v1.38.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php84.git", - "reference": "88486db2c389b290bf87ff1de7ebc1e13e42bb06" + "reference": "f4e1dfaee5b74aba5964fe1fd4dfc7ba5e3085fa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/88486db2c389b290bf87ff1de7ebc1e13e42bb06", - "reference": "88486db2c389b290bf87ff1de7ebc1e13e42bb06", + "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/f4e1dfaee5b74aba5964fe1fd4dfc7ba5e3085fa", + "reference": "f4e1dfaee5b74aba5964fe1fd4dfc7ba5e3085fa", "shasum": "" }, "require": { @@ -5461,7 +5567,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php84/tree/v1.37.0" + "source": "https://github.com/symfony/polyfill-php84/tree/v1.38.1" }, "funding": [ { @@ -5481,20 +5587,100 @@ "type": "tidelift" } ], - "time": "2026-04-10T18:47:49+00:00" + "time": "2026-05-26T12:51:13+00:00" + }, + { + "name": "symfony/polyfill-php85", + "version": "v1.38.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php85.git", + "reference": "ba2ba04f3352cfa2dcbbcb90aee13ed967f505b1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/ba2ba04f3352cfa2dcbbcb90aee13ed967f505b1", + "reference": "ba2ba04f3352cfa2dcbbcb90aee13ed967f505b1", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php85\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.5+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php85/tree/v1.38.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-05-26T02:25:22+00:00" }, { "name": "symfony/service-contracts", - "version": "v3.7.0", + "version": "v3.7.1", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "d25d82433a80eba6aa0e6c24b61d7370d99e444a" + "reference": "c0a284bab1ed8aa0417e3d69250ab437739563a0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/d25d82433a80eba6aa0e6c24b61d7370d99e444a", - "reference": "d25d82433a80eba6aa0e6c24b61d7370d99e444a", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/c0a284bab1ed8aa0417e3d69250ab437739563a0", + "reference": "c0a284bab1ed8aa0417e3d69250ab437739563a0", "shasum": "" }, "require": { @@ -5548,7 +5734,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.7.0" + "source": "https://github.com/symfony/service-contracts/tree/v3.7.1" }, "funding": [ { @@ -5568,24 +5754,24 @@ "type": "tidelift" } ], - "time": "2026-03-28T09:44:51+00:00" + "time": "2026-06-16T09:55:08+00:00" }, { "name": "symfony/stopwatch", - "version": "v8.0.8", + "version": "v8.1.0", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", - "reference": "85954ed72d5440ea4dc9a10b7e49e01df766ffa3" + "reference": "21c07b026905d596e8379caeb115d87aa479499d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/85954ed72d5440ea4dc9a10b7e49e01df766ffa3", - "reference": "85954ed72d5440ea4dc9a10b7e49e01df766ffa3", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/21c07b026905d596e8379caeb115d87aa479499d", + "reference": "21c07b026905d596e8379caeb115d87aa479499d", "shasum": "" }, "require": { - "php": ">=8.4", + "php": ">=8.4.1", "symfony/service-contracts": "^2.5|^3" }, "type": "library", @@ -5614,7 +5800,7 @@ "description": "Provides a way to profile code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/stopwatch/tree/v8.0.8" + "source": "https://github.com/symfony/stopwatch/tree/v8.1.0" }, "funding": [ { @@ -5634,24 +5820,24 @@ "type": "tidelift" } ], - "time": "2026-03-30T15:14:47+00:00" + "time": "2026-05-29T05:06:50+00:00" }, { "name": "symfony/string", - "version": "v8.0.8", + "version": "v8.1.0", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "ae9488f874d7603f9d2dfbf120203882b645d963" + "reference": "afd5944f4005862d961efb85c8bbd5c523c4e3c9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/ae9488f874d7603f9d2dfbf120203882b645d963", - "reference": "ae9488f874d7603f9d2dfbf120203882b645d963", + "url": "https://api.github.com/repos/symfony/string/zipball/afd5944f4005862d961efb85c8bbd5c523c4e3c9", + "reference": "afd5944f4005862d961efb85c8bbd5c523c4e3c9", "shasum": "" }, "require": { - "php": ">=8.4", + "php": ">=8.4.1", "symfony/polyfill-ctype": "^1.8", "symfony/polyfill-intl-grapheme": "^1.33", "symfony/polyfill-intl-normalizer": "^1.0", @@ -5704,7 +5890,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v8.0.8" + "source": "https://github.com/symfony/string/tree/v8.1.0" }, "funding": [ { @@ -5724,24 +5910,26 @@ "type": "tidelift" } ], - "time": "2026-03-30T15:14:47+00:00" + "time": "2026-05-29T05:06:50+00:00" }, { "name": "symfony/var-exporter", - "version": "v8.0.9", + "version": "v8.1.1", "source": { "type": "git", "url": "https://github.com/symfony/var-exporter.git", - "reference": "24cf67be4dd0926e4413635418682f4fff831412" + "reference": "75b74315b4e4be40e5534cf9c5cc30dd0907ed71" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-exporter/zipball/24cf67be4dd0926e4413635418682f4fff831412", - "reference": "24cf67be4dd0926e4413635418682f4fff831412", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/75b74315b4e4be40e5534cf9c5cc30dd0907ed71", + "reference": "75b74315b4e4be40e5534cf9c5cc30dd0907ed71", "shasum": "" }, "require": { - "php": ">=8.4" + "php": ">=8.4.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-deepclone": "^1.40" }, "require-dev": { "symfony/property-access": "^7.4|^8.0", @@ -5771,11 +5959,12 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Allows exporting any serializable PHP data structure to plain PHP code", + "description": "Provides tools to export, instantiate, hydrate, clone and lazy-load PHP objects", "homepage": "https://symfony.com", "keywords": [ "clone", "construct", + "deep-clone", "export", "hydrate", "instantiate", @@ -5784,7 +5973,7 @@ "serialize" ], "support": { - "source": "https://github.com/symfony/var-exporter/tree/v8.0.9" + "source": "https://github.com/symfony/var-exporter/tree/v8.1.1" }, "funding": [ { @@ -5804,7 +5993,7 @@ "type": "tidelift" } ], - "time": "2026-04-18T13:51:42+00:00" + "time": "2026-06-27T09:05:56+00:00" }, { "name": "webimpress/safe-writer", @@ -5867,23 +6056,23 @@ }, { "name": "webmozart/assert", - "version": "1.12.1", + "version": "2.4.1", "source": { "type": "git", "url": "https://github.com/webmozarts/assert.git", - "reference": "9be6926d8b485f55b9229203f962b51ed377ba68" + "reference": "2ccb7c2e821038c03a3e6e1700c570c158c55f70" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/9be6926d8b485f55b9229203f962b51ed377ba68", - "reference": "9be6926d8b485f55b9229203f962b51ed377ba68", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/2ccb7c2e821038c03a3e6e1700c570c158c55f70", + "reference": "2ccb7c2e821038c03a3e6e1700c570c158c55f70", "shasum": "" }, "require": { "ext-ctype": "*", "ext-date": "*", "ext-filter": "*", - "php": "^7.2 || ^8.0" + "php": "^8.2" }, "suggest": { "ext-intl": "", @@ -5892,8 +6081,12 @@ }, "type": "library", "extra": { + "psalm": { + "pluginClass": "Webmozart\\Assert\\PsalmPlugin" + }, "branch-alias": { - "dev-master": "1.10-dev" + "dev-master": "2.0-dev", + "dev-feature/2-0": "2.0-dev" } }, "autoload": { @@ -5909,6 +6102,10 @@ { "name": "Bernhard Schussek", "email": "bschussek@gmail.com" + }, + { + "name": "Woody Gilk", + "email": "woody.gilk@gmail.com" } ], "description": "Assertions to validate method input/output with nice error messages.", @@ -5919,9 +6116,9 @@ ], "support": { "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.12.1" + "source": "https://github.com/webmozarts/assert/tree/2.4.1" }, - "time": "2025-10-29T15:56:20+00:00" + "time": "2026-06-15T15:31:57+00:00" } ], "packages-dev": [ @@ -5991,28 +6188,29 @@ }, { "name": "composer/pcre", - "version": "3.3.2", + "version": "3.4.0", "source": { "type": "git", "url": "https://github.com/composer/pcre.git", - "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e" + "reference": "d5a341b3fb61f3001970940afb1d332968a183ed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/pcre/zipball/b2bed4734f0cc156ee1fe9c0da2550420d99a21e", - "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e", + "url": "https://api.github.com/repos/composer/pcre/zipball/d5a341b3fb61f3001970940afb1d332968a183ed", + "reference": "d5a341b3fb61f3001970940afb1d332968a183ed", "shasum": "" }, "require": { "php": "^7.4 || ^8.0" }, "conflict": { - "phpstan/phpstan": "<1.11.10" + "phpstan/phpstan": "<2.2.2" }, "require-dev": { - "phpstan/phpstan": "^1.12 || ^2", - "phpstan/phpstan-strict-rules": "^1 || ^2", - "phpunit/phpunit": "^8 || ^9" + "phpstan/phpstan": "^2", + "phpstan/phpstan-deprecation-rules": "^2", + "phpstan/phpstan-strict-rules": "^2", + "phpunit/phpunit": "^9" }, "type": "library", "extra": { @@ -6050,7 +6248,7 @@ ], "support": { "issues": "https://github.com/composer/pcre/issues", - "source": "https://github.com/composer/pcre/tree/3.3.2" + "source": "https://github.com/composer/pcre/tree/3.4.0" }, "funding": [ { @@ -6060,13 +6258,9 @@ { "url": "https://github.com/composer", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" } ], - "time": "2024-11-12T16:29:46+00:00" + "time": "2026-06-07T11:47:49+00:00" }, { "name": "composer/semver", @@ -6390,23 +6584,23 @@ }, { "name": "friendsofphp/php-cs-fixer", - "version": "v3.95.1", + "version": "v3.95.11", "source": { "type": "git", "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", - "reference": "a9727678fbd12997f1d9de8f4a37824ed9df1065" + "reference": "35f98e1293283397824d7f349ce5afb8747c3cd5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/a9727678fbd12997f1d9de8f4a37824ed9df1065", - "reference": "a9727678fbd12997f1d9de8f4a37824ed9df1065", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/35f98e1293283397824d7f349ce5afb8747c3cd5", + "reference": "35f98e1293283397824d7f349ce5afb8747c3cd5", "shasum": "" }, "require": { "clue/ndjson-react": "^1.3", "composer/semver": "^3.4", "composer/xdebug-handler": "^3.0.5", - "ergebnis/agent-detector": "^1.1.1", + "ergebnis/agent-detector": "^1.2", "ext-filter": "*", "ext-hash": "*", "ext-json": "*", @@ -6417,32 +6611,32 @@ "react/event-loop": "^1.5", "react/socket": "^1.16", "react/stream": "^1.4", - "sebastian/diff": "^4.0.6 || ^5.1.1 || ^6.0.2 || ^7.0 || ^8.0", + "sebastian/diff": "^4.0.6 || ^5.1.1 || ^6.0.2 || ^7.0 || ^8.0 || ^9.0", "symfony/console": "^5.4.47 || ^6.4.24 || ^7.0 || ^8.0", "symfony/event-dispatcher": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0", "symfony/filesystem": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0", "symfony/finder": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0", "symfony/options-resolver": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0", - "symfony/polyfill-mbstring": "^1.33", - "symfony/polyfill-php80": "^1.33", - "symfony/polyfill-php81": "^1.33", - "symfony/polyfill-php84": "^1.33", + "symfony/polyfill-mbstring": "^1.37", + "symfony/polyfill-php80": "^1.37", + "symfony/polyfill-php81": "^1.37", + "symfony/polyfill-php84": "^1.37", "symfony/process": "^5.4.47 || ^6.4.24 || ^7.2 || ^8.0", "symfony/stopwatch": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0" }, "require-dev": { - "facile-it/paraunit": "^1.3.1 || ^2.8.0", - "infection/infection": "^0.32.6", - "justinrainbow/json-schema": "^6.8.0", + "facile-it/paraunit": "^1.3.1 || ^2.11.0", + "infection/infection": "^0.32.7", + "justinrainbow/json-schema": "^6.10.0", "keradus/cli-executor": "^2.3", "mikey179/vfsstream": "^1.6.12", "php-coveralls/php-coveralls": "^2.9.1", "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.8", "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.8", "phpunit/phpunit": "^9.6.34 || ^10.5.63 || ^11.5.55", - "symfony/polyfill-php85": "^1.33", - "symfony/var-dumper": "^5.4.48 || ^6.4.32 || ^7.4.4 || ^8.0.8", - "symfony/yaml": "^5.4.45 || ^6.4.30 || ^7.4.1 || ^8.0.8" + "symfony/polyfill-php85": "^1.38", + "symfony/var-dumper": "^5.4.48 || ^6.4.36 || ^7.4.8 || ^8.1.0", + "symfony/yaml": "^5.4.53 || ^6.4.41 || ^7.4.13 || ^8.1.0" }, "suggest": { "ext-dom": "For handling output formats in XML", @@ -6483,7 +6677,7 @@ ], "support": { "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", - "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.95.1" + "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.95.11" }, "funding": [ { @@ -6491,7 +6685,7 @@ "type": "github" } ], - "time": "2026-04-12T17:00:09+00:00" + "time": "2026-06-25T14:17:04+00:00" }, { "name": "laminas/laminas-cli", @@ -6563,69 +6757,6 @@ ], "time": "2026-03-10T09:04:36+00:00" }, - { - "name": "laminas/laminas-code", - "version": "4.17.0", - "source": { - "type": "git", - "url": "https://github.com/laminas/laminas-code.git", - "reference": "40d61e2899ec17c5d08bbc0a2d586b3ca17ab9bd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-code/zipball/40d61e2899ec17c5d08bbc0a2d586b3ca17ab9bd", - "reference": "40d61e2899ec17c5d08bbc0a2d586b3ca17ab9bd", - "shasum": "" - }, - "require": { - "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" - }, - "require-dev": { - "doctrine/annotations": "^2.0.1", - "ext-phar": "*", - "laminas/laminas-coding-standard": "^3.0.0", - "laminas/laminas-stdlib": "^3.18.0", - "phpunit/phpunit": "^10.5.58", - "psalm/plugin-phpunit": "^0.19.0", - "vimeo/psalm": "^5.15.0" - }, - "suggest": { - "doctrine/annotations": "Doctrine\\Common\\Annotations >=1.0 for annotation features", - "laminas/laminas-stdlib": "Laminas\\Stdlib component" - }, - "type": "library", - "autoload": { - "psr-4": { - "Laminas\\Code\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "description": "Extensions to the PHP Reflection API, static code scanning, and code generation", - "homepage": "https://laminas.dev", - "keywords": [ - "code", - "laminas", - "laminasframework" - ], - "support": { - "chat": "https://laminas.dev/chat", - "docs": "https://docs.laminas.dev/laminas-code/", - "forum": "https://discourse.laminas.dev", - "issues": "https://github.com/laminas/laminas-code/issues", - "rss": "https://github.com/laminas/laminas-code/releases.atom", - "source": "https://github.com/laminas/laminas-code" - }, - "funding": [ - { - "url": "https://funding.communitybridge.org/projects/laminas-project", - "type": "community_bridge" - } - ], - "time": "2025-11-01T09:38:14+00:00" - }, { "name": "laminas/laminas-development-mode", "version": "3.15.0", @@ -6689,24 +6820,21 @@ }, { "name": "mezzio/mezzio-tooling", - "version": "2.12.0", + "version": "2.13.0", "source": { "type": "git", "url": "https://github.com/mezzio/mezzio-tooling.git", - "reference": "41e8242b27398d0511223d48bbd9efc97d6a2e40" + "reference": "7875384c2840faf51c94f8e3fe0990caf011c0e9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mezzio/mezzio-tooling/zipball/41e8242b27398d0511223d48bbd9efc97d6a2e40", - "reference": "41e8242b27398d0511223d48bbd9efc97d6a2e40", + "url": "https://api.github.com/repos/mezzio/mezzio-tooling/zipball/7875384c2840faf51c94f8e3fe0990caf011c0e9", + "reference": "7875384c2840faf51c94f8e3fe0990caf011c0e9", "shasum": "" }, "require": { "ext-json": "*", "laminas/laminas-cli": "^1.7.0", - "laminas/laminas-code": "^4.7.1", - "laminas/laminas-stdlib": "^3.15.0", - "laminas/laminas-stratigility": "^3.9.0", "mezzio/mezzio": "^3.13.0", "mezzio/mezzio-router": "^3.9.0", "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.1", @@ -6764,11 +6892,11 @@ }, "funding": [ { - "url": "https://funding.communitybridge.org/projects/laminas-project", - "type": "community_bridge" + "url": "https://crowdfunding.linuxfoundation.org/initiatives/laminas-project", + "type": "custom" } ], - "time": "2025-09-12T08:39:37+00:00" + "time": "2026-06-30T15:17:55+00:00" }, { "name": "myclabs/deep-copy", @@ -6950,11 +7078,11 @@ }, { "name": "phpstan/phpstan", - "version": "2.1.54", + "version": "2.2.3", "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/8be50c3992107dc837b17da4d140fbbdf9a5c5bd", - "reference": "8be50c3992107dc837b17da4d140fbbdf9a5c5bd", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/4048833dd47b377287818841877fb3087289509c", + "reference": "4048833dd47b377287818841877fb3087289509c", "shasum": "" }, "require": { @@ -6977,6 +7105,17 @@ "license": [ "MIT" ], + "authors": [ + { + "name": "Ondřej Mirtes" + }, + { + "name": "Markus Staab" + }, + { + "name": "Vincent Langlet" + } + ], "description": "PHPStan - PHP Static Analysis Tool", "keywords": [ "dev", @@ -6999,25 +7138,25 @@ "type": "github" } ], - "time": "2026-04-29T13:31:09+00:00" + "time": "2026-06-30T21:15:26+00:00" }, { "name": "phpstan/phpstan-doctrine", - "version": "2.0.21", + "version": "2.0.27", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-doctrine.git", - "reference": "81dac0ee4363c2359128aec844df31efb215dddc" + "reference": "39b4ca45a07cdd6366eeefa2f7a993cddf3b9f9f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-doctrine/zipball/81dac0ee4363c2359128aec844df31efb215dddc", - "reference": "81dac0ee4363c2359128aec844df31efb215dddc", + "url": "https://api.github.com/repos/phpstan/phpstan-doctrine/zipball/39b4ca45a07cdd6366eeefa2f7a993cddf3b9f9f", + "reference": "39b4ca45a07cdd6366eeefa2f7a993cddf3b9f9f", "shasum": "" }, "require": { "php": "^7.4 || ^8.0", - "phpstan/phpstan": "^2.1.34" + "phpstan/phpstan": "^2.2.2" }, "conflict": { "doctrine/collections": "<1.0", @@ -7046,6 +7185,7 @@ "phpstan/phpstan-strict-rules": "^2.0", "phpunit/phpunit": "^9.6.20", "ramsey/uuid": "^4.2", + "shipmonk/name-collision-detector": "^2.1", "symfony/cache": "^5.4", "symfony/uid": "^5.4 || ^6.4 || ^7.3" }, @@ -7073,9 +7213,9 @@ ], "support": { "issues": "https://github.com/phpstan/phpstan-doctrine/issues", - "source": "https://github.com/phpstan/phpstan-doctrine/tree/2.0.21" + "source": "https://github.com/phpstan/phpstan-doctrine/tree/2.0.27" }, - "time": "2026-04-17T13:00:39+00:00" + "time": "2026-06-10T10:39:35+00:00" }, { "name": "phpstan/phpstan-phpunit", @@ -7135,16 +7275,16 @@ }, { "name": "phpunit/php-code-coverage", - "version": "14.1.7", + "version": "14.2.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "da6e6b64940901650abcea62430c8c24926b7a71" + "reference": "10d7da3628a99289cdf4c662dd7f0d73f1baec83" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/da6e6b64940901650abcea62430c8c24926b7a71", - "reference": "da6e6b64940901650abcea62430c8c24926b7a71", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/10d7da3628a99289cdf4c662dd7f0d73f1baec83", + "reference": "10d7da3628a99289cdf4c662dd7f0d73f1baec83", "shasum": "" }, "require": { @@ -7156,14 +7296,14 @@ "php": ">=8.4", "phpunit/php-text-template": "^6.0", "sebastian/complexity": "^6.0", - "sebastian/environment": "^9.2", + "sebastian/environment": "^9.3.2", "sebastian/git-state": "^1.0", - "sebastian/lines-of-code": "^5.0", + "sebastian/lines-of-code": "^5.0.1", "sebastian/version": "^7.0", "theseer/tokenizer": "^2.0.1" }, "require-dev": { - "phpunit/phpunit": "^13.1" + "phpunit/phpunit": "^13.2.0" }, "suggest": { "ext-pcov": "PHP extension that provides line coverage", @@ -7172,7 +7312,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "14.1.x-dev" + "dev-main": "14.2.x-dev" } }, "autoload": { @@ -7201,7 +7341,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/14.1.7" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/14.2.2" }, "funding": [ { @@ -7221,7 +7361,7 @@ "type": "tidelift" } ], - "time": "2026-05-04T15:51:53+00:00" + "time": "2026-06-08T11:50:38+00:00" }, { "name": "phpunit/php-file-iterator", @@ -7518,16 +7658,16 @@ }, { "name": "phpunit/phpunit", - "version": "13.1.8", + "version": "13.2.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "f49a2b5e51ffb33421745368cc099cf66830d71b" + "reference": "492c067e618de7b3c76105082c90f9d2833401b7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/f49a2b5e51ffb33421745368cc099cf66830d71b", - "reference": "f49a2b5e51ffb33421745368cc099cf66830d71b", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/492c067e618de7b3c76105082c90f9d2833401b7", + "reference": "492c067e618de7b3c76105082c90f9d2833401b7", "shasum": "" }, "require": { @@ -7541,21 +7681,22 @@ "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", "php": ">=8.4.1", - "phpunit/php-code-coverage": "^14.1.6", + "phpunit/php-code-coverage": "^14.2.2", "phpunit/php-file-iterator": "^7.0.0", "phpunit/php-invoker": "^7.0.0", "phpunit/php-text-template": "^6.0.0", "phpunit/php-timer": "^9.0.0", "sebastian/cli-parser": "^5.0.0", - "sebastian/comparator": "^8.1.2", - "sebastian/diff": "^8.1.0", - "sebastian/environment": "^9.3.0", - "sebastian/exporter": "^8.0.2", + "sebastian/comparator": "^8.3.0", + "sebastian/diff": "^9.0", + "sebastian/environment": "^9.3.2", + "sebastian/exporter": "^8.1.0", + "sebastian/file-filter": "^1.0", "sebastian/git-state": "^1.0", - "sebastian/global-state": "^9.0.0", + "sebastian/global-state": "^9.0.1", "sebastian/object-enumerator": "^8.0.0", "sebastian/recursion-context": "^8.0.0", - "sebastian/type": "^7.0.0", + "sebastian/type": "^7.0.1", "sebastian/version": "^7.0.0", "staabm/side-effects-detector": "^1.0.5" }, @@ -7565,7 +7706,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "13.1-dev" + "dev-main": "13.2-dev" } }, "autoload": { @@ -7597,7 +7738,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/13.1.8" + "source": "https://github.com/sebastianbergmann/phpunit/tree/13.2.2" }, "funding": [ { @@ -7605,7 +7746,7 @@ "type": "other" } ], - "time": "2026-05-01T04:22:45+00:00" + "time": "2026-06-29T13:36:29+00:00" }, { "name": "psr/event-dispatcher", @@ -8254,27 +8395,27 @@ }, { "name": "sebastian/comparator", - "version": "8.1.2", + "version": "8.3.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "b3d09f4360ad97dcad8f82d1c047ad16ff38b7e1" + "reference": "c025fc7604afab3f195fab7cdaf72327331af241" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/b3d09f4360ad97dcad8f82d1c047ad16ff38b7e1", - "reference": "b3d09f4360ad97dcad8f82d1c047ad16ff38b7e1", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/c025fc7604afab3f195fab7cdaf72327331af241", + "reference": "c025fc7604afab3f195fab7cdaf72327331af241", "shasum": "" }, "require": { "ext-dom": "*", "ext-mbstring": "*", "php": ">=8.4", - "sebastian/diff": "^8.1", - "sebastian/exporter": "^8.0" + "sebastian/diff": "^9.0", + "sebastian/exporter": "^8.1.0" }, "require-dev": { - "phpunit/phpunit": "^13.0" + "phpunit/phpunit": "^13.2" }, "suggest": { "ext-bcmath": "For comparing BcMath\\Number objects" @@ -8282,7 +8423,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "8.1-dev" + "dev-main": "8.3-dev" } }, "autoload": { @@ -8322,7 +8463,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", "security": "https://github.com/sebastianbergmann/comparator/security/policy", - "source": "https://github.com/sebastianbergmann/comparator/tree/8.1.2" + "source": "https://github.com/sebastianbergmann/comparator/tree/8.3.0" }, "funding": [ { @@ -8342,7 +8483,7 @@ "type": "tidelift" } ], - "time": "2026-04-14T08:24:42+00:00" + "time": "2026-06-05T03:06:45+00:00" }, { "name": "sebastian/complexity", @@ -8416,29 +8557,29 @@ }, { "name": "sebastian/diff", - "version": "8.1.0", + "version": "9.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "9c957d730257f49c873f3761674559bd90098a7d" + "reference": "a3fb6a298a265ff487a91bbea46e03cd01dbb226" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/9c957d730257f49c873f3761674559bd90098a7d", - "reference": "9c957d730257f49c873f3761674559bd90098a7d", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/a3fb6a298a265ff487a91bbea46e03cd01dbb226", + "reference": "a3fb6a298a265ff487a91bbea46e03cd01dbb226", "shasum": "" }, "require": { "php": ">=8.4" }, "require-dev": { - "phpunit/phpunit": "^13.0", - "symfony/process": "^7.2" + "phpunit/phpunit": "^13.2", + "symfony/process": "^7.4.13" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "8.1-dev" + "dev-main": "9.0-dev" } }, "autoload": { @@ -8471,7 +8612,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", "security": "https://github.com/sebastianbergmann/diff/security/policy", - "source": "https://github.com/sebastianbergmann/diff/tree/8.1.0" + "source": "https://github.com/sebastianbergmann/diff/tree/9.0.0" }, "funding": [ { @@ -8491,27 +8632,27 @@ "type": "tidelift" } ], - "time": "2026-04-05T12:02:33+00:00" + "time": "2026-06-05T03:04:51+00:00" }, { "name": "sebastian/environment", - "version": "9.3.0", + "version": "9.3.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "6767059a30e4277ac95ee034809e793528464768" + "reference": "6c9e487c9eb706a8d258102a1c0b0a3e53e86c2e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/6767059a30e4277ac95ee034809e793528464768", - "reference": "6767059a30e4277ac95ee034809e793528464768", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/6c9e487c9eb706a8d258102a1c0b0a3e53e86c2e", + "reference": "6c9e487c9eb706a8d258102a1c0b0a3e53e86c2e", "shasum": "" }, "require": { "php": ">=8.4" }, "require-dev": { - "phpunit/phpunit": "^13.0" + "phpunit/phpunit": "^13.1.11" }, "suggest": { "ext-posix": "*" @@ -8547,7 +8688,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", "security": "https://github.com/sebastianbergmann/environment/security/policy", - "source": "https://github.com/sebastianbergmann/environment/tree/9.3.0" + "source": "https://github.com/sebastianbergmann/environment/tree/9.3.2" }, "funding": [ { @@ -8567,20 +8708,20 @@ "type": "tidelift" } ], - "time": "2026-04-15T12:14:03+00:00" + "time": "2026-05-25T13:41:38+00:00" }, { "name": "sebastian/exporter", - "version": "8.0.2", + "version": "8.1.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "9cee180ebe62259e3ed48df2212d1fc8cfd971bb" + "reference": "c0d29a945f8cf82f300a05e69874508e307ca4c6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/9cee180ebe62259e3ed48df2212d1fc8cfd971bb", - "reference": "9cee180ebe62259e3ed48df2212d1fc8cfd971bb", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/c0d29a945f8cf82f300a05e69874508e307ca4c6", + "reference": "c0d29a945f8cf82f300a05e69874508e307ca4c6", "shasum": "" }, "require": { @@ -8589,12 +8730,12 @@ "sebastian/recursion-context": "^8.0" }, "require-dev": { - "phpunit/phpunit": "^13.0" + "phpunit/phpunit": "^13.1.10" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "8.0-dev" + "dev-main": "8.1-dev" } }, "autoload": { @@ -8637,7 +8778,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", "security": "https://github.com/sebastianbergmann/exporter/security/policy", - "source": "https://github.com/sebastianbergmann/exporter/tree/8.0.2" + "source": "https://github.com/sebastianbergmann/exporter/tree/8.1.0" }, "funding": [ { @@ -8657,7 +8798,76 @@ "type": "tidelift" } ], - "time": "2026-04-15T12:38:05+00:00" + "time": "2026-05-21T11:50:56+00:00" + }, + { + "name": "sebastian/file-filter", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/file-filter.git", + "reference": "33a26f394330f6faa7684bb9cc73afb7727aae93" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/file-filter/zipball/33a26f394330f6faa7684bb9cc73afb7727aae93", + "reference": "33a26f394330f6faa7684bb9cc73afb7727aae93", + "shasum": "" + }, + "require": { + "php": ">=8.4" + }, + "require-dev": { + "phpunit/phpunit": "^13.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for filtering files", + "homepage": "https://github.com/sebastianbergmann/file-filter", + "support": { + "issues": "https://github.com/sebastianbergmann/file-filter/issues", + "security": "https://github.com/sebastianbergmann/file-filter/security/policy", + "source": "https://github.com/sebastianbergmann/file-filter/tree/1.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/file-filter", + "type": "tidelift" + } + ], + "time": "2026-04-22T07:20:04+00:00" }, { "name": "sebastian/git-state", @@ -8730,16 +8940,16 @@ }, { "name": "sebastian/global-state", - "version": "9.0.0", + "version": "9.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "e52e3dc22441e6218c710afe72c3042f8fc41ea7" + "reference": "ba68ba79da690cf7eddefd3ce5b78b20b9ba9945" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/e52e3dc22441e6218c710afe72c3042f8fc41ea7", - "reference": "e52e3dc22441e6218c710afe72c3042f8fc41ea7", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/ba68ba79da690cf7eddefd3ce5b78b20b9ba9945", + "reference": "ba68ba79da690cf7eddefd3ce5b78b20b9ba9945", "shasum": "" }, "require": { @@ -8749,7 +8959,7 @@ }, "require-dev": { "ext-dom": "*", - "phpunit/phpunit": "^13.0" + "phpunit/phpunit": "^13.1.13" }, "type": "library", "extra": { @@ -8780,7 +8990,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", "security": "https://github.com/sebastianbergmann/global-state/security/policy", - "source": "https://github.com/sebastianbergmann/global-state/tree/9.0.0" + "source": "https://github.com/sebastianbergmann/global-state/tree/9.0.1" }, "funding": [ { @@ -8800,28 +9010,28 @@ "type": "tidelift" } ], - "time": "2026-02-06T04:45:13+00:00" + "time": "2026-06-01T15:11:33+00:00" }, { "name": "sebastian/lines-of-code", - "version": "5.0.0", + "version": "5.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "4f21bb7768e1c997722ccc7efb1d6b5c11bfd471" + "reference": "d2cff273a90c79b0eb590baa682d4b5c318bdbb7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/4f21bb7768e1c997722ccc7efb1d6b5c11bfd471", - "reference": "4f21bb7768e1c997722ccc7efb1d6b5c11bfd471", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/d2cff273a90c79b0eb590baa682d4b5c318bdbb7", + "reference": "d2cff273a90c79b0eb590baa682d4b5c318bdbb7", "shasum": "" }, "require": { - "nikic/php-parser": "^5.0", + "nikic/php-parser": "^5.7.0", "php": ">=8.4" }, "require-dev": { - "phpunit/phpunit": "^13.0" + "phpunit/phpunit": "^13.1.10" }, "type": "library", "extra": { @@ -8850,7 +9060,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/5.0.0" + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/5.0.1" }, "funding": [ { @@ -8870,7 +9080,7 @@ "type": "tidelift" } ], - "time": "2026-02-06T04:45:54+00:00" + "time": "2026-05-19T16:23:37+00:00" }, { "name": "sebastian/object-enumerator", @@ -9088,23 +9298,23 @@ }, { "name": "sebastian/type", - "version": "7.0.0", + "version": "7.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", - "reference": "42412224607bd3931241bbd17f38e0f972f5a916" + "reference": "fee0309275847fefd7636167085e379c1dbf6990" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/42412224607bd3931241bbd17f38e0f972f5a916", - "reference": "42412224607bd3931241bbd17f38e0f972f5a916", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/fee0309275847fefd7636167085e379c1dbf6990", + "reference": "fee0309275847fefd7636167085e379c1dbf6990", "shasum": "" }, "require": { "php": ">=8.4" }, "require-dev": { - "phpunit/phpunit": "^13.0" + "phpunit/phpunit": "^13.1.10" }, "type": "library", "extra": { @@ -9133,7 +9343,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/type/issues", "security": "https://github.com/sebastianbergmann/type/security/policy", - "source": "https://github.com/sebastianbergmann/type/tree/7.0.0" + "source": "https://github.com/sebastianbergmann/type/tree/7.0.1" }, "funding": [ { @@ -9153,7 +9363,7 @@ "type": "tidelift" } ], - "time": "2026-02-06T04:52:09+00:00" + "time": "2026-05-20T06:49:11+00:00" }, { "name": "sebastian/version", @@ -9275,20 +9485,21 @@ }, { "name": "symfony/event-dispatcher", - "version": "v8.0.9", + "version": "v8.1.1", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "0c3c1a17604c4dbbec4b93fe162c538482096e1f" + "reference": "abd6c11dc468725d1627302ad10f6cd486e9e3d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/0c3c1a17604c4dbbec4b93fe162c538482096e1f", - "reference": "0c3c1a17604c4dbbec4b93fe162c538482096e1f", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/abd6c11dc468725d1627302ad10f6cd486e9e3d0", + "reference": "abd6c11dc468725d1627302ad10f6cd486e9e3d0", "shasum": "" }, "require": { - "php": ">=8.4", + "php": ">=8.4.1", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/event-dispatcher-contracts": "^2.5|^3" }, "conflict": { @@ -9336,7 +9547,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v8.0.9" + "source": "https://github.com/symfony/event-dispatcher/tree/v8.1.1" }, "funding": [ { @@ -9356,20 +9567,20 @@ "type": "tidelift" } ], - "time": "2026-04-18T13:51:42+00:00" + "time": "2026-06-09T12:28:30+00:00" }, { "name": "symfony/event-dispatcher-contracts", - "version": "v3.7.0", + "version": "v3.7.1", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "ccba7060602b7fed0b03c85bf025257f76d9ef32" + "reference": "c7de7a00ffb67842132da02ea92988a39ccd9f4e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/ccba7060602b7fed0b03c85bf025257f76d9ef32", - "reference": "ccba7060602b7fed0b03c85bf025257f76d9ef32", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/c7de7a00ffb67842132da02ea92988a39ccd9f4e", + "reference": "c7de7a00ffb67842132da02ea92988a39ccd9f4e", "shasum": "" }, "require": { @@ -9416,7 +9627,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.7.0" + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.7.1" }, "funding": [ { @@ -9436,24 +9647,25 @@ "type": "tidelift" } ], - "time": "2026-01-05T13:30:16+00:00" + "time": "2026-06-05T06:23:12+00:00" }, { "name": "symfony/filesystem", - "version": "v8.0.9", + "version": "v8.1.0", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "d1ec4543d5c6c2dac78503c2fae5ea0b3608ce40" + "reference": "99aec13b82b4967ec5088222c4a3ecca955949c2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/d1ec4543d5c6c2dac78503c2fae5ea0b3608ce40", - "reference": "d1ec4543d5c6c2dac78503c2fae5ea0b3608ce40", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/99aec13b82b4967ec5088222c4a3ecca955949c2", + "reference": "99aec13b82b4967ec5088222c4a3ecca955949c2", "shasum": "" }, "require": { - "php": ">=8.4", + "php": ">=8.4.1", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-mbstring": "~1.8" }, @@ -9486,7 +9698,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v8.0.9" + "source": "https://github.com/symfony/filesystem/tree/v8.1.0" }, "funding": [ { @@ -9506,24 +9718,24 @@ "type": "tidelift" } ], - "time": "2026-04-18T13:51:42+00:00" + "time": "2026-05-29T05:06:50+00:00" }, { "name": "symfony/finder", - "version": "v8.0.8", + "version": "v8.1.1", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "8da41214757b87d97f181e3d14a4179286151007" + "reference": "e2989e762c70f9490fa3a00a0ac0fae5aa97a531" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/8da41214757b87d97f181e3d14a4179286151007", - "reference": "8da41214757b87d97f181e3d14a4179286151007", + "url": "https://api.github.com/repos/symfony/finder/zipball/e2989e762c70f9490fa3a00a0ac0fae5aa97a531", + "reference": "e2989e762c70f9490fa3a00a0ac0fae5aa97a531", "shasum": "" }, "require": { - "php": ">=8.4" + "php": ">=8.4.1" }, "require-dev": { "symfony/filesystem": "^7.4|^8.0" @@ -9554,7 +9766,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v8.0.8" + "source": "https://github.com/symfony/finder/tree/v8.1.1" }, "funding": [ { @@ -9574,24 +9786,24 @@ "type": "tidelift" } ], - "time": "2026-03-30T15:14:47+00:00" + "time": "2026-06-27T09:05:56+00:00" }, { "name": "symfony/options-resolver", - "version": "v8.0.8", + "version": "v8.1.0", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "b48bce0a70b914f6953dafbd10474df232ed4de8" + "reference": "88f9c561f678a02d54b897014049fa839e33ff82" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/b48bce0a70b914f6953dafbd10474df232ed4de8", - "reference": "b48bce0a70b914f6953dafbd10474df232ed4de8", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/88f9c561f678a02d54b897014049fa839e33ff82", + "reference": "88f9c561f678a02d54b897014049fa839e33ff82", "shasum": "" }, "require": { - "php": ">=8.4", + "php": ">=8.4.1", "symfony/deprecation-contracts": "^2.5|^3" }, "type": "library", @@ -9625,7 +9837,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v8.0.8" + "source": "https://github.com/symfony/options-resolver/tree/v8.1.0" }, "funding": [ { @@ -9645,7 +9857,7 @@ "type": "tidelift" } ], - "time": "2026-03-30T15:14:47+00:00" + "time": "2026-05-29T05:06:50+00:00" }, { "name": "symfony/polyfill-php80", @@ -9733,16 +9945,16 @@ }, { "name": "symfony/polyfill-php81", - "version": "v1.37.0", + "version": "v1.38.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php81.git", - "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c" + "reference": "6bfb9c766cacffbc8e118cb87217d08ed84e5cd7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", - "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/6bfb9c766cacffbc8e118cb87217d08ed84e5cd7", + "reference": "6bfb9c766cacffbc8e118cb87217d08ed84e5cd7", "shasum": "" }, "require": { @@ -9789,7 +10001,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php81/tree/v1.37.0" + "source": "https://github.com/symfony/polyfill-php81/tree/v1.38.1" }, "funding": [ { @@ -9809,20 +10021,20 @@ "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2026-05-26T12:45:58+00:00" }, { "name": "symfony/process", - "version": "v6.4.33", + "version": "v6.4.41", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "c46e854e79b52d07666e43924a20cb6dc546644e" + "reference": "c8fc09bdfe9fde9aaa89b415a4477feaccec16a7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/c46e854e79b52d07666e43924a20cb6dc546644e", - "reference": "c46e854e79b52d07666e43924a20cb6dc546644e", + "url": "https://api.github.com/repos/symfony/process/zipball/c8fc09bdfe9fde9aaa89b415a4477feaccec16a7", + "reference": "c8fc09bdfe9fde9aaa89b415a4477feaccec16a7", "shasum": "" }, "require": { @@ -9854,7 +10066,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v6.4.33" + "source": "https://github.com/symfony/process/tree/v6.4.41" }, "funding": [ { @@ -9874,7 +10086,7 @@ "type": "tidelift" } ], - "time": "2026-01-23T16:02:12+00:00" + "time": "2026-05-23T13:47:21+00:00" }, { "name": "theseer/tokenizer",