Repository: php-fig/fig-standards Branch: master Commit: df5a2b9b37a4 Files: 69 Total size: 577.6 KB Directory structure: gitextract_dtncyr6s/ ├── CODEOWNERS ├── CONTRIBUTING.md ├── LICENSE-CC.md ├── LICENSE-MIT.md ├── LICENSE.md ├── PER.md ├── PSR.md ├── README.md ├── accepted/ │ ├── PSR-0-meta.md │ ├── PSR-0.md │ ├── PSR-1-basic-coding-standard-meta.md │ ├── PSR-1-basic-coding-standard.md │ ├── PSR-11-container-meta.md │ ├── PSR-11-container.md │ ├── PSR-12-extended-coding-style-guide-meta.md │ ├── PSR-12-extended-coding-style-guide.md │ ├── PSR-13-links-meta.md │ ├── PSR-13-links.md │ ├── PSR-14-event-dispatcher-meta.md │ ├── PSR-14-event-dispatcher.md │ ├── PSR-15-request-handlers-meta.md │ ├── PSR-15-request-handlers.md │ ├── PSR-16-simple-cache-meta.md │ ├── PSR-16-simple-cache.md │ ├── PSR-17-http-factory-meta.md │ ├── PSR-17-http-factory.md │ ├── PSR-18-http-client-meta.md │ ├── PSR-18-http-client.md │ ├── PSR-2-coding-style-guide-meta.md │ ├── PSR-2-coding-style-guide.md │ ├── PSR-20-clock-meta.md │ ├── PSR-20-clock.md │ ├── PSR-3-logger-interface-meta.md │ ├── PSR-3-logger-interface.md │ ├── PSR-4-autoloader-examples.md │ ├── PSR-4-autoloader-meta.md │ ├── PSR-4-autoloader.md │ ├── PSR-6-cache-meta.md │ ├── PSR-6-cache.md │ ├── PSR-7-http-message-meta.md │ └── PSR-7-http-message.md ├── bylaws/ │ ├── 001-mission-and-structure.md │ ├── 002-psr-workflow.md │ ├── 003-per-workflow.md │ ├── 004-votes.md │ ├── 005-elections-and-vacancies.md │ ├── 006-licensing-policies.md │ ├── 007-psr-amendments.md │ ├── 008-psr-evolution.md │ ├── 009-naming-conventions.md │ ├── 010-funding.md │ └── 100-implementation.md ├── personnel.md └── proposed/ ├── .placeholder ├── internationalization-meta.md ├── internationalization.md ├── phpdoc-meta.md ├── phpdoc-tags-meta.md ├── phpdoc-tags.md ├── phpdoc.md ├── psr-8-hug/ │ ├── PSR-8-hug-meta.md │ └── psr-8-hug.md ├── security-disclosure-publication-meta.md ├── security-disclosure-publication.md ├── security-disclosure-publication.xsd ├── security-reporting-process-meta.md ├── security-reporting-process.md ├── tracing-meta.md └── tracing.md ================================================ FILE CONTENTS ================================================ ================================================ FILE: CODEOWNERS ================================================ * @php-fig/secretaries /proposed/security* @php-fig/psr-9-10 /proposed/phpdoc* @php-fig/psr-5 /proposed/clock* @php-fig/psr-20 /proposed/internationalization* @php-fig/psr-21 /proposed/tracing* @php-fig/psr-22 ================================================ FILE: CONTRIBUTING.md ================================================ # Contributing to the PHP-FIG Anybody who subscribes to the Google Group, is part of the PHP-FIG. As soon as you subscribe to the [mailing list](http://groups.google.com/group/php-fig/) and/or join the [Discord Server](https://discord.gg/php-fig) you are a PHP-FIG Community Member, who can influence standards, make suggestions, give feedback, etc. Only PHP-FIG Voting Members can start or participate in votes, but the discussion and formation stages involve everyone. See the [PHP-FIG FAQ](https://www.php-fig.org/faqs/) for more information. # Licensing By contributing code you agree to license your contribution under the MIT license. By contributing documentation, examples, or any other non-code assets you agree to license your contribution under the CC BY 3.0 license. Attribution shall be given according to the current bylaws of this group. # Merge & Access Policy All Editors, Coordinators and Sponsors of specifications in draft & review stage have access to push to this (php-fig/fig-standards) repository; subject to secretary discretion All Editors, Coordinators and Sponsors of specifications have push access to utility and interface repositories and retrain this even after acceptance; subject to secretary discretion. The master branch of all repositories are protected and therefore cannot be forced pushed to. Secretaries have, and are the only ones to have, full administrative access to all repositories and to the GitHub organisation. ## Guidelines for merging * Never force push to any branch on a repository in the php-fig organisation * All changes must go through pull requests, a commit should never be pushed directly (excluding initial commits on new interface/util repositories) to master * All pull requests relating to a draft PSR must be approved (with a formal +1 comment) or merged by the PSR Editor, except in the review phase when the coordinator should seek comment from the editor, but merging is at the coordinator's discretion * You must never merge a pull request that affects any file in the repository other than those you are on a working group for; you should request a secretary or member of that working group (mention @php-fig/psr-x) do so * You should never merge your own pull request * A change should never be merged to an accepted PSR without approval from secretaries, who will attempt to seek confirmation from the former Editors * A change to bylaws shouldn't be merged by anyone other than a secretary * Pull requests may be triaged (have labels applied) by anyone with push access, no matter which PSR they are on the working group for or which PSR it affects; but they cannot close a pull request or issue affecting other PSRs * After approval of a specification, all merges to an interface or utility repository must be approved by a secretary; who is required to give suitable notice and seek comment from the working group team, but it is not required that they approve * Tags on utility and interface repositories should be created and PGP signed by Secretaries, who should publish their PGP public keys and register them on GitHub These guidelines are subject to exceptions and changes at secretaries discretion. Should you have any questions please contact the secretaries on info [at] php-fig [dot] org. Failure to comply with guidelines will result in revokation of merge access. Merge access is a privilege and not a right. # Tagging Tagging on utility and interface repository should be done regularly, ideally after every merge, or every batch of merges after PSR approval. Versioning should follow semantic versioning and primarily just be simple patch fix increments (following semantic versioning). The first 1.0.0 tag should be created on PSR approval. All tags should be PGP signed. A changelog should be provided including a list of changes, crediting the contributor and referencing the pull request/issue. ================================================ FILE: LICENSE-CC.md ================================================ Creative Commons Legal Code Attribution 3.0 Unported CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE. License THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS. 1. Definitions a. "Adaptation" means a work based upon the Work, or upon the Work and other pre-existing works, such as a translation, adaptation, derivative work, arrangement of music or other alterations of a literary or artistic work, or phonogram or performance and includes cinematographic adaptations or any other form in which the Work may be recast, transformed, or adapted including in any form recognizably derived from the original, except that a work that constitutes a Collection will not be considered an Adaptation for the purpose of this License. For the avoidance of doubt, where the Work is a musical work, performance or phonogram, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered an Adaptation for the purpose of this License. b. "Collection" means a collection of literary or artistic works, such as encyclopedias and anthologies, or performances, phonograms or broadcasts, or other works or subject matter other than works listed in Section 1(f) below, which, by reason of the selection and arrangement of their contents, constitute intellectual creations, in which the Work is included in its entirety in unmodified form along with one or more other contributions, each constituting separate and independent works in themselves, which together are assembled into a collective whole. A work that constitutes a Collection will not be considered an Adaptation (as defined above) for the purposes of this License. c. "Distribute" means to make available to the public the original and copies of the Work or Adaptation, as appropriate, through sale or other transfer of ownership. d. "Licensor" means the individual, individuals, entity or entities that offer(s) the Work under the terms of this License. e. "Original Author" means, in the case of a literary or artistic work, the individual, individuals, entity or entities who created the Work or if no individual or entity can be identified, the publisher; and in addition (i) in the case of a performance the actors, singers, musicians, dancers, and other persons who act, sing, deliver, declaim, play in, interpret or otherwise perform literary or artistic works or expressions of folklore; (ii) in the case of a phonogram the producer being the person or legal entity who first fixes the sounds of a performance or other sounds; and, (iii) in the case of broadcasts, the organization that transmits the broadcast. f. "Work" means the literary and/or artistic work offered under the terms of this License including without limitation any production in the literary, scientific and artistic domain, whatever may be the mode or form of its expression including digital form, such as a book, pamphlet and other writing; a lecture, address, sermon or other work of the same nature; a dramatic or dramatico-musical work; a choreographic work or entertainment in dumb show; a musical composition with or without words; a cinematographic work to which are assimilated works expressed by a process analogous to cinematography; a work of drawing, painting, architecture, sculpture, engraving or lithography; a photographic work to which are assimilated works expressed by a process analogous to photography; a work of applied art; an illustration, map, plan, sketch or three-dimensional work relative to geography, topography, architecture or science; a performance; a broadcast; a phonogram; a compilation of data to the extent it is protected as a copyrightable work; or a work performed by a variety or circus performer to the extent it is not otherwise considered a literary or artistic work. g. "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation. h. "Publicly Perform" means to perform public recitations of the Work and to communicate to the public those public recitations, by any means or process, including by wire or wireless means or public digital performances; to make available to the public Works in such a way that members of the public may access these Works from a place and at a place individually chosen by them; to perform the Work to the public by any means or process and the communication to the public of the performances of the Work, including by public digital performance; to broadcast and rebroadcast the Work by any means including signs, sounds or images. i. "Reproduce" means to make copies of the Work by any means including without limitation by sound or visual recordings and the right of fixation and reproducing fixations of the Work, including storage of a protected performance or phonogram in digital form or other electronic medium. 2. Fair Dealing Rights. Nothing in this License is intended to reduce, limit, or restrict any uses free from copyright or rights arising from limitations or exceptions that are provided for in connection with the copyright protection under copyright law or other applicable laws. 3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below: a. to Reproduce the Work, to incorporate the Work into one or more Collections, and to Reproduce the Work as incorporated in the Collections; b. to create and Reproduce Adaptations provided that any such Adaptation, including any translation in any medium, takes reasonable steps to clearly label, demarcate or otherwise identify that changes were made to the original Work. For example, a translation could be marked "The original work was translated from English to Spanish," or a modification could indicate "The original work has been modified."; c. to Distribute and Publicly Perform the Work including as incorporated in Collections; and, d. to Distribute and Publicly Perform Adaptations. e. For the avoidance of doubt: i. Non-waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme cannot be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; ii. Waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme can be waived, the Licensor waives the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; and, iii. Voluntary License Schemes. The Licensor waives the right to collect royalties, whether individually or, in the event that the Licensor is a member of a collecting society that administers voluntary licensing schemes, via that society, from any exercise by You of the rights granted under this License. The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. Subject to Section 8(f), all rights not expressly granted by Licensor are hereby reserved. 4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions: a. You may Distribute or Publicly Perform the Work only under the terms of this License. You must include a copy of, or the Uniform Resource Identifier (URI) for, this License with every copy of the Work You Distribute or Publicly Perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of the recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties with every copy of the Work You Distribute or Publicly Perform. When You Distribute or Publicly Perform the Work, You may not impose any effective technological measures on the Work that restrict the ability of a recipient of the Work from You to exercise the rights granted to that recipient under the terms of the License. This Section 4(a) applies to the Work as incorporated in a Collection, but this does not require the Collection apart from the Work itself to be made subject to the terms of this License. If You create a Collection, upon notice from any Licensor You must, to the extent practicable, remove from the Collection any credit as required by Section 4(b), as requested. If You create an Adaptation, upon notice from any Licensor You must, to the extent practicable, remove from the Adaptation any credit as required by Section 4(b), as requested. b. If You Distribute, or Publicly Perform the Work or any Adaptations or Collections, You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or if the Original Author and/or Licensor designate another party or parties (e.g., a sponsor institute, publishing entity, journal) for attribution ("Attribution Parties") in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; (ii) the title of the Work if supplied; (iii) to the extent reasonably practicable, the URI, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and (iv) , consistent with Section 3(b), in the case of an Adaptation, a credit identifying the use of the Work in the Adaptation (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). The credit required by this Section 4 (b) may be implemented in any reasonable manner; provided, however, that in the case of a Adaptation or Collection, at a minimum such credit will appear, if a credit for all contributing authors of the Adaptation or Collection appears, then as part of these credits and in a manner at least as prominent as the credits for the other contributing authors. For the avoidance of doubt, You may only use the credit required by this Section for the purpose of attribution in the manner set out above and, by exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection with, sponsorship or endorsement by the Original Author, Licensor and/or Attribution Parties, as appropriate, of You or Your use of the Work, without the separate, express prior written permission of the Original Author, Licensor and/or Attribution Parties. c. Except as otherwise agreed in writing by the Licensor or as may be otherwise permitted by applicable law, if You Reproduce, Distribute or Publicly Perform the Work either by itself or as part of any Adaptations or Collections, You must not distort, mutilate, modify or take other derogatory action in relation to the Work which would be prejudicial to the Original Author's honor or reputation. Licensor agrees that in those jurisdictions (e.g. Japan), in which any exercise of the right granted in Section 3(b) of this License (the right to make Adaptations) would be deemed to be a distortion, mutilation, modification or other derogatory action prejudicial to the Original Author's honor and reputation, the Licensor will waive or not assert, as appropriate, this Section, to the fullest extent permitted by the applicable national law, to enable You to reasonably exercise Your right under Section 3(b) of this License (right to make Adaptations) but not otherwise. 5. Representations, Warranties and Disclaimer UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU. 6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 7. Termination a. This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Adaptations or Collections from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License. b. Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above. 8. Miscellaneous a. Each time You Distribute or Publicly Perform the Work or a Collection, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License. b. Each time You Distribute or Publicly Perform an Adaptation, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License. c. If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. d. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent. e. This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You. f. The rights granted under, and the subject matter referenced, in this License were drafted utilizing the terminology of the Berne Convention for the Protection of Literary and Artistic Works (as amended on September 28, 1979), the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and the Universal Copyright Convention (as revised on July 24, 1971). These rights and subject matter take effect in the relevant jurisdiction in which the License terms are sought to be enforced according to the corresponding provisions of the implementation of those treaty provisions in the applicable national law. If the standard suite of rights granted under applicable copyright law includes additional rights not granted under this License, such additional rights are deemed to be included in the License; this License is not intended to restrict the license of any rights under applicable law. Creative Commons Notice Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of Licensor. Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, Creative Commons does not authorize the use by either party of the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time. For the avoidance of doubt, this trademark restriction does not form part of this License. Creative Commons may be contacted at http://creativecommons.org/. ================================================ FILE: LICENSE-MIT.md ================================================ Copyright (c) 2013-2017 PHP Framework Interop Group Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: LICENSE.md ================================================ # License Unless stated otherwise, all content is licensed under the Creative Commons Attribution License and code licensed under the MIT License. Copies of all licenses are included in this project's root directory. ================================================ FILE: PER.md ================================================ # PHP Evolving Recommendations A PHP Evolving Recommendation is a meta document accompanied by one or more artifacts that are set to evolve over time with multiple releases. Following the [PER Workflow Bylaw][workflow], when a PER has the first release, it will be listed here. As also described in the [Mission and Structure Bylaw][structure], the Editor of a working group working on a PER is essentially the lead contributor and writer, and they are supported by at least two voting members; the Sponsor, if present, is a Core Committee member that assists the Editor in keeping the evolution process on track. | Title | Editor | Sponsor | Last release(s) | |----------------------------------|----------------|------------------|----------------------------------------------| | [Coding Style][per-coding-style] | Larry Garfield | Chris Tankersley | [PER Coding Style][per-coding-style-release] | [workflow]: https://github.com/php-fig/fig-standards/blob/master/bylaws/003-per-workflow.md [structure]: https://github.com/php-fig/fig-standards/blob/master/bylaws/001-mission-and-structure.md [per-coding-style]: https://github.com/php-fig/per-coding-style/blob/1.0.0/spec.md [per-coding-style-release]: https://github.com/php-fig/per-coding-style/releases/latest ================================================ FILE: PSR.md ================================================ # PHP Standard Recommendations According to the [PSR Workflow Bylaw][workflow] each PSR has a status as it is being worked on. Once a proposal has passed the Entrance Vote it will be listed here as "Draft". Unless a PSR is marked as "Accepted" it is subject to change. Draft can change drastically, but Review will only have minor changes. As also described in the [PSR Workflow Bylaw][workflow]. The Editor, or editors, of a proposal are essentially the lead contributors and writers of the PSRs and they are supported by two voting members. Those voting members are the Coordinator who is responsible for managing the review stage and votes; and a second sponsor. ## Index by Status ### Accepted | Num | Title | Maintainer | |:---:|--------------------------------------|--------------------------------| | 1 | [Basic Coding Standard][psr1] | _vacant_ | | 3 | [Logger Interface][psr3] | Jordi Boggiano | | 4 | [Autoloading Standard][psr4] | _vacant_ | | 6 | [Caching Interface][psr6] | Larry Garfield | | 7 | [HTTP Message Interface][psr7] | Matthew Weier O'Phinney | | 11 | [Container Interface][psr11] | Matthieu Napoli, David Négrier | | 12 | [Extended Coding Style Guide][psr12] | Korvin Szanto | | 13 | [Hypermedia Links][psr13] | Larry Garfield | | 14 | [Event Dispatcher][psr14] | Larry Garfield | | 15 | [HTTP Handlers][psr15] | Woody Gilk | | 16 | [Simple Cache][psr16] | Paul Dragoonis | | 17 | [HTTP Factories][psr17] | Woody Gilk | | 18 | [HTTP Client][psr18] | Tobias Nyholm | | 20 | [Clock][psr20] | Chris Seufert | ### Draft | Num | Title | Editor(s) | Sponsor | |:---:|--------------------------------------|--------------------------------|--------------------------------| | 5 | [PHPDoc Standard][psr5] | Chuck Burgess | Michael Cullum | | 19 | [PHPDoc tags][psr19] | Chuck Burgess | Michael Cullum | | 21 | [Internationalization][psr21] | Navarr Barnier | Larry Garfield | | 22 | [Application Tracing][psr22] | Adam Allport | Alessandro Chitolina | ### Abandoned | Num | Title | Editor(s) | |:---:|--------------------------------------|--------------------------------| | 8 | [Huggable Interface][psr8] | Larry Garfield | | 9 | [Security Advisories][psr9] | Michael Hess | | 10 | [Security Reporting Process][psr10] | Michael Hess | ### Deprecated | Num | Title | | |:---:|--------------------------------------|-----| | 0 | [Autoloading Standard][psr0] | | | 2 | [Coding Style Guide][psr2] | | ## Numerical Index | Num | Title | Editor(s) / Maintainers | Status | |:---:|--------------------------------------|--------------------------------|------------| | 0 | [Autoloading Standard][psr0] | | Deprecated | | 1 | [Basic Coding Standard][psr1] | _vacant_ | Accepted | | 2 | [Coding Style Guide][psr2] | | Deprecated | | 3 | [Logger Interface][psr3] | Jordi Boggiano | Accepted | | 4 | [Autoloading Standard][psr4] | _vacant_ | Accepted | | 5 | [PHPDoc Standard][psr5] | Chuck Burgess | Draft | | 6 | [Caching Interface][psr6] | Larry Garfield | Accepted | | 7 | [HTTP Message Interface][psr7] | Matthew Weier O'Phinney | Accepted | | 8 | [Huggable Interface][psr8] | Larry Garfield | Abandoned | | 9 | [Security Advisories][psr9] | Michael Hess | Abandoned | | 10 | [Security Reporting Process][psr10] | Michael Hess | Abandoned | | 11 | [Container Interface][psr11] | Matthieu Napoli, David Négrier | Accepted | | 12 | [Extended Coding Style Guide][psr12] | Korvin Szanto | Accepted | | 13 | [Hypermedia Links][psr13] | Larry Garfield | Accepted | | 14 | [Event Dispatcher][psr14] | Larry Garfield | Accepted | | 15 | [HTTP Handlers][psr15] | Woody Gilk | Accepted | | 16 | [Simple Cache][psr16] | Paul Dragoonis | Accepted | | 17 | [HTTP Factories][psr17] | Woody Gilk | Accepted | | 18 | [HTTP Client][psr18] | Tobias Nyholm | Accepted | | 19 | [PHPDoc tags][psr19] | Chuck Burgess | Draft | | 20 | [Clock][psr20] | Chris Seufert | Accepted | | 21 | [Internationalization][psr21] | Navarr Barnier | Draft | | 22 | [Application Tracing][psr22] | Adam Allport | Draft | [workflow]: https://github.com/php-fig/fig-standards/blob/master/bylaws/002-psr-workflow.md [psr0]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md [psr1]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-1-basic-coding-standard.md [psr2]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md [psr3]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md [psr4]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader-meta.md [psr5]: https://github.com/php-fig/fig-standards/blob/master/proposed/phpdoc.md [psr6]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-6-cache.md [psr7]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-7-http-message.md [psr8]: https://github.com/php-fig/fig-standards/blob/master/proposed/psr-8-hug/ [psr9]: https://github.com/php-fig/fig-standards/blob/master/proposed/security-disclosure-publication.md [psr10]: https://github.com/php-fig/fig-standards/blob/master/proposed/security-reporting-process.md [psr11]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-11-container.md [psr12]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-12-extended-coding-style-guide.md [psr13]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-13-links.md [psr14]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-14-event-dispatcher.md [psr15]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-15-request-handlers.md [psr16]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-16-simple-cache.md [psr17]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-17-http-factory.md [psr18]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-18-http-client.md [psr19]: https://github.com/php-fig/fig-standards/blob/master/proposed/phpdoc-tags.md [psr20]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-20-clock.md [psr21]: https://github.com/php-fig/fig-standards/blob/master/proposed/internationalization.md [psr22]: https://github.com/php-fig/fig-standards/blob/master/proposed/tracing.md ================================================ FILE: README.md ================================================ PHP Framework Interoperability Group ==================================== The idea behind the group is for project representatives to talk about the commonalities between our projects and find ways we can work together. Our main audience is each other, but we’re very aware that the rest of the PHP community is watching. If other folks want to adopt what we’re doing they are welcome to do so, but that is not the aim. Proposing a Standard Recommendation ------------------------------------ To propose a PHP Standard Recommendation (PSR): - fork this repo, create a branch, checkout that branch, add the PSR in `proposed/`, push the branch to GitHub, and send a pull request; or, - create a ticket to start a discussion on GitHub; or, - start a conversation on the [mailing list][]. [mailing list]: http://groups.google.com/group/php-fig/ GitHub usage ------------ All discussion regarding a PSR happens on the [mailing list][]. Issues filed in GitHub are rarely monitored, and PRs are likely to be missed unless a message is sent to the mailing list regarding them. Reviews of a proposed PSR should be conducted on the mailing list, not through PR comments for the same reason. Please do not simply file an issue or PR and walk-away. The most likely outcome is that it will never get seen or addressed. Requesting Membership --------------------- You **do not** need to be a voting member to participate in discussion on the [mailing list][]. To become a voting member, you must send an email to the [mailing list][]. - The subject line should read: `Membership Request: {$your_name} ({$project_name})` - The body should include your name, the name of (and link to) the project you represent, and other details you feel are relevant. - Current members will vote on your request. Do not combine separate membership requests in a single thread; one request per thread, please. Language & Translations ----------------------- All PSRs are written in British English or American English (Different specifications may vary, but it is consistent within the same specification). The PHP FIG does not offer official translations into other languages but other external entities are free to translate the specifications in accordance with the license. Voting Members -------------- The current list of voting members is available on the [project website][]. [project website]: https://www.php-fig.org/ ================================================ FILE: accepted/PSR-0-meta.md ================================================ # PSR-0 Meta Document ## 1. Summary PSR-0 predates the official FIG structure and describes a standard for autoloader interoperability. It is superseded by PSR-4. This meta document was added after the PSR has been deprecated. ## 2. People ### 2.1 Editor * Matthew Weier O'Phinney ================================================ FILE: accepted/PSR-0.md ================================================ Autoloading Standard ==================== > **Deprecated** - As of 2014-10-21 PSR-0 has been marked as deprecated. [PSR-4] is now recommended as an alternative. [PSR-4]: https://www.php-fig.org/psr/psr-4/ The following describes the mandatory requirements that must be adhered to for autoloader interoperability. Mandatory --------- * A fully-qualified namespace and class must have the following structure `\\(\)*` * Each namespace must have a top-level namespace ("Vendor Name"). * Each namespace can have as many sub-namespaces as it wishes. * Each namespace separator is converted to a `DIRECTORY_SEPARATOR` when loading from the file system. * Each `_` character in the CLASS NAME is converted to a `DIRECTORY_SEPARATOR`. The `_` character has no special meaning in the namespace. * The fully-qualified namespace and class are suffixed with `.php` when loading from the file system. * Alphabetic characters in vendor names, namespaces, and class names may be of any combination of lower case and upper case. Examples -------- * `\Doctrine\Common\IsolatedClassLoader` => `/path/to/project/lib/vendor/Doctrine/Common/IsolatedClassLoader.php` * `\Symfony\Core\Request` => `/path/to/project/lib/vendor/Symfony/Core/Request.php` * `\Zend\Acl` => `/path/to/project/lib/vendor/Zend/Acl.php` * `\Zend\Mail\Message` => `/path/to/project/lib/vendor/Zend/Mail/Message.php` Underscores in Namespaces and Class Names ----------------------------------------- * `\namespace\package\Class_Name` => `/path/to/project/lib/vendor/namespace/package/Class/Name.php` * `\namespace\package_name\Class_Name` => `/path/to/project/lib/vendor/namespace/package_name/Class/Name.php` The standards we set here should be the lowest common denominator for painless autoloader interoperability. You can test that you are following these standards by utilizing this sample SplClassLoader implementation which is able to load PHP 5.3 classes. Example Implementation ---------------------- Below is an example function to simply demonstrate how the above proposed standards are autoloaded. ```php ` tags or the short-echo `` tags; it MUST NOT use the other tag variations. ### 2.2. Character Encoding PHP code MUST use only UTF-8 without BOM. ### 2.3. Side Effects A file SHOULD declare new symbols (classes, functions, constants, etc.) and cause no other side effects, or it SHOULD execute logic with side effects, but SHOULD NOT do both. The phrase "side effects" means execution of logic not directly related to declaring classes, functions, constants, etc., *merely from including the file*. "Side effects" include but are not limited to: generating output, explicit use of `require` or `include`, connecting to external services, modifying ini settings, emitting errors or exceptions, modifying global or static variables, reading from or writing to a file, and so on. The following is an example of a file with both declarations and side effects; i.e, an example of what to avoid: ```php \n"; // declaration function foo() { // function body } ``` The following example is of a file that contains declarations without side effects; i.e., an example of what to emulate: ```php "users SHOULD NOT pass a container into an object, so the object > can retrieve *its own dependencies*. Users doing so are using the container as a Service Locator. > Service Locator usage is generally discouraged." ```php // This is not OK, you are using the container as a service locator class BadExample { public function __construct(ContainerInterface $container) { $this->db = $container->get('db'); } } // Instead, please consider injecting directly the dependencies class GoodExample { public function __construct($db) { $this->db = $db; } } // You can then use the container to inject the $db object into your $goodExample object. ``` In the `BadExample` you should not inject the container because: - it makes the code **less interoperable**: by injecting the container, you have to use a container compatible with the Container PSR. With the other option, your code can work with ANY container. - you are forcing the developer into naming its entry "db". This naming could conflict with another package that has the same expectations for another service. - it is harder to test. - it is not directly clear from your code that the `BadExample` class will need the "db" service. Dependencies are hidden. Very often, the `ContainerInterface` will be used by other packages. As a end-user PHP developer using a framework, it is unlikely you will ever need to use containers or type-hint on the `ContainerInterface` directly. Whether using the Container PSR into your code is considered a good practice or not boils down to knowing if the objects you are retrieving are **dependencies** of the object referencing the container or not. Here are a few more examples: ```php class RouterExample { // ... public function __construct(ContainerInterface $container) { $this->container = $container; } public function getRoute($request) { $controllerName = $this->getContainerEntry($request->getUrl()); // This is OK, the router is finding the matching controller entry, the controller is // not a dependency of the router $controller = $this->container->get($controllerName); // ... } } ``` In this example, the router is transforming the URL into a controller entry name, then fetches the controller from the container. A controller is not really a dependency of the router. As a rule of thumb, if your object is *computing* the entry name among a list of entries that can vary, your use case is certainly legitimate. As an exception, factory objects whose only purpose is to create and return new instances may use the service locator pattern. The factory must then implement an interface so that it can itself be replaced by another factory using the same interface. ```php // ok: a factory interface + implementation to create an object interface FactoryInterface { public function newInstance(); } class ExampleFactory implements FactoryInterface { protected $container; public function __construct(ContainerInterface $container) { $this->container = $container; } public function newInstance() { return new Example($this->container->get('db')); } } ``` ## 5. History Before submitting the Container PSR to the PHP-FIG, the `ContainerInterface` was first proposed in a project named [container-interop](https://github.com/container-interop/container-interop/). The goal of the project was to provide a test-bed for implementing the `ContainerInterface`, and to pave the way for the Container PSR. In the rest of this meta document, you will see frequent references to `container-interop.` ## 6. Interface name The interface name is the same as the one discussed for `container-interop` (only the namespace is changed to match the other PSRs). It has been thoroughly discussed on `container-interop` [[4]](#link_naming_discussion) and was decided by a vote [[5]](#link_naming_vote). The list of options considered with their respective votes are: - `ContainerInterface`: +8 - `ProviderInterface`: +2 - `LocatorInterface`: 0 - `ReadableContainerInterface`: -5 - `ServiceLocatorInterface`: -6 - `ObjectFactory`: -6 - `ObjectStore`: -8 - `ConsumerInterface`: -9 ## 7. Interface methods The choice of which methods the interface would contain was made after a statistical analysis of existing containers. [[6]](#link_statistical_analysis). The summary of the analysis showed that: - all containers offer a method to get an entry by its id - a large majority name such method `get()` - for all containers, the `get()` method has 1 mandatory parameter of type string - some containers have an optional additional argument for `get()`, but it doesn't have the same purpose between containers - a large majority of the containers offer a method to test if it can return an entry by its id - a majority name such method `has()` - for all containers offering `has()`, the method has exactly 1 parameter of type string - a large majority of the containers throw an exception rather than returning null when an entry is not found in `get()` - a large majority of the containers don't implement `ArrayAccess` The question of whether to include methods to define entries has been discussed at the very start of the container-interop project [[4]](#link_naming_discussion). It has been judged that such methods do not belong in the interface described here because it is out of its scope (see the "Goal" section). As a result, the `ContainerInterface` contains two methods: - `get()`, returning anything, with one mandatory string parameter. Should throw an exception if the entry is not found. - `has()`, returning a boolean, with one mandatory string parameter. ### 7.1. Number of parameters in `get()` method While `ContainerInterface` only defines one mandatory parameter in `get()`, it is not incompatible with existing containers that have additional optional parameters. PHP allows an implementation to offer more parameters as long as they are optional, because the implementation *does* satisfy the interface. Difference with container-interop: [The container-interop spec](https://github.com/container-interop/container-interop/blob/master/docs/ContainerInterface.md) stated that: > While `ContainerInterface` only defines one mandatory parameter in `get()`, implementations MAY accept additional optional parameters. This sentence was removed from PSR-11 because: - It is something that stems from OO principles in PHP, so this is not directly related to PSR-11 - We do not want to encourage implementors to add additional parameters as we recommend coding against the interface and not the implementation However, some implementations have extra optional parameters; that's technically legal. Such implementations are compatible with PSR-11. [[11]](#link_get_optional_parameters) ### 7.2. Type of the `$id` parameter The type of the `$id` parameter in `get()` and `has()` has been discussed in the container-interop project. While `string` is used in all the containers that were analyzed, it was suggested that allowing anything (such as objects) could allow containers to offer a more advanced query API. An example given was to use the container as an object builder. The `$id` parameter would then be an object that would describe how to create an instance. The conclusion of the discussion [[7]](#link_method_and_parameters_details) was that this was beyond the scope of getting entries from a container without knowing how the container provided them, and it was more fit for a factory. ### 7.3. Exceptions thrown This PSR provides 2 interfaces meant to be implemented by container exceptions. #### 7.3.1 Base exception The `Psr\Container\ContainerExceptionInterface` is the base interface. It SHOULD be implemented by custom exceptions thrown directly by the container. It is expected that any exception that is part of the domain of the container implements the `ContainerExceptionInterface`. A few examples: - if a container relies on a configuration file and if that configuration file is flawed, the container might throw an `InvalidFileException` implementing the `ContainerExceptionInterface`. - if a cyclic dependency is detected between dependencies, the container might throw an `CyclicDependencyException` implementing the `ContainerExceptionInterface`. However, if the exception is thrown by some code out of the container's scope (for instance an exception thrown while instantiating an entry), the container is not required to wrap this exception in a custom exception implementing the `ContainerExceptionInterface`. The usefulness of the base exception interface was questioned: it is not an exception one would typically catch [[8]](#link_base_exception_usefulness). However, most PHP-FIG members considered it to be a best practice. Base exception interface are implemented in previous PSRs and several member projects. The base exception interface was therefore kept. #### 7.3.2 Not found exception A call to the `get` method with a non-existing id must throw an exception implementing the `Psr\Container\NotFoundExceptionInterface`. For a given identifier: - if the `has` method returns `false`, then the `get` method MUST throw a `Psr\Container\NotFoundExceptionInterface`. - if the `has` method returns `true`, this does not mean that the `get` method will succeed and throw no exception. It can even throw a `Psr\Container\NotFoundExceptionInterface` if one of the dependencies of the requested entry is missing. Therefore, when a user catches the `Psr\Container\NotFoundExceptionInterface`, it has 2 possible meanings [[9]](#link_not_found_behaviour): - the requested entry does not exist (bad request) - or a dependency of the requested entry does not exist (i.e. the container is misconfigured) The user can however easily make a distinction with a call to `has`. In pseudo-code: ```php if (!$container->has($id)) { // The requested instance does not exist return; } try { $entry = $container->get($id); } catch (NotFoundExceptionInterface $e) { // Since the requested entry DOES exist, a NotFoundExceptionInterface means that the container is misconfigured and a dependency is missing. } ``` ## 8. Implementations At the time of writing, the following projects already implement and/or consume the `container-interop` version of the interface. ### Implementors - [Acclimate](https://github.com/jeremeamia/acclimate-container) - [Aura.DI](https://github.com/auraphp/Aura.Di) - [dcp-di](https://github.com/estelsmith/dcp-di) - [League Container](https://github.com/thephpleague/container) - [Mouf](http://mouf-php.com) - [Njasm Container](https://github.com/njasm/container) - [PHP-DI](http://php-di.org) - [PimpleInterop](https://github.com/moufmouf/pimple-interop) - [XStatic](https://github.com/jeremeamia/xstatic) - [Zend ServiceManager](https://github.com/zendframework/zend-servicemanager) ### Middleware - [Alias-Container](https://github.com/thecodingmachine/alias-container) - [Prefixer-Container](https://github.com/thecodingmachine/prefixer-container) ### Consumers - [Behat](https://github.com/Behat/Behat) - [interop.silex.di](https://github.com/thecodingmachine/interop.silex.di) - [mindplay/middleman](https://github.com/mindplay-dk/middleman) - [PHP-DI Invoker](https://github.com/PHP-DI/Invoker) - [Prophiler](https://github.com/fabfuel/prophiler) - [Silly](https://github.com/mnapoli/silly) - [Slim](https://github.com/slimphp/Slim) - [Splash](http://mouf-php.com/packages/mouf/mvc.splash-common/version/8.0-dev/README.md) - [Zend Expressive](https://github.com/zendframework/zend-expressive) This list is not comprehensive and should be only taken as an example showing that there is considerable interest in the PSR. ## 9. People ### 9.1 Editors * [Matthieu Napoli](https://github.com/mnapoli) * [David Négrier](https://github.com/moufmouf) ### 9.2 Sponsors * [Matthew Weier O'Phinney](https://github.com/weierophinney) (Coordinator) * [Korvin Szanto](https://github.com/KorvinSzanto) ### 9.3 Contributors Are listed here all people that contributed in the discussions or votes (on container-interop and during migration to PSR-11), by alphabetical order: * [Alexandru Pătrănescu](https://github.com/drealecs) * [Amy Stephen](https://github.com/AmyStephen) * [Ben Peachey](https://github.com/potherca) * [David Négrier](https://github.com/moufmouf) * [Don Gilbert](https://github.com/dongilbert) * [Jason Judge](https://github.com/judgej) * [Jeremy Lindblom](https://github.com/jeremeamia) * [Larry Garfield](https://github.com/crell) * [Marco Pivetta](https://github.com/Ocramius) * [Matthieu Napoli](https://github.com/mnapoli) * [Nelson J Morais](https://github.com/njasm) * [Paul M. Jones](https://github.com/pmjones) * [Phil Sturgeon](https://github.com/philsturgeon) * [Stephan Hochdörfer](https://github.com/shochdoerfer) * [Taylor Otwell](https://github.com/taylorotwell) ## 10. Relevant links 1. [Discussion about the container PSR and the service locator](https://groups.google.com/forum/#!topic/php-fig/pyTXRvLGpsw) 1. [Container-interop's `ContainerInterface.php`](https://github.com/container-interop/container-interop/blob/master/src/Interop/Container/ContainerInterface.php) 1. [List of all issues](https://github.com/container-interop/container-interop/issues?labels=ContainerInterface&milestone=&page=1&state=closed) 1. [Discussion about the interface name and container-interop scope](https://github.com/container-interop/container-interop/issues/1) 1. [Vote for the interface name](https://github.com/container-interop/container-interop/wiki/%231-interface-name:-Vote) 1. [Statistical analysis of existing containers method names](https://gist.github.com/mnapoli/6159681) 1. [Discussion about the method names and parameters](https://github.com/container-interop/container-interop/issues/6) 1. [Discussion about the usefulness of the base exception](https://groups.google.com/forum/#!topic/php-fig/_vdn5nLuPBI) 1. [Discussion about the `NotFoundExceptionInterface`](https://groups.google.com/forum/#!topic/php-fig/I1a2Xzv9wN8) 1. Discussion about get optional parameters [in container-interop](https://github.com/container-interop/container-interop/issues/6) and on the [PHP-FIG mailing list](https://groups.google.com/forum/#!topic/php-fig/zY6FAG4-oz8) ## 11. Errata ## Type additions The 1.1 release of the `psr/container` package includes scalar parameter types. The 2.0 release of the package includes return types. This structure leverages PHP 7.2 covariance support to allow for a gradual upgrade process. Implementers MAY add return types to their own packages at their discretion, provided that: - the return types match those in the 2.0 package. - the implementation specifies a minimum PHP version of 7.2.0 or later. Implementers MAY add parameter types to their own packages in a new major release, either at the same time as adding return types or in a subsequent release, provided that: - the parameter types match those in the 1.1 package. - the implementation specifies a minimum PHP version of 7.2.0. - the implementation depends on `"psr/container": "^1.1 || ^2.0"` so as to exclude the untyped 1.0 version. Implementers are encouraged but not required to transition their packages toward the 2.0 version of the package at their earliest convenience. ================================================ FILE: accepted/PSR-11-container.md ================================================ # Container interface This document describes a common interface for dependency injection containers. The goal set by `ContainerInterface` is to standardize how frameworks and libraries make use of a container to obtain objects and parameters (called *entries* in the rest of this document). The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119][]. The word `implementor` in this document is to be interpreted as someone implementing the `ContainerInterface` in a dependency injection-related library or framework. Users of dependency injection containers (DIC) are referred to as `user`. [RFC 2119]: http://tools.ietf.org/html/rfc2119 ## 1. Specification ### 1.1 Basics #### 1.1.1 Entry identifiers An entry identifier is any PHP-legal string of at least one character that uniquely identifies an item within a container. An entry identifier is an opaque string, so callers SHOULD NOT assume that the structure of the string carries any semantic meaning. #### 1.1.2 Reading from a container - The `Psr\Container\ContainerInterface` exposes two methods: `get` and `has`. - `get` takes one mandatory parameter: an entry identifier, which MUST be a string. `get` can return anything (a *mixed* value), or throw a `NotFoundExceptionInterface` if the identifier is not known to the container. Two successive calls to `get` with the same identifier SHOULD return the same value. However, depending on the `implementor` design and/or `user` configuration, different values might be returned, so `user` SHOULD NOT rely on getting the same value on 2 successive calls. - `has` takes one unique parameter: an entry identifier, which MUST be a string. `has` MUST return `true` if an entry identifier is known to the container and `false` if it is not. If `has($id)` returns false, `get($id)` MUST throw a `NotFoundExceptionInterface`. ### 1.2 Exceptions Exceptions directly thrown by the container SHOULD implement the [`Psr\Container\ContainerExceptionInterface`](#container-exception). A call to the `get` method with a non-existing id MUST throw a [`Psr\Container\NotFoundExceptionInterface`](#not-found-exception). ### 1.3 Recommended usage Users SHOULD NOT pass a container into an object so that the object can retrieve *its own dependencies*. This means the container is used as a [Service Locator](https://en.wikipedia.org/wiki/Service_locator_pattern) which is a pattern that is generally discouraged. Please refer to section 4 of the META document for more details. ## 2. Package The interfaces and classes described as well as relevant exceptions are provided as part of the [psr/container](https://packagist.org/packages/psr/container) package. Packages providing a PSR container implementation should declare that they provide `psr/container-implementation` `1.0.0`. Projects requiring an implementation should require `psr/container-implementation` `1.0.0`. ## 3. Interfaces ### 3.1. `Psr\Container\ContainerInterface` ```php ### 3.2. `Psr\Container\ContainerExceptionInterface` ```php ### 3.3. `Psr\Container\NotFoundExceptionInterface` ```php The intent of this guide is to reduce cognitive friction when scanning code from > different authors. It does so by enumerating a shared set of rules and expectations > about how to format PHP code. > When various authors collaborate across multiple projects, it helps to have one set > of guidelines to be used among all those projects. Thus, the benefit of this guide is > not in the rules themselves, but in the sharing of those rules. This PSR is an extension of PSR-2, and therefore also an extension of PSR-1. The basis of PSR-12 is PSR-2 and therefore a list of differences is provided below to assist with migration but it should be considered as an independent specification. This PSR will include coding style guidelines related to new functionality added to PHP after the publication of PSR-2; this includes PHP 5.5, PHP 5.6 and PHP 7.0. This PSR will also include clarifications on the text of PSR-2, as described in the PSR-2 Errata. ## 3.2. Non-Goals It is not the intention of this PSR to add entirely new coding style guidelines. PSR-12 will also not change anything stipulated in PSR-1 and PSR-2. # 4. Approaches The overarching approach is to attempt to apply existing PSR-2 styling and rationale to new functionality as opposed to establishing new conventions. ## 4.1. Strict Types Declarations There was a discussion about whether or not strict types should be enforced in the standard https://github.com/cs-extended/fig-standards/issues/7. All were in agreement we should only use a MUST or MUST NOT statement and avoid the use of a SHOULD statement and nobody wanted to say that strict types could not be declared. The discussion was whether it should be considered a coding style item which should be covered or whether it was out of scope and it was decided to be out of scope of a coding style guide. ## 4.2. Finally and Return Types Declaration Spacing Numerous different options were suggested and they can be seen [here for return type declarations](https://gist.github.com/michaelcullum/c025f3870c9ea1dd2668#file-returntypesspacing-php) or [here for finally blocks](https://gist.github.com/michaelcullum/c025f3870c9ea1dd2668#file-finallyblocks-php) and the current implementation was chosen due to consistency with other parts of the PSR-12 specification that came from PSR-2. ## 4.3. Enforcing short form for all type keywords PHP 7.0 introduced [scalar types declaration](http://php.net/manual/en/functions.arguments.php#functions.arguments.type-declaration) which does not support long type aliases. Therefore it makes sense to enforce primary short type forms to be used to have uniform syntax and prevent possible confusion. ## 4.4. Public Survey In order to settle things using data, survey was conducted and responses from 142 people including 17 project representatives were gathered: ### 4.4.1. PHP-FIG Representative Results | Representative | Project | Compound namespaces with a depth of two or more MUST not be used | Header statement grouping and ordering | Declare statements must each be on their own line | Declare statements in PHP files containing markup | Declare statements have no spaces: `declare(strict_types=1);` | Block declare statement formatting | `new` keyword usage, parenthesis required |Return type declaration formatting |Use statement leading slashes disallowed | Block namespace declaration formatting | General operator spacing |Try, Catch, Finally formatting | Anonymous class declaration formatting | Keyword casing, only lower case | Type keywords, short form only | | -------------- | ------- | ---------------------------------------------------- | ---------------------------------- | ----------------------------------------- | ------------------------------------------- | -------------------------------------------------------- | ------------------------------- | ------------------------------------- |------------------------------- |------------------------------------ | ----------------------------------- | ---------------------- |--------------------------- | ----------------------------------- | --------------------------- | -------------------------- | | Alexander Makarov | Yii framework | ✓ | ✓ | ✓ | ❌ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | | Korvin Szanto | concrete5 | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | | Leo Feyer | Contao | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | | Larry Garfield | Drupal | ✓ | ✓ | ✓ | ✓ | ✓ | ❌ | ✓ | ✓ | ✓ | ❌ | ✓ | ✓ | ❌ | ✓ | ✓ | | André R. | eZ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | | Jan Schneider | Horde | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | | Karsten Dambekalns | Neos and Flow | ✓ | ✓ | ✓ | ✓ | ❌ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | | Andres Gutierrez | Phalcon | ❌ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | | Ryan Thompson | PyroCMS | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ❌ | ❌ | ✓ | ✓ | ✓ | ✓ | ✓ | | Matteo Beccati | Revive Adserver | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ❌ | ✓ | ✓ | ✓ | ✓ | | Damian Mooyman | SilverStripe | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | | Brian Retterer | Stormpath PHP SDK | ✓ | ✓ | ✓ | ❌ | ❌ | ✓ | ❌ | ✓ | ❌ | ✓ | ✓ | ✓ | ✓ | ❌ | ❌ | | Matthew Weier O'Phinney | Zend Framework | ❌ | ✓ | ✓ | ❌ | ✓ | ✓ | ✓ | ❌ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | | Jordi Boggiano | Composer | ❌ | ❌ | ❌ | ✓ | ✓ | ✓ | ❌ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | | Ben Marks | Magento | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | | Chuck Burgess | PEAR | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | | | **Totals**: |13/3|15/1|15/1|13/3|14/2|15/1|14/2|15/1|14/2|14/2|15/1|16/0|15/1|15/1|15/1| ### 4.4.2. General non-representative voters | Question | For | Against | Percentage For | | -------- | --- | ------- | -------------- | | Compound namespaces required depth | 114 | 12 | 89.47% | | Header statement grouping and ordering | 113 | 13 | 88.5% | | Declare statements must each be on their own line | 120 | 6 | 95% | | Declare statements in PHP files containing markup | 119 | 7 | 94.12% | | Declare statements have no spaces | 116 | 10 | 91.38% | | Block declare statement formatting | 118 | 8 | 93.22% | | `new` keyword usage, parenthesis required | 116 | 10 | 91.38% | | Return type declaration formatting | 115 | 11 | 90.43% | | Use statement leading slashes disallowed | 118 | 8 | 93.22% | | Block namespace declaration formatting | 120 | 6 | 95% | | General operator spacing | 123 | 3 | 97.56% | | Try, Catch, Finally formatting | 124 | 2 | 98.39% | | Anonymous class declaration formatting | 117 | 9 | 92.31% | | Keyword casing, only lower case | 124 | 2 | 98.39% | | Type keywords, short form only | 121 | 5 | 95.87% | ## 4.5. Multiline Function Arguments Mixed With Multiline Return A potential readability issue [was raised on the mailing list](https://groups.google.com/d/msg/php-fig/ULSL4gqK8GY/cgDELuPOCQAJ). We reviewed options for changes to the specification that could provide better readability and the floated option was to require a blank line after the opening bracket of a function if the arguments and the return are both multiline. Instead it was pointed out that this specification _already_ allows you to decide where you'd like to add blank lines and so we will leave it to the implementors to decide. # 5. Changelog from PSR-2 Please note this changelog is not a verbose list of changes from PSR-2 but highlights the most notable changes. It should be considered a new specification and therefore you should read the specification for a full understanding of its contents. ## 5.1. New Statements * Lowercase for all keywords - Section 2.5 * Short form for all type keywords - Section 2.5 * Use statement grouping - Section 3 * Use statement blocks - Section 3 * Declare statement/Strict types declaration usage - Section 3 * Parentheses are always required for class instantiation - Section 4 * Typed properties - Section 4.3 * Return type declarations - Section 4.5 * Variadic and reference argument operators - Section 4.5 * Type hints - Section 4.5 * Add finally block - Section 5.6 * Operators - Section 6 * Unary operators - Section 6.1 * Binary operators - Section 6.2 * Ternary operators - Section 6.3 * Anonymous classes - Section 8 ## 5.2. Clarifications and Errata * Adjust 'methods' to 'methods and functions' in a number of instances - Throughout * Adjust references to classes and interfaces to also include traits - Throughout * StudlyCaps meaning clarified as PascalCase - Section 2.1 * The last line should not be blank but contain an EOL character - Section 2.2 * Blank lines may be added for readability except where explicitly forbidden within the PSR - Section 2.3 * PSR-2 errata statement about multi-line arguments - Section 4 * PSR-2 errata statement about extending multiple interfaces - Section 4 * Forbid blank lines before/after closing/opening braces for classes - Section 4 # 6. People ## 6.1. Editor: * Korvin Szanto ## 6.2. Sponsor: * Chris Tankersley ## 6.3. Working Group Members: * Alessandro Lai * Alexander Makarov * Michael Cullum * Robert Deutz ## 6.4. Special Thanks * Michael Cullum for drafting the original specification * Alexandar Makarov for coordinating the draft during PHP-FIG 2.0 * Cees-Jan Kiewiet for moral support # 7. Votes * **Entrance Vote:** https://groups.google.com/forum/?utm_medium=email&utm_source=footer#!msg/php-fig/P9atZLOcUBM/_jwkvlYKEAAJ * **Approval Vote:** https://groups.google.com/forum/#!topic/php-fig/1uaeSMaDGbk # 8. Relevant Links _**Note:** Order descending chronologically._ * [Inspiration Mailing List Thread](https://groups.google.com/forum/?utm_medium=email&utm_source=footer#!topic/php-fig/wh9avopSR9k) * [Initial Mailing List PSR Proposal Thread](https://groups.google.com/forum/?utm_medium=email&utm_source=footer#!topic/php-fig/MkFacLdfGso) ================================================ FILE: accepted/PSR-12-extended-coding-style-guide.md ================================================ # Extended Coding Style Guide The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119][]. [RFC 2119]: http://tools.ietf.org/html/rfc2119 ## Overview This specification extends, expands and replaces [PSR-2][], the coding style guide and requires adherence to [PSR-1][], the basic coding standard. Like [PSR-2][], the intent of this specification is to reduce cognitive friction when scanning code from different authors. It does so by enumerating a shared set of rules and expectations about how to format PHP code. This PSR seeks to provide a set way that coding style tools can implement, projects can declare adherence to and developers can easily relate to between different projects. When various authors collaborate across multiple projects, it helps to have one set of guidelines to be used among all those projects. Thus, the benefit of this guide is not in the rules themselves but the sharing of those rules. [PSR-2][] was accepted in 2012 and since then a number of changes have been made to PHP which has implications for coding style guidelines. Whilst [PSR-2] is very comprehensive of PHP functionality that existed at the time of writing, new functionality is very open to interpretation. This PSR, therefore, seeks to clarify the content of PSR-2 in a more modern context with new functionality available, and make the errata to PSR-2 binding. ### Previous language versions Throughout this document, any instructions MAY be ignored if they do not exist in versions of PHP supported by your project. ### Example This example encompasses some of the rules below as a quick overview: ```php $b) { $foo->bar($arg1); } else { BazClass::bar($arg2, $arg3); } } final public static function bar() { // method body } } ``` ## 2. General ### 2.1 Basic Coding Standard Code MUST follow all rules outlined in [PSR-1]. The term 'StudlyCaps' in PSR-1 MUST be interpreted as PascalCase where the first letter of each word is capitalized including the very first letter. ### 2.2 Files All PHP files MUST use the Unix LF (linefeed) line ending only. All PHP files MUST end with a non-blank line, terminated with a single LF. The closing `?>` tag MUST be omitted from files containing only PHP. ### 2.3 Lines There MUST NOT be a hard limit on line length. The soft limit on line length MUST be 120 characters. Lines SHOULD NOT be longer than 80 characters; lines longer than that SHOULD be split into multiple subsequent lines of no more than 80 characters each. There MUST NOT be trailing whitespace at the end of lines. Blank lines MAY be added to improve readability and to indicate related blocks of code except where explicitly forbidden. There MUST NOT be more than one statement per line. ### 2.4 Indenting Code MUST use an indent of 4 spaces for each indent level, and MUST NOT use tabs for indenting. ### 2.5 Keywords and Types All PHP reserved keywords and types [[1]][keywords][[2]][types] MUST be in lower case. Any new types and keywords added to future PHP versions MUST be in lower case. Short form of type keywords MUST be used i.e. `bool` instead of `boolean`, `int` instead of `integer` etc. ## 3. Declare Statements, Namespace, and Import Statements The header of a PHP file may consist of a number of different blocks. If present, each of the blocks below MUST be separated by a single blank line, and MUST NOT contain a blank line. Each block MUST be in the order listed below, although blocks that are not relevant may be omitted. * Opening ` ``` Declare statements MUST contain no spaces and MUST be exactly `declare(strict_types=1)` (with an optional semicolon terminator). Block declare statements are allowed and MUST be formatted as below. Note position of braces and spacing: ```php declare(ticks=1) { // some code } ``` ## 4. Classes, Properties, and Methods The term "class" refers to all classes, interfaces, and traits. Any closing brace MUST NOT be followed by any comment or statement on the same line. When instantiating a new class, parentheses MUST always be present even when there are no arguments passed to the constructor. ```php new Foo(); ``` ### 4.1 Extends and Implements The `extends` and `implements` keywords MUST be declared on the same line as the class name. The opening brace for the class MUST go on its own line; the closing brace for the class MUST go on the next line after the body. Opening braces MUST be on their own line and MUST NOT be preceded or followed by a blank line. Closing braces MUST be on their own line and MUST NOT be preceded by a blank line. ```php bar($arg1); Foo::bar($arg2, $arg3); ``` Argument lists MAY be split across multiple lines, where each subsequent line is indented once. When doing so, the first item in the list MUST be on the next line, and there MUST be only one argument per line. A single argument being split across multiple lines (as might be the case with an anonymous function or array) does not constitute splitting the argument list itself. ```php bar( $longArgument, $longerArgument, $muchLongerArgument ); ``` ```php get('/hello/{name}', function ($name) use ($app) { return 'Hello ' . $app->escape($name); }); ``` ## 5. Control Structures The general style rules for control structures are as follows: - There MUST be one space after the control structure keyword - There MUST NOT be a space after the opening parenthesis - There MUST NOT be a space before the closing parenthesis - There MUST be one space between the closing parenthesis and the opening brace - The structure body MUST be indented once - The body MUST be on the next line after the opening brace - The closing brace MUST be on the next line after the body The body of each structure MUST be enclosed by braces. This standardizes how the structures look and reduces the likelihood of introducing errors as new lines get added to the body. ### 5.1 `if`, `elseif`, `else` An `if` structure looks like the following. Note the placement of parentheses, spaces, and braces; and that `else` and `elseif` are on the same line as the closing brace from the earlier body. ```php $value) { // foreach body } ``` ### 5.6 `try`, `catch`, `finally` A `try-catch-finally` block looks like the following. Note the placement of parentheses, spaces, and braces. ```php $b) { $foo = $a + $b * $c; } ``` ### 6.3. Ternary operators The conditional operator, also known simply as the ternary operator, MUST be preceded and followed by at least one space around both the `?` and `:` characters: ```php $variable = $foo ? 'foo' : 'bar'; ``` When the middle operand of the conditional operator is omitted, the operator MUST follow the same style rules as other binary [comparison][] operators: ```php $variable = $foo ?: 'bar'; ``` ## 7. Closures Closures MUST be declared with a space after the `function` keyword, and a space before and after the `use` keyword. The opening brace MUST go on the same line, and the closing brace MUST go on the next line following the body. There MUST NOT be a space after the opening parenthesis of the argument list or variable list, and there MUST NOT be a space before the closing parenthesis of the argument list or variable list. In the argument list and variable list, there MUST NOT be a space before each comma, and there MUST be one space after each comma. Closure arguments with default values MUST go at the end of the argument list. If a return type is present, it MUST follow the same rules as with normal functions and methods; if the `use` keyword is present, the colon MUST follow the `use` list closing parentheses with no spaces between the two characters. A closure declaration looks like the following. Note the placement of parentheses, commas, spaces, and braces: ```php bar( $arg1, function ($arg2) use ($var1) { // body }, $arg3 ); ``` ## 8. Anonymous Classes Anonymous Classes MUST follow the same guidelines and principles as closures in the above section. ```php withPropagationStopped(); } ``` The latter case, Dispatcher implementations, would require checks on the return value: ```php foreach ($provider->getListenersForEvent($event) as $listener) { $returnedEvent = $listener($event); if (! $returnedEvent instanceof $event) { // This is an exceptional case! // // We now have an event of a different type, or perhaps nothing was // returned by the listener. An event of a different type might mean: // // - we need to trigger the new event // - we have an event mismatch, and should raise an exception // - we should attempt to trigger the remaining listeners anyway // // In the case of nothing being returned, this could mean any of: // // - we should continue triggering, using the original event // - we should stop triggering, and treat this as a request to // stop propagation // - we should raise an exception, because the listener did not // return what was expected // // In short, this becomes very hard to specify, or enforce. } if ($returnedEvent instanceof StoppableEventInterface && $returnedEvent->isPropagationStopped() ) { break; } } ``` In both situations, we would be introducing more potential edge cases, with little benefit, and few language-level mechanisms to guide developers to correct implementation. Given these options, the Working Group felt mutable Events were the safer alternative. That said, *there is no requirement that an Event be mutable*. Implementers should provide mutator methods on an Event object *if and only if it is necessary* and appropriate to the use case at hand. ### 4.4 Listener registration Experimentation during development of the specification determined that there were a wide range of viable, legitimate means by which a Dispatcher could be informed of a Listener. A Listener: * could be registered explicitly; * could be registered explicitly based on reflection of its signature; * could be registered with a numeric priority order; * could be registered using a before/after mechanism to control ordering more precisely; * could be registered from a service container; * could use a pre-compile step to generate code; * could be based on method names on objects in the Event itself; * could be limited to certain situations or contexts based on arbitrarily complex logic (only for certain users, only on certain days, only if certain system settings are present, etc). These and other mechanisms all exist in the wild today in PHP, all are valid use cases worth supporting, and few if any can be conveniently represented as a special case of another. That is, standardizing one way, or even a small set of ways, to inform the system of a Listener turned out to be impractical if not impossible without cutting off many use cases that should be supported. The Working Group therefore chose to encapsulate the registration of Listeners behind the `ListenerProviderInterface`. A Provider object may have an explicit registration mechanism available, or multiple such mechanisms, or none. It could also be generated code produced by some compile step. However, that also splits the responsibility of managing the process of dispatching an Event from the process of mapping an Event to Listeners. That way different implementations may be mixed-and-matched with different Provider mechanisms as needed. It is even possible, and potentially advisable, to allow libraries to include their own Providers that get aggregated into a common Provider that aggregates their Listeners to return to the Dispatcher. That is one possible way to handle arbitrary Listener registration within an arbitrary framework, although the Working Group is clear that is not the only option. While combining the Dispatcher and Provider into a single object is a valid and permissible degenerate case, it is NOT RECOMMENDED as it reduces the flexibility of system integrators. Instead, the Provider SHOULD be composed as a dependent object. ### 4.5 Deferred listeners The specification requires that the callables returned by a Provider MUST all be invoked (unless propagation is explicitly stopped) before the Dispatcher returns. However, the specification also explicitly states that Listeners may enqueue Events for later processing rather than taking immediate action. It is also entirely permissible for a Provider to accept registration of a callable, but then wrap it in another callable before returning it to the Dispatcher. (In that case, the wrapper is the Listener from the Dispatcher's point of view.) That allows all of the following behaviors to be legal: * Providers return callable Listeners that were provided to them. * Providers return callables that create an entry in a queue that will react to the Event with another callable at some later point in time. * Listeners may themselves create an entry in a queue that will react to the Event at some later point in time. * Listeners or Providers may trigger an asynchronous task, if running in an environment with support for asynchronous behavior (assuming that the result of the asynchronous task is not needed by the Emitter.) * Providers may perform such delay or wrapping on Listeners selectively based on arbitrary logic. The net result is that Providers and Listeners are responsible for determining when it is safe to defer a response to an Event until some later time. In that case, the Provider or Listener is explicitly opting out of being able to pass meaningful data back to the Emitter, but the Working Group determined that they were in the best position to know if it was safe to do so. While technically a side effect of the design, it is essentially the same approach used by Laravel (as of Laravel 5) and has been proven in the wild. ### 4.6 Return values Per the spec, a Dispatcher MUST return the Event passed by the Emitter. This is specified to provide a more ergonomic experience for users, allowing short-hands similar to the following: ```php $event = $dispatcher->dispatch(new SomeEvent('some context')); $items = $dispatcher->dispatch(new ItemCollector())->getItems(); ``` The `EventDispatcher::dispatch()` interface, however, has no return type specified. That is primarily for backward compatibility with existing implementations to make it easier for them to adopt the new interface. Additionally, as Events can be any arbitrary object the return type could only have been `object`, which would provide only minimal (albeit non-zero) value, as that type declaration would not provide IDEs with any useful information nor would it effectively enforce that the same Event is returned. The method return was thus left syntactically untyped. However, returning the same Event object from `dispatch()` is still a requirement and failure to do so is a violation of the specification. ## 5. People The Event Manager Working Group consisted of: ### 5.1 Editor * Larry Garfield ### 5.2 Sponsor * Cees-Jan Kiewiet ### 5.3 Working Group Members * Benjamin Mack * Elizabeth Smith * Ryan Weaver * Matthew Weier O'Phinney ## 6. Votes * [Entrance vote](https://groups.google.com/d/topic/php-fig/6kQFX-lhuk4/discussion) * [Review Period Initiation](https://groups.google.com/d/topic/php-fig/sR4oEQC3Gz8/discussion) * [Acceptance](https://groups.google.com/d/topic/php-fig/o4ZSu7vJi2w/discussion) ## 7. Relevant Links * [Inspiration Mailing List Thread][] * [Entrance vote][] * [Informal poll on package structure][] * [Informal poll on naming structure][] [Inspiration Mailing List Thread]: https://groups.google.com/forum/#!topic/php-fig/-EJOStgxAwY [Entrance vote]: https://groups.google.com/d/topic/php-fig/6kQFX-lhuk4/discussion [Informal poll on package structure]: https://docs.google.com/forms/d/1fvhYUH6xvPgJ1UW9I-3pMGPUtxkt5_Ph6_x_3qXHIuM/edit#responses [Informal poll on naming structure]: https://docs.google.com/forms/d/1Rs6APuwNx4k2VzJbTgieeNvN48kLu7CG8qn6Dd2FhTw/edit#responses ================================================ FILE: accepted/PSR-14-event-dispatcher.md ================================================ Event Dispatcher ================ Event Dispatching is a common and well-tested mechanism to allow developers to inject logic into an application easily and consistently. The goal of this PSR is to establish a common mechanism for event-based extension and collaboration so that libraries and components may be reused more freely between various applications and frameworks. The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119][]. [RFC 2119]: http://tools.ietf.org/html/rfc2119 ## Goal Having common interfaces for dispatching and handling events allows developers to create libraries that can interact with many frameworks and other libraries in a common fashion. Some examples: * A security framework that will prevent saving/accessing data when a user doesn't have permission. * A common full page caching system. * Libraries that extend other libraries, regardless of what framework they are both integrated into. * A logging package to track all actions taken within the application ## Definitions * **Event** - An Event is a message produced by an *Emitter*. It may be any arbitrary PHP object. * **Listener** - A Listener is any PHP callable that expects to be passed an Event. Zero or more Listeners may be passed the same Event. A Listener MAY enqueue some other asynchronous behavior if it so chooses. * **Emitter** - An Emitter is any arbitrary code that wishes to dispatch an Event. This is also known as the "calling code". It is not represented by any particular data structure but refers to the use case. * **Dispatcher** - A Dispatcher is a service object that is given an Event object by an Emitter. The Dispatcher is responsible for ensuring that the Event is passed to all relevant Listeners, but MUST defer determining the responsible listeners to a Listener Provider. * **Listener Provider** - A Listener Provider is responsible for determining what Listeners are relevant for a given Event, but MUST NOT call the Listeners itself. A Listener Provider may specify zero or more relevant Listeners. ## Events Events are objects that act as the unit of communication between an Emitter and appropriate Listeners. Event objects MAY be mutable should the use case call for Listeners providing information back to the Emitter. However, if no such bidirectional communication is needed then it is RECOMMENDED that the Event be defined as immutable; i.e., defined such that it lacks mutator methods. Implementers MUST assume that the same object will be passed to all Listeners. It is RECOMMENDED, but NOT REQUIRED, that Event objects support lossless serialization and deserialization; `$event == unserialize(serialize($event))` SHOULD hold true. Objects MAY leverage PHP’s `Serializable` interface, `__sleep()` or `__wakeup()` magic methods, or similar language functionality if appropriate. ## Stoppable Events A **Stoppable Event** is a special case of Event that contains additional ways to prevent further Listeners from being called. It is indicated by implementing the `StoppableEventInterface`. An Event that implements `StoppableEventInterface` MUST return `true` from `isPropagationStopped()` when whatever Event it represents has been completed. It is up to the implementer of the class to determine when that is. For example, an Event that is asking for a PSR-7 `RequestInterface` object to be matched with a corresponding `ResponseInterface` object could have a `setResponse(ResponseInterface $res)` method for a Listener to call, which causes `isPropagationStopped()` to return `true`. ## Listeners A Listener may be any PHP callable. A Listener MUST have one and only one parameter, which is the Event to which it responds. Listeners SHOULD type hint that parameter as specifically as is relevant for their use case; that is, a Listener MAY type hint against an interface to indicate it is compatible with any Event type that implements that interface, or to a specific implementation of that interface. A Listener SHOULD have a `void` return, and SHOULD type hint that return explicitly. A Dispatcher MUST ignore return values from Listeners. A Listener MAY delegate actions to other code. That includes a Listener being a thin wrapper around an object that runs the actual business logic. A Listener MAY enqueue information from the Event for later processing by a secondary process, using cron, a queue server, or similar techniques. It MAY serialize the Event object itself to do so; however, care should be taken that not all Event objects may be safely serializable. A secondary process MUST assume that any changes it makes to an Event object will NOT propagate to other Listeners. ## Dispatcher A Dispatcher is a service object implementing `EventDispatcherInterface`. It is responsible for retrieving Listeners from a Listener Provider for the Event dispatched, and invoking each Listener with that Event. A Dispatcher: * MUST call Listeners synchronously in the order they are returned from a ListenerProvider. * MUST return the same Event object it was passed after it is done invoking Listeners. * MUST NOT return to the Emitter until all Listeners have executed. If passed a Stoppable Event, a Dispatcher * MUST call `isPropagationStopped()` on the Event before each Listener has been called. If that method returns `true` it MUST return the Event to the Emitter immediately and MUST NOT call any further Listeners. This implies that if an Event is passed to the Dispatcher that always returns `true` from `isPropagationStopped()`, zero listeners will be called. A Dispatcher SHOULD assume that any Listener returned to it from a Listener Provider is type-safe. That is, the Dispatcher SHOULD assume that calling `$listener($event)` will not produce a `TypeError`. ### Error handling An Exception or Error thrown by a Listener MUST block the execution of any further Listeners. An Exception or Error thrown by a Listener MUST be allowed to propagate back up to the Emitter. A Dispatcher MAY catch a thrown object to log it, allow additional action to be taken, etc., but then MUST rethrow the original throwable. ## Listener Provider A Listener Provider is a service object responsible for determining what Listeners are relevant to and should be called for a given Event. It may determine both what Listeners are relevant and the order in which to return them by whatever means it chooses. That MAY include: * Allowing for some form of registration mechanism so that implementers may assign a Listener to an Event in a fixed order. * Deriving a list of applicable Listeners through reflection based on the type and implemented interfaces of the Event. * Generating a compiled list of Listeners ahead of time that may be consulted at runtime. * Implementing some form of access control so that certain Listeners will only be called if the current user has a certain permission. * Extracting some information from an object referenced by the Event, such as an Entity, and calling pre-defined lifecycle methods on that object. * Delegating its responsibility to one or more other Listener Providers using some arbitrary logic. Any combination of the above, or other mechanisms, MAY be used as desired. Listener Providers SHOULD use the class name of an Event to differentiate one event from another. They MAY also consider any other information on the event as appropriate. Listener Providers MUST treat parent types identically to the Event's own type when determining listener applicability. In the following case: ```php class A {} class B extends A {} $b = new B(); function listener(A $event): void {}; ``` A Listener Provider MUST treat `listener()` as an applicable listener for `$b`, as it is type compatible, unless some other criteria prevents it from doing so. ## Object composition A Dispatcher SHOULD compose a Listener Provider to determine relevant listeners. It is RECOMMENDED that a Listener Provider be implemented as a distinct object from the Dispatcher but that is NOT REQUIRED. ## Interfaces ```php namespace Psr\EventDispatcher; /** * Defines a dispatcher for events. */ interface EventDispatcherInterface { /** * Provide all relevant listeners with an event to process. * * @param object $event * The object to process. * * @return object * The Event that was passed, now modified by listeners. */ public function dispatch(object $event); } ``` ```php namespace Psr\EventDispatcher; /** * Mapper from an event to the listeners that are applicable to that event. */ interface ListenerProviderInterface { /** * @param object $event * An event for which to return the relevant listeners. * @return iterable * An iterable (array, iterator, or generator) of callables. Each * callable MUST be type-compatible with $event. */ public function getListenersForEvent(object $event): iterable; } ``` ```php namespace Psr\EventDispatcher; /** * An Event whose processing may be interrupted when the event has been handled. * * A Dispatcher implementation MUST check to determine if an Event * is marked as stopped after each listener is called. If it is then it should * return immediately without calling any further Listeners. */ interface StoppableEventInterface { /** * Is propagation stopped? * * This will typically only be used by the Dispatcher to determine if the * previous listener halted propagation. * * @return bool * True if the Event is complete and no further listeners should be called. * False to continue calling listeners. */ public function isPropagationStopped(): bool; } ``` ================================================ FILE: accepted/PSR-15-request-handlers-meta.md ================================================ HTTP Server Request Handlers Meta Document ========================================== ## 1. Summary The purpose of this PSR is to define formal interfaces for HTTP server request handlers ("request handlers") and HTTP server request middleware ("middleware") that are compatible with HTTP messages as defined in [PSR-7][psr7] or subsequent replacement PSRs. _Note: All references to "request handlers" and "middleware" are specific to **server request** processing._ [psr7]: https://www.php-fig.org/psr/psr-7/ ## 2. Why Bother? The HTTP messages specification does not contain any reference to request handlers or middleware. Request handlers are a fundamental part of any web application. The handler is the component that receives a request and produces a response. Nearly all code that works with HTTP messages will have some kind of request handler. [Middleware][middleware] has existed for many years in the PHP ecosystem. The general concept of reusable middleware was popularized by [StackPHP][stackphp]. Since the release of HTTP messages as a PSR, many frameworks have adopted middleware that use HTTP message interfaces. Agreeing on formal request handler and middleware interfaces eliminates several problems and has a number of benefits: * Provides a formal standard for developers to commit to. * Enables any middleware component to run in any compatible framework. * Eliminates duplication of similar interfaces defined by various frameworks. * Avoids minor discrepancies in method signatures. [middleware]: https://en.wikipedia.org/wiki/Middleware [stackphp]: http://stackphp.com/ ## 3. Scope ### 3.1 Goals * Create a request handler interface that uses HTTP messages. * Create a middleware interface that uses HTTP messages. * Implement request handler and middleware signatures that are based on best practices. * Ensure that request handlers and middleware will be compatible with any implementation of HTTP messages. ### 3.2 Non-Goals * Attempting to define the mechanism by which HTTP responses are created. * Attempting to define interfaces for client/asynchronous middleware. * Attempting to define how middleware is dispatched. ## 4. Request Handler Approaches There are many approaches to request handlers that use HTTP messages. However, the general process is the same in all of them: Given an HTTP request, produce an HTTP response for that request. The internal requirements of that process will vary from framework to framework and application to application. This proposal makes no effort to determine what that process should be. ## 5. Middleware Approaches There are currently two common approaches to middleware that use HTTP messages. ### 5.1 Double Pass The signature used by most middleware implementations has been mostly the same and is based on [Express middleware][express], which is defined as: ``` fn(request, response, next): response ``` [express]: http://expressjs.com/en/guide/writing-middleware.html Based on the middleware implementations already used by frameworks that have adopted this signature, the following commonalities are observed: * The middleware is defined as a [callable][php-callable]. * The middleware is passed 3 arguments during invocation: 1. A `ServerRequestInterface` implementation. 2. A `ResponseInterface` implementation. 3. A `callable` that receives the request and response to delegate to the next middleware. [php-callable]: http://php.net/manual/language.types.callable.php A significant number of projects provide and/or use exactly the same interface. This approach is often referred to as "double pass" in reference to both the request and response being passed to the middleware. #### 5.1.1 Projects Using Double Pass * [mindplay/middleman v1](https://github.com/mindplay-dk/middleman/blob/1.0.0/src/MiddlewareInterface.php#L24) * [relay/relay v1](https://github.com/relayphp/Relay.Relay/blob/1.0.0/src/MiddlewareInterface.php#L24) * [slim/slim v3](https://github.com/slimphp/Slim/blob/3.4.0/Slim/MiddlewareAwareTrait.php#L66-L75) * [zendframework/zend-stratigility v1](https://github.com/zendframework/zend-stratigility/blob/1.0.0/src/MiddlewarePipe.php#L69-L79) #### 5.1.2 Middleware Implementing Double Pass * [bitexpert/adroit](https://github.com/bitExpert/adroit) * [akrabat/rka-ip-address-middleware](https://github.com/akrabat/rka-ip-address-middleware) * [akrabat/rka-scheme-and-host-detection-middleware](https://github.com/akrabat/rka-scheme-and-host-detection-middleware) * [bear/middleware](https://github.com/bearsunday/BEAR.Middleware) * [los/api-problem](https://github.com/Lansoweb/api-problem) * [los/los-rate-limit](https://github.com/Lansoweb/LosRateLimit) * [monii/monii-action-handler-psr7-middleware](https://github.com/monii/monii-action-handler-psr7-middleware) * [monii/monii-nikic-fast-route-psr7-middleware](https://github.com/monii/monii-nikic-fast-route-psr7-middleware) * [monii/monii-response-assertion-psr7-middleware](https://github.com/monii/monii-response-assertion-psr7-middleware) * [mtymek/blast-base-url](https://github.com/mtymek/blast-base-url) * [ocramius/psr7-session](https://github.com/Ocramius/PSR7Session) * [oscarotero/psr7-middlewares](https://github.com/oscarotero/psr7-middlewares) * [php-middleware/block-robots](https://github.com/php-middleware/block-robots) * [php-middleware/http-authentication](https://github.com/php-middleware/http-authentication) * [php-middleware/log-http-messages](https://github.com/php-middleware/log-http-messages) * [php-middleware/maintenance](https://github.com/php-middleware/maintenance) * [php-middleware/phpdebugbar](https://github.com/php-middleware/phpdebugbar) * [php-middleware/request-id](https://github.com/php-middleware/request-id) * [relay/middleware](https://github.com/relayphp/Relay.Middleware) The primary downside of this interface is that the while the interface itself is a callable, there is currently no way to strictly type a closure. ### 5.2 Single Pass (Lambda) The other approach to middleware is much closer to [StackPHP][stackphp] style and is defined as: ``` fn(request, next): response ``` Middleware taking this approach generally has the following commonalities: * The middleware is defined with a specific interface with a method that takes the request for processing. * The middleware is passed 2 arguments during invocation: 1. An HTTP request message. 2. A request handler to which the middleware can delegate the responsibility of producing an HTTP response message. In this form, middleware has no access to a response until one is generated by the request handler. Middleware can then modify the response before returning it. This approach is often referred to as "single pass" or "lambda" in reference to only the request being passed to the middleware. #### 5.2.1 Projects Using Single Pass There are fewer examples of this approach within projects using HTTP messages, with one notable exception. [Guzzle middleware][guzzle-middleware] is focused on outgoing (client) requests and uses this signature: ```php function (RequestInterface $request, array $options): ResponseInterface ``` #### 5.2.2 Additional Projects Using Single Pass There are also significant projects that predate HTTP messages using this approach. [StackPHP][stackphp] is based on [Symfony HttpKernel][httpkernel] and supports middleware with this signature: ```php function handle(Request $request, $type, $catch): Response ``` _Note: While Stack has multiple arguments, a response object is not included._ [Laravel middleware][laravel-middleware] uses Symfony components and supports middleware with this signature: ```php function handle(Request $request, callable $next): Response ``` [guzzle-middleware]: http://docs.guzzlephp.org/en/latest/handlers-and-middleware.html [httpkernel]: https://symfony.com/doc/2.0/components/http_kernel/introduction.html [laravel-middleware]: https://laravel.com/docs/master/middleware ### 5.3 Comparison of Approaches The single pass approach to middleware has been well established in the PHP community for many years. This is most evident with the large number of packages that are based around StackPHP. The double pass approach is much newer but has been almost universally used by early adopters of HTTP messages (PSR-7). ### 5.4 Chosen Approach Despite the nearly universal adoption of the double-pass approach, there are significant issues regarding implementation. The most severe is that passing an empty response has no guarantees that the response is in a usable state. This is further exacerbated by the fact that a middleware may modify the response before passing it for further processing. Further compounding the problem is that there is no way to ensure that the response body has not been written to, which can lead to incomplete output or error responses being sent with cache headers attached. It is also possible to end up with [corrupted body content][rob-allen-filtering] when writing over existing body content if the new content is shorter than the original. The most effective way to resolve these issues is to always provide a fresh stream when modifying the body of a message. [rob-allen-filtering]: https://akrabat.com/filtering-the-psr-7-body-in-middleware/ Some have argued that passing the response helps ensure dependency inversion. While it is true that it helps avoid depending on a specific implementation of HTTP messages, the problem can also be resolved by injecting factories into the middleware to create HTTP message objects, or by injecting empty message instances. With the creation of HTTP Factories in [PSR-17][psr17], a standard approach to handling dependency inversion is possible. [psr17]: https://github.com/php-fig/fig-standards/blob/master/proposed/http-factory/http-factory-meta.md A more subjective, but also important, concern is that existing double-pass middleware typically uses the `callable` type hint to refer to middleware. This makes strict typing impossible, as there is no assurance that the `callable` being passed implements a middleware signature, which reduces runtime safety. **Due to these significant issues, the lambda approach has been chosen for this proposal.** ## 6. Design Decisions ### 6.1 Request Handler Design The `RequestHandlerInterface` defines a single method that accepts a request and MUST return a response. The request handler MAY delegate to another handler. #### Why is a server request required? To make it clear that the request handler can only be used in a server side context. In an client side context, a [promise][promises] would likely be returned instead of a response. [promises]: https://promisesaplus.com/ #### Why the term "handler"? The term "handler" means something designated to manage or control. In terms of request processing, a request handler is the point where the request must be acted upon to create a response. As opposed to the term "delegate", which was used in a previous version of this specification, the internal behavior of this interface is not specified. As long as the request handler ultimately produces a response, it is valid. #### Why doesn't request handler use `__invoke`? Using `__invoke` is less transparent than using a named method. It also makes it easier to call the request handler when it is assigned to a class variable, without using `call_user_func` or other less common syntax. _See [PHP-FIG discussion of FrameInterface][] for additional information._ ### 6.2 Middleware Design The `MiddlewareInterface` defines a single method that accepts an HTTP request and a request handler and must return a response. The middleware may: - Evolve the request before passing it to the request handler. - Evolve the response received from the request handler before returning it. - Create and return a response without passing the request to the request handler, thereby handling the request itself. When delegating from one middleware to another in a sequence, one approach for dispatching systems is to use an intermediary request handler composing the middleware sequence as a way to link middleware together. The final or innermost middleware will act as a gateway to application code and generate a response from its results; alternately, the middleware MAY delegate this responsibility to a dedicated request handler. #### Why doesn't middleware use `__invoke`? Doing so would conflict with existing middleware that implements the double-pass approach and may want to implement the middleware interface for purposes of forward compatibility with this specification. #### Why the name `process()`? We reviewed a number of existing monolithic and middleware frameworks to determine what method(s) each defined for processing incoming requests. We found the following were commonly used: - `__invoke` (within middleware systems, such as Slim, Expressive, Relay, etc.) - `handle` (in particular, software derived from Symfony's [HttpKernel][HttpKernel]) - `dispatch` (Zend Framework's [DispatchableInterface][DispatchableInterface]) [HttpKernel]: https://symfony.com/doc/current/components/http_kernel.html [DispatchableInterface]: https://github.com/zendframework/zend-stdlib/blob/980ce463c29c1a66c33e0eb67961bba895d0e19e/src/DispatchableInterface.php We chose to allow a forward-compatible approach for such classes to repurpose themselves as middleware (or middleware compatible with this specification), and thus needed to choose a name not in common usage. As such, we chose `process`, to indicate _processing_ a request. #### Why is a server request required? To make it clear that the middleware can only be used in a synchronous, server side context. While not all middleware will need to use the additional methods defined by the server request interface, outbound requests are typically processed asynchronously and would typically return a [promise][promises] of a response. (This is primarily due to the fact that multiple requests can be made in parallel and processed as they are returned.) It is outside the scope of this proposal to address the needs of asynchronous request/response life cycles. Attempting to define client middleware would be premature at this point. Any future proposal that is focused on client side request processing should have the opportunity to define a standard that is specific to the nature of asynchronous middleware. _See [PHP-FIG discussion about client vs server side middleware][] for additional information._ #### What is the role of the request handler? Middleware has the following roles: - Producing a response on its own. If specific request conditions are met, the middleware can produce and return a response. - Returning the result of the request handler. In cases where the middleware cannot produce its own response, it can delegate to the request handler to produce one; sometimes this may involve providing a transformed request (e.g., to inject a request attribute, or the results of parsing the request body). - Manipulating and returning the response produced by the request handler. In some cases, the middleware may be interested in manipulating the response the request handler returns (e.g., to gzip the response body, to add CORS headers, etc.). In such cases, the middleware will capture the response returned by the request handler, and return a transformed response on completion. In these latter two cases, the middleware may have code such as the following: ```php // Straight delegation: return $handler->handle($request); // Capturing the response to manipulate: $response = $handler->handle($request); ``` How the handler acts is entirely up to the developer, so long as it produces a response. In one common scenario, the handler implements a _queue_ or a _stack_ of middleware instances internally. In such cases, calling `$handler->handle($request)` will advance the internal pointer, pull the middleware associated with that pointer, and call it using `$middleware->process($request, $this)`. If no more middleware exists, it will generally either raise an exception or return a canned response. Another possibility is for _routing middleware_ that matches the incoming server request to a specific handler, and then returns the response generated by executing that handler. If unable to route to a handler, it would instead execute the handler provided to the middleware. (This sort of mechanism can even be used in conjunction with middleware queues and stacks.) ### 6.3 Example Interface Interactions The two interfaces, `RequestHandlerInterface` and `MiddlewareInterface`, were designed to work in conjunction with one another. Middleware gains flexibility when de-coupled from any over-arching application layer, and instead only relying on the provided request handler to produce a response. Two approaches to middleware dispatch systems that the Working Group observed and/or implemented are demonstrated below. Additionally, examples of re-usable middleware are provided to demonstrate how to write middleware that is loosely-coupled. Please note that these are not suggested as definitive or exclusive approaches to defining middleware dispatch systems. #### Queue-based request handler In this approach, a request handler maintains a queue of middleware, and a fallback response to return if the queue is exhausted without returning a response. When executing the first middleware, the queue passes itself as a request handler to the middleware. ```php class QueueRequestHandler implements RequestHandlerInterface { private $middleware = []; private $fallbackHandler; public function __construct(RequestHandlerInterface $fallbackHandler) { $this->fallbackHandler = $fallbackHandler; } public function add(MiddlewareInterface $middleware) { $this->middleware[] = $middleware; } public function handle(ServerRequestInterface $request): ResponseInterface { // Last middleware in the queue has called on the request handler. if (0 === count($this->middleware)) { return $this->fallbackHandler->handle($request); } $middleware = array_shift($this->middleware); return $middleware->process($request, $this); } } ``` An application bootstrap might then look like this: ```php // Fallback handler: $fallbackHandler = new NotFoundHandler(); // Create request handler instance: $app = new QueueRequestHandler($fallbackHandler); // Add one or more middleware: $app->add(new AuthorizationMiddleware()); $app->add(new RoutingMiddleware()); // execute it: $response = $app->handle(ServerRequestFactory::fromGlobals()); ``` This system has two request handlers: one that will produce a response if the last middleware delegates to the request handler, and one for dispatching the middleware layers. (In this example, the `RoutingMiddleware` will likely execute composed handlers on a successful route match; see more on that below.) This approach has the following benefits: - Middleware does not need to know anything about any other middleware or how it is composed in the application. - The `QueueRequestHandler` is agnostic of the PSR-7 implementation in use. - Middleware is executed in the order it is added to the application, making the code explicit. - Generation of the "fallback" response is delegated to the application developer. This allows the developer to determine whether that should be a "404 Not Found" condition, a default page, etc. #### Decoration-based request handler In this approach, a request handler implementation decorates both a middleware instance and a fallback request handler to pass to it. The application is built from the outside-in, passing each request handler "layer" to the next outer one. ```php class DecoratingRequestHandler implements RequestHandlerInterface { private $middleware; private $nextHandler; public function __construct(MiddlewareInterface $middleware, RequestHandlerInterface $nextHandler) { $this->middleware = $middleware; $this->nextHandler = $nextHandler; } public function handle(ServerRequestInterface $request): ResponseInterface { return $this->middleware->process($request, $this->nextHandler); } } // Create a response prototype to return if no middleware can produce a response // on its own. This could be a 404, 500, or default page. $responsePrototype = (new Response())->withStatus(404); $innerHandler = new class ($responsePrototype) implements RequestHandlerInterface { private $responsePrototype; public function __construct(ResponseInterface $responsePrototype) { $this->responsePrototype = $responsePrototype; } public function handle(ServerRequestInterface $request): ResponseInterface { return $this->responsePrototype; } }; $layer1 = new DecoratingRequestHandler(new RoutingMiddleware(), $innerHandler); $layer2 = new DecoratingRequestHandler(new AuthorizationMiddleware(), $layer1); $response = $layer2->handle(ServerRequestFactory::fromGlobals()); ``` Similar to the queue-based middleware, request handlers serve two purposes in this system: - Producing a fallback response if no other layer does. - Dispatching middleware. #### Reusable Middleware Examples In the examples above, we have two middleware composed in each. In order for these to work in either situation, we need to write them such that they interact appropriately. Implementors of middleware striving for maximum interoperability may want to consider the following guidelines: - Test the request for a required condition. If it does not satisfy that condition, use a composed prototype response or a composed response factory to generate and return a response. - If pre-conditions are met, delegate creation of the response to the provided request handler, optionally providing a "new" request by manipulating the provided request (e.g., `$handler->handle($request->withAttribute('foo', 'bar')`). - Either pass the response returned by the request handler unaltered, or provide a new response by manipulating the one returned (e.g., `return $response->withHeader('X-Foo-Bar', 'baz')`). The `AuthorizationMiddleware` is one that will exercise all three of these guidelines: - If authorization is required, but the request is not authorized, it will use a composed prototype response to produce an "unauthorized" response. - If authorization is not required, it will delegate the request to the handler without changes. - If authorization is required and the request is authorized, it will delegate the request to the handler, and sign the response returned based on the request. ```php class AuthorizationMiddleware implements MiddlewareInterface { private $authorizationMap; public function __construct(AuthorizationMap $authorizationMap) { $this->authorizationMap = $authorizationMap; } public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { if (! $this->authorizationMap->needsAuthorization($request)) { return $handler->handle($request); } if (! $this->authorizationMap->isAuthorized($request)) { return $this->authorizationMap->prepareUnauthorizedResponse(); } $response = $handler->handle($request); return $this->authorizationMap->signResponse($response, $request); } } ``` Note that the middleware is not concerned with how the request handler is implemented; it merely uses it to produce a response when pre-conditions have been met. The `RoutingMiddleware` implementation described below follows a similar process: it analyzes the request to see if it matches known routes. In this particular implementation, routes map to request handlers, and the middleware essentially delegates to them in order to produce a response. However, in the case that no route is matched, it will execute the handler passed to it to produce the response to return. ```php class RoutingMiddleware implements MiddlewareInterface { private $router; public function __construct(Router $router) { $this->router = $router; } public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { $result = $this->router->match($request); if ($result->isSuccess()) { return $result->getHandler()->handle($request); } return $handler->handle($request); } } ``` ## 7. People This PSR was produced by a FIG Working Group with the following members: * Matthew Weier O'Phinney (sponsor), * Woody Gilk (editor), * Glenn Eggleton * Matthieu Napoli * Oscar Otero * Korvin Szanto * Stefano Torresi The working group would also like to acknowledge the contributions of: * Jason Coward, * Paul M. Jones, * Rasmus Schultz, ## 8. Votes * [Working Group Formation](https://groups.google.com/d/msg/php-fig/rPFRTa0NODU/tIU9BZciAgAJ) * [Review Period Initiation](https://groups.google.com/d/msg/php-fig/mfTrFinTvEM/PiYvU2S6BAAJ) * [Acceptance](https://groups.google.com/d/msg/php-fig/bhQmHt39hJE/ZCYrK_O2AQAJ) ## 9. Relevant Links _**Note:** Order descending chronologically._ * [PHP-FIG mailing list thread][] * [The PHP League middleware proposal][] * [PHP-FIG discussion of FrameInterface][] * [PHP-FIG discussion about client vs server side middleware][] ## 10. Errata ... [PHP-FIG mailing list thread]: https://groups.google.com/d/msg/php-fig/vTtGxdIuBX8/NXKieN9vDQAJ [The PHP League middleware proposal]: https://groups.google.com/d/msg/thephpleague/jyztj-Nz_rw/I4lHVFigAAAJ [PHP-FIG discussion of FrameInterface]: https://groups.google.com/d/msg/php-fig/V12AAcT_SxE/aRXmNnIVCwAJ [PHP-FIG discussion about client vs server side middleware]: https://groups.google.com/d/topic/php-fig/vBk0BRgDe2s/discussion ================================================ FILE: accepted/PSR-15-request-handlers.md ================================================ HTTP Server Request Handlers ============================ This document describes common interfaces for HTTP server request handlers ("request handlers") and HTTP server middleware components ("middleware") that use HTTP messages as described by [PSR-7][psr7] or subsequent replacement PSRs. HTTP request handlers are a fundamental part of any web application. Server-side code receives a request message, processes it, and produces a response message. HTTP middleware is a way to move common request and response processing away from the application layer. The interfaces described in this document are abstractions for request handlers and middleware. _Note: All references to "request handlers" and "middleware" are specific to **server request** processing._ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119][rfc2119]. [psr7]: https://www.php-fig.org/psr/psr-7/ [rfc2119]: http://tools.ietf.org/html/rfc2119 ### References - [PSR-7][psr7] - [RFC 2119][rfc2119] ## 1. Specification ### 1.1 Request Handlers A request handler is an individual component that processes a request and produces a response, as defined by PSR-7. A request handler MAY throw an exception if request conditions prevent it from producing a response. The type of exception is not defined. Request handlers using this standard MUST implement the following interface: - `Psr\Http\Server\RequestHandlerInterface` ### 1.2 Middleware A middleware component is an individual component participating, often together with other middleware components, in the processing of an incoming request and the creation of a resulting response, as defined by PSR-7. A middleware component MAY create and return a response without delegating to a request handler, if sufficient conditions are met. Middleware using this standard MUST implement the following interface: - `Psr\Http\Server\MiddlewareInterface` ### 1.3 Generating Responses It is RECOMMENDED that any middleware or request handler that generates a response will either compose a prototype of a PSR-7 `ResponseInterface` or a factory capable of generating a `ResponseInterface` instance in order to prevent dependence on a specific HTTP message implementation. ### 1.4 Handling Exceptions It is RECOMMENDED that any application using middleware includes a component that catches exceptions and converts them into responses. This middleware SHOULD be the first component executed and wrap all further processing to ensure that a response is always generated. ## 2. Interfaces ### 2.1 Psr\Http\Server\RequestHandlerInterface The following interface MUST be implemented by request handlers. ```php namespace Psr\Http\Server; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; /** * Handles a server request and produces a response. * * An HTTP request handler process an HTTP request in order to produce an * HTTP response. */ interface RequestHandlerInterface { /** * Handles a request and produces a response. * * May call other collaborating code to generate the response. */ public function handle(ServerRequestInterface $request): ResponseInterface; } ``` ### 2.2 Psr\Http\Server\MiddlewareInterface The following interface MUST be implemented by compatible middleware components. ```php namespace Psr\Http\Server; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; /** * Participant in processing a server request and response. * * An HTTP middleware component participates in processing an HTTP message: * by acting on the request, generating the response, or forwarding the * request to a subsequent middleware and possibly acting on its response. */ interface MiddlewareInterface { /** * Process an incoming server request. * * Processes an incoming server request in order to produce a response. * If unable to produce the response itself, it may delegate to the provided * request handler to do so. */ public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface; } ``` ================================================ FILE: accepted/PSR-16-simple-cache-meta.md ================================================ # PSR-16 Meta Document ## 1. Summary Caching is a common way to improve the performance of any project, and many libraries make use or could make use of it. Interoperability at this level means libraries can drop their own caching implementations and easily rely on the one given to them by the framework, or another dedicated cache library the user picked. ## 2. Why Bother? PSR-6 solves this problem already, but in a rather formal and verbose way for what the most simple use cases need. This simpler approach aims to build a standardized layer of simplicity on top of the existing PSR-6 interfaces. ## 3. Scope ### 3.1 Goals * A simple interface for cache operations. * Basic support for operations on multiple keys for performance (round-trip-time) reasons. * Providing an adapter class that turns a PSR-6 implementation into a PSR-Simple-Cache one. * It should be possible to expose both caching PSRs from a caching library. ### 3.2 Non-Goals * Solving all possible edge cases, PSR-6 does this well already. ## 4. Approaches The approach chosen here is very barebones by design, as it is to be used only by the most simple cases. It does not have to be implementable by all possible cache backends, nor be usable for all usages. It is merely a layer of convenience on top of PSR-6. ## 5. People ### 5.1 Editor(s) * Paul Dragoonis (@dragoonis) ### 5.2 Sponsors * Jordi Boggiano (@seldaek) - Composer (Coordinator) * Fabien Potencier (@fabpot) - Symfony ### 5.3 Contributors For their role in the writing the initial version of this cache PSR: * Evert Pot (@evert) * Florin Pățan (@dlsniper) For being an early reviewer * Daniel Messenger (@dannym87) ## 6. Votes * **Entrance Vote:** https://groups.google.com/d/topic/php-fig/vyQTKHS6pJ8/discussion * **Acceptance Vote:** https://groups.google.com/d/msg/php-fig/A8e6GvDRGIk/HQBJGEhbDQAJ ## 7. Relevant Links * [Survey of existing cache implementations][1], by @dragoonis [1]: https://docs.google.com/spreadsheet/ccc?key=0Ak2JdGialLildEM2UjlOdnA4ekg3R1Bfeng5eGlZc1E#gid=0 ## 8. Errata ### 8.1 Throwable The 2.0 release of the `psr/simple-cache` package updates `Psr\SimpleCache\CacheException` to extend `\Throwable`. This is considered a backwards compatible change for implementing libraries as of PHP 7.4. ### 8.2 Type additions The 2.0 release of the `psr/simple-cache` package includes scalar parameter types and increases the minimum PHP version to 8.0. This is considered a backwards compatible change for implementing libraries as PHP 7.2 introduces covariance for parameters. Any implementation of 1.0 is compatible with 2.0. For calling libraries, however, this reduces the types that they may pass (as previously any parameter that could be cast to string could be accepted) and as such requires incrementing the major version. The 3.0 release includes return types. Return types break backwards compatibility for implementing libraries as PHP does not support return type widening. Implementing libraries **MAY** add return types to their own packages at their discretion, provided that: * the return types match those in the 3.0 package. * the implementation specifies a minimum PHP version of 8.0.0 or later * the implementation depends on `"psr/simple-cache": "^2 || ^3"` so as to exclude the untyped 1.0 version. Implementing libraries **MAY** add parameter types to their own package in a new minor release, either at the same time as adding return types or in a subsequent release, provided that: * the parameter types match or widen those in the 2.0 package * the implementation specifies a minimum PHP version of 8.0 if using mixed or union types or later. * the implementation depends on `"psr/simple-cache": "^2 || ^3"` so as to exclude the untyped 1.0 version. Implementing libraries are encouraged, but not required to transition their packages toward the 3.0 version of the package at their earliest convenience. Calling libraries are encouraged to ensure they are sending the correct types and to update their requirement to `"psr/simple-cache": "^1 || ^2 || ^3"` at their earliest convenience. ================================================ FILE: accepted/PSR-16-simple-cache.md ================================================ Common Interface for Caching Libraries ====================================== This document describes a simple yet extensible interface for a cache item and a cache driver. The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119][]. The final implementations MAY decorate the objects with more functionality than the one proposed but they MUST implement the indicated interfaces/functionality first. [RFC 2119]: http://tools.ietf.org/html/rfc2119 # 1. Specification ## 1.1 Introduction Caching is a common way to improve the performance of any project, making caching libraries one of the most common features of many frameworks and libraries. Interoperability at this level means libraries can drop their own caching implementations and easily rely on the one given to them by the framework, or another dedicated cache library. PSR-6 solves this problem already, but in a rather formal and verbose way for what the most simple use cases need. This simpler approach aims to build a standardized streamlined interface for common cases. It is independent of PSR-6 but has been designed to make compatibility with PSR-6 as straightforward as possible. ## 1.2 Definitions Definitions for Calling Library, Implementing Library, TTL, Expiration and Key are copied from PSR-6 as the same assumptions are true. * **Calling Library** - The library or code that actually needs the cache services. This library will utilize caching services that implement this standard's interfaces, but will otherwise have no knowledge of the implementation of those caching services. * **Implementing Library** - This library is responsible for implementing this standard in order to provide caching services to any Calling Library. The Implementing Library MUST provide a class implementing the Psr\SimpleCache\CacheInterface interface. Implementing Libraries MUST support at minimum TTL functionality as described below with whole-second granularity. * **TTL** - The Time To Live (TTL) of an item is the amount of time between when that item is stored and it is considered stale. The TTL is normally defined by an integer representing time in seconds, or a DateInterval object. * **Expiration** - The actual time when an item is set to go stale. This is calculated by adding the TTL to the time when an object is stored. An item with a 300 second TTL stored at 1:30:00 will have an expiration of 1:35:00. Implementing Libraries MAY expire an item before its requested Expiration Time, but MUST treat an item as expired once its Expiration Time is reached. If a calling library asks for an item to be saved but does not specify an expiration time, or specifies a null expiration time or TTL, an Implementing Library MAY use a configured default duration. If no default duration has been set, the Implementing Library MUST interpret that as a request to cache the item forever, or for as long as the underlying implementation supports. If a negative or zero TTL is provided, the item MUST be deleted from the cache if it exists, as it is expired already. * **Key** - A string of at least one character that uniquely identifies a cached item. Implementing libraries MUST support keys consisting of the characters `A-Z`, `a-z`, `0-9`, `_`, and `.` in any order in UTF-8 encoding and a length of up to 64 characters. Implementing libraries MAY support additional characters and encodings or longer lengths, but MUST support at least that minimum. Libraries are responsible for their own escaping of key strings as appropriate, but MUST be able to return the original unmodified key string. The following characters are reserved for future extensions and MUST NOT be supported by implementing libraries: `{}()/\@:` * **Cache** - An object that implements the `Psr\SimpleCache\CacheInterface` interface. * **Cache Misses** - A cache miss will return null and therefore detecting if one stored `null` is not possible. This is the main deviation from PSR-6's assumptions. ## 1.3 Cache Implementations MAY provide a mechanism for a user to specify a default TTL if one is not specified for a specific cache item. If no user-specified default is provided implementations MUST default to the maximum legal value allowed by the underlying implementation. If the underlying implementation does not support TTL, the user-specified TTL MUST be silently ignored. ## 1.4 Data Implementing libraries MUST support all serializable PHP data types, including: * **Strings** - Character strings of arbitrary size in any PHP-compatible encoding. * **Integers** - All integers of any size supported by PHP, up to 64-bit signed. * **Floats** - All signed floating point values. * **Booleans** - True and False. * **Null** - The null value (although it will not be distinguishable from a cache miss when reading it back out). * **Arrays** - Indexed, associative and multidimensional arrays of arbitrary depth. * **Objects** - Any object that supports lossless serialization and deserialization such that $o == unserialize(serialize($o)). Objects MAY leverage PHP's Serializable interface, `__sleep()` or `__wakeup()` magic methods, or similar language functionality if appropriate. All data passed into the Implementing Library MUST be returned exactly as passed. That includes the variable type. That is, it is an error to return (string) 5 if (int) 5 was the value saved. Implementing Libraries MAY use PHP's serialize()/unserialize() functions internally but are not required to do so. Compatibility with them is simply used as a baseline for acceptable object values. If it is not possible to return the exact saved value for any reason, implementing libraries MUST respond with a cache miss rather than corrupted data. # 2. Interfaces ## 2.1 CacheInterface The cache interface defines the most basic operations on a collection of cache-entries, which entails basic reading, writing and deleting individual cache items. In addition, it has methods for dealing with multiple sets of cache entries such as writing, reading or deleting multiple cache entries at a time. This is useful when you have lots of cache reads/writes to perform, and lets you perform your operations in a single call to the cache server cutting down latency times dramatically. An instance of CacheInterface corresponds to a single collection of cache items with a single key namespace, and is equivalent to a "Pool" in PSR-6. Different CacheInterface instances MAY be backed by the same datastore, but MUST be logically independent. ```php value pairs. Cache keys that do not exist or are stale will have $default as value. * * @throws \Psr\SimpleCache\InvalidArgumentException * MUST be thrown if $keys is neither an array nor a Traversable, * or if any of the $keys are not a legal value. */ public function getMultiple($keys, $default = null); /** * Persists a set of key => value pairs in the cache, with an optional TTL. * * @param iterable $values A list of key => value pairs for a multiple-set operation. * @param null|int|\DateInterval $ttl Optional. The TTL value of this item. If no value is sent and * the driver supports TTL then the library may set a default value * for it or let the driver take care of that. * * @return bool True on success and false on failure. * * @throws \Psr\SimpleCache\InvalidArgumentException * MUST be thrown if $values is neither an array nor a Traversable, * or if any of the $values are not a legal value. */ public function setMultiple($values, $ttl = null); /** * Deletes multiple cache items in a single operation. * * @param iterable $keys A list of string-based keys to be deleted. * * @return bool True if the items were successfully removed. False if there was an error. * * @throws \Psr\SimpleCache\InvalidArgumentException * MUST be thrown if $keys is neither an array nor a Traversable, * or if any of the $keys are not a legal value. */ public function deleteMultiple($keys); /** * Determines whether an item is present in the cache. * * NOTE: It is recommended that has() is only to be used for cache warming type purposes * and not to be used within your live applications operations for get/set, as this method * is subject to a race condition where your has() will return true and immediately after, * another script can remove it, making the state of your app out of date. * * @param string $key The cache item key. * * @return bool * * @throws \Psr\SimpleCache\InvalidArgumentException * MUST be thrown if the $key string is not a legal value. */ public function has($key); } ``` ## 2.2 CacheException ```php - Matthew Weier O'Phinney (sponsor), - Stefano Torresi - Matthieu Napoli - Korvin Szanto - Glenn Eggleton - Oscar Otero - Tobias Nyholm The working group would also like to acknowledge the contributions of: - Paul M. Jones, - Rasmus Schultz, - Roman Tsjupa, ## 7. Votes - [Entrance Vote](https://groups.google.com/forum/#!topic/php-fig/6rZPZ8VglIM) - [Working Group Formation](https://groups.google.com/d/msg/php-fig/A5mZYTn5Jm8/j0FN6eZtBAAJ) - [Review Period Initiation](https://groups.google.com/d/msg/php-fig/OpUnkrnFhe0/y2dT7CakAQAJ) - [Acceptance Vote](https://groups.google.com/d/msg/php-fig/M8PapGXXE1E/uBq2Dq-ZAwAJ) ## 8. Relevant Links _**Note:** Order descending chronologically._ - [PSR-7 Middleware Proposal](https://github.com/php-fig/fig-standards/pull/755) - [PHP-FIG mailing list discussion of middleware](https://groups.google.com/forum/#!topic/php-fig/vTtGxdIuBX8) - [ircmaxwell All About Middleware](http://blog.ircmaxell.com/2016/05/all-about-middleware.html) - [shadowhand All About PSR-7 Middleware](http://shadowhand.me/all-about-psr-7-middleware/) - [AndrewCarterUK PSR-7 Objects Are Not Immutable](http://andrewcarteruk.github.io/programming/2016/05/22/psr-7-is-not-immutable.html) - [shadowhand Dependency Inversion and PSR-7 Bodies](http://shadowhand.me/dependency-inversion-and-psr-7-bodies/) - [PHP-FIG mailing list thread discussing factories](https://groups.google.com/d/msg/php-fig/G5pgQfQ9fpA/UWeM1gm1CwAJ) - [PHP-FIG mailing list thread feedback on proposal](https://groups.google.com/d/msg/php-fig/piRtB2Z-AZs/8UIwY1RtDgAJ) ## 9. Errata ### 9.1 Use of explicit nullable type Prior to PHP 8.4, it was allowed to declare a type accepting `null` by omitting the nullable part of the type if the default value of the property or parameter was set to null. This implicit type declaration is now deprecated and all types should be declared explicitly. This change also requires the minimum PHP version required by this PSR to be updated to 7.1, as nullable types were introduced in this version. Apart from this change, no breaking change is introduced by this update and the behavior of the interfaces remains the same. ================================================ FILE: accepted/PSR-17-http-factory.md ================================================ HTTP Factories ============== This document describes a common standard for factories that create [PSR-7][psr7] compliant HTTP objects. PSR-7 did not include a recommendation on how to create HTTP objects, which leads to difficulties when needing to create new HTTP objects within components that are not tied to a specific implementation of PSR-7. The interfaces outlined in this document describe methods by which PSR-7 objects can be instantiated. The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119][rfc2119]. [psr7]: https://www.php-fig.org/psr/psr-7/ [rfc2119]: https://tools.ietf.org/html/rfc2119 ## 1. Specification An HTTP factory is a method by which a new HTTP object, as defined by PSR-7, is created. HTTP factories MUST implement these interfaces for each object type that is provided by the package. ## 2. Interfaces The following interfaces MAY be implemented together within a single class or in separate classes. ### 2.1 RequestFactoryInterface Has the ability to create client requests. ```php namespace Psr\Http\Message; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\UriInterface; interface RequestFactoryInterface { /** * Create a new request. * * @param string $method The HTTP method associated with the request. * @param UriInterface|string $uri The URI associated with the request. */ public function createRequest(string $method, $uri): RequestInterface; } ``` ### 2.2 ResponseFactoryInterface Has the ability to create responses. ```php namespace Psr\Http\Message; use Psr\Http\Message\ResponseInterface; interface ResponseFactoryInterface { /** * Create a new response. * * @param int $code The HTTP status code. Defaults to 200. * @param string $reasonPhrase The reason phrase to associate with the status code * in the generated response. If none is provided, implementations MAY use * the defaults as suggested in the HTTP specification. */ public function createResponse(int $code = 200, string $reasonPhrase = ''): ResponseInterface; } ``` ### 2.3 ServerRequestFactoryInterface Has the ability to create server requests. ```php namespace Psr\Http\Message; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\UriInterface; interface ServerRequestFactoryInterface { /** * Create a new server request. * * Note that server parameters are taken precisely as given - no parsing/processing * of the given values is performed. In particular, no attempt is made to * determine the HTTP method or URI, which must be provided explicitly. * * @param string $method The HTTP method associated with the request. * @param UriInterface|string $uri The URI associated with the request. * @param array $serverParams An array of Server API (SAPI) parameters with * which to seed the generated request instance. */ public function createServerRequest(string $method, $uri, array $serverParams = []): ServerRequestInterface; } ``` ### 2.4 StreamFactoryInterface Has the ability to create streams for requests and responses. ```php namespace Psr\Http\Message; use Psr\Http\Message\StreamInterface; interface StreamFactoryInterface { /** * Create a new stream from a string. * * The stream SHOULD be created with a temporary resource. * * @param string $content String content with which to populate the stream. */ public function createStream(string $content = ''): StreamInterface; /** * Create a stream from an existing file. * * The file MUST be opened using the given mode, which may be any mode * supported by the `fopen` function. * * The `$filename` MAY be any string supported by `fopen()`. * * @param string $filename The filename or stream URI to use as basis of stream. * @param string $mode The mode with which to open the underlying filename/stream. * * @throws \RuntimeException If the file cannot be opened. * @throws \InvalidArgumentException If the mode is invalid. */ public function createStreamFromFile(string $filename, string $mode = 'r'): StreamInterface; /** * Create a new stream from an existing resource. * * The stream MUST be readable and may be writable. * * @param resource $resource The PHP resource to use as the basis for the stream. */ public function createStreamFromResource($resource): StreamInterface; } ``` Implementations of this interface SHOULD use a temporary stream when creating resources from strings. The RECOMMENDED method for doing so is: ```php $resource = fopen('php://temp', 'r+'); ``` ### 2.5 UploadedFileFactoryInterface Has the ability to create streams for uploaded files. ```php namespace Psr\Http\Message; use Psr\Http\Message\StreamInterface; use Psr\Http\Message\UploadedFileInterface; interface UploadedFileFactoryInterface { /** * Create a new uploaded file. * * If a size is not provided it will be determined by checking the size of * the stream. * * @link http://php.net/manual/features.file-upload.post-method.php * @link http://php.net/manual/features.file-upload.errors.php * * @param StreamInterface $stream The underlying stream representing the * uploaded file content. * @param int $size The size of the file in bytes. * @param int $error The PHP file upload error. * @param string $clientFilename The filename as provided by the client, if any. * @param string $clientMediaType The media type as provided by the client, if any. * * @throws \InvalidArgumentException If the file resource is not readable. */ public function createUploadedFile( StreamInterface $stream, ?int $size = null, int $error = \UPLOAD_ERR_OK, ?string $clientFilename = null, ?string $clientMediaType = null ): UploadedFileInterface; } ``` Since [psr/http-factory version 1.1](https://packagist.org/packages/psr/http-factory#1.1.0), the above interface has been updated to add explicit nullable types. ### 2.6 UriFactoryInterface Has the ability to create URIs for client and server requests. ```php namespace Psr\Http\Message; use Psr\Http\Message\UriInterface; interface UriFactoryInterface { /** * Create a new URI. * * @param string $uri The URI to parse. * * @throws \InvalidArgumentException If the given URI cannot be parsed. */ public function createUri(string $uri = '') : UriInterface; } ``` ================================================ FILE: accepted/PSR-18-http-client-meta.md ================================================ HTTP Client Meta Document ========================= ## Summary HTTP requests and responses are the two fundamental objects in web programming. All clients communicating to an external API use some form of HTTP client. Many libraries are coupled to one specific client or implement a client and/or adapter layer themselves. This leads to bad library design, version conflicts, or code unrelated to the library domain. ## Why bother? Thanks to PSR-7 we know how HTTP requests and responses ideally look, but nothing defines how a request should be sent and a response received. A common interface for HTTP clients will allow libraries to be decoupled from specific implementations. ## Scope ### Goals * A common interface for sending PSR-7 messages and returning PSR-7 responses. ### Non-Goals * Support for asynchronous HTTP requests is left for another future PSR. * This PSR does not define how to configure an HTTP client. It only specifies the default behaviours. * This PSR is neutral about the use of middleware. #### Asynchronous HTTP client The reason asynchronous requests are not covered by this PSR is the lack of a common standard for Promises. Promises are sufficiently complex enough that they deserve their own specification, and should not be wrapped into this one. A separate interface for asynchronous requests can be defined in a separate PSR once a Promise PSR is accepted. The method signature for asynchronous requests MUST be different from the method signature for synchronous requests because the return type of asynchronous calls will be a Promise. Thus this PSR is forwards compatible, and clients will be able to implement one or both interfaces based on the features they wish to expose. ## Approaches ### Default behavior The intention of this PSR is to provide library developers with HTTP clients that have a well defined behaviour. A library should be able to use any compliant client without special code to handle client implementation details (Liskov substitution principle). The PSR does not try to restrict nor define how to configure HTTP clients. An alternative approach would be to pass configuration to the client. That approach would have a few drawbacks: * Configuration must be defined by the PSR. * All clients must support the defined configuration. * If no configuration is passed to the client, the behavior is unpredictable. #### Naming rationale The main interface behaviour is defined by the method `sendRequest(RequestInterface $request): ResponseInterface`. While the shorter method name `send()` has been proposed, this was already used by existing and very common HTTP clients like Guzzle. As such, if they are to adopt this standard, they may need to break backwards compatibility in order to implement the specification. By defining `sendRequest()` instead, we ensure they can adopt without any immediate BC breaks. ### Exception Model The domain exceptions `NetworkExceptionInterface` and `RequestExceptionInterface` define a contract very similar to each other. The chosen approach is to not let them extend each other because inheritance does not make sense in the domain model. A `RequestExceptionInterface` is simply not a `NetworkExceptionInterface`. Allowing exceptions to extend a `RequestAwareException` and/or `ResponseAwareException` interface has been discussed but that is a convenience shortcut that one should not take. One should rather catch the specific exceptions and handle them accordingly. One could be more granular when defining exceptions. For example, `TimeOutException` and `HostNotFoundException` could be subtypes of `NetworkExceptionInterface`. The chosen approach is not to define such subtypes because the exception handling in a consuming library would in most cases not be different between those exceptions. #### Throwing exceptions for 4xx and 5xx responses The initial idea was to allow the client to be configured to throw exceptions for responses with HTTP status 4xx and 5xx. That approach is not desired because consuming libraries would have to check for 4xx and 5xx responses twice: first, by verifying the status code on the response, and second by catching potential exceptions. To make the specification more predictable, it was decided that HTTP clients never will throw exceptions for 4xx and 5xx responses. ## Middleware and wrapping a client The specification does not put any limitations on middleware or classes that want to wrap/decorate an HTTP client. If the decorating class also implements `ClientInterface` then it must also follow the specification. It is temping to allow configuration or add middleware to an HTTP client so it could i.e. follow redirects or throw exceptions. If that is a decision from an application developer, they have specifically said they want to break the specification. That is an issue (or feature) the application developer should handle. Third party libraries MUST NOT assume that a HTTP client breaks the specification. ## Background The HTTP client PSR has been inspired and created by the [php-http team](https://github.com/orgs/php-http/people). Back in 2015, they created HTTPlug as a common interface for HTTP clients. They wanted an abstraction that third party libraries could use so as not to rely on a specific HTTP client implementation. A stable version was tagged in January 2016, and the project has been widely adopted since then. With over 3 million downloads in the two years following the initial stable version, it was time to convert this "de-facto" standard into a formal specification. ## People ### 5.1 Editor * Tobias Nyholm ### 5.2 Sponsors * Sara Golemon ### 5.3 Workgroup * Simon Asika (Windwalker) * David Buchmann (HTTPlug) * David De Boer (HTTPlug) * Sara Golemon (Sponsor) * Jeremy Lindblom (Guzzle) * Christian Lück (Buzz react) * Tobias Nyholm (Editor) * Matthew Weier O'Phinney (Zend) * Mark Sagi-Kazar (Guzzle) * Joel Wurtz (HTTPlug) ## Votes * [Entrance vote](https://groups.google.com/d/topic/php-fig/MJGYRXfUJGk/discussion) * [Review Period Initiation](https://groups.google.com/d/topic/php-fig/dV9zIaOooZ4/discussion) * [Acceptance](https://groups.google.com/d/topic/php-fig/rScdiW38nLM/discussion) ## Proposed implementations Below are the two implementations provided by the working group to pass the review period: * HTTPlug has prepared a 2.0 to make sure it is supporting the new PSR. They are just waiting for the PSR to be released: https://github.com/php-http/httplug/tree/2.x * Buzz has been adapting to every version of the PSR and has their 0.17.3 release with the latest version of psr/http-client: https://github.com/kriswallsmith/Buzz ================================================ FILE: accepted/PSR-18-http-client.md ================================================ HTTP Client =========== This document describes a common interface for sending HTTP requests and receiving HTTP responses. The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119](http://tools.ietf.org/html/rfc2119). ## Goal The goal of this PSR is to allow developers to create libraries decoupled from HTTP client implementations. This will make libraries more reusable as it reduces the number of dependencies and lowers the likelihood of version conflicts. A second goal is that HTTP clients can be replaced as per the [Liskov substitution principle][Liskov]. This means that all clients MUST behave in the same way when sending a request. ## Definitions * **Client** - A Client is a library that implements this specification for the purposes of sending PSR-7-compatible HTTP Request messages and returning a PSR-7-compatible HTTP Response message to a Calling library. * **Calling Library** - A Calling Library is any code that makes use of a Client. It does not implement this specification's interfaces but consumes an object that implements them (a Client). ## Client A Client is an object implementing `ClientInterface`. A Client MAY: * Choose to send an altered HTTP request from the one it was provided. For example, it could compress an outgoing message body. * Choose to alter a received HTTP response before returning it to the calling library. For example, it could decompress an incoming message body. If a Client chooses to alter either the HTTP request or HTTP response, it MUST ensure that the object remains internally consistent. For example, if a Client chooses to decompress the message body then it MUST also remove the `Content-Encoding` header and adjust the `Content-Length` header. Note that as a result, since [PSR-7 objects are immutable](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-7-http-message-meta.md#why-value-objects), the Calling Library MUST NOT assume that the object passed to `ClientInterface::sendRequest()` will be the same PHP object that is actually sent. For example, the Request object that is returned by an exception MAY be a different object than the one passed to `sendRequest()`, so comparison by reference (===) is not possible. A Client MUST: * Reassemble a multi-step HTTP 1xx response itself so that what is returned to the Calling Library is a valid HTTP response of status code 200 or higher. ## Error handling A Client MUST NOT treat a well-formed HTTP request or HTTP response as an error condition. For example, response status codes in the 400 and 500 range MUST NOT cause an exception and MUST be returned to the Calling Library as normal. A Client MUST throw an instance of `Psr\Http\Client\ClientExceptionInterface` if and only if it is unable to send the HTTP request at all or if the HTTP response could not be parsed into a PSR-7 response object. If a request cannot be sent because the request message is not a well-formed HTTP request or is missing some critical piece of information (such as a Host or Method), the Client MUST throw an instance of `Psr\Http\Client\RequestExceptionInterface`. If the request cannot be sent due to a network failure of any kind, including a timeout, the Client MUST throw an instance of `Psr\Http\Client\NetworkExceptionInterface`. Clients MAY throw more specific exceptions than those defined here (a `TimeOutException` or `HostNotFoundException` for example), provided they implement the appropriate interface defined above. ## Interfaces ### ClientInterface ```php namespace Psr\Http\Client; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; interface ClientInterface { /** * Sends a PSR-7 request and returns a PSR-7 response. * * @param RequestInterface $request * * @return ResponseInterface * * @throws \Psr\Http\Client\ClientExceptionInterface If an error happens while processing the request. */ public function sendRequest(RequestInterface $request): ResponseInterface; } ``` ### ClientExceptionInterface ```php namespace Psr\Http\Client; /** * Every HTTP client related exception MUST implement this interface. */ interface ClientExceptionInterface extends \Throwable { } ``` ### RequestExceptionInterface ```php namespace Psr\Http\Client; use Psr\Http\Message\RequestInterface; /** * Exception for when a request failed. * * Examples: * - Request is invalid (e.g. method is missing) * - Runtime request errors (e.g. the body stream is not seekable) */ interface RequestExceptionInterface extends ClientExceptionInterface { /** * Returns the request. * * The request object MAY be a different object from the one passed to ClientInterface::sendRequest() * * @return RequestInterface */ public function getRequest(): RequestInterface; } ``` ### NetworkExceptionInterface ```php namespace Psr\Http\Client; use Psr\Http\Message\RequestInterface; /** * Thrown when the request cannot be completed because of network issues. * * There is no response object as this exception is thrown when no response has been received. * * Example: the target host name can not be resolved or the connection failed. */ interface NetworkExceptionInterface extends ClientExceptionInterface { /** * Returns the request. * * The request object MAY be a different object from the one passed to ClientInterface::sendRequest() * * @return RequestInterface */ public function getRequest(): RequestInterface; } ``` [Liskov]: https://en.wikipedia.org/wiki/Liskov_substitution_principle ================================================ FILE: accepted/PSR-2-coding-style-guide-meta.md ================================================ # PSR-2 Meta Document ## 1. Summary The intent of this guide is to reduce cognitive friction when scanning code from different authors. It does so by enumerating a shared set of rules and expectations about how to format PHP code. The style rules herein are derived from commonalities among the various member projects. When various authors collaborate across multiple projects, it helps to have one set of guidelines to be used among all those projects. Thus, the benefit of this guide is not in the rules themselves, but in the sharing of those rules. ## 2. Votes - **Acceptance Vote:** [ML](https://groups.google.com/d/msg/php-fig/c-QVvnZdMQ0/TdDMdzKFpdIJ) ## 3. Errata ### 3.1 - Multi-line Arguments (09/08/2013) Using one or more multi-line arguments (i.e: arrays or anonymous functions) does not constitute splitting the argument list itself, therefore Section 4.6 is not automatically enforced. Arrays and anonymous functions are able to span multiple lines. The following examples are perfectly valid in PSR-2: ```php get('/hello/{name}', function ($name) use ($app) { return 'Hello '.$app->escape($name); }); ``` ### 3.2 - Extending Multiple Interfaces (10/17/2013) When extending multiple interfaces, the list of `extends` should be treated the same as a list of `implements`, as declared in Section 4.1. ## 4. People ### 4.1 Editor * Paul M. Jones ================================================ FILE: accepted/PSR-2-coding-style-guide.md ================================================ # Coding Style Guide > **Deprecated** - As of 2019-08-10 PSR-2 has been marked as deprecated. [PSR-12] is now recommended as an alternative. [PSR-12]: https://www.php-fig.org/psr/psr-12/ This guide extends and expands on [PSR-1], the basic coding standard. The intent of this guide is to reduce cognitive friction when scanning code from different authors. It does so by enumerating a shared set of rules and expectations about how to format PHP code. The style rules herein are derived from commonalities among the various member projects. When various authors collaborate across multiple projects, it helps to have one set of guidelines to be used among all those projects. Thus, the benefit of this guide is not in the rules themselves, but in the sharing of those rules. The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119]. [RFC 2119]: http://www.ietf.org/rfc/rfc2119.txt [PSR-0]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md [PSR-1]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-1-basic-coding-standard.md ## 1. Overview - Code MUST follow a "coding style guide" PSR [[PSR-1]]. - Code MUST use 4 spaces for indenting, not tabs. - There MUST NOT be a hard limit on line length; the soft limit MUST be 120 characters; lines SHOULD be 80 characters or less. - There MUST be one blank line after the `namespace` declaration, and there MUST be one blank line after the block of `use` declarations. - Opening braces for classes MUST go on the next line, and closing braces MUST go on the next line after the body. - Opening braces for methods MUST go on the next line, and closing braces MUST go on the next line after the body. - Visibility MUST be declared on all properties and methods; `abstract` and `final` MUST be declared before the visibility; `static` MUST be declared after the visibility. - Control structure keywords MUST have one space after them; method and function calls MUST NOT. - Opening braces for control structures MUST go on the same line, and closing braces MUST go on the next line after the body. - Opening parentheses for control structures MUST NOT have a space after them, and closing parentheses for control structures MUST NOT have a space before. ### 1.1. Example This example encompasses some of the rules below as a quick overview: ```php $b) { $foo->bar($arg1); } else { BazClass::bar($arg2, $arg3); } } final public static function bar() { // method body } } ``` ## 2. General ### 2.1. Basic Coding Standard Code MUST follow all rules outlined in [PSR-1]. ### 2.2. Files All PHP files MUST use the Unix LF (linefeed) line ending. All PHP files MUST end with a single blank line. The closing `?>` tag MUST be omitted from files containing only PHP. ### 2.3. Lines There MUST NOT be a hard limit on line length. The soft limit on line length MUST be 120 characters; automated style checkers MUST warn but MUST NOT error at the soft limit. Lines SHOULD NOT be longer than 80 characters; lines longer than that SHOULD be split into multiple subsequent lines of no more than 80 characters each. There MUST NOT be trailing whitespace at the end of non-blank lines. Blank lines MAY be added to improve readability and to indicate related blocks of code. There MUST NOT be more than one statement per line. ### 2.4. Indenting Code MUST use an indent of 4 spaces, and MUST NOT use tabs for indenting. > N.b.: Using only spaces, and not mixing spaces with tabs, helps to avoid > problems with diffs, patches, history, and annotations. The use of spaces > also makes it easy to insert fine-grained sub-indentation for inter-line > alignment. ### 2.5. Keywords and True/False/Null PHP [keywords] MUST be in lower case. The PHP constants `true`, `false`, and `null` MUST be in lower case. [keywords]: http://php.net/manual/en/reserved.keywords.php ## 3. Namespace and Use Declarations When present, there MUST be one blank line after the `namespace` declaration. When present, all `use` declarations MUST go after the `namespace` declaration. There MUST be one `use` keyword per declaration. There MUST be one blank line after the `use` block. For example: ```php bar($arg1); Foo::bar($arg2, $arg3); ``` Argument lists MAY be split across multiple lines, where each subsequent line is indented once. When doing so, the first item in the list MUST be on the next line, and there MUST be only one argument per line. ```php bar( $longArgument, $longerArgument, $muchLongerArgument ); ``` ## 5. Control Structures The general style rules for control structures are as follows: - There MUST be one space after the control structure keyword - There MUST NOT be a space after the opening parenthesis - There MUST NOT be a space before the closing parenthesis - There MUST be one space between the closing parenthesis and the opening brace - The structure body MUST be indented once - The closing brace MUST be on the next line after the body The body of each structure MUST be enclosed by braces. This standardizes how the structures look, and reduces the likelihood of introducing errors as new lines get added to the body. ### 5.1. `if`, `elseif`, `else` An `if` structure looks like the following. Note the placement of parentheses, spaces, and braces; and that `else` and `elseif` are on the same line as the closing brace from the earlier body. ```php $value) { // foreach body } ``` ### 5.6. `try`, `catch` A `try catch` block looks like the following. Note the placement of parentheses, spaces, and braces. ```php bar( $arg1, function ($arg2) use ($var1) { // body }, $arg3 ); ``` ## 7. Conclusion There are many elements of style and practice intentionally omitted by this guide. These include but are not limited to: - Declaration of global variables and global constants - Declaration of functions - Operators and assignment - Inter-line alignment - Comments and documentation blocks - Class name prefixes and suffixes - Best practices Future recommendations MAY revise and extend this guide to address those or other elements of style and practice. ## Appendix A. Survey In writing this style guide, the group took a survey of member projects to determine common practices. The survey is retained herein for posterity. ### A.1. Survey Data url,http://www.horde.org/apps/horde/docs/CODING_STANDARDS,http://pear.php.net/manual/en/standards.php,http://solarphp.com/manual/appendix-standards.style,http://framework.zend.com/manual/en/coding-standard.html,https://symfony.com/doc/2.0/contributing/code/standards.html,http://www.ppi.io/docs/coding-standards.html,https://github.com/ezsystems/ezp-next/wiki/codingstandards,http://book.cakephp.org/2.0/en/contributing/cakephp-coding-conventions.html,https://github.com/UnionOfRAD/lithium/wiki/Spec%3A-Coding,http://drupal.org/coding-standards,http://code.google.com/p/sabredav/,http://area51.phpbb.com/docs/31x/coding-guidelines.html,https://docs.google.com/a/zikula.org/document/edit?authkey=CPCU0Us&hgd=1&id=1fcqb93Sn-hR9c0mkN6m_tyWnmEvoswKBtSc0tKkZmJA,http://www.chisimba.com,n/a,https://github.com/Respect/project-info/blob/master/coding-standards-sample.php,n/a,Object Calisthenics for PHP,http://doc.nette.org/en/coding-standard,http://flow3.typo3.org,https://github.com/propelorm/Propel2/wiki/Coding-Standards,http://developer.joomla.org/coding-standards.html voting,yes,yes,yes,yes,yes,yes,yes,yes,yes,yes,yes,yes,yes,yes,yes,no,no,no,?,yes,no,yes indent_type,4,4,4,4,4,tab,4,tab,tab,2,4,tab,4,4,4,4,4,4,tab,tab,4,tab line_length_limit_soft,75,75,75,75,no,85,120,120,80,80,80,no,100,80,80,?,?,120,80,120,no,150 line_length_limit_hard,85,85,85,85,no,no,no,no,100,?,no,no,no,100,100,?,120,120,no,no,no,no class_names,studly,studly,studly,studly,studly,studly,studly,studly,studly,studly,studly,lower_under,studly,lower,studly,studly,studly,studly,?,studly,studly,studly class_brace_line,next,next,next,next,next,same,next,same,same,same,same,next,next,next,next,next,next,next,next,same,next,next constant_names,upper,upper,upper,upper,upper,upper,upper,upper,upper,upper,upper,upper,upper,upper,upper,upper,upper,upper,upper,upper,upper,upper true_false_null,lower,lower,lower,lower,lower,lower,lower,lower,lower,upper,lower,lower,lower,upper,lower,lower,lower,lower,lower,upper,lower,lower method_names,camel,camel,camel,camel,camel,camel,camel,camel,camel,camel,camel,lower_under,camel,camel,camel,camel,camel,camel,camel,camel,camel,camel method_brace_line,next,next,next,next,next,same,next,same,same,same,same,next,next,same,next,next,next,next,next,same,next,next control_brace_line,same,same,same,same,same,same,next,same,same,same,same,next,same,same,next,same,same,same,same,same,same,next control_space_after,yes,yes,yes,yes,yes,no,yes,yes,yes,yes,no,yes,yes,yes,yes,yes,yes,yes,yes,yes,yes,yes always_use_control_braces,yes,yes,yes,yes,yes,yes,no,yes,yes,yes,no,yes,yes,yes,yes,no,yes,yes,yes,yes,yes,yes else_elseif_line,same,same,same,same,same,same,next,same,same,next,same,next,same,next,next,same,same,same,same,same,same,next case_break_indent_from_switch,0/1,0/1,0/1,1/2,1/2,1/2,1/2,1/1,1/1,1/2,1/2,1/1,1/2,1/2,1/2,1/2,1/2,1/2,0/1,1/1,1/2,1/2 function_space_after,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no closing_php_tag_required,no,no,no,no,no,no,no,no,yes,no,no,no,no,yes,no,no,no,no,no,yes,no,no line_endings,LF,LF,LF,LF,LF,LF,LF,LF,?,LF,?,LF,LF,LF,LF,?,,LF,?,LF,LF,LF static_or_visibility_first,static,?,static,either,either,either,visibility,visibility,visibility,either,static,either,?,visibility,?,?,either,either,visibility,visibility,static,? control_space_parens,no,no,no,no,no,no,yes,no,no,no,no,no,no,yes,?,no,no,no,no,no,no,no blank_line_after_php,no,no,no,no,yes,no,no,no,no,yes,yes,no,no,yes,?,yes,yes,no,yes,no,yes,no class_method_control_brace,next/next/same,next/next/same,next/next/same,next/next/same,next/next/same,same/same/same,next/next/next,same/same/same,same/same/same,same/same/same,same/same/same,next/next/next,next/next/same,next/same/same,next/next/next,next/next/same,next/next/same,next/next/same,next/next/same,same/same/same,next/next/same,next/next/next ### A.2. Survey Legend `indent_type`: The type of indenting. `tab` = "Use a tab", `2` or `4` = "number of spaces" `line_length_limit_soft`: The "soft" line length limit, in characters. `?` = not discernible or no response, `no` means no limit. `line_length_limit_hard`: The "hard" line length limit, in characters. `?` = not discernible or no response, `no` means no limit. `class_names`: How classes are named. `lower` = lowercase only, `lower_under` = lowercase with underscore separators, `studly` = StudlyCase. `class_brace_line`: Does the opening brace for a class go on the `same` line as the class keyword, or on the `next` line after it? `constant_names`: How are class constants named? `upper` = Uppercase with underscore separators. `true_false_null`: Are the `true`, `false`, and `null` keywords spelled as all `lower` case, or all `upper` case? `method_names`: How are methods named? `camel` = `camelCase`, `lower_under` = lowercase with underscore separators. `method_brace_line`: Does the opening brace for a method go on the `same` line as the method name, or on the `next` line? `control_brace_line`: Does the opening brace for a control structure go on the `same` line, or on the `next` line? `control_space_after`: Is there a space after the control structure keyword? `always_use_control_braces`: Do control structures always use braces? `else_elseif_line`: When using `else` or `elseif`, does it go on the `same` line as the previous closing brace, or does it go on the `next` line? `case_break_indent_from_switch`: How many times are `case` and `break` indented from an opening `switch` statement? `function_space_after`: Do function calls have a space after the function name and before the opening parenthesis? `closing_php_tag_required`: In files containing only PHP, is the closing `?>` tag required? `line_endings`: What type of line ending is used? `static_or_visibility_first`: When declaring a method, does `static` come first, or does the visibility come first? `control_space_parens`: In a control structure expression, is there a space after the opening parenthesis and a space before the closing parenthesis? `yes` = `if ( $expr )`, `no` = `if ($expr)`. `blank_line_after_php`: Is there a blank line after the opening PHP tag? `class_method_control_brace`: A summary of what line the opening braces go on for classes, methods, and control structures. ### A.3. Survey Results indent_type: tab: 7 2: 1 4: 14 line_length_limit_soft: ?: 2 no: 3 75: 4 80: 6 85: 1 100: 1 120: 4 150: 1 line_length_limit_hard: ?: 2 no: 11 85: 4 100: 3 120: 2 class_names: ?: 1 lower: 1 lower_under: 1 studly: 19 class_brace_line: next: 16 same: 6 constant_names: upper: 22 true_false_null: lower: 19 upper: 3 method_names: camel: 21 lower_under: 1 method_brace_line: next: 15 same: 7 control_brace_line: next: 4 same: 18 control_space_after: no: 2 yes: 20 always_use_control_braces: no: 3 yes: 19 else_elseif_line: next: 6 same: 16 case_break_indent_from_switch: 0/1: 4 1/1: 4 1/2: 14 function_space_after: no: 22 closing_php_tag_required: no: 19 yes: 3 line_endings: ?: 5 LF: 17 static_or_visibility_first: ?: 5 either: 7 static: 4 visibility: 6 control_space_parens: ?: 1 no: 19 yes: 2 blank_line_after_php: ?: 1 no: 13 yes: 8 class_method_control_brace: next/next/next: 4 next/next/same: 11 next/same/same: 1 same/same/same: 6 ================================================ FILE: accepted/PSR-20-clock-meta.md ================================================ # Clock Meta Document ## 1. Summary Getting the current time in applications is typically achieved using the `\time()` or `\microtime` functions, or by using a `new \DateTimeImmutable()` class. Due to the nature of time progression, these methods cannot be used when predictable results are needed, such as during testing. This `ClockInterface` aims to provide a standard way to consume time that allows interoperability, not only when consuming the "real" time, but also when predictable results need to be available. This avoids the need to use PHP extensions for testing or redeclaring the `\time()` function in a local namespace. ## 2. Why Bother? There are currently a few libraries that provide this functionality. However, there is no interoperability between these different libraries, as they ship with their own clock interfaces. Symfony provides a package called `symfony/phpunit-bridge` that has a `\Symfony\Bridge\PhpUnit\ClockMock` class, which allows mocking PHP's built-in time and date functions. However, this does not solve mocking calls to `new \DateTimeImmutable()`. It also does not fully mock time when called from other libraries that rely on the system time. `\Carbon\Carbon`, and its fork `\Cake\Chronos\Chronos`, do provide mocking via a static `setTestNow()` method, but this provides no isolation and must be called again to stop mocking. Pros: * Consistent interface to get the current time; * Easy to mock the wall clock time for repeatability. Cons: * Extra overhead and developer effort to get the current time; * Not as simple as calling `\time()` or `\date()`. ## 3. Scope ### 3.1 Goals * Provide a simple and mockable way to read the current time; * Allow interoperability between libraries when reading the clock. ### 3.2 Non-Goals * This PSR does not provide a recommendation on how and when to use the concepts described in this document, so it is not a coding standard; * This PSR does not provide a recommendation on how to handle timezones when retrieving the current time. This is left up to the implementation. * This PSR does not handle any scheduling methods like `sleep()` or `wait()` because such methods are not related to retrieving the current time. ## 4. Approaches ### 4.1 Chosen Approach We have decided to formalize the existing practices used by several other packages. Some popular packages providing this functionality are: * [`lcobucci/clock`](https://packagist.org/packages/lcobucci/clock) * [`kreait/clock`](https://packagist.org/packages/kreait/clock) * [`ergebnis/clock`](https://packagist.org/packages/ergebnis/clock) * [`mangoweb/clock`](https://packagist.org/packages/mangoweb/clock) (This list is not exhaustive!) Some of these provide interfaces and some rely on extending a clock class to mock the current time. These implementations all provide a `now()` method which returns a `\DateTimeImmutable` object. As the `\DateTimeImmutable` object allows retrieving the Unix timestamp, by calling `getTimestamp()` or `format('u.U')`, this interface does not define any special methods to retrieve a Unix timestamp or any other time information that is not available from a `\DateTimeImmutable` object. ### 4.2 Timezones Time by now is defined by interaction of electromagnetic radiation with the excited states of certain atoms, where the SI defines one second as the duration of 9192631770 cycles of radiation corresponding to the transition between two energy levels of the ground state of the caesium-133 atom at 0K. This means that retrieving the current time will always return the same time, no matter where it is observed. While the timezone defines *where* the time was observed, it does not modify the actual "slice" of time. This means that, for the sake of this PSR, the timezone is considered an implementation detail of the interface. It is up to the implementation to make sure that the timezone is handled according to the business logic of the application. That is either by making sure that a call to `now()` will only return a `\DateTimeImmutable` object with a known timezone (implicit contract) or by explicitly changing the timezone to be correct for the application. This can be done by calling `setTimezone()` to create a new `\DateTimeImmutable` object with the given timezone. These actions are not defined in this interface. ### 4.2 Example Implementations ```php final class SystemClock implements \Psr\Clock\ClockInterface { public function now(): \DateTimeImmutable { return new \DateTimeImmutable(); } } final class FrozenClock implements \Psr\Clock\ClockInterface { private \DateTimeImmutable $now; public function __construct(\DateTimeImmutable $now) { $this->now = $now; } public function now(): \DateTimeImmutable { return clone $this->now; } } ``` ## 5. People ### 5.1 Editor * Chris Seufert ### 5.2 Sponsor * Chuck Burgess ### 5.3 Working group members * Luís Cobucci * Pol Dellaiera * Ben Edmunds * Jérôme Gamez * Andreas Heigl * Andreas Möller ## 6. Votes * [Entrance Vote](https://groups.google.com/g/php-fig/c/hIKqd0an-GI) * [Acceptance Vote](https://groups.google.com/g/php-fig/c/4esd62o0QoU) ## 7. Relevant Links * https://github.com/ergebnis/clock/blob/main/src/Clock.php * https://github.com/icecave/chrono/blob/master/src/Clock/ClockInterface.php * https://github.com/Kdyby/DateTimeProvider/blob/master/src/DateTimeProviderInterface.php * https://github.com/kreait/clock-php/blob/main/src/Clock.php * https://github.com/lcobucci/clock/blob/2.1.x/src/Clock.php * https://github.com/mangoweb-backend/clock/blob/master/src/Clock.php * https://martinfowler.com/bliki/ClockWrapper.html ## 8. Past contributors This document stems from the work of many people in previous years, we recognize their effort: * _**Note:** Order descending chronologically._ ## 9. FAQ ### Why not simply use UTC? There are different reasons why this interface does not enforce a specific timezone. A purely _technical_ reason is that the interface itself provides an explicit contract. Part of this contract is the value returned by the `now()` method. At the _language_ level, the only thing we can enforce is that the returned value is of type `\DateTimeImmutable`. There is no way to enforce a certain timezone at the language level. A _logical_ reason is that the explicit contract should be usable in all situations where one needs a way to retrieve the current time. We should not make an assumption at the _contract_ level about what the caller needs. If the contract did define that only `UTC` is returned, then use cases that require something else would have to explicitly work around the returned `UTC` timezone. This is different from an issue like immutability, which also cannot be enforced on the language level, but which is still necessary to adhere to other calls on the contract. For this `ClockInterface`, there will be no other calls. Most importantly, the explicit contract provided by this interface does not _prevent_ a user from using an implicit contract alongside logic to return a `\DateTimeImmutable` with a specific timezone. Whether that is `UTC` or `Antarctica/Troll`, it is the _user_ who is in control of this. The explicit contract defined by the interface does not limit a user in what they are doing. It tries to solve the problem of getting the current time in a reliable way. Whatever view on the current time that is, it is not part of the explicit contract. Thus, this interface tries to be as _open as possible_, while at the same time, being as _strict as necessary_. ================================================ FILE: accepted/PSR-20-clock.md ================================================ Common Interface for Accessing the Clock ======================================== This document describes a simple interface for reading the system clock. The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119][]. The final implementations MAY decorate the objects with more functionality than the one proposed, but they MUST implement the indicated interfaces/functionality first. [RFC 2119]: http://tools.ietf.org/html/rfc2119 # 1. Specification ## 1.1 Introduction Creating a standard way of accessing the clock would allow interoperability during testing, when testing behavior that has timing-based side effects. Common ways to get the current time include calling `\time()` or `new \DateTimeImmutable('now')`. However, this makes mocking the current time impossible in some situations. ## 1.2 Definitions * **Clock** - The clock is able to read the current time and date. * **Timestamp** - The current time as an integer number of seconds since Jan 1, 1970 00:00:00 UTC. ## 1.3 Usage **Get the current timestamp** This should be done by using the `getTimestamp()` method on the returned `\DateTimeImmutable`: ```php $timestamp = $clock->now()->getTimestamp(); ``` # 2. Interfaces ## 2.1 ClockInterface The clock interface defines the most basic operations to read the current time and date from the clock. It MUST return the time as a `\DateTimeImmutable`. ```php $val) { // check that the value can be cast to string if (!is_array($val) && (!is_object($val) || method_exists($val, '__toString'))) { $replace['{' . $key . '}'] = $val; } } // interpolate replacement values into the message and return return strtr($message, $replace); } // a message with brace-delimited placeholder names $message = "User {username} created"; // a context array of placeholder names => replacement values $context = array('username' => 'bolivar'); // echoes "User bolivar created" echo interpolate($message, $context); ``` ### 1.3 Context - Every method accepts an array as context data. This is meant to hold any extraneous information that does not fit well in a string. The array can contain anything. Implementors MUST ensure they treat context data with as much lenience as possible. A given value in the context MUST NOT throw an exception nor raise any php error, warning or notice. - If an `Exception` object is passed in the context data, it MUST be in the `'exception'` key. Logging exceptions is a common pattern and this allows implementors to extract a stack trace from the exception when the log backend supports it. Implementors MUST still verify that the `'exception'` key is actually an `Exception` before using it as such, as it MAY contain anything. ### 1.4 Helper classes and interfaces - The `Psr\Log\AbstractLogger` class lets you implement the `LoggerInterface` very easily by extending it and implementing the generic `log` method. The other eight methods are forwarding the message and context to it. - Similarly, using the `Psr\Log\LoggerTrait` only requires you to implement the generic `log` method. Note that since traits can not implement interfaces, in this case you still have to implement `LoggerInterface`. - The `Psr\Log\NullLogger` is provided together with the interface. It MAY be used by users of the interface to provide a fall-back "black hole" implementation if no logger is given to them. However, conditional logging may be a better approach if context data creation is expensive. - The `Psr\Log\LoggerAwareInterface` only contains a `setLogger(LoggerInterface $logger)` method and can be used by frameworks to auto-wire arbitrary instances with a logger. - The `Psr\Log\LoggerAwareTrait` trait can be used to implement the equivalent interface easily in any class. It gives you access to `$this->logger`. - The `Psr\Log\LogLevel` class holds constants for the eight log levels. ## 2. Package The interfaces and classes described as well as relevant exception classes and a test suite to verify your implementation are provided as part of the [psr/log](https://packagist.org/packages/psr/log) package. ## 3. `Psr\Log\LoggerInterface` ```php register(); * * // register the base directories for the namespace prefix * $loader->addNamespace('Foo\Bar', '/path/to/packages/foo-bar/src'); * $loader->addNamespace('Foo\Bar', '/path/to/packages/foo-bar/tests'); * * The following line would cause the autoloader to attempt to load the * \Foo\Bar\Qux\Quux class from /path/to/packages/foo-bar/src/Qux/Quux.php: * * prefixes[$prefix]) === false) { $this->prefixes[$prefix] = array(); } // retain the base directory for the namespace prefix if ($prepend) { array_unshift($this->prefixes[$prefix], $base_dir); } else { array_push($this->prefixes[$prefix], $base_dir); } } /** * Loads the class file for a given class name. * * @param string $class The fully-qualified class name. * @return mixed The mapped file name on success, or boolean false on * failure. */ public function loadClass($class) { // the current namespace prefix $prefix = $class; // work backwards through the namespace names of the fully-qualified // class name to find a mapped file name while (false !== $pos = strrpos($prefix, '\\')) { // retain the trailing namespace separator in the prefix $prefix = substr($class, 0, $pos + 1); // the rest is the relative class name $relative_class = substr($class, $pos + 1); // try to load a mapped file for the prefix and relative class $mapped_file = $this->loadMappedFile($prefix, $relative_class); if ($mapped_file) { return $mapped_file; } // remove the trailing namespace separator for the next iteration // of strrpos() $prefix = rtrim($prefix, '\\'); } // never found a mapped file return false; } /** * Load the mapped file for a namespace prefix and relative class. * * @param string $prefix The namespace prefix. * @param string $relative_class The relative class name. * @return mixed Boolean false if no mapped file can be loaded, or the * name of the mapped file that was loaded. */ protected function loadMappedFile($prefix, $relative_class) { // are there any base directories for this namespace prefix? if (isset($this->prefixes[$prefix]) === false) { return false; } // look through base directories for this namespace prefix foreach ($this->prefixes[$prefix] as $base_dir) { // replace the namespace prefix with the base directory, // replace namespace separators with directory separators // in the relative class name, append with .php $file = $base_dir . str_replace('\\', '/', $relative_class) . '.php'; // if the mapped file exists, require it if ($this->requireFile($file)) { // yes, we're done return $file; } } // never found it return false; } /** * If a file exists, require it from the file system. * * @param string $file The file to require. * @return bool True if the file exists, false if not. */ protected function requireFile($file) { if (file_exists($file)) { require $file; return true; } return false; } } ``` ### Unit Tests The following example is one way of unit testing the above class loader: ```php files = $files; } protected function requireFile($file) { return in_array($file, $this->files); } } class Psr4AutoloaderClassTest extends \PHPUnit_Framework_TestCase { protected $loader; protected function setUp() { $this->loader = new MockPsr4AutoloaderClass; $this->loader->setFiles(array( '/vendor/foo.bar/src/ClassName.php', '/vendor/foo.bar/src/DoomClassName.php', '/vendor/foo.bar/tests/ClassNameTest.php', '/vendor/foo.bardoom/src/ClassName.php', '/vendor/foo.bar.baz.dib/src/ClassName.php', '/vendor/foo.bar.baz.dib.zim.gir/src/ClassName.php', )); $this->loader->addNamespace( 'Foo\Bar', '/vendor/foo.bar/src' ); $this->loader->addNamespace( 'Foo\Bar', '/vendor/foo.bar/tests' ); $this->loader->addNamespace( 'Foo\BarDoom', '/vendor/foo.bardoom/src' ); $this->loader->addNamespace( 'Foo\Bar\Baz\Dib', '/vendor/foo.bar.baz.dib/src' ); $this->loader->addNamespace( 'Foo\Bar\Baz\Dib\Zim\Gir', '/vendor/foo.bar.baz.dib.zim.gir/src' ); } public function testExistingFile() { $actual = $this->loader->loadClass('Foo\Bar\ClassName'); $expect = '/vendor/foo.bar/src/ClassName.php'; $this->assertSame($expect, $actual); $actual = $this->loader->loadClass('Foo\Bar\ClassNameTest'); $expect = '/vendor/foo.bar/tests/ClassNameTest.php'; $this->assertSame($expect, $actual); } public function testMissingFile() { $actual = $this->loader->loadClass('No_Vendor\No_Package\NoClass'); $this->assertFalse($actual); } public function testDeepFile() { $actual = $this->loader->loadClass('Foo\Bar\Baz\Dib\Zim\Gir\ClassName'); $expect = '/vendor/foo.bar.baz.dib.zim.gir/src/ClassName.php'; $this->assertSame($expect, $actual); } public function testConfusion() { $actual = $this->loader->loadClass('Foo\Bar\DoomClassName'); $expect = '/vendor/foo.bar/src/DoomClassName.php'; $this->assertSame($expect, $actual); $actual = $this->loader->loadClass('Foo\BarDoom\ClassName'); $expect = '/vendor/foo.bardoom/src/ClassName.php'; $this->assertSame($expect, $actual); } } ``` ================================================ FILE: accepted/PSR-4-autoloader-meta.md ================================================ # PSR-4 Meta Document ## 1. Summary The purpose is to specify the rules for an interoperable PHP autoloader that maps namespaces to file system paths, and that can co-exist with any other SPL registered autoloader. This would be an addition to, not a replacement for, PSR-0. ## 2. Why Bother? ### History of PSR-0 The PSR-0 class naming and autoloading standard rose out of the broad acceptance of the Horde/PEAR convention under the constraints of PHP 5.2 and previous. With that convention, the tendency was to put all PHP source classes in a single main directory, using underscores in the class name to indicate pseudo-namespaces, like so: /path/to/src/ VendorFoo/ Bar/ Baz.php # VendorFoo_Bar_Baz VendorDib/ Zim/ Gir.php # Vendor_Dib_Zim_Gir With the release of PHP 5.3 and the availability of namespaces proper, PSR-0 was introduced to allow both the old Horde/PEAR underscore mode *and* the use of the new namespace notation. Underscores were still allowed in the class name to ease the transition from the older namespace naming to the newer naming, and thereby to encourage wider adoption. /path/to/src/ VendorFoo/ Bar/ Baz.php # VendorFoo_Bar_Baz VendorDib/ Zim/ Gir.php # VendorDib_Zim_Gir Irk_Operation/ Impending_Doom/ V1.php V2.php # Irk_Operation\Impending_Doom\V2 This structure is informed very much by the fact that the PEAR installer moved source files from PEAR packages into a single central directory. ### Along Comes Composer With Composer, package sources are no longer copied to a single global location. They are used from their installed location and are not moved around. This means that with Composer there is no "single main directory" for PHP sources as with PEAR. Instead, there are multiple directories; each package is in a separate directory for each project. To meet the requirements of PSR-0, this leads to Composer packages looking like this: vendor/ vendor_name/ package_name/ src/ Vendor_Name/ Package_Name/ ClassName.php # Vendor_Name\Package_Name\ClassName tests/ Vendor_Name/ Package_Name/ ClassNameTest.php # Vendor_Name\Package_Name\ClassNameTest The "src" and "tests" directories have to include vendor and package directory names. This is an artifact of PSR-0 compliance. Many find this structure to be deeper and more repetitive than necessary. This proposal suggests that an additional or superseding PSR would be useful so that we can have packages that look more like the following: vendor/ vendor_name/ package_name/ src/ ClassName.php # Vendor_Name\Package_Name\ClassName tests/ ClassNameTest.php # Vendor_Name\Package_Name\ClassNameTest This would require an implementation of what was initially called "package-oriented autoloading" (as vs the traditional "direct class-to-file autoloading"). ### Package-Oriented Autoloading It's difficult to implement package-oriented autoloading via an extension or amendment to PSR-0, because PSR-0 does not allow for an intercessory path between any portions of the class name. This means the implementation of a package-oriented autoloader would be more complicated than PSR-0. However, it would allow for cleaner packages. Initially, the following rules were suggested: 1. Implementors MUST use at least two namespace levels: a vendor name, and package name within that vendor. (This top-level two-name combination is hereinafter referred to as the vendor-package name or the vendor-package namespace.) 2. Implementors MUST allow a path infix between the vendor-package namespace and the remainder of the fully qualified class name. 3. The vendor-package namespace MAY map to any directory. The remaining portion of the fully-qualified class name MUST map the namespace names to identically-named directories, and MUST map the class name to an identically-named file ending in .php. Note that this means the end of underscore-as-directory-separator in the class name. One might think underscores should be honored as they are under PSR-0, but seeing as their presence in that document is in reference to transitioning away from PHP 5.2 and previous pseudo-namespacing, it is acceptable to remove them here as well. ## 3. Scope ### 3.1 Goals - Retain the PSR-0 rule that implementors MUST use at least two namespace levels: a vendor name, and package name within that vendor. - Allow a path infix between the vendor-package namespace and the remainder of the fully qualified class name. - Allow the vendor-package namespace MAY map to any directory, perhaps multiple directories. - End the honoring of underscores in class names as directory separators ### 3.2 Non-Goals - Provide a general transformation algorithm for non-class resources ## 4. Approaches ### 4.1 Chosen Approach This approach retains key characteristics of PSR-0 while eliminating the deeper directory structures it requires. In addition, it specifies certain additional rules that make implementations explicitly more interoperable. Although not related to directory mapping, the final draft also specifies how autoloaders should handle errors. Specifically, it forbids throwing exceptions or raising errors. The reason is two-fold. 1. Autoloaders in PHP are explicitly designed to be stackable so that if one autoloader cannot load a class another has a chance to do so. Having an autoloader trigger a breaking error condition violates that compatibility. 2. `class_exists()` and `interface_exists()` allow "not found, even after trying to autoload" as a legitimate, normal use case. An autoloader that throws exceptions renders `class_exists()` unusable, which is entirely unacceptable from an interoperability standpoint. Autoloaders that wish to provide additional debugging information in a class-not-found case should do so via logging instead, either to a PSR-3 compatible logger or otherwise. Pros: - Shallower directory structures - More flexible file locations - Stops underscore in class name from being honored as directory separator - Makes implementations more explicitly interoperable Cons: - It is no longer possible, as under PSR-0, to merely examine a class name to determine where it is in the file system (the "class-to-file" convention inherited from Horde/PEAR). ### 4.2 Alternative: Stay With PSR-0 Only Staying with PSR-0 only, although reasonable, does leave us with relatively deeper directory structures. Pros: - No need to change anyone's habits or implementations Cons: - Leaves us with deeper directory structures - Leaves us with underscores in the class name being honored as directory separators ### 4.3 Alternative: Split Up Autoloading And Transformation Beau Simensen and others suggested that the transformation algorithm might be split out from the autoloading proposal so that the transformation rules could be referenced by other proposals. After doing the work to separate them, followed by a poll and some discussion, the combined version (i.e., transformation rules embedded in the autoloader proposal) was revealed as the preference. Pros: - Transformation rules could be referenced separately by other proposals Cons: - Not in line with the wishes of poll respondents and some collaborators ### 4.4 Alternative: Use More Imperative And Narrative Language After the second vote was pulled by a Sponsor after hearing from multiple +1 voters that they supported the idea but did not agree with (or understand) the wording of the proposal, there was a period during which the voted-on proposal was expanded with greater narrative and somewhat more imperative language. This approach was decried by a vocal minority of participants. After some time, Beau Simensen started an experimental revision with an eye to PSR-0; the Editor and Sponsors favored this more terse approach and shepherded the version now under consideration, written by Paul M. Jones and contributed to by many. ### Compatibility Note with PHP 5.3.2 and below PHP versions before 5.3.3 do not strip the leading namespace separator, so the responsibility to look out for this falls on the implementation. Failing to strip the leading namespace separator could lead to unexpected behavior. ## 5. People ### 5.1 Editor - Paul M. Jones, Solar/Aura ### 5.2 Sponsors - Phil Sturgeon, PyroCMS (Coordinator) - Larry Garfield, Drupal ### 5.3 Contributors - Andreas Hennings - Bernhard Schussek - Beau Simensen - Donald Gilbert - Mike van Riel - Paul Dragoonis - Too many others to name and count ## 6. Votes - **Entrance Vote:** - **Acceptance Vote:** - 1st attempt: , presented prior to new workflow; aborted due to accidental proposal modification - 2nd attempt: , cancelled at the discretion of the sponsor - 3rd attempt: TBD ## 7. Relevant Links - [Autoloader, round 4](https://groups.google.com/forum/#!topicsearchin/php-fig/autoload/php-fig/lpmJcmkNYjM) - [POLL: Autoloader: Split or Combined?](https://groups.google.com/forum/#!topicsearchin/php-fig/autoload/php-fig/fGwA6XHlYhI) - [PSR-X autoloader spec: Loopholes, ambiguities](https://groups.google.com/forum/#!topicsearchin/php-fig/autoload/php-fig/kUbzJAbHxmg) - [Autoloader: Combine Proposals?](https://groups.google.com/forum/#!topicsearchin/php-fig/autoload/php-fig/422dFBGs1Yc) - [Package-Oriented Autoloader, Round 2](https://groups.google.com/forum/#!topicsearchin/php-fig/autoload/php-fig/Y4xc71Q3YEQ) - [Autoloader: looking again at namespace](https://groups.google.com/forum/#!topicsearchin/php-fig/autoload/php-fig/bnoiTxE8L28) - [DISCUSSION: Package-Oriented Autoloader - vote against](https://groups.google.com/forum/#!topicsearchin/php-fig/autoload/php-fig/SJTL1ec46II) - [VOTE: Package-Oriented Autoloader](https://groups.google.com/forum/#!topicsearchin/php-fig/autoload/php-fig/Ua46E344_Ls) - [Proposal: Package-Oriented Autoloader](https://groups.google.com/forum/#!topicsearchin/php-fig/autoload/php-fig/qT7mEy0RIuI) - [Towards a Package Oriented Autoloader](https://groups.google.com/forum/#!searchin/php-fig/package$20oriented$20autoloader/php-fig/JdR-g8ZxKa8/jJr80ard-ekJ) - [List of Alternative PSR-4 Proposals](https://groups.google.com/forum/#!topic/php-fig/oXr-2TU1lQY) - [Summary of [post-Acceptance Vote pull] PSR-4 discussions](https://groups.google.com/forum/#!searchin/php-fig/psr-4$20summary/php-fig/bSTwUX58NhE/YPcFgBjwvpEJ) ================================================ FILE: accepted/PSR-4-autoloader.md ================================================ # Autoloader The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119](http://tools.ietf.org/html/rfc2119). ## 1. Overview This PSR describes a specification for [autoloading][] classes from file paths. It is fully interoperable, and can be used in addition to any other autoloading specification, including [PSR-0][]. This PSR also describes where to place files that will be autoloaded according to the specification. ## 2. Specification 1. The term "class" refers to classes, interfaces, traits, and other similar structures. 2. A fully qualified class name has the following form: \(\)*\ 1. The fully qualified class name MUST have a top-level namespace name, also known as a "vendor namespace". 2. The fully qualified class name MAY have one or more sub-namespace names. 3. The fully qualified class name MUST have a terminating class name. 4. Underscores have no special meaning in any portion of the fully qualified class name. 5. Alphabetic characters in the fully qualified class name MAY be any combination of lower case and upper case. 6. All class names MUST be referenced in a case-sensitive fashion. 3. When loading a file that corresponds to a fully qualified class name ... 1. A contiguous series of one or more leading namespace and sub-namespace names, not including the leading namespace separator, in the fully qualified class name (a "namespace prefix") corresponds to at least one "base directory". 2. The contiguous sub-namespace names after the "namespace prefix" correspond to a subdirectory within a "base directory", in which the namespace separators represent directory separators. The subdirectory name MUST match the case of the sub-namespace names. 3. The terminating class name corresponds to a file name ending in `.php`. The file name MUST match the case of the terminating class name. 4. Autoloader implementations MUST NOT throw exceptions, MUST NOT raise errors of any level, and SHOULD NOT return a value. ## 3. Examples The table below shows the corresponding file path for a given fully qualified class name, namespace prefix, and base directory. | Fully Qualified Class Name | Namespace Prefix | Base Directory | Resulting File Path | ----------------------------- |--------------------|--------------------------|------------------------------------------- | \Acme\Log\Writer\File_Writer | Acme\Log\Writer | ./acme-log-writer/lib/ | ./acme-log-writer/lib/File_Writer.php | \Aura\Web\Response\Status | Aura\Web | /path/to/aura-web/src/ | /path/to/aura-web/src/Response/Status.php | \Symfony\Core\Request | Symfony\Core | ./vendor/Symfony/Core/ | ./vendor/Symfony/Core/Request.php | \Zend\Acl | Zend | /usr/includes/Zend/ | /usr/includes/Zend/Acl.php For example implementations of autoloaders conforming to the specification, please see the [examples file][]. Example implementations MUST NOT be regarded as part of the specification and MAY change at any time. [autoloading]: https://php.net/manual/en/language.oop5.autoload.php [PSR-0]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md [examples file]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader-examples.md ================================================ FILE: accepted/PSR-6-cache-meta.md ================================================ # PSR-Cache Meta Document ## 1. Summary Caching is a common way to improve the performance of any project, making caching libraries one of the most common features of many frameworks and libraries. This has lead to a situation where many libraries roll their own caching libraries, with various levels of functionality. These differences are causing developers to have to learn multiple systems which may or may not provide the functionality they need. In addition, the developers of caching libraries themselves face a choice between only supporting a limited number of frameworks or creating a large number of adapter classes. ## 2. Why Bother? A common interface for caching systems will solve these problems. Library and framework developers can count on the caching systems working the way they're expecting, while the developers of caching systems will only have to implement a single set of interfaces rather than a whole assortment of adapters. Moreover, the implementation presented here is designed for future extensibility. It allows a variety of internally-different but API-compatible implementations and offers a clear path for future extension by later PSRs or by specific implementers. Pros: * A standard interface for caching allows free-standing libraries to support caching of intermediary data without effort; they may simply (optionally) depend on this standard interface and leverage it without being concerned about implementation details. * Commonly developed caching libraries shared by multiple projects, even if they extend this interface, are likely to be more robust than a dozen separately developed implementations. Cons: * Any interface standardization runs the risk of stifling future innovation as being "not the Way It's Done(tm)". However, we believe caching is a sufficiently commoditized problem space that the extension capability offered here mitigates any potential risk of stagnation. ## 3. Scope ### 3.1 Goals * A common interface for basic and intermediate-level caching needs. * A clear mechanism for extending the specification to support advanced features, both by future PSRs or by individual implementations. This mechanism must allow for multiple independent extensions without collision. ### 3.2 Non-Goals * Architectural compatibility with all existing cache implementations. * Advanced caching features such as namespacing or tagging that are used by a minority of users. ## 4. Approaches ### 4.1 Chosen Approach This specification adopts a "repository model" or "data mapper" model for caching rather than the more traditional "expire-able key-value" model. The primary reason is flexibility. A simple key/value model is much more difficult to extend. The model here mandates the use of a CacheItem object, which represents a cache entry, and a Pool object, which is a given store of cached data. Items are retrieved from the pool, interacted with, and returned to it. While a bit more verbose at times it offers a good, robust, flexible approach to caching, especially in cases where caching is more involved than simply saving and retrieving a string. Most method names were chosen based on common practice and method names in a survey of member projects and other popular non-member systems. Pros: * Flexible and extensible * Allows a great deal of variation in implementation without violating the interface * Does not implicitly expose object constructors as a pseudo-interface. Cons: * A bit more verbose than the naive approach Examples: Some common usage patterns are shown below. These are non-normative but should demonstrate the application of some design decisions. ```php /** * Gets a list of available widgets. * * In this case, we assume the widget list changes so rarely that we want * the list cached forever until an explicit clear. */ function get_widget_list() { $pool = get_cache_pool('widgets'); $item = $pool->getItem('widget_list'); if (!$item->isHit()) { $value = compute_expensive_widget_list(); $item->set($value); $pool->save($item); } return $item->get(); } ``` ```php /** * Caches a list of available widgets. * * In this case, we assume a list of widgets has been computed and we want * to cache it, regardless of what may already be cached. */ function save_widget_list($list) { $pool = get_cache_pool('widgets'); $item = $pool->getItem('widget_list'); $item->set($list); $pool->save($item); } ``` ```php /** * Clears the list of available widgets. * * In this case, we simply want to remove the widget list from the cache. We * don't care if it was set or not; the post condition is simply "no longer set". */ function clear_widget_list() { $pool = get_cache_pool('widgets'); $pool->deleteItems(['widget_list']); } ``` ```php /** * Clears all widget information. * * In this case, we want to empty the entire widget pool. There may be other * pools in the application that will be unaffected. */ function clear_widget_cache() { $pool = get_cache_pool('widgets'); $pool->clear(); } ``` ```php /** * Load widgets. * * We want to get back a list of widgets, of which some are cached and some * are not. This of course assumes that loading from the cache is faster than * whatever the non-cached loading mechanism is. * * In this case, we assume widgets may change frequently so we only allow them * to be cached for an hour (3600 seconds). We also cache newly-loaded objects * back to the pool en masse. * * Note that a real implementation would probably also want a multi-load * operation for widgets, but that's irrelevant for this demonstration. */ function load_widgets(array $ids) { $pool = get_cache_pool('widgets'); $keys = array_map(function($id) { return 'widget.' . $id; }, $ids); $items = $pool->getItems($keys); $widgets = array(); foreach ($items as $key => $item) { if ($item->isHit()) { $value = $item->get(); } else { $value = expensive_widget_load($id); $item->set($value); $item->expiresAfter(3600); $pool->saveDeferred($item, true); } $widget[$value->id()] = $value; } $pool->commit(); // If no items were deferred this is a no-op. return $widgets; } ``` ```php /** * This examples reflects functionality that is NOT included in this * specification, but is shown as an example of how such functionality MIGHT * be added by extending implementations. */ interface TaggablePoolInterface extends Psr\Cache\CachePoolInterface { /** * Clears only those items from the pool that have the specified tag. */ clearByTag($tag); } interface TaggableItemInterface extends Psr\Cache\CacheItemInterface { public function setTags(array $tags); } /** * Caches a widget with tags. */ function set_widget(TaggablePoolInterface $pool, Widget $widget) { $key = 'widget.' . $widget->id(); $item = $pool->getItem($key); $item->setTags($widget->tags()); $item->set($widget); $pool->save($item); } ``` ### 4.2 Alternative: "Weak item" approach A variety of earlier drafts took a simpler "key value with expiration" approach, also known as a "weak item" approach. In this model, the "Cache Item" object was really just a dumb array-with-methods object. Users would instantiate it directly, then pass it to a cache pool. While more familiar, that approach effectively prevented any meaningful extension of the Cache Item. It effectively made the Cache Item's constructor part of the implicit interface, and thus severely curtailed extensibility or the ability to have the cache item be where the intelligence lives. In a poll conducted in June 2013, most participants showed a clear preference for the more robust if less conventional "Strong item" / repository approach, which was adopted as the way forward. Pros: * More traditional approach. Cons: * Less extensible or flexible. ### 4.3 Alternative: "Naked value" approach Some of the earliest discussions of the Cache spec suggested skipping the Cache Item concept all together and just reading/writing raw values to be cached. While simpler, it was pointed out that made it impossible to tell the difference between a cache miss and whatever raw value was selected to represent a cache miss. That is, if a cache lookup returned NULL it's impossible to tell if there was no cached value or if NULL was the value that had been cached. (NULL is a legitimate value to cache in many cases.) Most more robust caching implementations we reviewed -- in particular the Stash caching library and the home-grown cache system used by Drupal -- use some sort of structured object on `get` at least to avoid confusion between a miss and a sentinel value. Based on that prior experience FIG decided that a naked value on `get` was impossible. ### 4.4 Alternative: ArrayAccess Pool There was a suggestion to make a Pool implement ArrayAccess, which would allow for cache get/set operations to use array syntax. That was rejected due to limited interest, limited flexibility of that approach (trivial get and set with default control information is all that's possible), and because it's trivial for a particular implementation to include as an add-on should it desire to do so. ## 5. People ### 5.1 Editor * Larry Garfield ### 5.2 Sponsors * Paul Dragoonis, PPI Framework (Coordinator) * Robert Hafner, Stash ## 6. Votes [Acceptance vote on the mailing list](https://groups.google.com/forum/#!msg/php-fig/dSw5IhpKJ1g/O9wpqizWAwAJ) ## 7. Relevant Links _**Note:** Order descending chronologically._ * [Survey of existing cache implementations][1], by @dragoonis * [Strong vs. Weak informal poll][2], by @Crell * [Implementation details informal poll][3], by @Crell [1]: https://docs.google.com/spreadsheet/ccc?key=0Ak2JdGialLildEM2UjlOdnA4ekg3R1Bfeng5eGlZc1E#gid=0 [2]: https://docs.google.com/spreadsheet/ccc?key=0AsMrMKNHL1uGdDdVd2llN1kxczZQejZaa3JHcXA3b0E#gid=0 [3]: https://docs.google.com/spreadsheet/ccc?key=0AsMrMKNHL1uGdEE3SU8zclNtdTNobWxpZnFyR0llSXc#gid=1 ## 8. Errata ### 8.1 Handling of incorrect DateTime values in expiresAt() The `CacheItemInterface::expiresAt()` method's `$expiration` parameter is untyped in the interface, but in the docblock is specified as `\DateTimeInterface`. The intent is that either a `\DateTime` or `\DateTimeImmutable` object is allowed. However, `\DateTimeInterface` and `\DateTimeImmutable` were added in PHP 5.5, and the authors chose not to impose a hard syntactic requirement for PHP 5.5 on the specification. Despite that, implementers MUST accept only `\DateTimeInterface` or compatible types (such as `\DateTime` and `\DateTimeImmutable`) as if the method was explicitly typed. (Note that the variance rules for a typed parameter may vary between language versions.) Simulating a failed type check unfortunately varies between PHP versions and thus is not recommended. Instead, implementors SHOULD throw an instance of `\Psr\Cache\InvalidArgumentException`. The following sample code is recommended in order to enforce the type check on the expiresAt() method: ```php class ExpiresAtInvalidParameterException implements Psr\Cache\InvalidArgumentException {} // ... if (! ( null === $expiration || $expiration instanceof \DateTime || $expiration instanceof \DateTimeInterface )) { throw new ExpiresAtInvalidParameterException(sprintf( 'Argument 1 passed to %s::expiresAt() must be an instance of DateTime or DateTimeImmutable; %s given', get_class($this), is_object($expiration) ? get_class($expiration) : gettype($expiration) )); } ``` ### 8.2 Type additions The 2.0 release of the `psr/cache` package includes scalar parameter types. The 3.0 release of the package includes return types. This structure leverages PHP 7.2 covariance support to allow for a gradual upgrade process, but requires PHP 8.0 for type compatibility. The 2.0 version also corrects the Errata 8.1 above by providing a correct type hint for the `CacheItemInterface::expiresAt()` method's `$expiration` parameter. That results in a slight change in the error thrown on invalid input; as it is still a fatal disallowed case, FIG has deemed it an acceptably small BC break in order to leverage correct native typing. Implementers MAY add return types to their own packages at their discretion, provided that: * the return types match those in the 3.0 package. * the implementation specifies a minimum PHP version of 8.0.0 or later. Implementers MAY add parameter types to their own packages in a new major release, either at the same time as adding return types or in a subsequent release, provided that: * the parameter types match those in the 2.0 package. * the implementation specifies a minimum PHP version of 8.0.0 or later. * the implementation depends on `"psr/cache": "^2.0 || ^3.0"` so as to exclude the untyped 1.0 version. Implementers are encouraged but not required to transition their packages toward the 3.0 version of the package at their earliest convenience. ================================================ FILE: accepted/PSR-6-cache.md ================================================ # Caching Interface Caching is a common way to improve the performance of any project, making caching libraries one of the most common features of many frameworks and libraries. This has lead to a situation where many libraries build their own caching libraries, with various levels of functionality. These differences are causing developers to have to learn multiple systems which may or may not provide the functionality they need. In addition, the developers of caching libraries themselves face a choice between only supporting a limited number of frameworks or creating a large number of adapter classes. A common interface for caching systems will solve these problems. Library and framework developers can count on the caching systems working the way they're expecting, while the developers of caching systems will only have to implement a single set of interfaces rather than a whole assortment of adapters. The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119][]. [RFC 2119]: http://tools.ietf.org/html/rfc2119 ## Goal The goal of this PSR is to allow developers to create cache-aware libraries that can be integrated into existing frameworks and systems without the need for custom development. ## Definitions * **Calling Library** - The library or code that actually needs the cache services. This library will utilize caching services that implement this standard's interfaces, but will otherwise have no knowledge of the implementation of those caching services. * **Implementing Library** - This library is responsible for implementing this standard in order to provide caching services to any Calling Library. The Implementing Library MUST provide classes which implement the Cache\CacheItemPoolInterface and Cache\CacheItemInterface interfaces. Implementing Libraries MUST support at minimum TTL functionality as described below with whole-second granularity. * **TTL** - The Time To Live (TTL) of an item is the amount of time between when that item is stored and it is considered stale. The TTL is normally defined by an integer representing time in seconds, or a DateInterval object. * **Expiration** - The actual time when an item is set to go stale. This is typically calculated by adding the TTL to the time when an object is stored, but may also be explicitly set with DateTime object. An item with a 300 second TTL stored at 1:30:00 will have an expiration of 1:35:00. Implementing Libraries MAY expire an item before its requested Expiration Time, but MUST treat an item as expired once its Expiration Time is reached. If a calling library asks for an item to be saved but does not specify an expiration time, or specifies a null expiration time or TTL, an Implementing Library MAY use a configured default duration. If no default duration has been set, the Implementing Library MUST interpret that as a request to cache the item forever, or for as long as the underlying implementation supports. * **Key** - A string of at least one character that uniquely identifies a cached item. Implementing libraries MUST support keys consisting of the characters `A-Z`, `a-z`, `0-9`, `_`, and `.` in any order in UTF-8 encoding and a length of up to 64 characters. Implementing libraries MAY support additional characters and encodings or longer lengths, but must support at least that minimum. Libraries are responsible for their own escaping of key strings as appropriate, but MUST be able to return the original unmodified key string. The following characters are reserved for future extensions and MUST NOT be supported by implementing libraries: `{}()/\@:` * **Hit** - A cache hit occurs when a Calling Library requests an Item by key and a matching value is found for that key, and that value has not expired, and the value is not invalid for some other reason. Calling Libraries SHOULD make sure to verify isHit() on all get() calls. * **Miss** - A cache miss is the opposite of a cache hit. A cache miss occurs when a Calling Library requests an item by key and that value not found for that key, or the value was found but has expired, or the value is invalid for some other reason. An expired value MUST always be considered a cache miss. * **Deferred** - A deferred cache save indicates that a cache item may not be persisted immediately by the pool. A Pool object MAY delay persisting a deferred cache item in order to take advantage of bulk-set operations supported by some storage engines. A Pool MUST ensure that any deferred cache items are eventually persisted and data is not lost, and MAY persist them before a Calling Library requests that they be persisted. When a Calling Library invokes the commit() method all outstanding deferred items MUST be persisted. An Implementing Library MAY use whatever logic is appropriate to determine when to persist deferred items, such as an object destructor, persisting all on save(), a timeout or max-items check or any other appropriate logic. Requests for a cache item that has been deferred MUST return the deferred but not-yet-persisted item. ## Data Implementing libraries MUST support all serializable PHP data types, including: * **Strings** - Character strings of arbitrary size in any PHP-compatible encoding. * **Integers** - All integers of any size supported by PHP, up to 64-bit signed. * **Floats** - All signed floating point values. * **Boolean** - True and False. * **Null** - The actual null value. * **Arrays** - Indexed, associative and multidimensional arrays of arbitrary depth. * **Object** - Any object that supports lossless serialization and deserialization such that `$o == unserialize(serialize($o))`. Objects MAY leverage PHP's Serializable interface, `__sleep()` or `__wakeup()` magic methods, or similar language functionality if appropriate. All data passed into the Implementing Library MUST be returned exactly as passed. That includes the variable type. That is, it is an error to return (string) 5 if (int) 5 was the value saved. Implementing Libraries MAY use PHP's `serialize()`/`unserialize()` functions internally but are not required to do so. Compatibility with them is simply used as a baseline for acceptable object values. If it is not possible to return the exact saved value for any reason, implementing libraries MUST respond with a cache miss rather than corrupted data. ## Key Concepts ### Pool The Pool represents a collection of items in a caching system. The pool is a logical Repository of all items it contains. All cacheable items are retrieved from the Pool as an Item object, and all interaction with the whole universe of cached objects happens through the Pool. ### Items An Item represents a single key/value pair within a Pool. The key is the primary unique identifier for an Item and MUST be immutable. The Value MAY be changed at any time. ## Error handling While caching is often an important part of application performance, it should never be a critical part of application functionality. Thus, an error in a cache system SHOULD NOT result in application failure. For that reason, Implementing Libraries MUST NOT throw exceptions other than those defined by the interface, and SHOULD trap any errors or exceptions triggered by an underlying data store and not allow them to bubble. An Implementing Library SHOULD log such errors or otherwise report them to an administrator as appropriate. If a Calling Library requests that one or more Items be deleted, or that a pool be cleared, it MUST NOT be considered an error condition if the specified key does not exist. The post-condition is the same (the key does not exist, or the pool is empty), thus there is no error condition. ## Interfaces ### CacheItemInterface CacheItemInterface defines an item inside a cache system. Each Item object MUST be associated with a specific key, which can be set according to the implementing system and is typically passed by the `Cache\CacheItemPoolInterface` object. The `Cache\CacheItemInterface` object encapsulates the storage and retrieval of cache items. Each `Cache\CacheItemInterface` is generated by a `Cache\CacheItemPoolInterface` object, which is responsible for any required setup as well as associating the object with a unique Key. `Cache\CacheItemInterface` objects MUST be able to store and retrieve any type of PHP value defined in the Data section of this document. Calling Libraries MUST NOT instantiate Item objects themselves. They may only be requested from a Pool object via the `getItem()` method. Calling Libraries SHOULD NOT assume that an Item created by one Implementing Library is compatible with a Pool from another Implementing Library. ```php 'Bearer ' . $token, 'Accept' => 'application/json', ]); $request = $baseRequest->withUri($uri->withPath('/user'))->withMethod('GET'); $response = $client->sendRequest($request); // get user id from $response $body = new StringStream(json_encode(['tasks' => [ 'Code', 'Coffee', ]])); $request = $baseRequest ->withUri($uri->withPath('/tasks/user/' . $userId)) ->withMethod('POST') ->withHeader('Content-Type', 'application/json') ->withBody($body); $response = $client->sendRequest($request) // No need to overwrite headers or body! $request = $baseRequest->withUri($uri->withPath('/tasks'))->withMethod('GET'); $response = $client->sendRequest($request); ``` On the server-side, developers will need to: - Deserialize the request message body. - Decrypt HTTP cookies. - Write to the response. These operations can be accomplished with value objects as well, with a number of benefits: - The original request state can be stored for retrieval by any consumer. - A default response state can be created with default headers and/or message body. Most popular PHP frameworks have fully mutable HTTP messages today. The main changes necessary in consuming true value objects are: - Instead of calling setter methods or setting public properties, mutator methods will be called, and the result assigned. - Developers must notify the application on a change in state. As an example, in Zend Framework 2, instead of the following: ```php function (MvcEvent $e) { $response = $e->getResponse(); $response->setHeaderLine('x-foo', 'bar'); } ``` one would now write: ```php function (MvcEvent $e) { $response = $e->getResponse(); $e->setResponse( $response->withHeader('x-foo', 'bar') ); } ``` The above combines assignment and notification in a single call. This practice has a side benefit of making explicit any changes to application state being made. ### New instances vs returning $this One observation made on the various `with*()` methods is that they can likely safely `return $this;` if the argument presented will not result in a change in the value. One rationale for doing so is performance (as this will not result in a cloning operation). The various interfaces have been written with verbiage indicating that immutability MUST be preserved, but only indicate that "an instance" must be returned containing the new state. Since instances that represent the same value are considered equal, returning `$this` is functionally equivalent, and thus allowed. ### Using streams instead of X `MessageInterface` uses a body value that must implement `StreamInterface`. This design decision was made so that developers can send and receive (and/or receive and send) HTTP messages that contain more data than can practically be stored in memory while still allowing the convenience of interacting with message bodies as a string. While PHP provides a stream abstraction by way of stream wrappers, stream resources can be cumbersome to work with: stream resources can only be cast to a string using `stream_get_contents()` or manually reading the remainder of a string. Adding custom behavior to a stream as it is consumed or populated requires registering a stream filter; however, stream filters can only be added to a stream after the filter is registered with PHP (i.e., there is no stream filter autoloading mechanism). The use of a well- defined stream interface allows for the potential of flexible stream decorators that can be added to a request or response pre-flight to enable things like encryption, compression, ensuring that the number of bytes downloaded reflects the number of bytes reported in the `Content-Length` of a response, etc. Decorating streams is a well-established [pattern in the Java](http://docs.oracle.com/javase/7/docs/api/java/io/package-tree.html) and [Node](http://nodejs.org/api/stream.html#stream_class_stream_transform_1) communities that allows for very flexible streams. The majority of the `StreamInterface` API is based on [Python's io module](http://docs.python.org/3.1/library/io.html), which provides a practical and consumable API. Instead of implementing stream capabilities using something like a `WritableStreamInterface` and `ReadableStreamInterface`, the capabilities of a stream are provided by methods like `isReadable()`, `isWritable()`, etc. This approach is used by Python, [C#, C++](http://msdn.microsoft.com/en-us/library/system.io.stream.aspx), [Ruby](http://www.ruby-doc.org/core-2.0.0/IO.html), [Node](http://nodejs.org/api/stream.html), and likely others. #### What if I just want to return a file? In some cases, you may want to return a file from the filesystem. The typical way to do this in PHP is one of the following: ```php readfile($filename); stream_copy_to_stream(fopen($filename, 'r'), fopen('php://output', 'w')); ``` Note that the above omits sending appropriate `Content-Type` and `Content-Length` headers; the developer would need to emit these prior to calling the above code. The equivalent using HTTP messages would be to use a `StreamInterface` implementation that accepts a filename and/or stream resource, and to provide this to the response instance. A complete example, including setting appropriate headers: ```php // where Stream is a concrete StreamInterface: $stream = new Stream($filename); $finfo = new finfo(FILEINFO_MIME); $response = $response ->withHeader('Content-Type', $finfo->file($filename)) ->withHeader('Content-Length', (string) filesize($filename)) ->withBody($stream); ``` Emitting this response will send the file to the client. #### What if I want to directly emit output? Directly emitting output (e.g. via `echo`, `printf`, or writing to the `php://output` stream) is generally only advisable as a performance optimization or when emitting large data sets. If it needs to be done and you still wish to work in an HTTP message paradigm, one approach would be to use a callback-based `StreamInterface` implementation, per [this example](https://github.com/phly/psr7examples#direct-output). Wrap any code emitting output directly in a callback, pass that to an appropriate `StreamInterface` implementation, and provide it to the message body: ```php $output = new CallbackStream(function () use ($request) { printf("The requested URI was: %s
\n", $request->getUri()); return ''; }); return (new Response()) ->withHeader('Content-Type', 'text/html') ->withBody($output); ``` #### What if I want to use an iterator for content? Ruby's Rack implementation uses an iterator-based approach for server-side response message bodies. This can be emulated using an HTTP message paradigm via an iterator-backed `StreamInterface` approach, as [detailed in the psr7examples repository](https://github.com/phly/psr7examples#iterators-and-generators). ### Why are streams mutable? The `StreamInterface` API includes methods such as `write()` which can change the message content -- which directly contradicts having immutable messages. The problem that arises is due to the fact that the interface is intended to wrap a PHP stream or similar. A write operation therefore will proxy to writing to the stream. Even if we made `StreamInterface` immutable, once the stream has been updated, any instance that wraps that stream will also be updated -- making immutability impossible to enforce. Our recommendation is that implementations use read-only streams for server-side requests and client-side responses. ### Rationale for ServerRequestInterface The `RequestInterface` and `ResponseInterface` have essentially 1:1 correlations with the request and response messages described in [RFC 7230](http://www.ietf.org/rfc/rfc7230.txt). They provide interfaces for implementing value objects that correspond to the specific HTTP message types they model. For server-side applications there are other considerations for incoming requests: - Access to server parameters (potentially derived from the request, but also potentially the result of server configuration, and generally represented via the `$_SERVER` superglobal; these are part of the PHP Server API (SAPI)). - Access to the query string arguments (usually encapsulated in PHP via the `$_GET` superglobal). - Access to the parsed body (i.e., data deserialized from the incoming request body; in PHP, this is typically the result of POST requests using `application/x-www-form-urlencoded` content types, and encapsulated in the `$_POST` superglobal, but for non-POST, non-form-encoded data, could be an array or an object). - Access to uploaded files (encapsulated in PHP via the `$_FILES` superglobal). - Access to cookie values (encapsulated in PHP via the `$_COOKIE` superglobal). - Access to attributes derived from the request (usually, but not limited to, those matched against the URL path). Uniform access to these parameters increases the viability of interoperability between frameworks and libraries, as they can now assume that if a request implements `ServerRequestInterface`, they can get at these values. It also solves problems within the PHP language itself: - Until 5.6.0, `php://input` was read-once; as such, instantiating multiple request instances from multiple frameworks/libraries could lead to inconsistent state, as the first to access `php://input` would be the only one to receive the data. - Unit testing against superglobals (e.g., `$_GET`, `$_FILES`, etc.) is difficult and typically brittle. Encapsulating them inside the `ServerRequestInterface` implementation eases testing considerations. ### Why "parsed body" in the ServerRequestInterface? Arguments were made to use the terminology "BodyParams", and require the value to be an array, with the following rationale: - Consistency with other server-side parameter access. - `$_POST` is an array, and the 80% use case would target that superglobal. - A single type makes for a strong contract, simplifying usage. The main argument is that if the body parameters are an array, developers have predictable access to values: ```php $foo = isset($request->getBodyParams()['foo']) ? $request->getBodyParams()['foo'] : null; ``` The argument for using "parsed body" was made by examining the domain. A message body can contain literally anything. While traditional web applications use forms and submit data using POST, this is a use case that is quickly being challenged in current web development trends, which are often API-centric, and thus use alternate request methods (notably PUT and PATCH), as well as non-form-encoded content (generally JSON or XML) that _can_ be coerced to arrays in many cases, but in many cases also _cannot_ or _should not_. If forcing the property representing the parsed body to be only an array, developers then need a shared convention about where to put the results of parsing the body. These might include: - A special key under the body parameters, such as `__parsed__`. - A specially named attribute, such as `__body__`. The end result is that a developer now has to look in multiple locations: ```php $data = $request->getBodyParams(); if (isset($data['__parsed__']) && is_object($data['__parsed__'])) { $data = $data['__parsed__']; } // or: $data = $request->getBodyParams(); if ($request->hasAttribute('__body__')) { $data = $request->getAttribute('__body__'); } ``` The solution presented is to use the terminology "ParsedBody", which implies that the values are the results of parsing the message body. This also means that the return value _will_ be ambiguous; however, because this is an attribute of the domain, this is also expected. As such, usage will become: ```php $data = $request->getParsedBody(); if (! $data instanceof \stdClass) { // raise an exception! } // otherwise, we have what we expected ``` This approach removes the limitations of forcing an array, at the expense of ambiguity of return value. Considering that the other suggested solutions — pushing the parsed data into a special body parameter key or into an attribute — also suffer from ambiguity, the proposed solution is simpler as it does not require additions to the interface specification. Ultimately, the ambiguity enables the flexibility required when representing the results of parsing the body. ### Why is no functionality included for retrieving the "base path"? Many frameworks provide the ability to get the "base path," usually considered the path up to and including the front controller. As an example, if the application is served at `http://example.com/b2b/index.php`, and the current URI used to request it is `http://example.com/b2b/index.php/customer/register`, the functionality to retrieve the base path would return `/b2b/index.php`. This value can then be used by routers to strip that path segment prior to attempting a match. This value is often also then used for URI generation within applications; parameters will be passed to the router, which will generate the path, and prefix it with the base path in order to return a fully-qualified URI. Other tools — typically view helpers, template filters, or template functions — are used to resolve a path relative to the base path in order to generate a URI for linking to resources such as static assets. On examination of several different implementations, we noticed the following: - The logic for determining the base path varies widely between implementations. As an example, compare the [logic in ZF2](https://github.com/zendframework/zf2/blob/release-2.3.7/library/Zend/Http/PhpEnvironment/Request.php#L477-L575) to the [logic in Symfony 2](https://github.com/symfony/symfony/blob/2.7/src/Symfony/Component/HttpFoundation/Request.php#L1858-L1877). - Most implementations appear to allow manual injection of a base path to the router and/or any facilities used for URI generation. - The primary use cases — routing and URI generation — typically are the only consumers of the functionality; developers usually do not need to be aware of the base path concept as other objects take care of that detail for them. As examples: - A router will strip off the base path for you during routing; you do not need to pass the modified path to the router. - View helpers, template filters, etc. typically are injected with a base path prior to invocation. Sometimes this is manually done, though more often it is the result of framework wiring. - All sources necessary for calculating the base path *are already in the `RequestInterface` instance*, via server parameters and the URI instance. Our stance is that base path detection is framework and/or application specific, and the results of detection can be easily injected into objects that need it, and/or calculated as needed using utility functions and/or classes from the `RequestInterface` instance itself. ### Why does getUploadedFiles() return objects instead of arrays? `getUploadedFiles()` returns a tree of `Psr\Http\Message\UploadedFileInterface` instances. This is done primarily to simplify specification: instead of requiring paragraphs of implementation specification for an array, we specify an interface. Additionally, the data in an `UploadedFileInterface` is normalized to work in both SAPI and non-SAPI environments. This allows the creation of processes to parse the message body manually and assign contents to streams without first writing to the filesystem, while still allowing proper handling of file uploads in SAPI environments. ### What about "special" header values? A number of header values contain unique representation requirements which can pose problems both for consumption as well as generation; in particular, cookies and the `Accept` header. This proposal does not provide any special treatment of any header types. The base `MessageInterface` provides methods for header retrieval and setting, and all header values are, in the end, string values. Developers are encouraged to write commodity libraries for interacting with these header values, either for the purposes of parsing or generation. Users may then consume these libraries when needing to interact with those values. Examples of this practice already exist in libraries such as [willdurand/Negotiation](https://github.com/willdurand/Negotiation) and [Aura.Accept](https://github.com/auraphp/Aura.Accept). So long as the object has functionality for casting the value to a string, these objects can be used to populate the headers of an HTTP message. ## 6. People ### 6.1 Editor(s) * Matthew Weier O'Phinney ### 6.2 Sponsors * Paul M. Jones * Beau Simensen (coordinator) ### 6.3 Contributors * Michael Dowling * Larry Garfield * Evert Pot * Tobias Schultze * Bernhard Schussek * Anton Serdyuk * Phil Sturgeon * Chris Wilkinson ## 7. Errata ### 7.1 Validation of Header Names and Values Some special characters within the name or value of an HTTP header might affect the parsing of the serialized message in a way that the contents of unrelated headers are changed. This misparsing can open up an application to security vulnerabilities. A common type of vulnerability is CRLF injection, allowing an attacker to inject additional headers or to end the list of headers early. For this reason classes implementing the `MessageInterface` SHOULD strictly validate the header names and contents according to the most recent HTTP specification ([RFC 7230#3.2][1] at the time of writing). The implementation SHOULD reject invalid values and SHOULD NOT make any attempt to automatically correct the provided values. A minimally viable validator is expected to reject header names containing the following characters: - NUL (0x00) - `\r` (0x0D) - `\n` (0x0A) - Any character less than or equal to 0x20. Further characters or sequences in header names should be rejected according to the HTTP specification. A minimally viable validator is expected to reject header values containing the following characters: - NUL (0x00) - `\r` (0x0D) - `\n` (0x0A) If compatibility with older systems is desired then the sequence `\r\n` (0x0D0A) within a header value MAY be accepted if and only if it is immediately followed by either SPACE (0x20) or `\t` (0x09). The full sequence SHOULD then internally be normalized to a single SPACE (0x20). Further characters or sequences in header values should be rejected according to the HTTP specification. ### 7.2 Type Additions The 1.1 release of the `psr/http-message` package includes scalar parameter types. The 2.0 release of the package includes return types. This structure leverages PHP 7.2 covariance support to allow for a gradual upgrade process, but requires PHP 8.0 for type compatibility. Implementers MAY add return types to their own packages at their discretion, provided that: * the return types match those in the 2.0 package. * the implementation specifies a minimum PHP version of 7.2.0 or later. Implementers MAY add parameter types to their own packages in a new major release, either at the same time as adding return types or in a subsequent release, provided that: * the parameter types match those in the 1.1 package. * the implementation specifies a minimum PHP version of 7.2.0 or later. * the implementation depends on `"psr/http-message": "^1.1 || ^2.0"` so as to exclude the untyped 1.0 version. Implementers are encouraged but not required to transition their packages toward the 2.0 version of the package at their earliest convenience. [1]: https://datatracker.ietf.org/doc/html/rfc7230#section-3.2 ### 7.3 Escaping User Info Some characters are reserved in the user info part of the authority section. According to (RFC3986 2.2 and 3.2.1)[https://www.rfc-editor.org/rfc/rfc3986], the reserved characters are `"/" / "?" / "#" / "[" / "]" / "@"`. Additionally, `:` must be encoded when in the username because it is used to separate username and password. `UriInterface::withUserInfo()` MUST NOT double encode reserved characters. `UriInterface::getUserInfo()` MUST encode the reserved characters according to RFC3986 when returning the authority. If there is a password, the `:` between username and password MUST NOT be encoded. ================================================ FILE: accepted/PSR-7-http-message.md ================================================ # HTTP message interfaces This document describes common interfaces for representing HTTP messages as described in [RFC 7230](http://tools.ietf.org/html/rfc7230) and [RFC 7231](http://tools.ietf.org/html/rfc7231), and URIs for use with HTTP messages as described in [RFC 3986](http://tools.ietf.org/html/rfc3986). HTTP messages are the foundation of web development. Web browsers and HTTP clients such as cURL create HTTP request messages that are sent to a web server, which provides an HTTP response message. Server-side code receives an HTTP request message, and returns an HTTP response message. HTTP messages are typically abstracted from the end-user consumer, but as developers, we typically need to know how they are structured and how to access or manipulate them in order to perform our tasks, whether that might be making a request to an HTTP API, or handling an incoming request. Every HTTP request message has a specific form: ```http POST /path HTTP/1.1 Host: example.com foo=bar&baz=bat ``` The first line of a request is the "request line", and contains, in order, the HTTP request method, the request target (usually either an absolute URI or a path on the web server), and the HTTP protocol version. This is followed by one or more HTTP headers, an empty line, and the message body. HTTP response messages have a similar structure: ```http HTTP/1.1 200 OK Content-Type: text/plain This is the response body ``` The first line is the "status line", and contains, in order, the HTTP protocol version, the HTTP status code, and a "reason phrase," a human-readable description of the status code. Like the request message, this is then followed by one or more HTTP headers, an empty line, and the message body. The interfaces described in this document are abstractions around HTTP messages and the elements composing them. The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119](http://tools.ietf.org/html/rfc2119). ### References - [RFC 2119](http://tools.ietf.org/html/rfc2119) - [RFC 3986](http://tools.ietf.org/html/rfc3986) - [RFC 7230](http://tools.ietf.org/html/rfc7230) - [RFC 7231](http://tools.ietf.org/html/rfc7231) ## 1. Specification ### 1.1 Messages An HTTP message is either a request from a client to a server or a response from a server to a client. This specification defines interfaces for the HTTP messages `Psr\Http\Message\RequestInterface` and `Psr\Http\Message\ResponseInterface` respectively. Both `Psr\Http\Message\RequestInterface` and `Psr\Http\Message\ResponseInterface` extend `Psr\Http\Message\MessageInterface`. While `Psr\Http\Message\MessageInterface` MAY be implemented directly, implementors SHOULD implement `Psr\Http\Message\RequestInterface` and `Psr\Http\Message\ResponseInterface`. From here forward, the namespace `Psr\Http\Message` will be omitted when referring to these interfaces. ### 1.2 HTTP Headers [cf. [errata](https://www.php-fig.org/psr/psr-7/meta/#71-validation-of-header-names-and-values)] #### Case-insensitive header field names HTTP messages include case-insensitive header field names. Headers are retrieved by name from classes implementing the `MessageInterface` in a case-insensitive manner. For example, retrieving the `foo` header will return the same result as retrieving the `FoO` header. Similarly, setting the `Foo` header will overwrite any previously set `foo` header value. ```php $message = $message->withHeader('foo', 'bar'); echo $message->getHeaderLine('foo'); // Outputs: bar echo $message->getHeaderLine('FOO'); // Outputs: bar $message = $message->withHeader('fOO', 'baz'); echo $message->getHeaderLine('foo'); // Outputs: baz ``` Despite that headers may be retrieved case-insensitively, the original case MUST be preserved by the implementation, in particular when retrieved with `getHeaders()`. Non-conforming HTTP applications may depend on a certain case, so it is useful for a user to be able to dictate the case of the HTTP headers when creating a request or response. #### Headers with multiple values In order to accommodate headers with multiple values yet still provide the convenience of working with headers as strings, headers can be retrieved from an instance of a `MessageInterface` as an array or a string. Use the `getHeaderLine()` method to retrieve a header value as a string containing all header values of a case-insensitive header by name concatenated with a comma. Use `getHeader()` to retrieve an array of all the header values for a particular case-insensitive header by name. ```php $message = $message ->withHeader('foo', 'bar') ->withAddedHeader('foo', 'baz'); $header = $message->getHeaderLine('foo'); // $header contains: 'bar,baz' $header = $message->getHeader('foo'); // ['bar', 'baz'] ``` Note: Not all header values can be concatenated using a comma (e.g., `Set-Cookie`). When working with such headers, consumers of `MessageInterface`-based classes SHOULD rely on the `getHeader()` method for retrieving such multi-valued headers. #### Host header In requests, the `Host` header typically mirrors the host component of the URI, as well as the host used when establishing the TCP connection. However, the HTTP specification allows the `Host` header to differ from each of the two. During construction, implementations MUST attempt to set the `Host` header from a provided URI if no `Host` header is provided. `RequestInterface::withUri()` will, by default, replace the returned request's `Host` header with a `Host` header matching the host component of the passed `UriInterface`. You can opt-in to preserving the original state of the `Host` header by passing `true` for the second (`$preserveHost`) argument. When this argument is set to `true`, the returned request will not update the `Host` header of the returned message -- unless the message contains no `Host` header. This table illustrates what `getHeaderLine('Host')` will return for a request returned by `withUri()` with the `$preserveHost` argument set to `true` for various initial requests and URIs. Request Host header[1](#rhh) | Request host component[2](#rhc) | URI host component[3](#uhc) | Result ----------------------------------------|--------------------------------------------|----------------------------------------|-------- '' | '' | '' | '' '' | foo.com | '' | foo.com '' | foo.com | bar.com | foo.com foo.com | '' | bar.com | foo.com foo.com | bar.com | baz.com | foo.com - 1 `Host` header value prior to operation. - 2 Host component of the URI composed in the request prior to the operation. - 3 Host component of the URI being injected via `withUri()`. ### 1.3 Streams HTTP messages consist of a start-line, headers, and a body. The body of an HTTP message can be very small or extremely large. Attempting to represent the body of a message as a string can easily consume more memory than intended because the body must be stored completely in memory. Attempting to store the body of a request or response in memory would preclude the use of that implementation from being able to work with large message bodies. `StreamInterface` is used in order to hide the implementation details when a stream of data is read from or written to. For situations where a string would be an appropriate message implementation, built-in streams such as `php://memory` and `php://temp` may be used. `StreamInterface` exposes several methods that enable streams to be read from, written to, and traversed effectively. Streams expose their capabilities using three methods: `isReadable()`, `isWritable()`, and `isSeekable()`. These methods can be used by stream collaborators to determine if a stream is capable of their requirements. Each stream instance will have various capabilities: it can be read-only, write-only, or read-write. It can also allow arbitrary random access (seeking forwards or backwards to any location), or only sequential access (for example in the case of a socket, pipe, or callback-based stream). Finally, `StreamInterface` defines a `__toString()` method to simplify retrieving or emitting the entire body contents at once. Unlike the request and response interfaces, `StreamInterface` does not model immutability. In situations where an actual PHP stream is wrapped, immutability is impossible to enforce, as any code that interacts with the resource can potentially change its state (including cursor position, contents, and more). Our recommendation is that implementations use read-only streams for server-side requests and client-side responses. Consumers should be aware of the fact that the stream instance may be mutable, and, as such, could alter the state of the message; when in doubt, create a new stream instance and attach it to a message to enforce state. ### 1.4 Request Targets and URIs Per RFC 7230, request messages contain a "request-target" as the second segment of the request line. The request target can be one of the following forms: - **origin-form**, which consists of the path, and, if present, the query string; this is often referred to as a relative URL. Messages as transmitted over TCP typically are of origin-form; scheme and authority data are usually only present via CGI variables. - **absolute-form**, which consists of the scheme, authority ("[user-info@]host[:port]", where items in brackets are optional), path (if present), query string (if present), and fragment (if present). This is often referred to as an absolute URI, and is the only form to specify a URI as detailed in RFC 3986. This form is commonly used when making requests to HTTP proxies. - **authority-form**, which consists of the authority only. This is typically used in CONNECT requests only, to establish a connection between an HTTP client and a proxy server. - **asterisk-form**, which consists solely of the string `*`, and which is used with the OPTIONS method to determine the general capabilities of a web server. Aside from these request-targets, there is often an 'effective URL' which is separate from the request target. The effective URL is not transmitted within an HTTP message, but it is used to determine the protocol (http/https), port and hostname for making the request. The effective URL is represented by `UriInterface`. `UriInterface` models HTTP and HTTPS URIs as specified in RFC 3986 (the primary use case). The interface provides methods for interacting with the various URI parts, which will obviate the need for repeated parsing of the URI. It also specifies a `__toString()` method for casting the modeled URI to its string representation. When retrieving the request-target with `getRequestTarget()`, by default this method will use the URI object and extract all the necessary components to construct the _origin-form_. The _origin-form_ is by far the most common request-target. If it's desired by an end-user to use one of the other three forms, or if the user wants to explicitly override the request-target, it is possible to do so with `withRequestTarget()`. Calling this method does not affect the URI, as it is returned from `getUri()`. For example, a user may want to make an asterisk-form request to a server: ```php $request = $request ->withMethod('OPTIONS') ->withRequestTarget('*') ->withUri(new Uri('https://example.org/')); ``` This example may ultimately result in an HTTP request that looks like this: ```http OPTIONS * HTTP/1.1 ``` But the HTTP client will be able to use the effective URL (from `getUri()`), to determine the protocol, hostname and TCP port. An HTTP client MUST ignore the values of `Uri::getPath()` and `Uri::getQuery()`, and instead use the value returned by `getRequestTarget()`, which defaults to concatenating these two values. Clients that choose to not implement 1 or more of the 4 request-target forms, MUST still use `getRequestTarget()`. These clients MUST reject request-targets they do not support, and MUST NOT fall back on the values from `getUri()`. `RequestInterface` provides methods for retrieving the request-target or creating a new instance with the provided request-target. By default, if no request-target is specifically composed in the instance, `getRequestTarget()` will return the origin-form of the composed URI (or "/" if no URI is composed). `withRequestTarget($requestTarget)` creates a new instance with the specified request target, and thus allows developers to create request messages that represent the other three request-target forms (absolute-form, authority-form, and asterisk-form). When used, the composed URI instance can still be of use, particularly in clients, where it may be used to create the connection to the server. ### 1.5 Server-side Requests `RequestInterface` provides the general representation of an HTTP request message. However, server-side requests need additional treatment, due to the nature of the server-side environment. Server-side processing needs to take into account Common Gateway Interface (CGI), and, more specifically, PHP's abstraction and extension of CGI via its Server APIs (SAPI). PHP has provided simplification around input marshaling via superglobals such as: - `$_COOKIE`, which deserializes and provides simplified access to HTTP cookies. - `$_GET`, which deserializes and provides simplified access to query string arguments. - `$_POST`, which deserializes and provides simplified access for urlencoded parameters submitted via HTTP POST; generically, it can be considered the results of parsing the message body. - `$_FILES`, which provides serialized metadata around file uploads. - `$_SERVER`, which provides access to CGI/SAPI environment variables, which commonly include the request method, the request scheme, the request URI, and headers. `ServerRequestInterface` extends `RequestInterface` to provide an abstraction around these various superglobals. This practice helps reduce coupling to the superglobals by consumers, and encourages and promotes the ability to test request consumers. The server request provides one additional property, "attributes", to allow consumers the ability to introspect, decompose, and match the request against application-specific rules (such as path matching, scheme matching, host matching, etc.). As such, the server request can also provide messaging between multiple request consumers. ### 1.6 Uploaded files `ServerRequestInterface` specifies a method for retrieving a tree of upload files in a normalized structure, with each leaf an instance of `UploadedFileInterface`. The `$_FILES` superglobal has some well-known problems when dealing with arrays of file inputs. As an example, if you have a form that submits an array of files — e.g., the input name "files", submitting `files[0]` and `files[1]` — PHP will represent this as: ```php array( 'files' => array( 'name' => array( 0 => 'file0.txt', 1 => 'file1.html', ), 'type' => array( 0 => 'text/plain', 1 => 'text/html', ), /* etc. */ ), ) ``` instead of the expected: ```php array( 'files' => array( 0 => array( 'name' => 'file0.txt', 'type' => 'text/plain', /* etc. */ ), 1 => array( 'name' => 'file1.html', 'type' => 'text/html', /* etc. */ ), ), ) ``` The result is that consumers need to know this language implementation detail, and write code for gathering the data for a given upload. Additionally, scenarios exist where `$_FILES` is not populated when file uploads occur: - When the HTTP method is not `POST`. - When unit testing. - When operating under a non-SAPI environment, such as [ReactPHP](http://reactphp.org). In such cases, the data will need to be seeded differently. As examples: - A process might parse the message body to discover the file uploads. In such cases, the implementation may choose *not* to write the file uploads to the file system, but instead wrap them in a stream in order to reduce memory, I/O, and storage overhead. - In unit testing scenarios, developers need to be able to stub and/or mock the file upload metadata in order to validate and verify different scenarios. `getUploadedFiles()` provides the normalized structure for consumers. Implementations are expected to: - Aggregate all information for a given file upload, and use it to populate a `Psr\Http\Message\UploadedFileInterface` instance. - Re-create the submitted tree structure, with each leaf being the appropriate `Psr\Http\Message\UploadedFileInterface` instance for the given location in the tree. The tree structure referenced should mimic the naming structure in which files were submitted. In the simplest example, this might be a single named form element submitted as: ```html ``` In this case, the structure in `$_FILES` would look like: ```php array( 'avatar' => array( 'tmp_name' => 'phpUxcOty', 'name' => 'my-avatar.png', 'size' => 90996, 'type' => 'image/png', 'error' => 0, ), ) ``` The normalized form returned by `getUploadedFiles()` would be: ```php array( 'avatar' => /* UploadedFileInterface instance */ ) ``` In the case of an input using array notation for the name: ```html ``` `$_FILES` ends up looking like this: ```php array ( 'my-form' => array ( 'name' => array ( 'details' => array ( 'avatar' => 'my-avatar.png', ), ), 'type' => array ( 'details' => array ( 'avatar' => 'image/png', ), ), 'tmp_name' => array ( 'details' => array ( 'avatar' => 'phpmFLrzD', ), ), 'error' => array ( 'details' => array ( 'avatar' => 0, ), ), 'size' => array ( 'details' => array ( 'avatar' => 90996, ), ), ), ) ``` And the corresponding tree returned by `getUploadedFiles()` should be: ```php array( 'my-form' => array( 'details' => array( 'avatar' => /* UploadedFileInterface instance */ ), ), ) ``` In some cases, you may specify an array of files: ```html Upload an avatar: Upload an avatar: ``` (As an example, JavaScript controls might spawn additional file upload inputs to allow uploading multiple files at once.) In such a case, the specification implementation must aggregate all information related to the file at the given index. The reason is because `$_FILES` deviates from its normal structure in such cases: ```php array ( 'my-form' => array ( 'name' => array ( 'details' => array ( 'avatars' => array ( 0 => 'my-avatar.png', 1 => 'my-avatar2.png', 2 => 'my-avatar3.png', ), ), ), 'type' => array ( 'details' => array ( 'avatars' => array ( 0 => 'image/png', 1 => 'image/png', 2 => 'image/png', ), ), ), 'tmp_name' => array ( 'details' => array ( 'avatars' => array ( 0 => 'phpmFLrzD', 1 => 'phpV2pBil', 2 => 'php8RUG8v', ), ), ), 'error' => array ( 'details' => array ( 'avatars' => array ( 0 => 0, 1 => 0, 2 => 0, ), ), ), 'size' => array ( 'details' => array ( 'avatars' => array ( 0 => 90996, 1 => 90996, 2 => 90996, ), ), ), ), ) ``` The above `$_FILES` array would correspond to the following structure as returned by `getUploadedFiles()`: ```php array( 'my-form' => array( 'details' => array( 'avatars' => array( 0 => /* UploadedFileInterface instance */, 1 => /* UploadedFileInterface instance */, 2 => /* UploadedFileInterface instance */, ), ), ), ) ``` Consumers would access index `1` of the nested array using: ```php $request->getUploadedFiles()['my-form']['details']['avatars'][1]; ``` Because the uploaded files data is derivative (derived from `$_FILES` or the request body), a mutator method, `withUploadedFiles()`, is also present in the interface, allowing delegation of the normalization to another process. In the case of the original examples, consumption resembles the following: ```php $file0 = $request->getUploadedFiles()['files'][0]; $file1 = $request->getUploadedFiles()['files'][1]; printf( "Received the files %s and %s", $file0->getClientFilename(), $file1->getClientFilename() ); // "Received the files file0.txt and file1.html" ``` This proposal also recognizes that implementations may operate in non-SAPI environments. As such, `UploadedFileInterface` provides methods for ensuring operations will work regardless of environment. In particular: - `moveTo($targetPath)` is provided as a safe and recommended alternative to calling `move_uploaded_file()` directly on the temporary upload file. Implementations will detect the correct operation to use based on environment. - `getStream()` will return a `StreamInterface` instance. In non-SAPI environments, one proposed possibility is to parse individual upload files into `php://temp` streams instead of directly to files; in such cases, no upload file is present. `getStream()` is therefore guaranteed to work regardless of environment. As examples: ``` // Move a file to an upload directory $filename = sprintf( '%s.%s', create_uuid(), pathinfo($file0->getClientFilename(), PATHINFO_EXTENSION) ); $file0->moveTo(DATA_DIR . '/' . $filename); // Stream a file to Amazon S3. // Assume $s3wrapper is a PHP stream that will write to S3, and that // Psr7StreamWrapper is a class that will decorate a StreamInterface as a PHP // StreamWrapper. $stream = new Psr7StreamWrapper($file1->getStream()); stream_copy_to_stream($stream, $s3wrapper); ``` ## 2. Package The interfaces and classes described are provided as part of the [psr/http-message](https://packagist.org/packages/psr/http-message) package. ## 3. Interfaces ### 3.1 `Psr\Http\Message\MessageInterface` ```php getHeaders() as $name => $values) { * echo $name . ': ' . implode(', ', $values); * } * * // Emit headers iteratively: * foreach ($message->getHeaders() as $name => $values) { * foreach ($values as $value) { * header(sprintf('%s: %s', $name, $value), false); * } * } * * While header names are not case-sensitive, getHeaders() will preserve the * exact case in which headers were originally specified. * * @return string[][] Returns an associative array of the message's headers. * Each key MUST be a header name, and each value MUST be an array of * strings for that header. */ public function getHeaders(); /** * Checks if a header exists by the given case-insensitive name. * * @param string $name Case-insensitive header field name. * @return bool Returns true if any header names match the given header * name using a case-insensitive string comparison. Returns false if * no matching header name is found in the message. */ public function hasHeader($name); /** * Retrieves a message header value by the given case-insensitive name. * * This method returns an array of all the header values of the given * case-insensitive header name. * * If the header does not appear in the message, this method MUST return an * empty array. * * @param string $name Case-insensitive header field name. * @return string[] An array of string values as provided for the given * header. If the header does not appear in the message, this method MUST * return an empty array. */ public function getHeader($name); /** * Retrieves a comma-separated string of the values for a single header. * * This method returns all of the header values of the given * case-insensitive header name as a string concatenated together using * a comma. * * NOTE: Not all header values may be appropriately represented using * comma concatenation. For such headers, use getHeader() instead * and supply your own delimiter when concatenating. * * If the header does not appear in the message, this method MUST return * an empty string. * * @param string $name Case-insensitive header field name. * @return string A string of values as provided for the given header * concatenated together using a comma. If the header does not appear in * the message, this method MUST return an empty string. */ public function getHeaderLine($name); /** * Return an instance with the provided value replacing the specified header. * * While header names are case-insensitive, the casing of the header will * be preserved by this function, and returned from getHeaders(). * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return an instance that has the * new and/or updated header and value. * * @param string $name Case-insensitive header field name. * @param string|string[] $value Header value(s). * @return static * @throws \InvalidArgumentException for invalid header names or values. */ public function withHeader($name, $value); /** * Return an instance with the specified header appended with the given value. * * Existing values for the specified header will be maintained. The new * value(s) will be appended to the existing list. If the header did not * exist previously, it will be added. * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return an instance that has the * new header and/or value. * * @param string $name Case-insensitive header field name to add. * @param string|string[] $value Header value(s). * @return static * @throws \InvalidArgumentException for invalid header names. * @throws \InvalidArgumentException for invalid header values. */ public function withAddedHeader($name, $value); /** * Return an instance without the specified header. * * Header resolution MUST be done without case-sensitivity. * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return an instance that removes * the named header. * * @param string $name Case-insensitive header field name to remove. * @return static */ public function withoutHeader($name); /** * Gets the body of the message. * * @return StreamInterface Returns the body as a stream. */ public function getBody(); /** * Return an instance with the specified message body. * * The body MUST be a StreamInterface object. * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return a new instance that has the * new body stream. * * @param StreamInterface $body Body. * @return static * @throws \InvalidArgumentException When the body is not valid. */ public function withBody(StreamInterface $body); } ``` ### 3.2 `Psr\Http\Message\RequestInterface` ```php getQuery()` * or from the `QUERY_STRING` server param. * * @return array */ public function getQueryParams(); /** * Return an instance with the specified query string arguments. * * These values SHOULD remain immutable over the course of the incoming * request. They MAY be injected during instantiation, such as from PHP's * $_GET superglobal, or MAY be derived from some other value such as the * URI. In cases where the arguments are parsed from the URI, the data * MUST be compatible with what PHP's parse_str() would return for * purposes of how duplicate query parameters are handled, and how nested * sets are handled. * * Setting query string arguments MUST NOT change the URI stored by the * request, nor the values in the server params. * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return an instance that has the * updated query string arguments. * * @param array $query Array of query string arguments, typically from * $_GET. * @return static */ public function withQueryParams(array $query); /** * Retrieve normalized file upload data. * * This method returns upload metadata in a normalized tree, with each leaf * an instance of Psr\Http\Message\UploadedFileInterface. * * These values MAY be prepared from $_FILES or the message body during * instantiation, or MAY be injected via withUploadedFiles(). * * @return array An array tree of UploadedFileInterface instances; an empty * array MUST be returned if no data is present. */ public function getUploadedFiles(); /** * Create a new instance with the specified uploaded files. * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return an instance that has the * updated body parameters. * * @param array $uploadedFiles An array tree of UploadedFileInterface instances. * @return static * @throws \InvalidArgumentException if an invalid structure is provided. */ public function withUploadedFiles(array $uploadedFiles); /** * Retrieve any parameters provided in the request body. * * If the request Content-Type is either application/x-www-form-urlencoded * or multipart/form-data, and the request method is POST, this method MUST * return the contents of $_POST. * * Otherwise, this method may return any results of deserializing * the request body content; as parsing returns structured content, the * potential types MUST be arrays or objects only. A null value indicates * the absence of body content. * * @return null|array|object The deserialized body parameters, if any. * These will typically be an array or object. */ public function getParsedBody(); /** * Return an instance with the specified body parameters. * * These MAY be injected during instantiation. * * If the request Content-Type is either application/x-www-form-urlencoded * or multipart/form-data, and the request method is POST, use this method * ONLY to inject the contents of $_POST. * * The data IS NOT REQUIRED to come from $_POST, but MUST be the results of * deserializing the request body content. Deserialization/parsing returns * structured data, and, as such, this method ONLY accepts arrays or objects, * or a null value if nothing was available to parse. * * As an example, if content negotiation determines that the request data * is a JSON payload, this method could be used to create a request * instance with the deserialized parameters. * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return an instance that has the * updated body parameters. * * @param null|array|object $data The deserialized body data. This will * typically be in an array or object. * @return static * @throws \InvalidArgumentException if an unsupported argument type is * provided. */ public function withParsedBody($data); /** * Retrieve attributes derived from the request. * * The request "attributes" may be used to allow injection of any * parameters derived from the request: e.g., the results of path * match operations; the results of decrypting cookies; the results of * deserializing non-form-encoded message bodies; etc. Attributes * will be application and request specific, and CAN be mutable. * * @return mixed[] Attributes derived from the request. */ public function getAttributes(); /** * Retrieve a single derived request attribute. * * Retrieves a single derived request attribute as described in * getAttributes(). If the attribute has not been previously set, returns * the default value as provided. * * This method obviates the need for a hasAttribute() method, as it allows * specifying a default value to return if the attribute is not found. * * @see getAttributes() * @param string $name The attribute name. * @param mixed $default Default value to return if the attribute does not exist. * @return mixed */ public function getAttribute($name, $default = null); /** * Return an instance with the specified derived request attribute. * * This method allows setting a single derived request attribute as * described in getAttributes(). * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return an instance that has the * updated attribute. * * @see getAttributes() * @param string $name The attribute name. * @param mixed $value The value of the attribute. * @return static */ public function withAttribute($name, $value); /** * Return an instance that removes the specified derived request attribute. * * This method allows removing a single derived request attribute as * described in getAttributes(). * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return an instance that removes * the attribute. * * @see getAttributes() * @param string $name The attribute name. * @return static */ public function withoutAttribute($name); } ``` ### 3.3 `Psr\Http\Message\ResponseInterface` ```php * [user-info@]host[:port] * * * If the port component is not set or is the standard port for the current * scheme, it SHOULD NOT be included. * * @see https://tools.ietf.org/html/rfc3986#section-3.2 * @return string The URI authority, in "[user-info@]host[:port]" format. */ public function getAuthority(); /** * Retrieve the user information component of the URI. * * If no user information is present, this method MUST return an empty * string. * * If a user is present in the URI, this will return that value; * additionally, if the password is also present, it will be appended to the * user value, with a colon (":") separating the values. * * The trailing "@" character is not part of the user information and MUST * NOT be added. * * @return string The URI user information, in "username[:password]" format. */ public function getUserInfo(); /** * Retrieve the host component of the URI. * * If no host is present, this method MUST return an empty string. * * The value returned MUST be normalized to lowercase, per RFC 3986 * Section 3.2.2. * * @see http://tools.ietf.org/html/rfc3986#section-3.2.2 * @return string The URI host. */ public function getHost(); /** * Retrieve the port component of the URI. * * If a port is present, and it is non-standard for the current scheme, * this method MUST return it as an integer. If the port is the standard port * used with the current scheme, this method SHOULD return null. * * If no port is present, and no scheme is present, this method MUST return * a null value. * * If no port is present, but a scheme is present, this method MAY return * the standard port for that scheme, but SHOULD return null. * * @return null|int The URI port. */ public function getPort(); /** * Retrieve the path component of the URI. * * The path can either be empty or absolute (starting with a slash) or * rootless (not starting with a slash). Implementations MUST support all * three syntaxes. * * Normally, the empty path "" and absolute path "/" are considered equal as * defined in RFC 7230 Section 2.7.3. But this method MUST NOT automatically * do this normalization because in contexts with a trimmed base path, e.g. * the front controller, this difference becomes significant. It's the task * of the user to handle both "" and "/". * * The value returned MUST be percent-encoded, but MUST NOT double-encode * any characters. To determine what characters to encode, please refer to * RFC 3986, Sections 2 and 3.3. * * As an example, if the value should include a slash ("/") not intended as * delimiter between path segments, that value MUST be passed in encoded * form (e.g., "%2F") to the instance. * * @see https://tools.ietf.org/html/rfc3986#section-2 * @see https://tools.ietf.org/html/rfc3986#section-3.3 * @return string The URI path. */ public function getPath(); /** * Retrieve the query string of the URI. * * If no query string is present, this method MUST return an empty string. * * The leading "?" character is not part of the query and MUST NOT be * added. * * The value returned MUST be percent-encoded, but MUST NOT double-encode * any characters. To determine what characters to encode, please refer to * RFC 3986, Sections 2 and 3.4. * * As an example, if a value in a key/value pair of the query string should * include an ampersand ("&") not intended as a delimiter between values, * that value MUST be passed in encoded form (e.g., "%26") to the instance. * * @see https://tools.ietf.org/html/rfc3986#section-2 * @see https://tools.ietf.org/html/rfc3986#section-3.4 * @return string The URI query string. */ public function getQuery(); /** * Retrieve the fragment component of the URI. * * If no fragment is present, this method MUST return an empty string. * * The leading "#" character is not part of the fragment and MUST NOT be * added. * * The value returned MUST be percent-encoded, but MUST NOT double-encode * any characters. To determine what characters to encode, please refer to * RFC 3986, Sections 2 and 3.5. * * @see https://tools.ietf.org/html/rfc3986#section-2 * @see https://tools.ietf.org/html/rfc3986#section-3.5 * @return string The URI fragment. */ public function getFragment(); /** * Return an instance with the specified scheme. * * This method MUST retain the state of the current instance, and return * an instance that contains the specified scheme. * * Implementations MUST support the schemes "http" and "https" case * insensitively, and MAY accommodate other schemes if required. * * An empty scheme is equivalent to removing the scheme. * * @param string $scheme The scheme to use with the new instance. * @return static A new instance with the specified scheme. * @throws \InvalidArgumentException for invalid schemes. * @throws \InvalidArgumentException for unsupported schemes. */ public function withScheme($scheme); /** * Return an instance with the specified user information. * * This method MUST retain the state of the current instance, and return * an instance that contains the specified user information. * * Password is optional, but the user information MUST include the * user; an empty string for the user is equivalent to removing user * information. * * @param string $user The user name to use for authority. * @param null|string $password The password associated with $user. * @return static A new instance with the specified user information. */ public function withUserInfo($user, $password = null); /** * Return an instance with the specified host. * * This method MUST retain the state of the current instance, and return * an instance that contains the specified host. * * An empty host value is equivalent to removing the host. * * @param string $host The hostname to use with the new instance. * @return static A new instance with the specified host. * @throws \InvalidArgumentException for invalid hostnames. */ public function withHost($host); /** * Return an instance with the specified port. * * This method MUST retain the state of the current instance, and return * an instance that contains the specified port. * * Implementations MUST raise an exception for ports outside the * established TCP and UDP port ranges. * * A null value provided for the port is equivalent to removing the port * information. * * @param null|int $port The port to use with the new instance; a null value * removes the port information. * @return static A new instance with the specified port. * @throws \InvalidArgumentException for invalid ports. */ public function withPort($port); /** * Return an instance with the specified path. * * This method MUST retain the state of the current instance, and return * an instance that contains the specified path. * * The path can either be empty or absolute (starting with a slash) or * rootless (not starting with a slash). Implementations MUST support all * three syntaxes. * * If an HTTP path is intended to be host-relative rather than path-relative * then it must begin with a slash ("/"). HTTP paths not starting with a slash * are assumed to be relative to some base path known to the application or * consumer. * * Users can provide both encoded and decoded path characters. * Implementations ensure the correct encoding as outlined in getPath(). * * @param string $path The path to use with the new instance. * @return static A new instance with the specified path. * @throws \InvalidArgumentException for invalid paths. */ public function withPath($path); /** * Return an instance with the specified query string. * * This method MUST retain the state of the current instance, and return * an instance that contains the specified query string. * * Users can provide both encoded and decoded query characters. * Implementations ensure the correct encoding as outlined in getQuery(). * * An empty query string value is equivalent to removing the query string. * * @param string $query The query string to use with the new instance. * @return static A new instance with the specified query string. * @throws \InvalidArgumentException for invalid query strings. */ public function withQuery($query); /** * Return an instance with the specified URI fragment. * * This method MUST retain the state of the current instance, and return * an instance that contains the specified URI fragment. * * Users can provide both encoded and decoded fragment characters. * Implementations ensure the correct encoding as outlined in getFragment(). * * An empty fragment value is equivalent to removing the fragment. * * @param string $fragment The fragment to use with the new instance. * @return static A new instance with the specified fragment. */ public function withFragment($fragment); /** * Return the string representation as a URI reference. * * Depending on which components of the URI are present, the resulting * string is either a full URI or relative reference according to RFC 3986, * Section 4.1. The method concatenates the various components of the URI, * using the appropriate delimiters: * * - If a scheme is present, it MUST be suffixed by ":". * - If an authority is present, it MUST be prefixed by "//". * - The path can be concatenated without delimiters. But there are two * cases where the path has to be adjusted to make the URI reference * valid as PHP does not allow to throw an exception in __toString(): * - If the path is rootless and an authority is present, the path MUST * be prefixed by "/". * - If the path is starting with more than one "/" and no authority is * present, the starting slashes MUST be reduced to one. * - If a query is present, it MUST be prefixed by "?". * - If a fragment is present, it MUST be prefixed by "#". * * @see http://tools.ietf.org/html/rfc3986#section-4.1 * @return string */ public function __toString(); } ``` ### 3.6 `Psr\Http\Message\UploadedFileInterface` ```php **Deprecated** - As of 2014-12-30 PSR-0 has been marked as deprecated. [PSR-4] is now recommended as an alternative. > [PSR-4]: http://php-fig.org/psr/psr-4 ## 2. Dependencies As documents are expected to be replaced rather than amended, dependencies on other PSR's should be avoided whenever possible. For instance, the following is no longer permitted: > - Namespaces and classes MUST follow PSR-0. Instead - if a dependency is considered necessary by the working group creating it - then the following example can be used: > - Namespaces and classes MUST follow an autoloading PSR: [ [PSR-0] ]. The outer set of square brackets denote a "dependency list", which is a list of PSRs that are considered a compatible dependency. When more PSR's are added to the "dependency list" the same example would look like this: > - Namespaces and classes MUST follow an autoloading PSR: [ [PSR-0], [PSR-4] ]. New PSR's can be added to the "dependency list", but old PSR's can never be removed as this would break backwards compatibility. ## 3. Acceptable Amendments Other than updating the "dependency list", there are two other potentially acceptable amendment scenarios which do not require their own special vote. ### 3.1. Annotations If Errata is added which is deemed important enough by whoever is initiating the errata vote, annotations may be placed in or near the offending line so that readers know to view the errata for more information, with a link containing an anchor to that specific piece of errata. > - Something confusing about where brackets go. [cf. [errata](foo-meta.md#errata-1-foo)] This will be done as part of the errata vote, not its own. ### 3.2. Formatting & Typos If formatting is broken for any reason then changing formatting must not be considered a change to the document. These can be merged or pushed without hesitation by a secretary, as long as they don't change anything of any meaning or syntax. Some typos as trivial as a misplaced comma could have a subtle impact on meaning. Take special care not to alter backwards compatibility and create a vote if unsure. Common sense will help here. Examples: 1. HTML Tables are currently broken on php-fig.org because of the syntax used. 2. Somebody spelled something wrong and nobody spotted it for a year. 3. Problems with GitHub Markdown [workflow bylaw]: https://github.com/php-fig/fig-standards/blob/master/bylaws/002-psr-workflow.md [votes bylaw]: https://github.com/php-fig/fig-standards/blob/master/bylaws/004-votes.md ================================================ FILE: bylaws/008-psr-evolution.md ================================================ # PSR evolution ## Scope and objectives A PSR is often comprised of text and code, generally interfaces. Those interfaces are pieces of code that are released and tagged at a specific moment in time. However, the PHP language doesn't stand still; it evolves over time. This means that those interfaces need to both provide a stable contract, as well as evolve to leverage new language features that could help better enforce the behaviors described in the PSR itself. At the same time, a PSR cannot be changed after its release (at which point only erratas are allowed), to protect a package that declared compatibility from becoming de facto incompatible. This document defines a process to be followed in updating PSR interfaces, in a way that is not breaking in regard to behavior for end users, and with an appropriate upgrade path for the consumers. ## Definitions * **Consumer** - libraries and projects that consume one or more interfaces from the code released as part of the PSR in question. * **Implementer** - libraries and projects that implement one or more interfaces from code released as part of the PSR in question. * **Cross-compatibility** - the ability for a consumer or an implementer to support more than one code version of the PSR with a single release of their own. ## New releases A new minor release of a PHP-FIG package containing interfaces for a PSR MUST follow these rules: * the new release MUST follow [Semantic Versioning](https://semver.org/) rules; * the PSR behavior MUST NOT be altered; * consumers or implementers of those interfaces MUST NOT suffer breaking changes; * the PHP version constraint of the PHP-FIG package MAY be increased to leverage newer language features, especially those that would aid cross-compatibility of consumers and implementers with the old and the new versions; * the PHP version constraint of the PHP-FIG package MUST NOT be altered to use newer language features that would create cross-compatibility issues. A new major release of a PHP-FIG package containing interfaces for a PSR MUST follow the same rules, with this exception: * the new major version of the package MAY contain breaking changes if the implementing packages have a reasonable upgrade path, like the possibility of releasing a cross-compatible implementation with the previous releases; * the new major version of the package MAY refer to a new, superseding PSR. Note that if the upgrade path causes the consumers or implementers to maintain multiple versions of their libraries side-by-side, only to support multiple versions of the same PSR, the upgrade path is to be considered too steep. ### Workflow Since releasing new versions of the interfaces MUST NOT alter the PSR in its behavior, those releases can be voted in with the same process as errata changes. The new releases MUST be declared and embedded with a brief explanation and a link in the PSR document, like in the following example: > \`\`\`php interface ContainerInterface { > // code snippet here } \`\`\` > > Since [psr/container version 1.1](https://packagist.org/packages/psr/container#1.1.0), the above interface has been updated to add argument type hints. In the example above, the last line is indicative of what should be added to the specification. The meta document MUST be amended with information detailing the consumer and/or implementer upgrade path. ### Practical example A common case for an upgrade in the interfaces is to add types for parameters and returns, since they are new language features introduced by PHP 7, and many PSR interfaces predate that release. We'll use a method from PSR-11 as an example of how a PSR interface could be updated. #### PSR-11: the interface PSR-11 is released with the [`psr/container` package](https://packagist.org/packages/psr/container) and it holds the `ContainerInterface`, which has this method: ```php /** * @param string $id Identifier of the entry to look for. * * @return bool */ public function has($id); ``` This method could be updated with a new minor release that adds the argument type for `$id`: ```php public function has(string $id); ``` This change would technically be a breaking change, but thanks to the [limited contravariance possible in PHP 7.2](https://wiki.php.net/rfc/parameter-no-type-variance), we can avoid that. This means that just by requiring `"php": "^7.2"` in the `psr/container` `composer.json`, we could tag this change as a minor release, and have all the consumers and implementers be automatically cross-compatible, provided that they declare `"psr/container": "^1.0"` (or equivalent) as a constraint. After this intermediate step, it would be possible to release a new major version, adding a return type hint: ```php public function has(string $id): bool; ``` This must be released as a new major version of `psr/container` (2.0); any package that would implement this would be able to declare `"psr/container": "^1.1 || ^2.0"`, since backward compatibility to the first release would be impossible, due to the sum of covariance and contravariance rules. #### PSR-11: the implementation On the other side, the implementers would be able to do a release cycle in the opposite fashion. The first release looks like this: ```php public function has($id); ``` The second release would add the return type, maintaining compatibility with the original interface: ```php public function has($id): bool; ``` A third release would also add the argument type hint: ```php public function has(string $id): bool; ``` ================================================ FILE: bylaws/009-naming-conventions.md ================================================ Naming conventions for code released by PHP FIG =============================================== 1. Interfaces MUST be suffixed by `Interface`: e.g. `Psr\Foo\BarInterface`. 2. Abstract classes MUST be prefixed by `Abstract`: e.g. `Psr\Foo\AbstractBar`. 3. Traits MUST be suffixed by `Trait`: e.g. `Psr\Foo\BarTrait`. 4. PSR-1, 4, and 12 MUST be followed. 5. For code released as part of a PSR, the vendor namespace MUST be `Psr` and the Composer package name MUST be `psr/` (e.g., `psr/log`). 6. For code released as part of a PER or any other Auxiliary Resources, the vendor namespace MUST be `Fig` and the Composer package name MUST be `fig/` (e.g., `fig/cache-util`). 7. There MUST be a package/second-level namespace in relation with the PSR or PER that covers the code. 8. Implementations of a given PSR or PER SHOULD declare a `provides` key in their `composer.json` file in the form `psr/-implementation` with a version number that matches the PSR being implemented. For example, `"psr/-implementation": "1.0.0"`. ================================================ FILE: bylaws/010-funding.md ================================================ # Funding ## Scope and objectives The PHP-FIG is an organization composed entirely of unpaid volunteers and community members focused on improving the PHP ecosystem. As part of realizing its goals, the PHP-FIG has limited operating expenses like domain names, email accounts, and similar that require ongoing funding. This document defines the roles, rules and processes that must be followed inside the PHP-FIG in all matters regarding money, to guarantee fairness, transparency and impartiality. This document intentionally describes the only allowed processes and ways to raise, administer and spend money, so that any change to such processes requires a bylaw change vote. The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [BCP 14][] [[RFC 2119][]] [[RFC 8174][]] when, and only when, they appear in all capitals, as shown here. [BCP 14]: https://datatracker.ietf.org/doc/html/bcp14/ [RFC 2119]: https://datatracker.ietf.org/doc/html/rfc2119 [RFC 8174]: https://datatracker.ietf.org/doc/html/rfc8174 ## Fiscal hosting For handling money and fiscal matters, the PHP-FIG MUST use [Open Collective](https://opencollective.com/) as a platform and [Open Source Collective](https://opencollective.com/opensource) as a fiscal host. All three Secretaries, but only the Secretaries, MUST have full administrative access to PHP-FIG's OpenCollective account. They SHALL NOT use those permissions in a discretionary manner, except as stated explicitly in this bylaw. All funds MUST be collected into the Open Collective PHP-FIG account, so that any tax considerations will be handled automatically by the fiscal host, and every financial decision MUST be published and publicly available at the bottom of this document in the appropriate table and through Open Collective's budgetary tools. ## Raising money PHP-FIG SHALL NOT solicit donations from any party, especially from private individuals; all exceptions to this rule MUST be listed below. ### Through OpenCollective Contributions SHALL be accepted via [Open Collective](https://opencollective.com/) with the following requirements: - The description MUST include the following: 1. A link back to this document on the PHP-FIG website. 2. A statement that we'd prefer contributions from companies rather than from private individuals. 3. A statement that donations do not give any rights nor provide any corresponding service. 4. Links to other preferred options for individuals to contribute as listed in this document, if any such exist. 5. A list of overfunding recipients as listed in this document, if any such exist. - The following information MUST be configured to show: 1. Contributors and the amounts they contributed; 2. The total amount of money raised; 3. The total amount of money spent; 4. The total amount of money remaining in the account; 5. The itemized transactions, including the date, amount, and description ## Spending money Funds donated to the PHP-FIG MUST be used solely for non-personnel operating expenses. The PHP-FIG MUST NOT pay individual contributors or other personnel, like Core Committee members, Secretaries, Project Representatives or working groups unless the payment is associated with an approved expense, such as reimbursing a Secretary that has paid for an approved expense out of pocket. All expenses MUST be approved by an Approval Vote of the Core Committee. The expense MUST include whether it is one time or recurring and the frequency at which the expense is incurred. All expenses MUST be justified by their contribution to the mission of PHP-FIG. In response to supplier price changes for previously accepted recurring expenses, Secretaries MAY request Implicit Approval from the Core Committee to raise the approved amount, only if that does not exceed a 10% increase. Expenses approved by the Core Committee MUST be listed below, and reported in the Open Collective budget. ### Overfunding and disbursement Overfunding SHALL be defined as any amount raised that exceeds the amount needed to cover the existing PHP-FIG's yearly expenses, as defined in the "Approved expenses" section below, for a period of three years with a 10% cost increase year over year. For example, if the approved budget is $10, the maximum amount before funds are considered overfunding would be `$10 + $11 + $12.1 = $33.10`. The amount of funding to keep can be easily calculated by multiplying the total yearly expenses by `3.31` and rounding up. Every year in January, the Secretaries SHALL review the Open Collective account and disburse any overfunding to the list of recipients defined in the Approved disbursement recipients section below, in the amounts defined in aforementioned section. Disbursements MUST be made in such a way to prevent any additional tax or fees from being incurred by the PHP-FIG or the recipient, such as via [Collective to Collective Donations][c2c]. If for any reason disbursement to a recipient is not possible, the recipient MUST be skipped and the Core Committee MUST be notified so this bylaw can be updated accordingly. ### Approved disbursement recipients This section contains a list of recipients that are eligible to receive disbursements from the PHP-FIG. As an exception to Votes Bylaw, changes to this section only SHOULD NOT trigger a Bylaw Change Vote, but only an Approval Vote by the Core Committee. | Recipient name | Disbursement Method | Disbursement percentage | |----------------|----------------------------------|-------------------------| | PHP Foundation | [PHP Foundation Open Collective][phpfoundation] | `100%` | [phpfoundation]: https://opencollective.com/phpfoundation [c2c]: https://documentation.opencollective.com/giving-to-collectives/giving-to-other-collectives ### Approved expenses This section contains a chronological list of approved budgeting decisions, either recurring or not. As an exception to Votes Bylaw, changes to this section only SHOULD NOT trigger a Bylaw Change Vote, but only an Approval Vote by the Core Committee. | Approved since | Expense | Provider | Approved amount and frequency | |----------------|-----------------------------------------------|-----------|--------------------------------| | 2023-09-14 | Two Domains (`php-fig.com` and `php-fig.org`) | Namecheap | $33 USD / year | | 2023-09-14 | Email account (`info@php-fig.org`) | Namecheap | $15 USD / year | [RFC 2119]: https://tools.ietf.org/html/rfc2119 ================================================ FILE: bylaws/100-implementation.md ================================================ # Implementation of FIG 3.0 changes Upon the adoption of these bylaws: All currently sitting Secretaries will remain in their positions with no change in their term. The recognized Voting Representatives for member projects have 30 days to state their intent to remain member projects. If they do so, the Voting Representative will be recognized as the Project Representative going forward. No new admission vote is necessary. If the Voting Representative does not make any statement for 30 days, the project will be dropped from FIG membership. The Core Committee will be selected as though there were a 12-member vacancy according to the Vacancy rules above. The timetable of when this vote will occur will be announced by the Secretaries. All previously-approved PSRs will remain approved with no change, and will retain their current PSR number. The Editor of any PSR that is in Draft or Needs Review state will have a time limit from the time the Core Committee is seated to form an official Working Group, as defined in these bylaws. If a 5-member working group is formed then the PSR will automatically continue under these rules with no need for re-approval or re-entry. If the 5-member Working Group cannot be formed in that time then the PSR will automatically be listed as Abandoned. The exact timetable will be announced by the Secretaries. This bylaw (100) may be removed by the Secretaries when it is deemed no longer necessary to be kept and the transition to the new structure is complete. ================================================ FILE: personnel.md ================================================ # Personnel ## Current ### Secretaries | Name | Term | |---------------------------------------------------|-------------------------| | Alessandro Lai ([@AlessandroLai@phpc.social]) | 2017-11-12 - 2026-08-30 | | Mark Niebergall ([@mbniebergall@phpc.social]) | 2023-05-25 - 2027-05-30 | | Lane Staples ([@mstaples]) | 2024-02-25 - 2026-02-28 | Feel free to contact the secretaries at info AT php-fig.org. For more information on the secretaries' role, see the [bylaws](https://www.php-fig.org/bylaws/mission-and-structure/#secretaries). ### Core Committee Members | Name | Term | |---------------------------------------------------|-------------------------| | Larry Garfield ([@Crell@phpc.social]) | 2016-12-24 - 2027-05-30 | | Cees-Jan Kiewiet ([@wyri@haxim.us]) | 2016-12-24 - 2026-08-30 | | Korvin Szanto ([@korvinszanto]) | 2016-12-24 - 2026-02-22 | | Ken Guest ([@kenguest@phpc.social]) | 2022-01-30 - 2026-02-28 | | Jaap van Otterdijk ([@jaapio@phpc.social]) | 2022-08-28 - 2026-08-30 | | Navarr Barnier ([@navarr]) | 2022-08-28 - 2026-08-30 | | Vincent de Lau ([@vdelau]) | 2023-05-25 - 2027-05-30 | | Steve McDougall ([@JustSteveKing]) | 2024-02-25 - 2026-02-22 | | Matteo Beccati ([@mbeccati@phpc.social]) | 2024-02-25 - 2026-02-22 | | Mathieu Rochette ([@mathroc]) | 2024-09-29 - 2026-08-30 | | Chris Tankersley ([@dragonmantank]) | 2025-05-31 - 2027-05-30 | ### Member Projects | Project | Representative | |---------------------------------|-------------------------------------------| | [AMPHP] | Aaron Piotrowski ([@_trowski]) | | [AzuraCast] | Buster Neece ([@BusterNeece@phpc.social]) | | [CakePHP] | Larry Masters ([@PhpNut]) | | [Composer] | Jordi Boggiano ([@seldaek]) | | [Concrete CMS] | Korvin Szanto ([@korvinszanto]) | | [Contao Open Source CMS] | Leo Feyer ([@leofeyer]) | | [Horde] | Jan Schneider ([@yunosh]) | | [Hyperf] | Leo Cavalcante ([@leocavalcante]) | | [IBM i Toolkit] | Adam Culp ([@adamculp]) | | [Jackalope] | Lukas Kahwe Smith ([@lsmith]) | | [Joomla] | Robert Deutz ([@rdeutz]) | | [Laminas Project] | Matthew Weier O'Phinney ([@mwop]) | | [Lithium] | Nate Abele ([@nateabele]) | | [Magento] | Igor Miniailo ([@iminyaylo]) | | [PEAR] | Chuck Burgess ([@ashnazg@phpc.social]) | | [Phalcon] | Nikolaos Dimopoulos ([@phalconphp]) | | [Phing] | Siad Ardroumli | | [phpBB] | Marc Alexander ([@marc1706]) | | [phpDocumentor] | Mike van Riel ([@mvriel]) | | [PHPixie] | Roman Tsiupa ([@dracony_gimp]) | | [Pimcore] | Bernhard Rusch ([@bernhard_rusch]) | | [PPI Framework] | Paul Dragoonis ([@dr4goonis]) | | [PrestaShop] | Rémi Gaillard ([@xtaz07]) | | [PyroCMS] | Ryan Thompson ([@RyanThePyro]) | | [ReactPHP] | Cees-Jan Kiewiet ([@wyri@haxim.us]) | | [Revive Adserver] | Matteo Beccati ([@mbeccati@phpc.social]) | | [Sculpin] | Chris Tankersley ([@dragonmantank]) | | [Slim] | Jason Coward ([@drumshaman]) | | [SilverStripe] | Damian Mooyman | | [Stash] | Robert Hafner ([@tedivm]) | | [SugarCRM] | Andreas Sandberg ([@yellowandy]) | | [TYPO3] | Benni Mack ([@bennimack]) | | [Flow] and [Neos] | Karsten Dambekalns ([@kdambekalns]) | | [Wikibase] and [Semantic Media] | Jeroen De Dauw ([@JeroenDeDauw]) | | [Yii framework] | Alexander Makarov ([@sam_dark]) | | [Zikula] | Shefik Macauley ([@shefik_info]) | ## Past ### Former Secretaries | Name | Term | |-------------------------------------------|-------------------------| | Gary Hockin ([@GeeH]) | 2016-01-31 - 2016-01-31 | | Joe Ferguson ([@joepferguson]) | 2016-01-31 - 2016-06-29 | | Samantha Quiñones ([@ieatkillerbees]) | 2016-02-28 - 2016-12-24 | | Amanda Folson ([@ambassadorawsum]) | 2016-08-28 - 2017-11-12 | | Michael Cullum ([@michaelcullumuk]) | 2016-01-31 - 2018-01-31 | | Mark Railton ([@railto]) | 2018-01-28 - 2018-06-05 | | Margret Staples ([@mstaples]) | 2017-11-12 - 2019-05-31 | | Ian Littman ([@iansltx]) | 2018-08-26 - 2020-01-25 | | Asmir Mustafic ([@goetas_asmir]) | 2019-05-26 - 2021-05-30 | | Buster Neece ([@BusterNeece@phpc.social]) | 2020-01-25 - 2022-01-31 | | Vincent de Lau ([@vdelau]) | 2021-06-25 - 2023-05-25 | | Steve Winter ([@SteveWinterNZ]) | 2022-01-30 - 2024-02-25 | ### Former Core Committee Members | Name | Term | |-----------------------------------------------|-------------------------| | Graham Hoefer ([@greydnls]) | 2016-12-24 - 2018-01-31 | | Gary Hockin ([@GeeH]) | 2016-12-24 - 2018-04-04 | | Lukas Kahwe Smith ([@lsmith]) | 2016-12-24 - 2019-05-26 | | Sara Golemon ([@SaraMG]) | 2016-12-24 - 2019-05-26 | | Stefano Torresi ([@storresi]) | 2016-12-24 - 2020-01-25 | | Samantha Quiñones ([@ieatkillerbees]) | 2016-12-24 - 2020-08-31 | | Beau Simensen ([@beausimensen]) | 2016-12-24 - 2020-08-31 | | Michael Cullum ([@michaelcullumuk]) | 2018-01-28 - 2020-01-25 | | Matteo Beccati ([@mbeccati@phpc.social]) | 2019-05-26 - 2021-05-30 | | Massimiliano Arione ([@garakkio]) | 2020-01-25 - 2022-01-31 | | Ben Edmunds ([@benedmunds]) | 2020-08-31 - 2022-08-31 | | Woody Gilk ([@shadowhand]) | 2019-05-26 - 2022-08-31 | | Michelle Sanver ([@michellesanver]) | 2021-06-25 - 2023-05-25 | | Chris Tankersley ([@dragonmantank]) | 2016-12-24 - 2024-02-25 | | Enrico Zimuel ([@ezimuel]) | 2020-01-25 - 2024-02-25 | | Chuck Burgess ([@ashnazg@phpc.social]) | 2018-08-26 - 2024-08-28 | | Alessandro Chitolina ([@alekitto@phpc.social])| 2021-06-25 - 2025-05-30 | | Matthew Weier O'Phinney ([@mwop]) | 2016-12-24 - 2025-05-30 | ### Former Member Projects | Project | Representative | |----------------------------------------|---------------------------------------| | [Aura] and [Solar] | Paul M. Jones ([@pmjones]) | | [Assetic] | Kris Wallsmith ([@kriswallsmith]) | | [Agavi] | David Zülke ([@dzuelke]) | | [Doctrine] | Guilherme Blanco ([@guilhermeblanco]) | | [eZ Publish] | Andre Romcke ([@andrerom]) | | [Guzzle] | Jeremy Lindblom ([@jeremeamia]) | | [Laravel] | Taylor Otwell ([@taylorotwell]) | | [The League of Extraordinary Packages] | Graham Hoefer ([@greydnls]) | | [Phergie] | Joe Ferguson ([@joepferguson]) | | [Propel] | William Durand ([@couac]) | | [sabre/dav] | Evert Pot ([@evertp]) | | [Stormpath PHP SDK] | Brian Retterer ([@bretterer]) | | [Symfony] | Fabien Potencier ([@fabpot]) | | [Drupal] | *vacant* | [@adamculp]: https://twitter.com/adamculp [@alekitto@phpc.social]: https://phpc.social/@alekitto [@AlessandroLai@phpc.social]: https://phpc.social/@AlessandroLai [@ambassadorawsum]: https://twitter.com/ambassadorawsum [@andrerom]: https://twitter.com/andrerom [@ashnazg@phpc.social]: https://phpc.social/@ashnazg [@beausimensen]: https://twitter.com/beausimensen [@benedmunds]: https://twitter.com/benedmunds [@bennimack]: https://twitter.com/bennimack [@bernhard_rusch]: https://twitter.com/bernhard_rusch [@bretterer]: https://twitter.com/bretterer [@BusterNeece@phpc.social]: https://phpc.social/@BusterNeece [@couac]: https://twitter.com/couac [@Crell@phpc.social]: https://phpc.social/@Crell [@mstaples]: https://github.com/mstaples [@dr4goonis]: https://twitter.com/dr4goonis [@dracony_gimp]: https://twitter.com/dracony_gimp [@dragonmantank]: https://twitter.com/dragonmantank [@drumshaman]: https://twitter.com/drumshaman [@dzuelke]: https://twitter.com/dzuelke [@evertp]: https://twitter.com/evertp [@ezimuel]: https://twitter.com/ezimuel [@fabpot]: https://twitter.com/fabpot [@garakkio]: https://twitter.com/garakkio [@GeeH]: https://twitter.com/GeeH [@goetas_asmir]: https://twitter.com/goetas_asmir [@greydnls]: https://twitter.com/greydnls [@guilhermeblanco]: https://twitter.com/guilhermeblanco [@iansltx]: https://twitter.com/iansltx [@ieatkillerbees]: https://twitter.com/ieatkillerbees [@iminyaylo]: https://twitter.com/iminyaylo [@jaapio@phpc.social]: https://phpc.social/@jaapio [@jeremeamia]: https://twitter.com/jeremeamia [@JeroenDeDauw]: https://twitter.com/JeroenDeDauw [@joepferguson]: https://twitter.com/joepferguson [@JustSteveKing]: https://twitter.com/JustSteveKing [@kdambekalns]: https://twitter.com/kdambekalns [@kenguest@phpc.social]: https://phpc.social/@kenguest [@korvinszanto]: https://twitter.com/korvinszanto [@kriswallsmith]: https://twitter.com/kriswallsmith [@leocavalcante]: https://twitter.com/leocavalcante [@leofeyer]: https://twitter.com/leofeyer [@lsmith]: https://twitter.com/lsmith [@marc1706]: https://twitter.com/marc1706 [@mathroc]: https://github.com/mathroc [@mbeccati@phpc.social]: https://phpc.social/@mbeccati [@mbniebergall@phpc.social]: https://phpc.social/@mbniebergall [@michaelcullumuk]: https://twitter.com/michaelcullumuk [@michellesanver]: https://twitter.com/michellesanver [@michieltcs]: https://twitter.com/michieltcs [@mvriel]: https://twitter.com/mvriel [@mwop]: https://twitter.com/mwop [@nateabele]: https://twitter.com/nateabele [@navarr]: https://twitter.com/navarr [@phalconphp]: https://twitter.com/phalconphp [@PhpNut]: https://twitter.com/PhpNut [@pmjones]: https://twitter.com/pmjones [@railto]: https://twitter.com/railto [@rdeutz]: https://twitter.com/rdeutz [@RyanThePyro]: https://twitter.com/RyanThePyro [@sam_dark]: https://twitter.com/sam_dark [@SaraMG]: https://twitter.com/SaraMG [@seldaek]: https://twitter.com/seldaek [@shadowhand]: https://twitter.com/shadowhand [@shefik_info]: https://twitter.com/shefik_info [@SteveWinterNZ]: http://www.tedivm.com/SteveWinterNZ [@storresi]: https://twitter.com/storresi [@taylorotwell]: https://twitter.com/taylorotwell [@tedivm]: https://twitter.com/tedivm [@_trowski]: https://twitter.com/_trowski [@vdelau]: https://github.com/vdelau [@wyri@haxim.us]: https://toot-toot.wyrihaxim.us/@wyri [@xtaz07]: https://twitter.com/xtaz07 [@yellowandy]: https://twitter.com/yellowandy [@yunosh]: https://twitter.com/yunosh [Agavi]: http://www.agavi.org [AMPHP]: https://amphp.org/ [Assetic]: https://github.com/kriswallsmith/assetic [Aura]: http://auraphp.github.com [AzuraCast]: https://www.azuracast.com/ [CakePHP]: http://cakephp.org [Composer]: http://getcomposer.org [Concrete CMS]: http://www.concretecms.org [Contao Open Source CMS]: https://contao.org [Doctrine]: http://www.doctrine-project.org [Drupal]: http://drupal.org [eZ Publish]: http://ez.no [Flow]: https://flow.neos.io/ [Guzzle]: http://guzzlephp.org [Horde]: http://www.horde.org [Hyperf]: https://hyperf.io [IBM i Toolkit]: https://github.com/zendtech/IbmiToolkit [Jackalope]: http://jackalope.github.com [Joomla]: http://www.joomla.org [Laminas Project]: https://getlaminas.org [Laravel]: http://laravel.com [Lithium]: http://li3.me [Magento]: http://magento.com [Neos]: https://neos.io [PEAR]: http://pear.php.net [Phalcon]: http://www.phalconphp.com [Phergie]: https://www.phergie.org [Phing]: http://www.phing.info [phpBB]: http://www.phpbb.com [phpDocumentor]: http://www.phpdoc.org [PHPixie]: http://phpixie.com [Pimcore]: http://www.pimcore.org [PPI Framework]: https://github.com/ppi [PrestaShop]: http://www.prestashop.com [Propel]: http://www.propelorm.org [PyroCMS]: http://www.pyrocms.com [ReactPHP]: http://reactphp.org [Revive Adserver]: http://www.revive-adserver.com [sabre/dav]: http://sabre.io [Sculpin]: https://sculpin.io [Semantic Media]: http://www.semantic-mediawiki.org [SilverStripe]: http://www.silverstripe.org [Slim]: http://www.slimframework.com [Solar]: http://solarphp.com [Stash]: http://www.tedivm.com/stash [Stormpath PHP SDK]: http://www.stormpath.com [SugarCRM]: http://developers.sugarcrm.com/wordpress [Symfony]: http://www.symfony.com [The League of Extraordinary Packages]: http://thephpleague.com [TYPO3]: https://typo3.org [Wikibase]: http://www.wikiba.se [Yii framework]: http://www.yiiframework.com [Zikula]: https://github.com/zikula ================================================ FILE: proposed/.placeholder ================================================ ================================================ FILE: proposed/internationalization-meta.md ================================================ # Internationalization Meta Document ## 1. Summary Developers of components that are not coupled to any specific framework often find themselves in need of displaying a message to the user of the web application. However, being as they are not a component of a specific framework, their options for making these messages localizable is limited. In many cases, developers will make no attempt to pass these messages through a localization layer. To resolve this issue and encourage the localization of libraries, a standard method for translating and transforming messages is necessary. ## 3. Scope ### 3.1. Goals * Provide a method for an independent component to display a message in a language other than the one in which the component was written. ### 3.2. Non-Goals * This PSR does not provide a standard for the storage and management of translatable items and their translations. That is, this PSR is about denoting that a _message is translatable_ - **not** providing the specific translations for it. * This PSR does not provide a mechanism for collecting translatable items from a component's source code. * This PSR only addresses text present in PHP code. It is not concerned with the translation of user content stored in a database or similar data store. ## 4. Approaches To solve this, we currently aim to create an interface that a framework-independent component can rely on for transforming a message key and context into a translated and formatted string. ## 5. People ### 5.1. Editor * Navarr Barnier ### 5.2. Sponsor * Larry Garfield ### 5.3. Working group members * Alexander Makarov * Susanne Moog * Ken Guest * Ben Ramsey * Vincent de Lau ## 6. Votes * [Entrance Vote](https://groups.google.com/g/php-fig/c/AgzdtXS_iU0) ================================================ FILE: proposed/internationalization.md ================================================ # Common Interfaces and Functionality for Interoperability of Message Translation and Formatting The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMEND", "MAY", and "OPTIONAL" in this document are to interpreted as described in [RFC 2119][]. The final implementations MAY decorate the objects with more functionality than covered in the recommendation however they MUST implement the indicated interfaces and functionality first. [RFC 2119]: http://tools.ietf.org/html/rfc2119 # 1. Specification TBD. ================================================ FILE: proposed/phpdoc-meta.md ================================================ # PHPDoc Meta Document ## 1. Summary PHPDoc as a notation was first presented in 2000 by Ulf Wendel, is heavily inspired by JavaDoc, and is currently in use by a significant percentage of public PHP projects in the field. ## 2. Why Bother? PHPDocumentor has facilitated the growth of the PHPDoc notation, but with the growing number of other tools that use the PHPDoc notation, it is becoming increasingly important to have a formal standard instead of its de-facto status. Pros: * Developers (consumers) have a common reference to refer to when confronted with PHPDoc. * Projects and their Developers (contributors) have an authoritative reference which they can consult. * IDE vendors can standardize the way they use PHPDoc to aid in concerns such as auto-completion and navigation. * Projects using the PHPDoc data to complement their functionality, such as Documentation generators or static analysis tools, will have a common language with their consumers. Cons: * If there are different uses of elements in the PHPDoc notation, then it is desirable for projects to align with this specification, which will cost effort to introduce. * Given the age of the current standard and widespread adoption, it is not possible to introduce significant breaks in backwards compatibility with the current practices without a significant risk of alienating existing users or vendors. ## 3. Scope ### 3.1 Goals * Provide a complete technical definition, or schema, of the PHPDoc notation. * Introduce new concepts matching best practices or design patterns in use today. ### 3.2 Non-Goals * This PSR does not provide a recommendation on how and when to use the concepts described in this document, so it is not a coding standard. ## 4. Approaches ### 4.1 Chosen Approach We have decided to formalize the existing practices, observe non-documented usages (such as Doctrine-style annotations), and observe feature requests with Documentation generators (such as phpDocumentor). The combination of these should be described in sufficient detail as to reduce the amount of possible interpretation. In addition to the above, the authors have taken care to provide for future expansions and tag additions that do not affect the Syntax of PHPDoc itself. Pros: * Delivers a machine-parsable and verifiable specification. * Well-rounded proposal due to the number of factors considered. Cons: * Technical and verbose. * Can only be extended when the syntax is not affected. ## 5. People ### 5.1 Editor * Jaap van Otterdijk - [phpDocumentor](https://github.com/phpDocumentor/phpDocumentor2) ### 5.2 Sponsor * Ken Guest - [PHP-FIG](https://www.php-fig.org/) ### 5.3 Working Group members * Alexander Makarov - [Yii](https://github.com/yiisoft/yii2) * TBA ## 6. Votes * [Entrance Vote](https://groups.google.com/forum/#!topic/php-fig/5Yd0XGd349Q) * **Acceptance Vote**: TBD ## 7. Relevant Links Most of the relevant links are mentioned in the PSR itself as support for individual chapters. ## 8. Past contributors Since this document stems from the work of a lot of people in previous years, we should recognize their effort: * Chuck Burgess (previous Editor) * Mike van Riel (previous Editor) * Phil Sturgeon (previous sponsor) * Donald Gilbert (previous sponsor) * Gary Jones (contributor) _**Note:** Order descending chronologically._ * [Original draft](https://github.com/phpDocumentor/phpDocumentor2/commit/0dbdbfa318d197279b414e5c0d1ffb142b31a528#docs/PSR.md) ================================================ FILE: proposed/phpdoc-tags-meta.md ================================================ # PHPDoc Tags Meta Document ## 1. Summary The purpose of this PSR is to document (in a catalog style) the de facto list of tags historically in use in the PHP community. ## 2. Why Bother? We wish to properly standardize the de facto usage of tags as code documentation. ## 3. Scope ### 3.1 Goals * Provide a complete technical definition, or schema, of the common tags in PHPDoc notation. * Introduce new concepts matching best practices or design patterns in use today. ### 3.2 Non-Goals * This PSR does not provide a recommendation on how and when to use the concepts described in this document, so it is not a coding standard. ## 4. Approaches ### 4.1 Chosen Approach We have decided to formalize the existing practices, observe non-documented usages (such as Doctrine-style annotations), and observe feature requests with Documentation generators (such as phpDocumentor). The combination of these should be described in sufficient detail as to reduce the amount of possible interpretation. In addition to the above, the authors have taken care to provide for future expansions and tag additions that do not affect the Syntax of PHPDoc itself. Pros: * Delivers a machine-parsable and verifiable specification. * Well-rounded proposal due to the number of factors considered. Cons: * Technical and verbose. * Can only be extended when the syntax is not affected. ## 5. People ### 5.1 Editor * Chuck Burgess - [PEAR](https://pear.php.net) ### 5.2 Sponsor * Ken Guest ### 5.3 Working group members * Jaap van Otterdijk - [phpDocumentor](https://github.com/phpDocumentor/phpDocumentor2) * Alexander Makarov - [Yii](https://github.com/yiisoft/yii2) ## 6. Votes * [Entrance Vote](https://groups.google.com/forum/#!topic/php-fig/5Yd0XGd349Q) * **Acceptance Vote**: TBD ## 7. Relevant Links Most of the relevant links are mentioned in the PSR itself as support for individual chapters. _**Note:** Order descending chronologically._ * [Original draft](https://github.com/phpDocumentor/phpDocumentor2/commit/0dbdbfa318d197279b414e5c0d1ffb142b31a528#docs/PSR.md) ================================================ FILE: proposed/phpdoc-tags.md ================================================ PSR-19: PHPDoc tags ============= ## Table Of Contents - [1. Introduction](#1-introduction) - [2. Conventions Used In This Document](#2-conventions-used-in-this-document) - [3. Definitions](#3-definitions) - [4. Inheritance](#4-inheritance) - [4.1. Making inheritance explicit using the @inheritDoc tag](#41-making-inheritance-explicit-using-the-inheritdoc-tag) - [4.2. Using the {@inheritDoc} inline tag to augment a Description](#42-using-the-inheritdoc-inline-tag-to-augment-a-description) - [4.3. Element-specific inherited parts](#43-element-specific-inherited-parts) - [4.3.1. Class Or Interface](#431-class-or-interface) - [4.3.2. Function Or Method](#432-function-or-method) - [4.3.3. Constant Or Property](#433-constant-or-property) - [5. Tags](#5-tags) - [5.1. @api](#51-api) - [5.2. @author](#52-author) - [5.3. @copyright](#53-copyright) - [5.4. @deprecated](#54-deprecated) - [5.5. @generated](#55-generated) - [5.6. @internal](#56-internal) - [5.7. @link](#57-link) - [5.8. @method](#58-method) - [5.9. @package](#59-package) - [5.10. @param](#510-param) - [5.11. @property](#511-property) - [5.12. @return](#512-return) - [5.13. @see](#513-see) - [5.14. @since](#514-since) - [5.15. @throws](#515-throws) - [5.16. @todo](#516-todo) - [5.17. @uses](#517-uses) - [5.18. @var](#518-var) - [5.19. @version](#519-version) ## 1. Introduction The main purpose of this PSR is to provide a complete catalog of Tags in the [PHPDoc standard][PHPDOC_PSR]. This document SHALL NOT: * Describe a catalog of Annotations. * Describe best practices or recommendations for Coding Standards on the application of the PHPDoc standard. This document is limited to a formal specification of syntax and intention. ## 2. Conventions Used In This Document The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119][RFC2119]. ## 3. Definitions See the Definitions section of the [PHPDoc PSR][PHPDOC_PSR], as those definitions apply here as well. ## 4. Regarding Inheritance A PHPDoc that is associated with a "Structural Element" that implements, extends or overrides a "Structural Element" has the ability to inherit parts of information from the PHPDoc associated with the "Structural Element" that is implemented, extended or overridden. The PHPDoc for every type of "Structural Element" MUST inherit the following parts if that part is absent: * [Summary][PHPDOC_PSR_SUMMARY] * [Description][PHPDOC_PSR_DESCRIPTION] and * A specific subset of [Tags][PHPDOC_PSR_TAGS]: * [@author](#52-author) * [@copyright](#53-copyright) * [@version](#519-version) The PHPDoc for each type of "Structural Element" MUST also inherit a specialized subset of tags depending on which "Structural Element" is associated. If a PHPDoc does not feature a part, such as Summary or Description, that is present in the PHPDoc of a super-element, then that part is always implicitly inherited. The following is a list of all elements whose DocBlocks are able to inherit information from a super-element's DocBlock: 1. a Class' or Interface's DocBlock can inherit information from a Class or Interface which it extends. 2. a Property's DocBlock can inherit information from a Property with the same name that is declared in a superclass. 3. a Method's DocBlock can inherit information from a Method with the same name that is declared in a superclass. 4. a Method's DocBlock can inherit information from a Method with the same name that is declared in an implemented interface in the current Class or that is implemented in a superclass. > For example: > > Let's assume you have a method `\SubClass::myMethod()` and its class > `\SubClass` extends the class `\SuperClass`. And in the class `\SuperClass` > there is a method with the same name (e.g. `\SuperClass::myMethod`). > > If the above applies then the DocBlock of `\SubClass::myMethod()` will > inherit any of the parts mentioned above from the PHPDoc of > `\SuperClass::myMethod`. So if the `@version` tag was not redefined then it > is assumed that `\SubClass::myMethod()` will have the same `@version` > tag. Inheritance takes place from the root of a class hierarchy graph to its leafs. This means that anything inherited in the bottom of the tree MUST 'bubble' up to the top unless overridden. ## 4.1. Making inheritance explicit using the @inheritDoc tag Because inheritance is implicit it may happen that it is not necessary to include a PHPDoc with a "Structural Element". This can cause confusion as it is now ambiguous whether the PHPDoc was omitted on purpose or whether the author of the code had forgotten to add documentation. In order to resolve this ambiguity the `@inheritDoc` tag can be used to indicate that this element will inherit its information from a super-element. Example: /** * This is a summary. */ class SuperClass { } /** * @inheritDoc */ class SubClass extends SuperClass { } In the example above the SubClass' Summary can be considered equal to that of the SuperClass element, which is thus "This is a summary.". ## 4.2. Using the {@inheritDoc} inline tag to augment a Description Sometimes you want to inherit the Description of a super-element and add your own text with it to provide information specific to your "Structural Element". This MUST be done using the `{@inheritDoc}` inline tag. The `{@inheritDoc}` inline tag will indicate that at that location the super-element's description MUST be injected or inferred. Example: /** * This is the Summary for this element. * * {@inheritDoc} * * In addition this description will contain more information that * will provide a detailed piece of information specific to this * element. */ In the example above it is indicated that the Description of this PHPDoc is a combination of the Description of the super-element, indicated by the `{@inheritDoc}` inline tag, and the subsequent body text. ## 4.3. Element-specific inherited parts ### 4.3.1. Class Or Interface In addition to the inherited descriptions and tags as defined in this chapter's root, a class or interface MUST inherit the following tags: * [@package](#59-package) ### 4.3.2. Function Or Method In addition to the inherited descriptions and tags as defined in this chapter's root, a function or method in a class or interface MUST inherit the following tags: * [@param](#510-param) * [@return](#512-return) * [@throws](#515-throws) ### 4.3.3. Constant Or Property In addition to the inherited descriptions and tags as defined in this chapter's root, a constant or property in a class MUST inherit the following tags: * [@var](#518-type) ## 5. Tags Unless specifically mentioned in the description each tag MAY occur zero or more times in each "DocBlock". ### 5.1. @api The @api tag is used to highlight "Structural Elements" as being part of the primary public API of a package. #### Syntax @api #### Description The `@api` tag MAY be applied to public "Structural Elements" to highlight them in generated documentation, pointing the consumer to the primary public API components of a library or framework. Other "Structural Elements" with a public visibility MAY be listed less prominently in generated documentation. See also the [`@internal`](#56-internal), which MAY be used to hide internal "Structural Elements" from generated documentation. #### Examples ```php class UserService { /** * This method is public-API. * * @api */ public function getUser() { <...> } /** * This method is "package scope", not public-API */ public function callMefromAnotherClass() { <...> } } ``` ### 5.2. @author The @author tag is used to document the author of any "Structural Element". #### Syntax @author [name] [] #### Description The @author tag can be used to indicate who has created a "Structural Element" or has made significant modifications to it. This tag MAY also contain an e-mail address. If an e-mail address is provided it MUST follow the author's name and be contained in chevrons, or angle brackets, and MUST adhere to the syntax defined in RFC 2822. #### Examples ```php /** * @author My Name * @author My Name */ ``` ### 5.3. @copyright The @copyright tag is used to document the copyright information of any "Structural element". #### Syntax @copyright #### Description The @copyright tag defines who holds the copyright over the "Structural Element". The copyright indicated with this tag applies to the "Structural Element" to which it applies and all child elements unless otherwise noted. The format of the description is governed by the coding standard of each individual project. It is RECOMMENDED to mention the year or years which are covered by this copyright and the organization involved. #### Examples ```php /** * @copyright 1997-2005 The PHP Group */ ``` ### 5.4. @deprecated The @deprecated tag is used to indicate which 'Structural elements' are deprecated and are to be removed in a future version. #### Syntax @deprecated [<"Semantic Version">] [] #### Description The @deprecated tag declares that the associated 'Structural elements' will be removed in a future version as it has become obsolete or its usage is otherwise not recommended, effective from the "Semantic Version" if provided. This tag MAY provide an additional description stating why the associated element is deprecated. If the associated element is superseded by another it is RECOMMENDED to add a @see tag in the same 'PHPDoc' pointing to the new element. #### Examples ```php /** * @deprecated * * @deprecated 1.0.0 * * @deprecated No longer used by internal code and not recommended. * * @deprecated 1.0.0 No longer used by internal code and not recommended. */ ``` ### 5.5. @generated The @generated tag indicates that the code has been generated using an automation script. #### Syntax @generated [description] #### Description The `@generated` is used to denote a class or a function that has been generated using an automation script. This tag should be used to warn to not change the code, since the change will be overwritten by the automation script. #### Examples Mark a class that has been generated using a PHP script. ```php /** * Index a document in Elasticsearch * * @generated class generated using bin/script.php, please DO NOT EDIT! * * @version 7.14.0 Elasticsearch */ class Index { <...> } ``` Mark a function that has been generated using a PHP script. ```php /** * @generated function generated using bin/script.php, please DO NOT EDIT! * * @param array $params parameters for the index API endpoint * * @return array response from Elasticsearch */ function index(array $params): array { <...> } ``` ### 5.6. @internal The @internal tag is used to denote that the associated "Structural Element" is a structure internal to this application or library. It may also be used inside a description to insert a piece of text that is only applicable for the developers of this software. #### Syntax @internal [description] or inline: {@internal [description]} Contrary to other inline tags, the inline version of this tag may also contain other inline tags (see second example below). #### Description The `@internal` tag indicates that the associated "Structural Element" is intended only for use within the application, library or package to which it belongs. Authors MAY use this tag to indicate that an element with public visibility should be regarded as exempt from the API - for example: * Library authors MAY regard breaking changes to internal elements as being exempt from semantic versioning. * Static analysis tools MAY indicate the use of internal elements from another library/package with a warning or notice. When generating documentation from PHPDoc comments it is RECOMMENDED to hide the associated element unless the user has explicitly indicated that internal elements should be included. An additional use of @internal is to add internal comments or additional description text inline to the Description. This may be done, for example, to withhold certain business-critical or confusing information when generating documentation from the source code of this piece of software. #### Examples Mark the count function as being internal to this project: ```php /** * @internal * * @return int Indicates the number of items. */ function count() { <...> } ``` Include a note in the Description that only Developer Docs would show. ```php /** * Counts the number of Foo. * * This method gets a count of the Foo. * {@internal Developers should note that it silently * adds one extra Foo (see {@link http://example.com}).} * * @return int Indicates the number of items. */ function count() { <...> } ``` ### 5.7. @link The @link tag indicates a custom relation between the associated "Structural Element" and a website, which is identified by an absolute URI. #### Syntax @link [URI] [description] or inline {@link [URI] [description]} #### Description The @link tag can be used to define a relation, or link, between the "Structural Element", or part of the description when used inline, to an URI. The URI MUST be complete and well-formed as specified in [RFC 2396][RFC2396]. The @link tag MAY have a description appended to indicate the type of relation defined by this occurrence. #### Examples ```php /** * @link http://example.com/my/bar Documentation of Foo. * * @return int Indicates the number of items. */ function count() { <...> } /** * This method counts the occurrences of Foo. * * When no more Foo ({@link http://example.com/my/bar}) are given this * function will add one as there must always be one Foo. * * @return int Indicates the number of items. */ function count() { <...> } ``` ### 5.8. @method The @method allows a class to know which 'magic' methods are callable. #### Syntax @method [return type] [name]([type] [parameter], [...]) [description] #### Description The @method tag is used in situation where a class contains the `__call()` magic method and defines some definite uses. An example of this is a child class whose parent has a `__call()` to have dynamic getters or setters for predefined properties. The child knows which getters and setters need to be present but relies on the parent class to use the `__call()` method to provide it. In this situation, the child class would have a @method tag for each magic setter or getter method. The @method tag allows the author to communicate the type of the arguments and return value by including those types in the signature. When the intended method does not have a return value then the return type MAY be omitted; in which case 'void' is implied. @method tags can ONLY be used in a PHPDoc that is associated with a *class* or *interface*. #### Examples ```php class Parent { public function __call() { <...> } } /** * @method setInteger(int $integer) * @method string getString() * @method void setString(int $integer) */ class Child extends Parent { <...> } ``` ### 5.9. @package The @package tag is used to categorize "Structural Elements" into logical subdivisions. #### Syntax @package [level 1]\[level 2]\[etc.] #### Description The @package tag can be used as a counterpart or supplement to Namespaces. Namespaces provide a functional subdivision of "Structural Elements" where the @package tag can provide a *logical* subdivision in which way the elements can be grouped with a different hierarchy. If, across the board, both logical and functional subdivisions are equal it is NOT RECOMMENDED to use the @package tag, to prevent maintenance overhead. Each level in the logical hierarchy MUST separated with a backslash (`\`) to be familiar to Namespaces. A hierarchy MAY be of endless depth but it is RECOMMENDED to keep the depth at less or equal than six levels. The package applies to that namespace, class or interface and their contained elements. This means that a function which is contained in a namespace with the @package tag assumes that package. This tag MUST NOT occur more than once in a "DocBlock". #### Examples ```php /** * @package PSR\Documentation\API */ ``` ### 5.10. @param The @param tag is used to document a single parameter of a function or method. #### Syntax @param ["Type"] [...]$[name] [] #### Description With the @param tag it is possible to document the type and function of a single parameter of a function or method. When provided it MUST contain a "Type" to indicate what is expected. The "name" is required only when some @param tags are omitted due to all useful info already being visible in the code signature itself. The description is OPTIONAL yet RECOMMENDED. The name of the parameter MUST be prefixed with a dollar sign (`$`) to indicate the start of a variable name. OPTIONAL variadic operator (`...`) MAY be used to indicate that the parameter is variadic. The variadic operator MAY only be applied to the last parameter. It is RECOMMENDED when documenting to use this tag with every function and method. This tag MUST NOT occur more than once per parameter in a "PHPDoc" and is limited to "Structural Elements" of type method or function. #### Examples ```php /** * Counts the number of items in the provided array. * * @param mixed[] $items Array structure to count the elements of. * * @return int Returns the number of elements. */ function count(array $items) { <...> } ``` ### 5.11. @property The `@property` tag is used to declare which "magic" properties are supported. #### Syntax @property[<-read|-write>] ["Type"] $[name] [] #### Description The `@property` tag is used when a `class` (or `trait`) implements the `__get()` and/or `__set()` "magic" methods to resolve non-literal properties at run-time. The `@property-read` and `@property-write` variants MAY be used to indicate "magic" properties that can only be read or written. The `@property` tags can ONLY be used in a PHPDoc that is associated with a *class* or *trait*. #### Example In the following example, a class `User` implements the magic `__get()` method, in order to implement a "magic", read-only `$full_name` property: ```php /** * @property-read string $full_name */ class User { /** * @var string */ public $first_name; /** * @var string */ public $last_name; public function __get($name) { if ($name === "full_name") { return "{$this->first_name} {$this->last_name}"; } } } ``` ### 5.12. @return The @return tag is used to document the return value of functions or methods. #### Syntax @return <"Type"> [description] #### Description With the @return tag it is possible to document the return type of a function or method. When provided, it MUST contain a "Type" to indicate what is returned; the description on the other hand is OPTIONAL yet RECOMMENDED in case of complicated return structures, such as associative arrays. It is RECOMMENDED to use this tag with every function or method where PHP native types are insufficient. If no `@return` type is given, and no return type declaration is provided in the code signature, an interpreter MUST interpret this as if `@return mixed` is provided. This tag MUST NOT occur more than once in a "DocBlock" and is limited to the "DocBlock" of a "Structural Element" of a method or function. #### Examples ```php /** * @return int Indicates the number of items. */ function count() { <...> } /** * @return string|null The label's text or null if none provided. */ function getLabel() { <...> } ``` ### 5.13. @see The @see tag indicates a reference from the associated "Structural Elements" to a website or other "Structural Elements". #### Syntax @see [URI | "FQSEN"] [] #### Description The @see tag can be used to define a reference to other "Structural Elements" or to a URI. When defining a reference to another "Structural Elements" you can refer to a specific element by appending a double colon and providing the name of that element (also called the "FQSEN"). A URI MUST be complete and well-formed as specified in [RFC 2396][RFC2396]. The @see tag SHOULD have a description to provide additional information regarding the relationship between the element and its target. #### Examples ```php /** * @see number_of() :alias: * @see MyClass::$items For the property whose items are counted. * @see MyClass::setItems() To set the items for this collection. * @see http://example.com/my/bar Documentation of Foo. * * @return int Indicates the number of items. */ function count() { <...> } ``` ### 5.14. @since The @since tag is used to denote _when_ an element was introduced or modified, using some description of "versioning" to that element. #### Syntax @since [<"Semantic Version">] [] #### Description Documents the "version" of the introduction or modification of any element. It is RECOMMENDED that the version matches a semantic version number (x.x.x) and MAY have a description to provide additional information. This information can be used to generate a set of API Documentation where the consumer is informed which application version is necessary for a specific element. The @since tag SHOULD NOT be used to show the current version of an element... the @version tag SHOULD be used for that purpose. #### Examples ```php /** * This is Foo * @version 2.1.7 MyApp * @since 2.0.0 introduced */ class Foo { /** * Make a bar. * * @since 2.1.5 bar($arg1 = '', $arg2 = null) * introduced the optional $arg2 * @since 2.1.0 bar($arg1 = '') * introduced the optional $arg1 * @since 2.0.0 bar() * introduced new method bar() */ public function bar($arg1 = '', $arg2 = null) { <...> } } ``` ### 5.15. @throws The @throws tag is used to indicate whether "Structural Elements" throw a specific type of Throwable (exception or error). #### Syntax @throws ["Type"] [] #### Description The @throws tag MAY be used to indicate that "Structural Elements" throw a specific type of error. The type provided with this tag MUST represent an object that is a subtype of Throwable. This tag is used to present in your documentation which error COULD occur and under which circumstances. It is RECOMMENDED to provide a description that describes the reason an exception is thrown. It is also RECOMMENDED that this tag occurs for every occurrence of an exception, even if it has the same type. By documenting every occurrence a detailed view is created and the consumer knows for which errors to check. #### Examples ```php /** * Counts the number of items in the provided array. * * @param mixed[] $array Array structure to count the elements of. * * @throws InvalidArgumentException if the provided argument is not of type * 'array'. * * @return int Returns the number of elements. */ function count($items) { <...> } ``` ### 5.16. @todo The @todo tag is used to indicate whether any development activities should still be executed on associated "Structural Elements". #### Syntax @todo [description] #### Description The @todo tag is used to indicate that an activity surrounding the associated "Structural Elements" must still occur. Each tag MUST be accompanied by a description that communicates the intent of the original author; this could however be as short as providing an issue number. #### Examples ```php /** * Counts the number of items in the provided array. * * @todo add an array parameter to count * * @return int Returns the number of elements. */ function count() { <...> } ``` ### 5.17. @uses Indicates whether the current "Structural Element" consumes the "Structural Element", or project file, that is provided as target. #### Syntax @uses ["FQSEN"] [] #### Description The `@uses` tag describes whether any part of the associated "Structural Element" uses, or consumes, another "Structural Element" that is situated in the current project. When defining a reference to another "Structural Element" you can refer to a specific element by appending a double colon and providing the name of that element (also called the "FQSEN"). This tag MUST NOT be used to indicate relations to elements outside of the system, so URLs are not usable. To indicate relations with outside elements the @see tag can be used. Applications consuming this tag, such as generators, are RECOMMENDED to provide a `@usedby` tag on the destination element. This can be used to provide a bi-directional experience and allow for static analysis. #### Examples ```php /** * @uses \SimpleXMLElement::__construct() */ function initializeXml() { <...> } ``` ```php /** * @uses MyView.php */ function executeMyView() { <...> } ``` ### 5.18. @var You may use the @var tag to document the "Type" of the following "Structural Elements": * Constants, both class and global scope * Properties * Variables, both global and local scope #### Syntax @var ["Type"] [element_name] [] #### Description The @var tag defines which type of data is represented by a value of a Constant, Property or Variable. Each Constant or Property definition or Variable where the type is ambiguous or unknown SHOULD be preceded by a DocBlock containing the @var tag. Any other variable MAY be preceded with a DocBlock containing the @var tag. The @var tag MUST contain the name of the element it documents, unless this property declaration only refers to a single property. In this case the name of the property MAY be omitted. `element_name` is used when compound statements are used to define a series of Constants or Properties. Such a compound statement can only have one DocBlock while several items are represented. #### Examples ```php /** @var int $int This is a counter. */ $int = 0; // there should be no docblock here $int++; ``` Or: ```php class Foo { /** * @var string|null Should contain a description */ protected $description = null; public function setDescription($description) { // there should be no docblock here $this->description = $description; } } ``` Another example is to document the variable in a foreach explicitly; many IDEs use this information to help you with auto-completion: ```php /** @var \Sqlite3 $sqlite */ foreach ($connections as $sqlite) { // there should be no docblock here $sqlite->open('/my/database/path'); <...> } ``` Even compound statements may be documented... these two property blocks are equivalent: ```php class Foo { /** * @var string $name Should contain a description * @var string $description Should contain a description */ protected $name, $description; } ``` ```php class Foo { protected /** * @var string Should contain a description */ $name, /** * @var string Should contain a description */ $description; } ``` Or constants: ```php class Foo { const /** * @var string Should contain a description */ MY_CONST1 = "1", /** * @var string Should contain a description */ MY_CONST2 = "2"; } ``` ### 5.19. @version The @version tag is used to denote some description of "versioning" to an element. #### Syntax @version ["Semantic Version"] [] #### Description Documents the current "version" of any element. This information can be used to generate a set of API Documentation where the consumer is informed about elements at a particular version. It is RECOMMENDED that the version number matches a semantic version number as described in the [Semantic Versioning Standard version 2.0][SEMVER2]. Version vectors from Version Control Systems are also supported, though they MUST follow the form: name-of-vcs: $vector$ A description MAY be provided, for the purpose of communicating any additional version-specific information. The @version tag MAY NOT be used to show the last modified or introduction version of an element, the @since tag SHOULD be used for that purpose. #### Examples ```php /** * File for class Foo * @version 2.1.7 MyApp * (this string denotes the application's overall version number) * @version @package_version@ * (this PEAR replacement keyword expands upon package installation) * @version $Id$ * (this CVS keyword expands to show the CVS file revision number) */ /** * This is Foo */ class Foo { <...> } ``` [RFC2119]: https://tools.ietf.org/html/rfc2119 [RFC2396]: https://tools.ietf.org/html/rfc2396 [SEMVER2]: http://www.semver.org [PHP_SUBSTR]: https://php.net/manual/function.substr.php [SPDX]: https://www.spdx.org/licenses [PHPDOC_PSR]: https://github.com/php-fig/fig-standards/blob/master/proposed/phpdoc.md [PHPDOC_PSR_SUMMARY]: https://github.com/php-fig/fig-standards/blob/master/proposed/phpdoc.md#51-summary [PHPDOC_PSR_DESCRIPTION]: https://github.com/php-fig/fig-standards/blob/master/proposed/phpdoc.md#52-description [PHPDOC_PSR_TAGS]: https://github.com/php-fig/fig-standards/blob/master/proposed/phpdoc.md#53-tags ================================================ FILE: proposed/phpdoc.md ================================================ PSR-5: PHPDoc ============= ## Table Of Contents - [1. Introduction](#1-introduction) - [2. Conventions Used In This Document](#2-conventions-used-in-this-document) - [3. Definitions](#3-definitions) - [4. Basic Principles](#4-basic-principles) - [5. The PHPDoc Format](#5-the-phpdoc-format) - [5.1. Summary](#51-summary) - [5.2. Description](#52-description) - [5.3. Tags](#53-tags) - [5.3.1. Tag Name](#531-tag-name) - [5.4. Examples](#54-examples) - [Appendix A. Types](#appendix-a-types) - [ABNF](#abnf) - [Details](#details) - [Valid Class Name](#valid-class-name) - [Keyword](#keyword) ## 1. Introduction The purpose of the PSR is to provide a formal definition of the PHPDoc standard. ## 2. Conventions Used In This Document The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119][RFC2119]. ## 3. Definitions * "PHPDoc" is a section of documentation which provides information on aspects of a "Structural Element". > It is important to note that a PHPDoc and a DocBlock are two separate > entities. The DocBlock is the combination of a DocComment, which is a type > of comment, and a PHPDoc entity. It is the PHPDoc entity that contains the > syntax as described in this specification (such as the Description and tags). * "Structural Element" is a collection of programming constructs which MAY be preceded by a DocBlock. The collection contains the following constructs: * `require` / `include` (and their `\_once` variants) * `class` / `interface` / `trait` * `function` (both standalone functions and class methods) * variables (local and global scope) and class properties * constants (global constants via `define` and class constants) It is RECOMMENDED to precede a "Structural Element" with a DocBlock where it is defined. It is common practice to have the DocBlock precede a Structural Element but it MAY also be separated by any number of empty lines. Examples: ```php /** * This is a counter. * @var int $int */ $int = 0; /** @var int $int This is a counter. */ $int = 0; /* comment block... this is not a docblock */ $int++; // single line comment... this is not a docblock $int++; ``` ```php /** * This class shows an example on where to position a DocBlock. */ class Foo { /** @var ?string $title contains a title for the Foo */ protected $title = null; /** * Sets a single-line title. * * @param string $title A text for the title. * * @return void */ public function setTitle($title) { // there should be no docblock here $this->title = $title; } } ``` It is NOT RECOMMENDED to use compound definitions for constants or properties, since the handling of DocBlocks in these situations can lead to unexpected results. If a compound statement is used, each element SHOULD have a preceding DocBlock. Example: ```php class Foo { protected /** * @var string Should contain a name */ $name, /** * @var string Should contain a description */ $description; } ``` * "DocComment" is a special type of comment which MUST - start with the character sequence `/**` followed by a whitespace character - end with `*/` and - have zero or more lines in between. When a DocComment spans multiple lines, every line MUST start with an asterisk (`*`) that SHOULD be aligned with the first asterisk of the opening clause. Single line example: ```php /** <...> */ ``` Multiline example: ```php /** * <...> */ ``` * "DocBlock" is a "DocComment" containing a single "PHPDoc" structure and represents the basic in-source representation. * "Tag" is a single piece of meta information regarding a "Structural Element". * "Type" is the determination of what type of data is associated with an element. This is used to determine the exact data type (primitive, class, object) of arguments, properties, constants, etc. See Appendix A for more detailed information about types. * "FQSEN" is an abbreviation for Fully Qualified Structural Element Name. This notation expands on the Fully Qualified Class Name and adds a notation to identify class / interface / trait members and re-apply the principles of the FQCN to Interfaces, Traits, Functions and global Constants. The following notations can be used per type of "Structural Element": - *Namespace*: `\My\Space` - *Function*: `\My\Space\myFunction()` - *Constant*: `\My\Space\MY_CONSTANT` - *Class*: `\My\Space\MyClass` - *Interface*: `\My\Space\MyInterface` - *Trait*: `\My\Space\MyTrait` - *Method*: `\My\Space\MyClass::myMethod()` - *Property*: `\My\Space\MyClass::$my_property` - *Class Constant*: `\My\Space\MyClass::MY_CONSTANT` A FQSEN has the following [ABNF][RFC5234] definition: FQSEN = fqnn / fqcn / constant / method / property / function fqnn = "\" [name] *("\" [name]) fqcn = fqnn "\" name constant = ((fqnn "\") / (fqcn "::")) name method = fqcn "::" name "()" property = fqcn "::$" name function = fqnn "\" name "()" name = (ALPHA / "_") *(ALPHA / DIGIT / "_") ## 4. Basic Principles * A PHPDoc MUST always be contained in a "DocComment"; the combination of these two is called a "DocBlock". * A DocBlock MUST directly precede a "Structural Element". ## 5. The PHPDoc Format The PHPDoc format has the following [ABNF][RFC5234] definition: PHPDoc = [summary [description]] [tags] eol = [CR] LF ; to compatible with PSR-12 summary = 1*CHAR 2*eol description = 1*(CHAR / inline-tag) 1*eol ; any amount of characters ; with inline tags inside tags = *(tag 1*eol) inline-tag = "{" tag "}" tag = "@" tag-name [":" tag-specialization] [tag-details] tag-name = (ALPHA / "\") *(ALPHA / DIGIT / "\" / "-" / "_") tag-specialization = 1*(ALPHA / DIGIT / "-") tag-details = (1*SP tag-description) tag-description = (CHAR / inline-tag) *(CHAR / inline-tag / eol) tag-argument = *SP 1*CHAR [","] *SP Examples of use are included in chapter 5.4. ### 5.1. Summary A Summary MUST contain an abstract of the "Structural Element" defining the purpose. It is RECOMMENDED for Summaries to span a single line or two, but not more than that. A Summary MUST end with two sequential line breaks, unless it is the only content in the PHPDoc. If a Description is provided, then it MUST be preceded by a Summary. Otherwise the Description risks being mistaken as the Summary. Because a Summary is comparable to a chapter title, it is RECOMMENDED to use as little formatting as possible. Contrary to the Description, no recommendation is made to support a mark-up language. ### 5.2. Description The Description is OPTIONAL but SHOULD be included when the "Structural Element" contains more complexity than can be described by the Summary alone. Any application parsing the Description is RECOMMENDED to support the Markdown mark-up language, to make it possible for the author to provide formatting and a clear way of representing code examples. Common uses for the Description: * To provide more detail on what this method does than the Summary can do * To specify of what child elements an array / object is composed * To provide a set of common use cases or scenarios in which the "Structural Element" may be applied ### 5.3. Tags Tags supply concise metadata for a "Structural Element". Each tag starts on a new line, followed by an at-sign (`@`) and a tag-name, followed by whitespace and metadata (including a description). If metadata is provided, it MAY span multiple lines and COULD follow a strict format, as dictated by the specific tag. > `@param string $argument1 This is a parameter.` > > The above tag consists of a _name_ (`param`) and metadata > ('string $argument1 This is a parameter.'), where the metadata is split into a > _type_ (`string`), variable name (`$argument1`), and description (`This is a > parameter.`). The description MUST support Markdown as a formatting language. The description of the tag MAY start on the same line or next line. The following tags are semantically identical: ```php /** * @var string This is a description. * @var string This is a * description. * @var string * This is a description. */ ``` This definition does NOT apply to _Annotation_ tags, which are not in scope. #### 5.3.1. Tag Name Tag names indicate what type of information is represented by this tag. ### 5.4. Examples The following examples serve to illustrate the basic use of DocBlocks; it is advised to read through the list of tags in the [Tag Catalog PSR][TAG_PSR]. A complete example could look like this: ```php /** * This is a Summary. * * This is a Description. It may span multiple lines * or contain `code` examples using the _Markdown_ markup * language. * * @see Markdown * * @param int $parameter1 A parameter description. * @param \Exception $e Another parameter description. * * @\Doctrine\Orm\Mapper\Entity() * * @return string */ function test($parameter1, $e) { ... } ``` The Description MAY be omitted: ```php /** * This is a Summary. * * @see Markdown * * @param int $parameter1 A parameter description. * @param \Exception $parameter2 Another parameter description. * * @\Doctrine\Orm\Mapper\Entity() * * @return string */ function test($parameter1, $parameter2) { } ``` Tags MAY also be omitted: ```php /** * This is a Summary. */ function test($parameter1, $parameter2) { } ``` A DocBlock may also span a single line: ```php /** @var \ArrayObject $array An array of things. */ public $array = null; ``` ## Appendix A. Types ### ABNF A Type has the following [ABNF][RFC5234] definition: type-expression = type *("|" type) *("&" type) type = class-name / keyword / array array = (type / array-expression) "[]" array-expression = "(" type-expression ")" class-name = ["\"] label *("\" label) label = (ALPHA / %x7F-FF) *(ALPHA / DIGIT / %x7F-FF) keyword = "array" / "bool" / "callable" / "false" / "float" / "int" / "iterable" / "mixed" / "never" keyword =/ "null" / "object" / "resource" / "self" / "static" / "string" / "true" / "void" / "$this" ### Details When a "Type" is used, the user will expect a value, or set of values, as detailed below. When the "Type" consists of multiple types, then these MUST be separated with either the vertical bar (|) for union type or the ampersand (&) for intersection type. Any interpreter supporting this specification MUST recognize this and split the "Type" before evaluating. Union type example: >`@return int|null` Intersection type example: >`@var \MyClass&\PHPUnit\Framework\MockObject\MockObject $myMockObject` #### Arrays The value represented by "Type" can be an array. The type MUST be defined following one of the following options: 1. unspecified: no definition of the contents of the array is given. Example: `@return array` 2. specified as a specific type: each member of the array is the same one type. Example: `@return int[]` Note that `mixed` is also a single type and thus can explicitly indicate that each member is any possible type. 3. specified as containing multiple explicit types: each member can be of any of the given types. Example: `@return (int|string)[]` ### Valid Class Name A valid class name is based on the context where this type is mentioned. This may be a Fully Qualified Class Name (FQCN) or a local name if present in a namespace. The element to which this type applies is either an instance of this class or an instance of a class that is a sub/child to the given class. > It is RECOMMENDED for applications that collect and shape this information to > show a list of child classes with each representation of the class. This makes > it more obvious for the user which classes are acceptable as this type. ### Keyword A keyword defines the purpose of this type. Not every element is determined by a class, but it is still worthy of classification to assist the developer in understanding the code covered by the DocBlock. > Some of these keywords are allowed as class names in PHP and can be difficult > to distinguish from real classes. As such, the keywords MUST be lowercase, as > most class names start with an uppercase first character... it is RECOMMENDED > that you not use classes with these names in your code. The following keywords are recognized by this PSR: 1. `bool`: the element to which this type applies only has state `TRUE` or `FALSE`. 2. `int`: the element to which this type applies is a whole number or integer. 3. `float`: the element to which this type applies is a continuous, or real, number. 4. `string`: the element to which this type applies is a string of binary characters. 5. `object`: the element to which this type applies is the instance of an undetermined class. 6. `array`: the element to which this type applies is an array of values. 7. `iterable`: the element to which this type applies is an array or Traversable object per the [definition of PHP][PHP_ITERABLE]. 8. `resource`: the element to which this type applies is a resource per the [definition of PHP][PHP_RESOURCE]. 9. `mixed`: the element to which this type applies can be of any type as specified here. It is not known at compile time which type will be used. 10. `void`: this type is commonly only used when defining the return type of a method or function, indicating "nothing is returned", and thus the user should not rely on any returned value. ```php /** * @return void */ function outputHello() { echo 'Hello world'; } ``` 11. `null`: the element to which this type applies is a `NULL` value or, in technical terms, does not exist. Compared to `void`, this type is used in any situation where the described element may at any given time contain an explicit `NULL` value. ```php /** * @return null */ function foo() { echo 'Hello world'; return null; } ``` ```php /** * @param bool $create_new When true returns a new stdClass. * * @return stdClass|null */ function foo($create_new) { if ($create_new) { return new stdClass(); } return null; } ``` 12. `callable`: the element to which this type applies is a pointer to a function call. This may be any type of callable as per the [definition of PHP][PHP_CALLABLE]. 13. `false` or `true`: the element to which this type applies will have the exact value `TRUE` or `FALSE`. No other value will be returned from this element. 14. `self`: the element to which this type applies is of the same class in which the documented element is originally contained. **Example:** > Method *c* is contained in class *A*. The DocBlock states that its return value is of type `self`. As such, method > *c* returns an instance of class *A*. This may lead to confusing situations when inheritance is involved. **Example (previous example situation still applies):** > Class *B* extends class *A* and does not redefine method *c*. As such, it is possible to invoke method *c* from > class *B*. In this situation, ambiguity may arise as `self` could be interpreted as either class *A* or *B*. In these cases, `self` MUST be interpreted as being an instance of the class where the DocBlock containing the `self` type is written. In the examples above, `self` MUST always refer to class *A*, since it is defined with method *c* in class *A*. > Due to the above nature, it is RECOMMENDED for applications that collect and shape this information to show a list > of child classes with each representation of the class. This would make it obvious for the user which classes are > acceptable as type. 15. `static`: the element to which this type applies is of the same class in which the documented element is contained, or, when encountered in a subclass, is of type of that subclass instead of the original class. This keyword behaves the same way as the [keyword for late static binding][PHP_OOP5LSB] (not the static method, property, nor variable modifier) as defined by PHP. 16. `$this`: the element to which this type applies is the same exact instance as the current class in the given context. As such, this type is a stricter version of `static`, because the returned instance must not only be of the same class but also the same instance. This type is often used as return value for methods implementing the [Fluent Interface][FLUENT] design pattern. 17. `never`: denotes that element isn't going to return anything and always throws exception or terminates the program abnormally (such as by calling the library function `exit`). [RFC2119]: https://tools.ietf.org/html/rfc2119 [RFC5234]: https://tools.ietf.org/html/rfc5234 [PHP_RESOURCE]: https://php.net/manual/language.types.resource.php [PHP_ITERABLE]: https://php.net/manual/language.types.iterable.php [PHP_PSEUDO]: https://php.net/manual/language.pseudo-types.php [PHP_CALLABLE]: https://php.net/manual/language.types.callable.php [PHP_OOP5LSB]: https://php.net/manual/language.oop5.late-static-bindings.php [DEFACTO]: http://www.phpdoc.org/docs/latest/index.html [PHPDOC.ORG]: http://www.phpdoc.org/ [FLUENT]: https://en.wikipedia.org/wiki/Fluent_interface [TAG_PSR]: TBD ================================================ FILE: proposed/psr-8-hug/PSR-8-hug-meta.md ================================================ PSR-8 Meta Document =================== 1. Summary ---------- The intent of this spec is to improve the overall amicability and cooperative spirit of the PHP community through a standardized means of inter-project affection and support. 2. Votes -------- - **Entrance Vote:** - **Acceptance Vote:** 3. Errata --------- 4. People --------- ### 5.1 Editor - Larry Garfield ### 5.2 Sponsors - Vacant (Coordinator) - Vacant ================================================ FILE: proposed/psr-8-hug/psr-8-hug.md ================================================ Mutually Assured Hug ==================== The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119](http://tools.ietf.org/html/rfc2119). ## 1. Overview This standard establishes a common way for objects to express mutual appreciation and support by hugging. This allows objects to support each other in a constructive fashion, furthering cooperation between different PHP projects. ## 2. Specification This specification defines two interfaces, \Psr\Hug\Huggable and \Psr\Hug\GroupHuggable. ### Huggable objects 1. A Huggable object expresses affection and support for another object by invoking its hug() method, passing $this as the first parameter. 2. An object whose hug() method is invoked MUST hug() the calling object back at least once. 3. Two objects that are engaged in a hug MAY continue to hug each other back for any number of iterations. However, every huggable object MUST have a termination condition that will prevent an infinite loop. For example, an object MAY be configured to only allow up to 3 mutual hugs, after which it will break the hug chain and return. 4. An object MAY take additional actions, including modifying state, when hugged. A common example is to increment an internal happiness or satisfaction counter. ### GroupHuggable objects 1. An object may optionally implement GroupHuggable to indicate that it is able to support and affirm multiple objects at once. ## 3. Interfaces ### HuggableInterface ```php namespace Psr\Hug; /** * Defines a huggable object. * * A huggable object expresses mutual affection with another huggable object. */ interface Huggable { /** * Hugs this object. * * All hugs are mutual. An object that is hugged MUST in turn hug the other * object back by calling hug() on the first parameter. All objects MUST * implement a mechanism to prevent an infinite loop of hugging. * * @param Huggable $h * The object that is hugging this object. */ public function hug(Huggable $h); } ``` ```php namespace Psr\Hug; /** * Defines a huggable object. * * A huggable object expresses mutual affection with another huggable object. */ interface GroupHuggable extends Huggable { /** * Hugs a series of huggable objects. * * When called, this object MUST invoke the hug() method of every object * provided. The order of the collection is not significant, and this object * MAY hug each of the objects in any order provided that all are hugged. * * @param Huggable[] $huggables * An array or iterator of objects implementing the Huggable interface. */ public function groupHug($huggables); } ``` ================================================ FILE: proposed/security-disclosure-publication-meta.md ================================================ Security Disclosure Meta Document ================================= 1. Summary ---------- There are two aspects with dealing with security issues: One is the process by which security issues are reported and fixed in projects, the other is how the general public is informed about the issues and any remedies available. While PSR-9 addresses the former, this PSR, ie. PSR-10, deals with the later. So the goal of PSR-10 is to define how security issues are disclosed to the public and what format such disclosures should follow. Especially today where PHP developers are sharing code across projects more than ever, this PSR aims to ease the challenges in keeping an overview of security issues in all dependencies and the steps required to address them. 2. Why Bother? -------------- End users want to ensure that they stay informed about security issues. However they also want to be able to quickly check if they are affected to be able to take the necessary steps. Upstream users of code will also want to know these details. Furthermore they will want to know if its possible for them to be included into possible closed discussions before details about a security issue are made public. 3. Scope -------- ## 3.1 Goals * Means to help in (semi-)automating discovery and fixing of known security issues in projects using the affected code ## 3.2 Non-Goals * Process for how vulnerabilities are reported and fixed * Methods for reducing security vulnerabilities 4. Approaches ------------- A key aspect here is that the information flow should be as structured as possible to enable as much automation as possible. For example, vulnerabilities should be published in a defined location and in a defined format. Inspiration could be taken from [1]. That being said, the standard should not rely on any central authority above the projects. This is to ensure that no project becomes depend on an outside authority for something as sensitive as security related topics. However due to defined locations and formats, it will become possible for other people to build centralized tools around this information. 5. People --------- ### 5.1 Editor * Michael Hess ### 5.2 Sponsors * Larry Garfield (Drupal) * Korvin Szanto (concrete5) ### 5.3 Coordinator * Korvin Szanto (concrete5) ### 5.4 Contributors * Lukas Kahwe Smith 6. Votes -------- 7. Relevant Links ----------------- [1]: https://github.com/FriendsOfPHP/security-advisories Initial discussion: https://groups.google.com/d/msg/php-fig/45AIj5bPHJ4/ThERB43j-u8J Discussion: https://groups.google.com/forum/#!forum/php-fig-psr-9-discussion ================================================ FILE: proposed/security-disclosure-publication.md ================================================ ## Introduction There are two aspects with dealing with security issues: One is the process by which security issues are reported and fixed in projects, the other is how the general public is informed about the issues and any remedies available. While PSR-9 addresses the former, this PSR, ie. PSR-10, deals with the later. So the goal of PSR-10 is to define how security issues are disclosed to the public and what format such disclosures should follow. Especially today where PHP developers are sharing code across projects more than ever, this PSR aims to ease the challenges in keeping an overview of security issues in all dependencies and the steps required to address them. The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119][]. [RFC 2119]: http://tools.ietf.org/html/rfc2119 ## Goal The goal of this PSR is to give project leads a clearly defined approach to enabling end users to discover security disclosures using a clearly defined structured format for these disclosures. ## Disclosure Discovery Every project MUST provide a link to its security vulnerability database in an obvious place. Ideally this should be on the root page of the main domain of the given project. This MAY be a sub-domain in case it is a sub-project of a larger initiative. If the project has a dedicated page for its disclosure process discovery then this is also considered a good place for this link. The link MAY use the custom link relation ``php-vuln-disclosures``, ie. for example ````. Note that projects MAY choose to host their disclosure files on a domain other than their main project page. It is RECOMMENDED to not store the disclosures in a VCS as this can lead to the confusions about which branch is the relevant branch. If a VCS is used then additional steps SHOULD be taken to clearly document to users which branch contains all vulnerabilities for all versions. If necessary projects MAY however split vulnerability disclosure files by major version number. In this case again this SHOULD be clearly documented. ## Disclosure Format The disclosure format is based on Atom [1], which in turn is based on XML. It leverages the "The Common Vulnerability Reporting Framework (CVRF) v1.1" [2]. Specifically it leverages its dictionary [3] as its base terminology. **TODO**: Should we also provide a JSON serialization to lower the bar for projects. Aggregation services can then spring up to provide an Atom representation of these disclosures in JSON format. The Atom extensions [4] allow a structured description of the vulnerability to enable automated tools to determine if installed is likely affected by the vulnerability. However human readability is considered highly important and as such not the full CVRF is used. **TODO**: Review the Atom format and the supplied XSD Note that for each vulnerability only a single entry MUST be created. In case any information changes the original file MUST be updated along with the last update field. Any disclosure uses ``entryType`` using the following tags from the Atom namespace (required tags are labeled with "MUST"): * title (short description of the vulnerability and affected versions, MUST) * summary (description of the vulnerability) * author (contact information, MUST) * published (initial publication date, MUST) * updated (date of the last update) * link (to reference more information) * id (project specific vulnerability id) In addition the following tags are added: * reported (initial report date) * reportedBy (contact information for the persons or entity that initially reported the vulnerability) * resolvedBy (contact information for the persons or entity that resolved the vulnerability) * name (name of the product, MUST) * cve (unique CVE ID) * cwe (unique CWE ID) * severity (low, medium high) * affected (version(s) using composer syntax [5]) * status (open, in progress, disputed, completed, MUST) * remediation (textual description for how to fix an affected system) * remediationType (workaround, mitigation, vendor fix, none available, will not fix) * remediationLink (URL to give additional information for remediation) [1] https://tools.ietf.org/html/rfc4287 [2] http://www.icasi.org/cvrf-1.1 [3] http://www.icasi.org/cvrf-1.1-dictionary [4] security-disclosure-publication.xsd [5] https://getcomposer.org/doc/01-basic-usage.md#package-versions ================================================ FILE: proposed/security-disclosure-publication.xsd ================================================ The PHP FIG security disclosure remediation construct is to be used to specify a specific remediation option for a specific vulnerability. ================================================ FILE: proposed/security-reporting-process-meta.md ================================================ Security Disclosure Meta Document ================================= 1. Summary ---------- There are two aspects with dealing with security issues: One is the process by which security issues are reported and fixed in projects, the other is how the general public is informed about the issues and any remedies available. While PSR-10 addresses the later, this PSR, ie. PSR-9, deals with the former. So the goal of PSR-9 is to define the process by which security researchers and report security vulnerabilities to projects. It is important that when security vulnerabilities are found that researchers have an easy channel to the projects in question allowing them to disclose the issue to a controlled group of people. 2. Why Bother? -------------- As of right now, there isn't a common standard for most parts of this process. That is there isn't a standard where researchers can find out about the process for handling security issues for any given project. There is also no standard that explains to researchers what they can expect to happen if they report a vulnerability. More importantly there is no standard on which projects can base the security reporting process that best fits them. 3. Scope -------- ## 3.1 Goals * A defined process for how vulnerabilities are reported, the process by which these get fixed and finally disclosed to the public ## 3.2 Non-Goals * Methods for reducing security vulnerabilities * Publication of security issues and fixes (see PSR-10) 4. Approaches ------------- Currently the most viable approach seems to be defining a base line workflow for how security vulnerabilities go from discovery to fixing to public disclosure. Inspiration could be drawn from this list of security disclosure processes in various PHP and non-PHP projects: * https://symfony.com/security * https://framework.zend.com/security * https://www.yiiframework.com/security * https://www.drupal.org/security * https://codex.wordpress.org/FAQ_Security * https://www.sugarcrm.com/legal/security-policy * https://typo3.org/teams/security/ * https://book.cakephp.org/3.0/en/contributing/tickets.html#reporting-security-issues * https://www.concrete5.org/developers/security/ * https://developer.joomla.org/security.html * https://wiki.horde.org/SecurityManagement * https://www.revive-adserver.com/support/bugs/ * https://magento.com/security * https://www.apache.org/security/committers.html * https://www.mozilla.org/en-US/about/governance/policies/security-group/bugs/ * https://www.openbsd.org/security.html A summary of the differences and similarities can be found here: https://groups.google.com/d/msg/php-fig-psr-9-discussion/puGV_X0bj_M/Jr_IAS40StsJ 5. People --------- ### 5.1 Editor * Michael Hess ### 5.2 Sponsors * Larry Garfield (Drupal) * Korvin Szanto (concrete5) ### 5.3 Coordinator * Larry Garfield (Drupal) ### 5.4 Contributors * Lukas Kahwe Smith 6. Votes -------- 7. Relevant Links ----------------- ================================================ FILE: proposed/security-reporting-process.md ================================================ ## Introduction There are two aspects with dealing with security issues: One is the process by which security issues are reported and fixed in projects, the other is how the general public is informed about the issues and any remedies available. While PSR-10 addresses the later, this PSR, ie. PSR-9, deals with the former. So the goal of PSR-9 is to define the process by which security researchers and report security vulnerabilities to projects. It is important that when security vulnerabilities are found that researchers have an easy channel to the projects in question allowing them to disclose the issue to a controlled group of people. The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119][]. [RFC 2119]: http://tools.ietf.org/html/rfc2119 ## Goal The goal of this PSR is to give researchers, project leads, upstream project leads and end users a defined and structured process for disclosing security vulnerabilities. ## Security Disclosure Process Discovery Every project MUST provide a link to its security disclosure process in an obvious place. Ideally this should be on the root page the main domain of the given project. This MAY be a sub-domain in case it is a sub-project of a larger initiative. The link MAY use the custom link relation ``php-vuln-reporting``, ie. for example ````. Projects SHOULD ideally make the location prominent itself by either creating a dedicated sub-domain like ``http://security.example.org`` or by making it a top level directory like ``http://example.org/security``. Alternatively projects MAY also simply reference this document, ie. PSR-9. By referencing PSR-9 a project basically states that they follow the default procedures as noted in the section "Default Procedures" towards the end of this document. Projects MUST list the variables noted at the start of that section in this reference (ie. project name, project domain, etc.). Projects MAY choose to list any part of the procedures that is not a MUST which they choose to omit. Note that projects MAY not have a dedicated domain. For example a project hosted on GitHub, Bitbucket or other service should still ensure that the process is referenced on the landing page, ie. for example http://github.com/example/somelib should ensure that the default branch has a README file which references the procedures used so that it is automatically displayed. If necessary projects MAY have different disclosure process for different major version number. In this case one URL MUST be provided for each major version. In the case a major version is no longer receiving security fixes, instead of an URL a project MAY opt to instead simply note that the version is no longer receiving security fixes. ## Security Disclosure Process Every project MUST provide an email address in their security disclosure process description as the ``contact email address``. Projects SHALL NOT use contact forms. **TODO**: Add more things found here https://groups.google.com/d/msg/php-fig-psr-9-discussion/puGV_X0bj_M/Jr_IAS40StsJ? ## Default Procedures * ``[project name]`` denotes the name on which the project uses to identify itself. * ``[project domain]`` denotes the main (sub)domain on which the project relies. If not specified otherwise, the ``contact email address`` is ``security@[project domain]``. **TODO**: Add more things noted in the previous section ================================================ FILE: proposed/tracing-meta.md ================================================ # Application Tracing Meta Document ## 1. Summary Application performance monitoring (APM) tools are becoming increasingly popular, however, the lack of formal interfaces is holding back the ecosystem. Whilst developers are inherently free to use whichever tools suit their use-cases, this is not the case for framework and library creators/maintainers. This standard aims to create an intentionally minimalist set of interfaces that can be used to provide tracing signals to 3rd party libraries in a unified manner. ## 2. Why Bother? The [OpenTelemetry][] team are in the process of releasing a set of SDK's that would allow frameworks and libraries to send signals to providers in a uniformed manner. However, there is a perceived expectation of a 1-way flow of responsibility, where Frameworks and Libraries are expected to accept Jagger-formatted traces. This PSR provides a bridge between Frameworks and Libraries who want to provide tracing signals, and Providers, without the requirement to make large scale changes to their infrastructure, etc. By taking transmission mechanisms [out of scope](#31-non-goals) for this PSR, we drastically increase the simple adoptability of tracing for all parties involved [OpenTelemetry]: https://opentelemetry.io/ ## 3. Scope ### 3.1 Goals * To provide a set of interfaces for library and framework developers to add tracing signals to their codebase. This would in turn allow other libraries to receive the same signals for further processing or analysis. * To allow traces collected by various providers to be reused by other providers. * This PSR may provide a minimal `TraceProvider`, etc. for other providers to extend, should they choose. ### 3.1 Non-goals * This PSR does not provide a comprehensive tracing client * This PSR does not define the mechanisms used for transmitting the data to 3rd party systems * This PSR does not cover collecting metrics within a codebase, only traces ## 4. Approaches To fulfil the requirement of most providers, this PSR will be loosely modeled on the [OpenTelemetry Tracing Specification][OTelTrace] and [Tracing API][OtelTraceApi]. We aim to allow the majority of providers to use the PSR interfaces with minimum backwards incompatible changes to encourage adoption. [OtelTrace]: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/overview.md#tracing-signal [OtelTraceApi]: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/api.md ## 5. People ### 5.1 Editor * Adam Allport ### 5.1 Sponsor * Alessandro Chitolina ### Working Group Members * Alex Bouma * Ben Edmunds * Brett McBride * Timo Michna ## 6. Votes * [Entrance Vote](TBD) ================================================ FILE: proposed/tracing.md ================================================ # Application Tracing The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMEND", "MAY", and "OPTIONAL" in this document are to interpreted as described in [RFC 2119][]. The final implementations MAY decorate the objects with more functionality than covered in the recommendation however they MUST implement the indicated interfaces and functionality first. [RFC 2119]: http://tools.ietf.org/html/rfc2119 ## Goal TBD ## Definitions * **Framework** - An application framework (or micro-framework) that runs a developers code. Eg. Laravel, Symfony, CakePHP, Slim * **Library** - Any library that developers may include that adds additional functionality Eg. Image Manipulation, HTTP Clients, 3rd Party SDK's * **Provider** - An organization that offers APM as a service. Typically via a composer package