{"id":13545681,"url":"https://github.com/php-tmdb/api","last_synced_at":"2025-10-05T02:10:04.543Z","repository":{"id":11571985,"uuid":"14060127","full_name":"php-tmdb/api","owner":"php-tmdb","description":"PHP 7.3+ API Wrapper for The Movie Database","archived":false,"fork":false,"pushed_at":"2025-05-18T17:48:24.000Z","size":2365,"stargazers_count":416,"open_issues_count":6,"forks_count":114,"subscribers_count":27,"default_branch":"4.1","last_synced_at":"2025-08-03T23:16:59.320Z","etag":null,"topics":["movie","movie-database","movies","php-tmdb","psr-12","psr-17","psr-18","psr-3","psr-6","psr-7","tmdb","tmdb-api","tv"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/php-tmdb.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":["php-tmdb","wtfzdotnet"],"custom":["https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick\u0026hosted_button_id=SMLZ362KQ8K8W"]}},"created_at":"2013-11-02T03:34:50.000Z","updated_at":"2025-07-31T09:16:55.000Z","dependencies_parsed_at":"2024-01-16T17:02:28.236Z","dependency_job_id":"9eaeb2dc-3589-4517-bb21-a48c499de36b","html_url":"https://github.com/php-tmdb/api","commit_stats":{"total_commits":753,"total_committers":49,"mean_commits":15.36734693877551,"dds":"0.24169986719787517","last_synced_commit":"77533d50bf428531a77c116280e01beb8a862066"},"previous_names":[],"tags_count":78,"template":false,"template_full_name":null,"purl":"pkg:github/php-tmdb/api","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/php-tmdb%2Fapi","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/php-tmdb%2Fapi/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/php-tmdb%2Fapi/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/php-tmdb%2Fapi/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/php-tmdb","download_url":"https://codeload.github.com/php-tmdb/api/tar.gz/refs/heads/4.1","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/php-tmdb%2Fapi/sbom","scorecard":{"id":732409,"data":{"date":"2025-08-11","repo":{"name":"github.com/php-tmdb/api","commit":"3c1487aafe955ec4d185c5c29218f3aa340e385c"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.9,"checks":[{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Code-Review","score":5,"reason":"Found 6/11 approved changesets -- score normalized to 5","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/coding-standards.yml:1","Warn: no topLevel permission defined: .github/workflows/continuous-integration.yml:1","Warn: no topLevel permission defined: .github/workflows/static-analysis.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/coding-standards.yml:17: update your workflow using https://app.stepsecurity.io/secureworkflow/php-tmdb/api/coding-standards.yml/4.1?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/coding-standards.yml:22: update your workflow using https://app.stepsecurity.io/secureworkflow/php-tmdb/api/coding-standards.yml/4.1?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/coding-standards.yml:29: update your workflow using https://app.stepsecurity.io/secureworkflow/php-tmdb/api/coding-standards.yml/4.1?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/continuous-integration.yml:55: update your workflow using https://app.stepsecurity.io/secureworkflow/php-tmdb/api/continuous-integration.yml/4.1?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/continuous-integration.yml:60: update your workflow using https://app.stepsecurity.io/secureworkflow/php-tmdb/api/continuous-integration.yml/4.1?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/continuous-integration.yml:65: update your workflow using https://app.stepsecurity.io/secureworkflow/php-tmdb/api/continuous-integration.yml/4.1?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/continuous-integration.yml:87: update your workflow using https://app.stepsecurity.io/secureworkflow/php-tmdb/api/continuous-integration.yml/4.1?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/continuous-integration.yml:92: update your workflow using https://app.stepsecurity.io/secureworkflow/php-tmdb/api/continuous-integration.yml/4.1?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/static-analysis.yml:19: update your workflow using https://app.stepsecurity.io/secureworkflow/php-tmdb/api/static-analysis.yml/4.1?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/static-analysis.yml:22: update your workflow using https://app.stepsecurity.io/secureworkflow/php-tmdb/api/static-analysis.yml/4.1?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/static-analysis.yml:29: update your workflow using https://app.stepsecurity.io/secureworkflow/php-tmdb/api/static-analysis.yml/4.1?enable=pin","Warn: downloadThenRun not pinned by hash: .github/workflows/coding-standards.yml:46","Info:   0 out of   6 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   5 third-party GitHubAction dependencies pinned","Info:   0 out of   1 downloadThenRun dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch '4.1'","Warn: branch protection not enabled for branch '4.0'","Warn: branch protection not enabled for branch '3.0'","Warn: branch protection not enabled for branch '2.1'","Warn: branch protection not enabled for branch '2.0'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 29 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-22T14:47:58.115Z","repository_id":11571985,"created_at":"2025-08-22T14:47:58.115Z","updated_at":"2025-08-22T14:47:58.115Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278399690,"owners_count":25980332,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-05T02:00:06.059Z","response_time":54,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["movie","movie-database","movies","php-tmdb","psr-12","psr-17","psr-18","psr-3","psr-6","psr-7","tmdb","tmdb-api","tv"],"created_at":"2024-08-01T11:01:09.467Z","updated_at":"2025-10-05T02:10:04.504Z","avatar_url":"https://github.com/php-tmdb.png","language":"PHP","funding_links":["https://github.com/sponsors/php-tmdb","https://github.com/sponsors/wtfzdotnet","https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick\u0026hosted_button_id=SMLZ362KQ8K8W"],"categories":["PHP"],"sub_categories":[],"readme":"# A PHP Wrapper for use with the [TMDB API](https://developers.themoviedb.org/4/).\n\n[![License](https://poser.pugx.org/php-tmdb/api/license.png)](https://packagist.org/packages/php-tmdb/api)\n[![License](https://img.shields.io/github/v/tag/php-tmdb/api)](https://github.com/php-tmdb/api/releases)\n[![Build Status](https://img.shields.io/github/actions/workflow/status/php-tmdb/api/continuous-integration.yml?label=phpunit)](https://github.com/php-tmdb/api/actions/workflows/continuous-integration.yml)\n[![Build Status](https://img.shields.io/github/actions/workflow/status/php-tmdb/api/coding-standards.yml?label=phpcs)](https://github.com/php-tmdb/api/actions/workflows/coding-standards.yml)\n[![codecov](https://img.shields.io/codecov/c/github/php-tmdb/api?token=gTM9AiO5vH)](https://codecov.io/gh/php-tmdb/api)\n[![PHP](https://img.shields.io/badge/php-\u003e=7.3,%20\u003e=7.4,%20\u003e=8.0-8892BF.svg)](https://packagist.org/packages/php-tmdb/api)\n[![Total Downloads](https://poser.pugx.org/php-tmdb/api/downloads.svg)](https://packagist.org/packages/php-tmdb/api)\n\nTests run with minimal, normal and development dependencies.\n\n## Buy me a coffee, or a beer :-)\n\n\u003ca href=\"https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick\u0026hosted_button_id=SMLZ362KQ8K8W\"\u003e\u003cimg alt=\"\" border=\"0\" src=\"https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif\"\u003e\u003c/a\u003e\n\nMy stomach will appreciate your donation! \n\n## Main features\n\n- Array implementation of the movie database (RAW)\n- Model implementation of the movie database (By making use of the repositories)\n- An `ImageHelper` class to help build image urls or html \u003cimg\u003e elements.\n\n## Attention newcomers to php\n\n_If  you are new to php and starting a project to learn, I'd recommend [you skip down to the installation](#install-php-tmdbapi),\nand then follow [the quickstart](#new-to-psr-standards-or-composer) that's just  for you!_\n\nI do advise you to take a broader look later on what all these PSR standards mean and do for the php community :-). \n\n## PSR Compliance\n\nWe try to leave as many options open to the end users of this library, as such with 4.0 changes have\nbeen made to introduce PSR compliance where we can. You bring the dependencies you prefer that are compliant\nwith PSR standards, register the listeners, and we handle the rest. \n\n- [PSR-3: Logger Interface](https://www.php-fig.org/psr/psr-3/), [jump to section](#logging).\n    - Logs TMDB API exceptions, [jump to section](#tmdbeventlistenerloggerlogapierrorlistener).\n    - Logs PSR-18 client exceptions, [jump to section](#tmdbeventlistenerloggerloghttpmessagelistener).\n    - Logs requests and responses, [jump to section](#tmdbeventlistenerloggerloghttpmessagelistener).\n    - Logs response hydration, [jump to section](#tmdbeventlistenerloggerloghydrationlistener).\n    - Logs caching behavior , [jump to section](#todo).\n- [PSR-6: Caching Interface](https://www.php-fig.org/psr/psr-6/), [jump to section](#caching).\n- [PSR-7: HTTP Message Interface](https://www.php-fig.org/psr/psr-7/)\n    - Requests and responses will be modified via relevant event listeners.\n- _[PSR-12: Extended Coding Style](https://www.php-fig.org/psr/psr-12/)._\n    - Work in progress, I'll do my best to finish before `4.1` but there is a lot to review and refactor.\n      It would be nice to get contributions going our way helping out with this massive task. I can imagine\n      it may take several months of doing small bits here and there to achieve this.\n- [PSR-14: Event Dispatcher](https://www.php-fig.org/psr/psr-7/), [jump to section](#event-dispatching).\n    - Register our listeners and events, we handle the rest.   \n- _[PSR-16: Simple Cache](https://www.php-fig.org/psr/psr-16/), by adapting to PSR-6_\n    - Although we do not implement this at the current stage, there are plenty of adapters converting `PSR-16` implementations to `PSR-6`.\n    - We might rework this at a later stage to prevent the extra dependencies that the `php-http/cache-plugin` brings along.\n- [PSR-17: HTTP Factories](https://www.php-fig.org/psr/psr-17/)\n    - Bring along the http factories of your choice.\n- [PSR-18: HTTP Client](https://www.php-fig.org/psr/psr-18/)\n    - Bring along the PSR-18 http client of your choice.\n\n## Framework implementations\n\n- Symfony _(maintained by php-tmdb developers)_\n  - [php-tmdb/symfony](https://github.com/php-tmdb/symfony)\n- Laravel _(community maintained)_\n  - [php-tmdb/laravel](https://github.com/php-tmdb/laravel)\n\n## Installation\n\nInstall [composer](https://getcomposer.org/download/).\n\nBefore we can install the api library, you need to install a set of dependencies that provide the following implementations.\n\n## Dependencies you have to fulfill yourself\n\n- For `PSR-7: HTTP Message Interface`, for example `nyholm/psr7`.\n- For `PSR-14: Event Dispatcher`, for example `symfony/event-dispatcher`.\n- For `PSR-17: HTTP Factories`, for example `nyholm/psr7`.\n- For `PSR-18: HTTP Client`, for example `guzzlehttp/guzzle`.\n\n**I urge you to implement the optional caching implementation**\n\nWhen making use of caching, make sure to also include `php-http/cache-plugin` in composer, this plugin handles the logic for us, \nso we don't have to re-invent the wheel. You are however also free to choose to implement your own cache listener, or add \nthe caching logic inside the http client of your choice.\n\n```shell script\ncomposer require php-http/cache-plugin:^1.7\n```\n\nEven though [themoviedb.org](https://www.themoviedb.org/) disabled [rate limiting](https://developers.themoviedb.org/3/getting-started/request-rate-limiting) since the end of 2019, \nI'd still recommend enabling the cache to make your application run a bit smoother. As such the `427` retry subscriber in previous versions is not present anymore.\n\n- For `PSR-6: Caching Interface`, for example `symfony/cache`.\n- For `PSR-16: Simple Cache`, with an PSR-6 adapter for example `symfony/cache`, then use [the PSR-16 to PSR-6 adapter](https://symfony.com/doc/current/components/cache/psr6_psr16_adapters.html).\n\nNot only will this make your application more responsive, by loading from cache when we can, it also decreases the amount of requests we need to send.\n\n_Optional dependencies_\n\n- For `PSR-3: Logger Interface`, for example `monolog/monolog`.\n\n## Install php-tmdb/api\n\nIf the required dependencies above are met, you are ready to install the library.\n\n```shell script\ncomposer require php-tmdb/api:^4\n```\n\nInclude Composer's autoloader:\n\n```php\nrequire_once dirname(__DIR__).'/vendor/autoload.php';\n```\n\nTo use the examples provided, copy the `examples/apikey.php.dist` to `examples/apikey.php` and change the settings.\n\n### New to PSR standards or composer?\n\nIf you came here looking to start a fun project to start learning, the above might seem a little daunting.\n\nDon't worry! The documentation here was setup with beginners in mind as well.\n\nWe also provide a bunch of examples in the `examples/` folder.\n\nTo get started;\n\n```shell script\ncomposer require php-tmdb/api:^4 symfony/event-dispatcher guzzlehttp/guzzle symfony/cache monolog/monolog nyholm/psr7\n```\n\nNow that we have everything we need installed, let's get started setting up to be able to use the library.\n\n## Quick setup\n\nReview the setup files below and go over the [examples](examples/) folder, for example \n[examples/movies/api/get.php](examples/movies/model/get.php) or [examples/movies/api/get.php](examples/movies/api/get.php) files.\n\n## Constructing the Client\n\n_If you have chosen different implementations than the examples suggested beforehand, obviously all the upcoming documentation won't match. Adjust accordingly to your dependencies, we will go along with the examples given earlier._\n\n- [Minimal setup](examples/setup-client.php)\n- [Minimal setup with psr-6 caching](examples/setup-client-cache-psr6.php)\n- [Full setup](examples/setup-client-full.php)\n    - Includes logging\n    - Includes caching\n    - Includes filtering by region\n    - Includes filtering by language\n    - Includes filtering by adult content\n\n## General API Usage\n\nIf you're looking for a simple array entry point the API namespace is the place to be, however we recommend you use the \n[repositories and model](#model-usage)'s functionality up ahead.\n\n```php\nuse Tmdb\\Client;\n\n$client = new Client();\n$movie = $client-\u003egetMoviesApi()-\u003egetMovie(550);\n```\n\nIf you want to provide any other query arguments.\n\n```php\nuse Tmdb\\Client;\n\n$client = new Client();\n$movie = $client-\u003egetMoviesApi()-\u003egetMovie(550, ['language' =\u003e 'en']);\n```\n\nFor all further calls just review the [unit tests](test/Tmdb/Tests) or [examples](examples/) provided, or the API classes themselves.\n\n## Model Usage\n\nThe library can also be used in an object oriented manner, which I reckon is the __preferred__ way of doing things.\n\nInstead of calling upon the client, you pass the client onto one of the many repositories and do then some work on it.\n\n```php\nuse Tmdb\\Repository\\MovieRepository;\nuse Tmdb\\Client;\n\n$client = new Client();\n$repository = new MovieRepository($client);\n$movie = $repository-\u003eload(87421);\n\necho $movie-\u003egetTitle();\n```\n\n__The repositories also contain the other API methods that are available through the API namespace.__\n\n```php\nuse Tmdb\\Repository\\MovieRepository;\nuse Tmdb\\Client;\n\n$client = new Client();\n$repository = new MovieRepository($client);\n$topRated = $repository-\u003egetTopRated(['page' =\u003e 3]);\n// or\n$popular = $repository-\u003egetPopular();\n```\n\nFor all further calls just review the [unit tests](test/Tmdb/Tests) or [examples](examples/) provided, or the model's themselves.\n\n## Event Dispatching\n\nWe (can) dispatch the following events inside the library, which by using event listeners you could modify some behavior.\n\n### HTTP Client exceptions\n- `Tmdb\\Event\\HttpClientExceptionEvent`\n  - Allows to still set a successful response if the error can be corrected, by calling `$event-\u003eisPropagated()` in your listener,\n    this does require you to provide a PSR-7 response object and set it with `$event-\u003esetResponse($response)`.\n\n### TMDB API exceptions \n- `Tmdb\\Event\\TmdbExceptionEvent`\n  - Allows to still set a successful response if the error can be corrected, by calling `$event-\u003eisPropagated()` in your listener, \n    this does require you to provide a PSR-7 response object and set it with `$event-\u003esetResponse($response)`.\n\n### Hydration\n\n- `Tmdb\\Event\\BeforeHydrationEvent`, _allows modification of the response data before being hydrated._\n    - This event will still be thrown regardless if the `event_listener_handles_hydration` option is set to false, this\n      allows for example the logger to still produce records.\n- `Tmdb\\Event\\AfterHydrationEvent`, _allows modification of the eventual subject returned._\n  \nThe current implementation within the event dispatcher causes significant overhead, you might actually not want at all.\n\n_In the future we will look into this further for improvement, for now we have bigger fish to catch._\n\nFrom `4.0` moving forward by default the hydration events have been disabled.\n\nTo re-enable this functionality, we recommend only using it for models you need to modify data for;\n\n```php\nuse Tmdb\\Client;\n\n$client = new Client([\n    'hydration' =\u003e [\n        'event_listener_handles_hydration' =\u003e true,\n        'only_for_specified_models' =\u003e [\n            Tmdb\\Model\\Movie::class\n        ]\n    ]\n]);\n```\n\nIf that configuration has been applied, also make sure the event dispatcher you use is aware of our `HydrationListener`;\n\n```php\nuse Symfony\\Component\\EventDispatcher\\EventDispatcher;\nuse Tmdb\\Event\\HydrationEvent;\nuse Tmdb\\Event\\Listener\\HydrationListener;\n\n$eventDispatcher = new EventDispatcher();\n$hydrationListener = new HydrationListener($eventDispatcher);\n$eventDispatcher-\u003eaddListener(HydrationEvent::class, $hydrationListener);\n```\n\n_If you re-enable this functionality without specifying any models, all hydration will be done through the event listeners._\n\n### Requests \u0026 Responses  \n- `Tmdb\\Event\\BeforeRequestEvent`\n  - Allows modification of the PSR-7 request data before being sent.\n  - Allows early response behavior ( think of caching ), by calling `$event-\u003eisPropagated()` in your listener,\n    this does require you to provide a PSR-7 response object and set it with `$event-\u003esetResponse($response)`\n- `Tmdb\\Event\\ResponseEvent`\n  - Contains the `Request` object.\n  - Allows modification of the PSR-7 response before being hydrated, this does require you to provide a PSR-7\n    response object and set it with `$event-\u003esetResponse($response)`\n  - Allows end-user to implement their own cache, or any other actions you'd like to perform on the given response. \n\n## Event listeners \n\nWe have a couple of optional event listeners that you could add to provide additional functionality.\n\n### Caching\n\nInstead of constructing the default `RequestListener`, construct the client with the `Psr6CachedRequestListener`.\n\n```php\nuse Symfony\\Component\\Cache\\Adapter\\FilesystemAdapter;\nuse Tmdb\\Event\\Listener\\Psr6CachedRequestListener;\nuse Tmdb\\Repository\\MovieRepository;\nuse Tmdb\\Client;\n\n$client = new Client();\n\n$cache = new FilesystemAdapter('php-tmdb', 86400, __DIR__ . '/cache');\n$requestListener = new Psr6CachedRequestListener(\n    $client-\u003egetHttpClient(),\n    $client-\u003egetEventDispatcher(),\n    $cache,\n    $client-\u003egetHttpClient()-\u003egetPsr17StreamFactory(),\n    []\n);\n\n$repository = new MovieRepository($client);\n$popular = $repository-\u003egetPopular();\n```\n\n_The current implementation will change again in the future, it will either involve a small change in listener registration, \nor will just happen without you being aware._ We currently base this on `php-http/cache-plugin`, which pulls in extra\ndependencies we don't really use. Since caching is quite a subject itself, for now we have chosen the \"quick 'n dirty way\".\n\n### Logging\n\nThe logging is divided in a couple of listeners, so you can decide what you want to log, or not. All of these \nlisteners have support for writing custom formatted messages. See the relevant interfaces and classes located in the \n`Tmdb\\Formatter` namespace.\n\nInstead of monolog you can pass any PSR-3 compatible logger.\n\n#### Tmdb\\Event\\Listener\\Logger\\LogApiErrorListener\n\n```php\nuse Monolog\\Logger;\nuse Symfony\\Component\\EventDispatcher\\EventDispatcher;\nuse Tmdb\\Event\\Listener\\Logger\\LogApiErrorListener;\nuse Tmdb\\Event\\TmdbExceptionEvent;\nuse Tmdb\\Formatter\\TmdbApiException\\SimpleTmdbApiExceptionFormatter;\n\n$eventDispatcher = new EventDispatcher();\n$apiErrorListener = new LogApiErrorListener(\n    new Logger(),\n    new SimpleTmdbApiExceptionFormatter()\n);\n\n$eventDispatcher-\u003eaddListener(TmdbExceptionEvent::class, $apiErrorListener);\n```\n\nThis will log exceptions thrown when a response has successfully been received, but the response indicated the request was not successful.\n\n```log\n[2021-01-01 13:24:14] php-tmdb.CRITICAL: Critical API exception: 7 Invalid API key: You must be granted a valid key. [] []\n```\n\n#### Tmdb\\Event\\Listener\\Logger\\LogHttpMessageListener\n\n```php\nuse Symfony\\Component\\EventDispatcher\\EventDispatcher;\nuse Tmdb\\Event\\BeforeRequestEvent;\nuse Tmdb\\Event\\HttpClientExceptionEvent;\nuse Tmdb\\Event\\Listener\\Logger\\LogHttpMessageListener;\nuse Tmdb\\Event\\ResponseEvent;\nuse Tmdb\\Formatter\\HttpMessage\\SimpleHttpMessageFormatter;\n\n$eventDispatcher = new EventDispatcher();\n$requestLoggerListener = new LogHttpMessageListener(\n    new Monolog\\Logger(),\n    new SimpleHttpMessageFormatter()\n);\n\n$eventDispatcher-\u003eaddListener(BeforeRequestEvent::class, $requestLoggerListener);\n$eventDispatcher-\u003eaddListener(ResponseEvent::class, $requestLoggerListener);\n$eventDispatcher-\u003eaddListener(HttpClientExceptionEvent::class, $requestLoggerListener);\n```\n\nThis will log outgoing requests and responses.\n\n```log\n[2021-01-01 13:11:18] php-tmdb.INFO: Sending request: GET https://api.themoviedb.org/3/company/1?include_adult=true\u0026language=en-US\u0026region=us 1.1 {\"length\":0,\"has_session_token\":false} []\n[2021-01-01 13:11:18] php-tmdb.INFO: Received response: 200 OK 1.1 {\"status_code\":200,\"length\":223} []\n```\n\nIn case of any other PSR-18 client exceptions ( connection errors for example ), these will also be written to the log.\n\n```log\n[2021-01-01 13:36:39] php-tmdb.INFO: Sending request: GET https://api.themoviedb.org/3/company/1?include_adult=true\u0026language=en-US\u0026region=us 1.1 {\"length\":0,\"has_session_token\":false} []\n[2021-01-01 13:36:39] php-tmdb.CRITICAL: Critical http client error: 0 cURL error 7: Failed to connect to api.themoviedb.org port 443: Connection refused (see https://curl.haxx.se/libcurl/c/libcurl-errors.html) {\"request\":\"https://api.themoviedb.org/3/company/1?include_adult=true\u0026language=en-US\u0026region=us\"} []\n```\n\n#### Tmdb\\Event\\Listener\\Logger\\LogHydrationListener\n\n```php\nuse Symfony\\Component\\EventDispatcher\\EventDispatcher;\nuse Tmdb\\Event\\BeforeHydrationEvent;\nuse Tmdb\\Event\\Listener\\Logger\\LogHydrationListener;\nuse Tmdb\\Formatter\\Hydration\\SimpleHydrationFormatter;\n\n$eventDispatcher = new EventDispatcher();\n$hydrationLoggerListener = new LogHydrationListener(\n    new Monolog\\Logger(),\n    new SimpleHydrationFormatter(),\n    false // set to true if you wish to add the json data passed for each hydration, do not use this in production!\n);\n\n$eventDispatcher-\u003eaddListener(BeforeHydrationEvent::class, $hydrationLoggerListener);\n```\n\nThis will log hydration of models with (optionally) their data, useful for debugging.\n\n```log\n[2021-01-01 13:11:18] php-tmdb.DEBUG: Hydrating model \"Tmdb\\Model\\Image\\LogoImage\". {\"data\":{\"file_path\":\"/o86DbpburjxrqAzEDhXZcyE8pDb.png\"},\"data_size\":49} []\n[2021-01-01 13:11:18] php-tmdb.DEBUG: Hydrating model \"Tmdb\\Model\\Company\". {\"data\":{\"description\":\"\",\"headquarters\":\"San Francisco, California\",\"homepage\":\"https://www.lucasfilm.com/\",\"id\":1,\"logo_path\":\"/o86DbpburjxrqAzEDhXZcyE8pDb.png\",\"name\":\"Lucasfilm Ltd.\",\"origin_country\":\"US\",\"parent_company\":null},\"data_size\":227} []\n```\n\nFor calls with a lot of appended data, this quickly becomes a large dump in the log file, and I would advise to \nonly use this when necessary. \n\n**Do not enable the hydration data dumping on production, it will generate massive logs**.\n\n### Adult filter\n\nTo enable inclusion of results considered \"adult\", add the following listener.\n\n```php\nuse Symfony\\Component\\EventDispatcher\\EventDispatcher;\nuse Tmdb\\Event\\BeforeRequestEvent;\nuse Tmdb\\Event\\Listener\\Request\\AdultFilterRequestListener;\n\n$eventDispatcher = new EventDispatcher();\n$adultFilterListener = new AdultFilterRequestListener(true);\n\n$eventDispatcher-\u003eaddListener(BeforeRequestEvent::class, $adultFilterListener);\n```\n\n### Language filter\n\nTo enable filtering contents on language, add the following listener.\n\n```php\nuse Symfony\\Component\\EventDispatcher\\EventDispatcher;\nuse Tmdb\\Event\\BeforeRequestEvent;\nuse Tmdb\\Event\\Listener\\Request\\LanguageFilterRequestListener;\n\n$eventDispatcher = new EventDispatcher();\n$languageFilterListener = new LanguageFilterRequestListener('nl-NL');\n\n$eventDispatcher-\u003eaddListener(BeforeRequestEvent::class, $languageFilterListener);\n```\n\n### Region filter\n\nTo enable filtering contents on region, add the following listener.\n\n```php\nuse Symfony\\Component\\EventDispatcher\\EventDispatcher;\nuse Tmdb\\Event\\BeforeRequestEvent;\nuse Tmdb\\Event\\Listener\\Request\\RegionFilterRequestListener;\n\n$eventDispatcher = new EventDispatcher();\n$regionFilterListener = new RegionFilterRequestListener('nl');\n\n$eventDispatcher-\u003eaddListener(BeforeRequestEvent::class, $regionFilterListener);\n```\n\n## Guest session\n\nIf you want to make use of guest sessions, you need to specify this explicitly on the client.\n\n```php\nuse Tmdb\\Client;\nuse Tmdb\\Token\\Session\\GuestSessionToken;\n\n$client = new Client();\n$client-\u003esetGuestSessionToken(new GuestSessionToken('my_token'));\n\n// Now you can make calls in the guest sessions namespace. \n```\n\n## Image Helper\n\nAn `ImageHelper` class is present to take care of the images, which does require the configuration to be loaded:\n\n```php\nuse Tmdb\\Client;\nuse Tmdb\\Helper\\ImageHelper;\nuse Tmdb\\Model\\Image;\nuse Tmdb\\Repository\\ConfigurationRepository;\n\n$client = new Client();\n$image = new Image();\n$configRepository = new ConfigurationRepository($client);\n$config = $configRepository-\u003eload();\n\n$imageHelper = new ImageHelper($config);\n\necho $imageHelper-\u003egetHtml($image, 'w154', 154, 80);\n```\n\n\n## Collection Filtering\n\nWe also provide some easy methods to filter any collection, you should note however you can always implement your own filter easily by using Closures:\n\n```php\nuse Tmdb\\Model\\Movie;\nuse Tmdb\\Model\\Image\\PosterImage;\n\n$movie = new Movie();\n\nforeach($movie-\u003egetImages()-\u003efilter(\n        function($key, $value){\n            return $value instanceof PosterImage;\n        }\n    ) as $image) {\n        // do something with all poster images\n}\n```\n\nThese basic filters however are already covered in the `Images` collection object:\n\n```php\nuse Tmdb\\Model\\Movie;\n\n/** @var $movie Movie **/\n$backdrop = $movie\n    -\u003egetImages()\n    -\u003efilterBackdrops()\n;\n```\n_And there are more Collections which provide filters, but you will find those out along the way._\n\n### The GenericCollection and the ResultCollection\n\n- The `GenericCollection` holds any collection of objects (e.g. an collection of movies).\n- The `ResultCollection` is an extension of the `GenericCollection`, and inherits the response parameters _(page, total_pages, total_results)_ from an result set, this can be used to create pagination.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fphp-tmdb%2Fapi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fphp-tmdb%2Fapi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fphp-tmdb%2Fapi/lists"}