diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..25a5e41
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,673 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ Copyright (C) 2020 MugglePay.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ MugglePayForWooCommerce Copyright (C) 2020 MugglePay.
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+.
\ No newline at end of file
diff --git a/README.md b/README.md
deleted file mode 100644
index 6c5e212..0000000
--- a/README.md
+++ /dev/null
@@ -1,2 +0,0 @@
-# MugglePayForWordPress
-MugglePay is a one-stop payment solution for merchants with an online payment need.
diff --git a/assets/.DS_Store b/assets/.DS_Store
new file mode 100644
index 0000000..8b882af
Binary files /dev/null and b/assets/.DS_Store differ
diff --git a/assets/images/.DS_Store b/assets/images/.DS_Store
new file mode 100644
index 0000000..a80fd82
Binary files /dev/null and b/assets/images/.DS_Store differ
diff --git a/assets/images/btc.png b/assets/images/btc.png
new file mode 100644
index 0000000..a36a0da
Binary files /dev/null and b/assets/images/btc.png differ
diff --git a/assets/images/eth.png b/assets/images/eth.png
new file mode 100644
index 0000000..530e535
Binary files /dev/null and b/assets/images/eth.png differ
diff --git a/assets/images/mugglepay-logo-c.png b/assets/images/mugglepay-logo-c.png
new file mode 100644
index 0000000..cc24021
Binary files /dev/null and b/assets/images/mugglepay-logo-c.png differ
diff --git a/assets/images/usdc.png b/assets/images/usdc.png
new file mode 100644
index 0000000..f472ec6
Binary files /dev/null and b/assets/images/usdc.png differ
diff --git a/assets/images/usdt.png b/assets/images/usdt.png
new file mode 100644
index 0000000..f896614
Binary files /dev/null and b/assets/images/usdt.png differ
diff --git a/assets/js/blocks/mpwp.js b/assets/js/blocks/mpwp.js
new file mode 100644
index 0000000..dc5f915
--- /dev/null
+++ b/assets/js/blocks/mpwp.js
@@ -0,0 +1,35 @@
+(() => {
+ const { wcSettings, wcBlocksRegistry } = window.wc;
+
+ const data = wcSettings.getSetting("mpwp_data");
+
+ console.log('data', data)
+
+ const mpwpTitle = wp.htmlEntities.decodeEntities(data.title || "");
+ const decodeDescription = () => wp.htmlEntities.decodeEntities(data.description || "");
+
+ const mpwpPaymentMethod = {
+ name: "mpwp",
+ ariaLabel: mpwpTitle,
+ label: window.React.createElement(
+ () => {
+ return window.React.createElement(() => mpwpTitle);
+ },
+ null
+ ),
+ content: window.React.createElement(decodeDescription, null),
+ edit: window.React.createElement(decodeDescription, null),
+ canMakePayment: () => {
+ console.log("canMakePaymentcanMakePayment");
+ console.log("canMakePayment");
+ return true;
+ },
+ supports: {
+ showSavedCards: false,
+ showSaveOption: false,
+ features: data.supports,
+ },
+ };
+
+ wcBlocksRegistry.registerPaymentMethod(mpwpPaymentMethod);
+})();
\ No newline at end of file
diff --git a/assets/setting.jpg b/assets/setting.jpg
new file mode 100644
index 0000000..ea8f7e1
Binary files /dev/null and b/assets/setting.jpg differ
diff --git a/class/class-mpwp-gateway-blocks-support.php b/class/class-mpwp-gateway-blocks-support.php
new file mode 100644
index 0000000..eb1edd2
--- /dev/null
+++ b/class/class-mpwp-gateway-blocks-support.php
@@ -0,0 +1,100 @@
+settings = get_option( 'woocommerce_mpwp_settings', array() );
+
+ add_action( 'woocommerce_rest_checkout_process_payment_with_context', array( $this, 'failed_payment_notice' ), 8, 2 );
+ }
+
+ /**
+ * Returns if this payment method should be active. If false, the scripts will not be enqueued.
+ *
+ * @return boolean
+ */
+ public function is_active() {
+ // $payment_gateways_class = WC()->payment_gateways();
+ // $payment_gateways = $payment_gateways_class->payment_gateways();
+ // if ( ! isset( $payment_gateways[ $this->name ] ) ) {
+ // return false;
+ // }
+
+ // return $payment_gateways[ $this->name ]->is_available();
+
+ return true;
+ }
+
+ /**
+ * Returns an array of scripts/handles to be registered for this payment method.
+ *
+ * @return array
+ */
+ public function get_payment_method_script_handles() {
+ $script_url = plugins_url( "/assets/js/blocks/mpwp.js", MPWP_MAIN_FILE );
+
+ wp_register_script(
+ "wc-mpwp-blocks",
+ $script_url,
+ array('wc-blocks-checkout', 'react', 'wc-blocks-registry', 'wc-settings', 'wp-html-entities', 'wp-i18n'),
+ '1.2',
+ true
+ );
+ wp_set_script_translations( 'wc-mpwp-blocks', 'mpwp' );
+ return array( "wc-mpwp-blocks" );
+ }
+
+ /**
+ * Returns an array of key=>value pairs of data made available to the payment methods script.
+ *
+ * @return array
+ */
+ public function get_payment_method_data() {
+ $payment_gateways_class = WC()->payment_gateways();
+ $payment_gateways = $payment_gateways_class->payment_gateways();
+ $gateway = $payment_gateways[ $this->name ];
+ return array(
+ 'title' => $this->get_setting( 'title' ),
+ 'description' => $this->get_setting( 'description' ),
+ 'supports' => array_filter( $gateway->supports, array( $gateway, 'supports' ) ),
+ 'allow_saved_cards' => is_user_logged_in(),
+ );
+ }
+
+ /**
+ * Add failed payment notice to the payment details.
+ *
+ * @param PaymentContext $context Holds context for the payment.
+ * @param PaymentResult $result Result object for the payment.
+ */
+ public function failed_payment_notice( PaymentContext $context, PaymentResult &$result ) {
+ if ( 'mpwp' === $context->payment_method ) {
+ // add_action(
+ // 'wc_gateway_mpwp_process_payment_error',
+ // function( $failed_notice ) use ( &$result ) {
+ // $payment_details = $result->payment_details;
+ // $payment_details['errorMessage'] = wp_strip_all_tags( $failed_notice );
+ // $result->set_payment_details( $payment_details );
+ // }
+ // );
+ }
+ }
+}
\ No newline at end of file
diff --git a/class/class-mpwp-gateway.php b/class/class-mpwp-gateway.php
new file mode 100644
index 0000000..31289db
--- /dev/null
+++ b/class/class-mpwp-gateway.php
@@ -0,0 +1,677 @@
+mugglepay_request = new MugglePay_Request($this);
+
+ $this->id = 'mpwp';
+ $this->icon = '';
+ $this->has_fields = false;
+ $this->order_button_text = __('Proceed to MugglePay', 'mpwp');
+ $this->method_title = __('MugglePay', 'mpwp');
+
+ $this->gateway_methods = array(
+ 'muggle_pay_methods' => array(
+ 'title' => __('MugglePay', 'mpwp'),
+ 'currency' => '',
+ 'order_button_text' => __('Proceed to MugglePay', 'mpwp')
+ ),
+ // 'card_methods' => array(
+ // 'title' => __('Card', 'mpwp'),
+ // 'currency' => 'CARD',
+ // 'order_button_text' => __('Proceed to Card', 'mpwp')
+ // ),
+ // 'alipay_methods' => array(
+ // 'title' => __('Alipay', 'mpwp'),
+ // 'currency' => 'ALIPAY',
+ // 'order_button_text' => __('Proceed to Alipay', 'mpwp')
+ // ),
+ // 'alipay_global_methods' => array(
+ // 'title' => __('Alipay Global', 'mpwp'),
+ // 'currency' => 'ALIGLOBAL',
+ // 'order_button_text' => __('Proceed to Alipay Global', 'mpwp')
+ // ),
+ // 'wechat_methods' => array(
+ // 'title' => __('Wechat', 'mpwp'),
+ // 'currency' => 'WECHAT',
+ // 'order_button_text' => __('Proceed to Wechat', 'mpwp')
+ // ),
+ // 'btc_methods' => array(
+ // 'title' => __('BTC', 'mpwp'),
+ // 'currency' => 'BTC',
+ // 'order_button_text' => __('Proceed to BTC', 'mpwp')
+ // ),
+ // 'ltc_methods' => array(
+ // 'title' => __('LTC', 'mpwp'),
+ // 'currency' => 'LTC',
+ // 'order_button_text' => __('Proceed to LTC', 'mpwp')
+ // ),
+ // 'eos_methods' => array(
+ // 'title' => __('EOS', 'mpwp'),
+ // 'currency' => 'EOS',
+ // 'order_button_text' => __('Proceed to EOS', 'mpwp')
+ // ),
+ // 'bch_methods' => array(
+ // 'title' => __('BCH', 'mpwp'),
+ // 'currency' => 'BCH',
+ // 'order_button_text' => __('Proceed to BCH', 'mpwp')
+ // ),
+ // 'lbtc_methods' => array(
+ // 'title' => __('LBTC (for Lightening BTC)', 'mpwp'),
+ // 'currency' => 'LBTC',
+ // 'order_button_text' => __('Proceed to LBTC', 'mpwp')
+ // ),
+ // 'cusd_methods' => array(
+ // 'title' => __('CUSD (for Celo Dollars)', 'mpwp'),
+ // 'currency' => 'CUSD',
+ // 'order_button_text' => __('Proceed to CUSD', 'mpwp')
+ // ),
+ 'usdt_methods' => array(
+ 'title' => __('USDT', 'mpwp'),
+ 'currency' => 'USDT',
+ 'order_button_text' => __('Proceed to USDT', 'mpwp')
+ ),
+ 'usdc_methods' => array(
+ 'title' => __('USDC', 'mpwp'),
+ 'currency' => 'USDC',
+ 'order_button_text' => __('Proceed to USDC', 'mpwp')
+ ),
+ 'eth_methods' => array(
+ 'title' => __('ETH', 'mpwp'),
+ 'currency' => 'ETH',
+ 'order_button_text' => __('Proceed to ETH', 'mpwp')
+ ),
+ );
+
+ // supported features.
+ $this->supports = array(
+ 'products',
+ 'refunds'
+ );
+
+ // Load the settings.
+ $this->init_form_fields();
+ $this->init_settings();
+
+ // Define user set variables.
+ $this->title = $this->get_option('title');
+ $this->method_description = $this->get_option('description');
+ $this->debug = 'yes' === $this->get_option('debug', 'no');
+
+ self::$log_enabled = $this->debug;
+
+ add_action('woocommerce_update_options_payment_gateways_' . $this->id, array( $this, 'process_admin_options' ));
+ add_action('woocommerce_api_wc_gateway_mpwp', array( $this, 'check_response' ));
+ add_filter('woocommerce_order_data_store_cpt_get_orders_query', array( $this, 'custom_query_var' ), 10, 2);
+ // add_action('woocommerce_cancelled_order', array( $this, 'cancel_order' ), 10 ,1);
+ // add_action( 'woocommerce_order_status_processing', array( $this, 'capture_payment' ) );
+ // add_action( 'woocommerce_order_status_completed', array( $this, 'capture_payment' ) );
+ }
+
+ /**
+ * Logging method.
+ *
+ * @param string $message Log message.
+ * @param string $level Optional. Default 'info'.
+ * emergency|alert|critical|error|warning|notice|info|debug
+ * @param boolean $is_end insert log end flag
+ */
+ public static function log($message, $level = 'info', $is_end = true)
+ {
+ if (self::$log_enabled) {
+ if (empty(self::$log)) {
+ self::$log = wc_get_logger();
+ }
+ self::$log->log($level, $message, array( 'source' => 'mpwp' ));
+ if ($is_end) {
+ self::$log->log($level, '=========================================== ↑↑↑ END ↑↑↑ ===========================================', array( 'source' => 'mpwp' ));
+ }
+ }
+ }
+
+ /**
+ * Check if MPWP gateway is enabled.
+ *
+ * @return bool
+ */
+ public function is_available() {
+ if ( 'yes' == $this->enabled && $this->get_option('api_key') ) {
+ return true;
+ }
+ return false;
+
+ }
+
+ /**
+ * Initialise Gateway Settings Form Fields.
+ */
+ public function init_form_fields()
+ {
+ $this->form_fields = array(
+ 'enabled' => array(
+ 'title' => __('Enable/Disable', 'mpwp'),
+ 'type' => 'checkbox',
+ 'label' => __('Enable MugglePay', 'mpwp'),
+ 'default' => 'no'
+ ),
+ 'title' => array(
+ 'title' => __('Title', 'mpwp'),
+ 'type' => 'text',
+ 'description' => __('This controls the title which the user sees during checkout.', 'mpwp'),
+ 'default' => __('MugglePay', 'mpwp'),
+ 'desc_tip' => true,
+ ),
+ 'description' => array(
+ 'title' => __('Description', 'mpwp'),
+ 'type' => 'text',
+ 'desc_tip' => true,
+ 'description' => __('This controls the description which the user sees during checkout.', 'mpwp'),
+ 'default' => __('MugglePay is a one-stop payment solution for merchants with an online payment need.', 'mpwp'),
+ ),
+ 'check_orders' => array(
+ 'title' => __('Check Orders', 'mpwp'),
+ 'type' => 'title',
+ 'description' => __('The plugin automatically checks the order payment status by default and updates the order status every 5 minutes.', 'mpwp'),
+ ),
+ // You can click the button to check and update the payment status of all outstanding orders.
+ // 'check_orders_btn' => array(
+ // 'title' => '',
+ // 'type' => 'title'
+ // ),
+ 'setting' => array(
+ 'title' => __('Setting', 'mpwp'),
+ 'type' => 'title',
+ 'description' => '',
+ ),
+ 'api_key' => array(
+ 'title' => __('API Auth Token (API key) ', 'mpwp'),
+ 'type' => 'text',
+ 'placeholder' => 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
+ /* translators: %s: URL */
+ 'description' => sprintf(__('Register your MugglePay merchant accounts with your invitation code and get your API key at Merchants Portal. You will find your API Auth Token (API key) for authentication. MORE', 'mpwp'), 'https://merchants.mugglepay.com/user/register', 'https://mugglepay.docs.stoplight.io/api-overview/authentication'),
+ ),
+ 'button_styles' => array(
+ 'title' => __('Button Styles', 'mpwp'),
+ 'type' => 'textarea',
+ 'placeholder' => '.payment_method_muggle_pay_methods { any; }',
+ /* translators: %s: URL */
+ 'description' => __('If the style of your payment page is not displayed properly, you can overwrite your new style here', 'mpwp'),
+ ),
+ 'debug' => array(
+ 'title' => __('Debug log', 'mpwp'),
+ 'type' => 'checkbox',
+ 'label' => __('Enable logging', 'mpwp'),
+ 'default' => 'no',
+ // translators: Description for 'Debug log' section of settings page.
+ 'description' => sprintf(__('Log MPWP API events inside %s', 'mpwp'), '' . WC_Log_Handler_File::get_log_file_path('mpwp') . ''),
+ ),
+ 'payment_gateway' => array(
+ 'title' => __('Payment Gateway', 'mpwp'),
+ 'type' => 'title',
+ 'description' => '',
+ )
+ );
+
+ foreach ($this->gateway_methods as $key => $value) {
+ $this->form_fields[$key] = array(
+ 'title' => '',
+ 'type' => 'checkbox',
+ 'label' => $value['title']
+ );
+ }
+ }
+
+ /**
+ * Process the payment and return the result.
+ *
+ * @param int $order_id Order ID.
+ * @return array
+ */
+ public function process_payment($order_id)
+ {
+ global $woocommerce;
+
+ $order = wc_get_order($order_id);
+
+ $result = $this->get_payment_url($order, $this->current_method);
+
+ if (is_wp_error($result)) {
+ wc_add_notice($result->get_error_message(), 'error');
+ return;
+ }
+
+ return array(
+ 'result' => 'success',
+ 'redirect' => $result,
+ );
+ }
+
+ /**
+ * Process a refund if supported.
+ *
+ * @param int $order_id Order ID.
+ * @param float $amount Refund amount.
+ * @param string $reason Refund reason.
+ * @return bool|WP_Error
+ */
+ public function process_refund($order_id, $amount = null, $reason = '')
+ {
+ $order = wc_get_order($order_id);
+
+ if (! $order || ! $order->get_transaction_id()) {
+ return new WP_Error('error', __('Refund failed.', 'mpwp'));
+ }
+
+ $result = $this->refund_transaction($order, $amount, $reason);
+
+ if (is_wp_error($result)) {
+ return new WP_Error('error', $result->get_error_message());
+ }
+
+ return true;
+ }
+
+ /**
+ * Payment Callback (Webhook)
+ * Send Post Request Url Like /?wc-api=WC_Gateway_MPWP
+ */
+ public function check_response()
+ {
+ try {
+ $posted = wp_unslash(json_decode(file_get_contents('php://input'), true));
+
+ if (! empty($posted) && ! empty($posted['merchant_order_id']) && $posted['token']) { // WPCS: CSRF ok.
+
+ $order_id = wc_get_order_id_by_order_key($posted['merchant_order_id']);
+ $order = wc_get_order($order_id);
+
+ if (! $order) {
+ self::log('Failed to Checking IPN response order callback for: ' . $order_id, 'error');
+ throw new Exception('Checking IPN response is valid');
+ }
+
+ if (! $this->check_order_token($order, $posted['token'])) {
+ self::log('Checking IPN response is valid: ', 'error', false);
+ self::log(print_r($posted, true), 'error', false);
+ self::log(print_r($order, true), 'error');
+ throw new Exception('Checking IPN response is valid');
+ }
+
+ if ($order->has_status(wc_get_is_paid_statuses())) {
+ self::log('Aborting, Order #' . $order_id. ' is already complete.', 'error');
+ } else {
+ $this->order_complete($order, $posted);
+ }
+
+ wp_send_json(array(
+ 'status' => 200
+ ), 200);
+ exit;
+ }
+ self::log('Failed to check response order callback : ', 'error', false);
+ self::log(print_r($posted, true), 'error', false);
+ throw new Exception('MugglePay IPN Request Failure');
+ } catch (Exception $e) {
+ wp_send_json(array(
+ 'message' => $e->getMessage(),
+ 'status' => 500
+ ), 500);
+ exit;
+ }
+ }
+
+ /**
+ * Complete order payment
+ */
+ public function order_complete($order, $voucher)
+ {
+
+ // Payment is complete
+ $order->payment_complete();
+ // Set transaction id.
+ $order->set_transaction_id($voucher['order_id']);
+ // Save payment voucher data
+ $order->update_meta_data('_mpwp_payment_voucher', $voucher);
+ // Change active status
+ $order->update_meta_data('_mpwp_payment_active', false);
+ // Save metadata
+ $order->save();
+
+ return true;
+ }
+
+ /**
+ * Check payment statuses on orders and update order statuses.
+ */
+ public function check_orders()
+ {
+ // Check the status of non-archived MugglePay orders.
+ $orders = wc_get_orders(array( 'mpwp_payment_active' => true, 'status' => array( 'wc-pending' ) ));
+ foreach ($orders as $order) {
+ $transaction_id = $order->get_meta('_mpwp_prev_payment_transaction_id');
+
+ usleep(1000000 * 3); // Ensure we don't hit the rate limit. Delay 5 seconds.
+
+ $mugglepay_order = $this->mugglepay_request->get_order($transaction_id);
+
+ self::log('Auto Checking Order #' . $order->get_id(), 'info', false);
+ self::log(print_r($mugglepay_order, true), 'info');
+
+ if (is_wp_error($mugglepay_order)) {
+ continue;
+ }
+
+ if ($mugglepay_order['invoice']['status'] !== 'PAID') {
+ continue;
+ }
+
+ $this->order_complete($order, $mugglepay_order['invoice']);
+
+ self::log('Auto Complete Order #' . $order->get_id(), 'info');
+ }
+ }
+
+
+ /**
+ * Get the MugglePay request URL for an order.
+ *
+ * @param WC_Order $order Order object.
+ * @param string $pay_currency Only use this field if you have the payment gateway enabled, and it will select the payment gateway. e.g. ALIPAY, ALIGLOBAL, WECHAT, BTC, LTC, ETH, EOS, BCH, LBTC (for Lightening BTC), CUSD (for Celo Dollars)
+ * @return string
+ */
+ public function get_payment_url($order, $pay_currency)
+ {
+ // Create description for charge based on order's products. Ex: 1 x Product1, 2 x Product2
+ try {
+ $order_items = array_map(function ($item) {
+ return $item['name'] . ' x ' . $item['quantity'];
+ }, $order->get_items());
+
+ $description = mb_substr(implode(', ', $order_items), 0, 200);
+ } catch (Exception $e) {
+ $description = null;
+ }
+
+ if ($order->get_currency() !== 'USD' && $order->get_currency() !== 'CNY') {
+ return new WP_Error('error', "{$order->get_currency()} currency is not supported; only USD and CNY are supported. Please adjust the Currency parameter.", array());
+ }
+
+ $mugglepay_args = array(
+ 'merchant_order_id' => $order->get_order_key(),
+ 'price_amount' => $order->get_total(),
+ 'price_currency' => $order->get_currency(),
+ 'pay_currency' => $pay_currency,
+ 'title' => sprintf(__('Payment order #%s', 'mpwp'), $order->get_id()),
+ 'description' => $description,
+ 'callback_url' => WC()->api_request_url('WC_Gateway_MPWP'),
+ 'cancel_url' => esc_url_raw($order->get_cancel_order_url_raw()),
+ 'success_url' => esc_url_raw($this->get_return_url($order)),
+ 'mobile' => wp_is_mobile(),
+ // 'fast' => '',
+ 'token' => $this->create_order_token($order)
+ );
+ self::log(print_r($mugglepay_args, true), 'info');
+
+ // Send Request
+ $raw_response = $this->mugglepay_request->send_request(
+ '/orders',
+ $mugglepay_args,
+ array(
+ 'token' => $this->get_option('api_key')
+ )
+ );
+
+ self::log('Create Payment Url: ', 'info', false);
+ self::log(print_r($raw_response, true), 'info');
+
+ if (
+ (($raw_response['status'] === 200 || $raw_response['status'] === 201) && $raw_response['payment_url']) ||
+ (($raw_response['status'] === 400 && $raw_response['error_code'] === 'ORDER_MERCHANTID_EXIST') && $raw_response['payment_url'])
+ ) {
+ // Insert mugglepay order active flag
+ $order->update_meta_data('_mpwp_payment_active', true);
+ // Save payment order id
+ $order->update_meta_data('_mpwp_prev_payment_transaction_id', $raw_response['order']['order_id']);
+ // Save metadata
+ $order->save();
+
+ return $raw_response['payment_url'];
+ } elseif (!empty($raw_response['error_code'])) {
+ return new WP_Error('error', $this->get_error_str($raw_response['error_code']), $raw_response);
+ }
+
+ return new WP_Error('error', $raw_response['error'], $raw_response);
+ }
+
+ /**
+ * Refund an order via MugglePay.
+ *
+ * @param WC_Order $order Order object.
+ * @param float $amount Refund amount.
+ * @param string $reason Refund reason.
+ * @return object Either an object of name value pairs for a success, or a WP_ERROR object.
+ */
+ public function refund_transaction($order, $amount = null, $reason = '')
+ {
+ // Send Request
+ $raw_response = $this->mugglepay_request->send_request(
+ '/orders/' . $order->get_transaction_id() . '/refund',
+ array(),
+ array(
+ 'token' => $this->get_option('api_key')
+ )
+ );
+
+ if (is_wp_error($raw_response)) {
+ return $raw_response;
+ } elseif (empty($raw_response['status'] || $raw_response['status'] !== 200)) {
+ return new WP_Error('error', __('Empty Response', 'mpwp'));
+ }
+
+ return (object) $raw_response;
+ }
+
+ /**
+ * Get Order token to validate Payment
+ *
+ * @param WC_Order $order Order object.
+ * @return string
+ */
+ public function create_order_token($order)
+ {
+ return wp_hash_password($order->get_order_key());
+ }
+
+ /**
+ * Check Order token to validate Payment
+ */
+ public function check_order_token($order, $token)
+ {
+ return wp_check_password($order->get_order_key(), $token);
+ }
+
+ /**
+ * HTTP Response and Error Codes
+ * Most common API errors are as follows, including message, reason and status code.
+ */
+ public function get_error_str($code)
+ {
+ switch ($code) {
+ case 'AUTHENTICATION_FAILED':
+ return __('Authentication Token is not set or expired.', 'mpwp');
+ case 'INVOICE_NOT_EXIST':
+ return __('Invoice does not exist.', 'mpwp');
+ case 'INVOICE_VERIFIED_ALREADY':
+ return __('It has been verified already.', 'mpwp');
+ case 'INVOICE_CANCELED_FAIILED':
+ return __('Invoice does not exist, or it cannot be canceled.', 'mpwp');
+ case 'ORDER_NO_PERMISSION':
+ return __('Order does not exist or permission denied.', 'mpwp');
+ case 'ORDER_CANCELED_FAIILED':
+ return __('Order does not exist, or it cannot be canceled.', 'mpwp');
+ case 'ORDER_REFUND_FAILED':
+ return __('Order does not exist, or it`s status is not refundable.', 'mpwp');
+ case 'ORDER_VERIFIED_ALREADY':
+ return __('Payment has been verified with payment already.', 'mpwp');
+ case 'ORDER_VERIFIED_PRICE_NOT_MATCH':
+ return __('Payment money does not match the order money, please double check the price.', 'mpwp');
+ case 'ORDER_VERIFIED_MERCHANT_NOT_MATCH':
+ return __('Payment money does not the order of current merchant , please double check the order.', 'mpwp');
+ case 'ORDER_NOT_VALID':
+ return __('Order id is not valid.', 'mpwp');
+ case 'ORDER_PAID_FAILED':
+ return __('Order not exist or is not paid yet.', 'mpwp');
+ case 'ORDER_MERCHANTID_EXIST':
+ return __('Order with same merchant_order_id exisits.', 'mpwp');
+ case 'ORDER_NOT_NEW':
+ return __('The current order is not new, and payment method cannot be switched.', 'mpwp');
+ case 'PAYMENT_NOT_AVAILABLE':
+ return __('The payment method is not working, please retry later.', 'mpwp');
+ case 'MERCHANT_CALLBACK_STATUS_WRONG':
+ return __('The current payment status not ready to send callback.', 'mpwp');
+ case 'PARAMETERS_MISSING':
+ return __('Missing parameters.', 'mpwp');
+ case 'PAY_PRICE_ERROR':
+ switch ($this->current_method) {
+ case 'WECHAT':
+ case 'ALIPAY':
+ case 'ALIGLOBAL':
+ return __('The payment is temporarily unavailable, please use another payment method', 'mpwp');
+ }
+ return __('Price amount or currency is not set correctly.', 'mpwp');
+ case 'CREDENTIALS_NOT_MATCH':
+ return __('The email or password does not match.', 'mpwp');
+ case 'USER_NOT_EXIST':
+ return __('The user does not exist or no permission.', 'mpwp');
+ case 'USER_FAILED':
+ return __('The user operatioin failed.', 'mpwp');
+ case 'INVITATION_FAILED':
+ return __('The invitation code is not filled correctly.', 'mpwp');
+ case 'ERROR':
+ return __('Error.', 'mpwp');
+ case '(Unauthorized)':
+ return __('API credentials are not valid', 'mpwp');
+ case '(Not Found)':
+ return __('Page, action not found', 'mpwp');
+ case '(Too Many Requests)':
+ return __('API request limit is exceeded', 'mpwp');
+ case '(InternalServerError)':
+ return __('Server error in MugglePay', 'mpwp');
+ }
+ return __('Server error in MugglePay', 'mpwp');
+ }
+
+ /**
+ * Get gateway icon.
+ *
+ * @return string
+ */
+ public function get_icon()
+ {
+ // We need a base country for the link to work, bail if in the unlikely event no country is set.
+ $base_country = WC()->countries->get_base_country();
+
+ $icon_html = '';
+ $icon = (array) $this->get_icon_image($this->current_method, $base_country);
+
+ if (empty($icon[0])) {
+ return '';
+ }
+
+ foreach ($icon as $i) {
+ $icon_html .= '';
+ }
+
+ // Insert Styles
+ if (!empty($this->get_option('button_styles'))) {
+ $buttonStyles = htmlspecialchars($this->get_option('button_styles'), ENT_QUOTES, 'UTF-8');
+ $icon_html = '';
+ }
+
+ return apply_filters('woocommerce_gateway_icon', $icon_html, $this->id);
+ }
+
+ /**
+ * Get MugglePay images for a country.
+ *
+ * @param string $method switch mulit language.
+ * @param string $country Country code.
+ * @return array of image URLs
+ */
+ protected function get_icon_image($method, $country)
+ {
+ $uri = get_stylesheet_directory_uri();
+ switch ($method) {
+ case '':
+ $icon = '/mugglepay-logo-c.png';
+ break;
+ case 'BTC':
+ $icon = '/btc.png';
+ break;
+ case 'ETH':
+ $icon = '/eth.png';
+ break;
+ case 'USDT':
+ $icon = '/usdt.png';
+ break;
+ case 'USDC':
+ $icon = '/usdc.png';
+ break;
+ default:
+ return '';
+ }
+ return apply_filters('woocommerce_mpwp_icon', $uri . '/assets/images/' .$icon);
+ }
+
+ /**
+ * Handle a custom 'mpwp_prev_payment_transaction_id' query var to get orders
+ * payed through MugglePay with the 'mpwp_prev_payment_transaction_id' meta.
+ * @param array $query - Args for WP_Query.
+ * @param array $query_vars - Query vars from WC_Order_Query.
+ * @return array modified $query
+ */
+ public function custom_query_var($query, $query_vars)
+ {
+ if (array_key_exists('mpwp_payment_active', $query_vars)) {
+ // Only check the order with MugglePay payment voucher
+ $query['meta_query'][] = array(
+ 'key' => '_mpwp_payment_active',
+ 'compare' => $query_vars['mpwp_payment_active'] ? 'EXISTS' : 'NOT EXISTS',
+ );
+ }
+
+ if (array_key_exists('mpwp_prev_payment_transaction_id', $query_vars)) {
+ $query['meta_query'][] = array(
+ 'key' => '_mpwp_prev_payment_transaction_id',
+ 'value' => esc_attr($query_vars['mpwp_prev_payment_transaction_id'])
+ );
+ }
+
+ return $query;
+ }
+}
diff --git a/class/class-mugglepay-request.php b/class/class-mugglepay-request.php
new file mode 100644
index 0000000..394c777
--- /dev/null
+++ b/class/class-mugglepay-request.php
@@ -0,0 +1,96 @@
+gateway = $gateway;
+ }
+
+ /**
+ * Get MugglePay Order
+ * @param string $order_id MugglePay order ID. It's provided in the response of Create Order.
+ */
+ public function get_order($order_id)
+ {
+ // Send Request
+ $raw_response = $this->send_request(
+ '/orders/' . $order_id,
+ array(),
+ array(
+ 'token' => $this->gateway->get_option('api_key')
+ ),
+ 'GET'
+ );
+
+ if ($raw_response['status'] === 200) {
+ return $raw_response;
+ }
+
+ return new WP_Error('error', $raw_response['error'], $raw_response);
+ }
+
+ /**
+ * Get the response from an API request.
+ * @param string $endpoint
+ * @param array $params
+ * @param array $header
+ * @param string $method
+ * @return array
+ */
+ public function send_request($endpoint, $params = array(), $header = array(), $method = 'POST')
+ {
+ $args = array(
+ 'method' => $method,
+ 'headers' => array(
+ 'Content-Type' => 'application/json'
+ )
+ );
+
+ if (is_array($header) && count($header)) {
+ $args['headers'] = array_merge($args['headers'], $header);
+ }
+
+ $url = $this->api_url . $endpoint;
+
+ if (in_array($method, array( 'POST', 'PUT' ))) {
+ $args['body'] = json_encode($params);
+ } else {
+ $url = add_query_arg($params, $url);
+ }
+
+ $response = wp_remote_request(esc_url_raw($url), $args);
+
+ if (is_wp_error($response)) {
+ return $response;
+ } else {
+ return json_decode($response['body'], true);
+ }
+ }
+}
diff --git a/i18n/.DS_Store b/i18n/.DS_Store
new file mode 100644
index 0000000..a8fa27b
Binary files /dev/null and b/i18n/.DS_Store differ
diff --git a/i18n/languages/.DS_Store b/i18n/languages/.DS_Store
new file mode 100644
index 0000000..9f089cf
Binary files /dev/null and b/i18n/languages/.DS_Store differ
diff --git a/i18n/languages/mpwp-zh_CN.mo b/i18n/languages/mpwp-zh_CN.mo
new file mode 100644
index 0000000..3271986
Binary files /dev/null and b/i18n/languages/mpwp-zh_CN.mo differ
diff --git a/i18n/languages/mpwp-zh_CN.po b/i18n/languages/mpwp-zh_CN.po
new file mode 100644
index 0000000..71cfb16
--- /dev/null
+++ b/i18n/languages/mpwp-zh_CN.po
@@ -0,0 +1,390 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: MugglePayForWP\n"
+"POT-Creation-Date: 2021-07-22 17:10+0800\n"
+"PO-Revision-Date: 2021-07-22 17:10+0800\n"
+"Last-Translator: \n"
+"Language-Team: \n"
+"Language: zh_CN\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+"X-Generator: Poedit 3.0\n"
+"X-Poedit-Basepath: ../..\n"
+"X-Poedit-Flags-xgettext: --add-comments=translators:\n"
+"X-Poedit-WPHeader: muggle-pay-for-wp.php\n"
+"X-Poedit-SourceCharset: UTF-8\n"
+"X-Poedit-KeywordsList: __;_e;_n:1,2;_x:1,2c;_ex:1,2c;_nx:4c,1,2;esc_attr__;"
+"esc_attr_e;esc_attr_x:1,2c;esc_html__;esc_html_e;esc_html_x:1,2c;"
+"_n_noop:1,2;_nx_noop:3c,1,2;__ngettext_noop:1,2\n"
+"X-Poedit-SearchPath-0: .\n"
+"X-Poedit-SearchPathExcluded-0: *.min.js\n"
+
+#: class/class-mpwp-gateway.php:33 class/class-mpwp-gateway.php:40
+msgid "Proceed to MugglePay"
+msgstr "继续使用 MugglePay 支付"
+
+#: class/class-mpwp-gateway.php:34 class/class-mpwp-gateway.php:38
+#: class/class-mpwp-gateway.php:161
+msgid "MugglePay"
+msgstr "MugglePay"
+
+#: class/class-mpwp-gateway.php:43
+msgid "Card"
+msgstr "国际信用卡"
+
+#: class/class-mpwp-gateway.php:45
+msgid "Proceed to Card"
+msgstr "继续使用 国际信用卡 支付"
+
+#: class/class-mpwp-gateway.php:48
+msgid "Alipay"
+msgstr "支付宝"
+
+#: class/class-mpwp-gateway.php:50
+msgid "Proceed to Alipay"
+msgstr "继续使用 支付宝 支付"
+
+#: class/class-mpwp-gateway.php:53
+msgid "Alipay Global"
+msgstr "支付宝(国际)"
+
+#: class/class-mpwp-gateway.php:55
+msgid "Proceed to Alipay Global"
+msgstr "继续使用 支付宝(国际) 支付"
+
+#: class/class-mpwp-gateway.php:58
+msgid "Wechat"
+msgstr "微信支付"
+
+#: class/class-mpwp-gateway.php:60
+msgid "Proceed to Wechat"
+msgstr "继续使用 微信 支付"
+
+#: class/class-mpwp-gateway.php:63
+msgid "BTC"
+msgstr ""
+
+#: class/class-mpwp-gateway.php:65
+msgid "Proceed to BTC"
+msgstr "继续使用 BTC 支付"
+
+#: class/class-mpwp-gateway.php:68
+msgid "LTC"
+msgstr ""
+
+#: class/class-mpwp-gateway.php:70
+msgid "Proceed to LTC"
+msgstr "继续使用 LTC 支付"
+
+#: class/class-mpwp-gateway.php:73
+msgid "ETH"
+msgstr ""
+
+#: class/class-mpwp-gateway.php:75
+msgid "Proceed to ETH"
+msgstr "继续使用 ETH 支付"
+
+#: class/class-mpwp-gateway.php:78
+msgid "EOS"
+msgstr ""
+
+#: class/class-mpwp-gateway.php:80
+msgid "Proceed to EOS"
+msgstr "继续使用 EOS 支付"
+
+#: class/class-mpwp-gateway.php:83
+msgid "BCH"
+msgstr ""
+
+#: class/class-mpwp-gateway.php:85
+msgid "Proceed to BCH"
+msgstr "继续使用 BCH 支付"
+
+#: class/class-mpwp-gateway.php:88
+msgid "LBTC (for Lightening BTC)"
+msgstr ""
+
+#: class/class-mpwp-gateway.php:90
+msgid "Proceed to LBTC"
+msgstr "继续使用 LBTC 支付"
+
+#: class/class-mpwp-gateway.php:93
+msgid "CUSD (for Celo Dollars)"
+msgstr ""
+
+#: class/class-mpwp-gateway.php:95
+msgid "Proceed to CUSD"
+msgstr "继续使用 CUSD 支付"
+
+#: class/class-mpwp-gateway.php:152
+msgid "Enable/Disable"
+msgstr "启用/禁用"
+
+#: class/class-mpwp-gateway.php:154
+msgid "Enable MugglePay"
+msgstr "启用 MugglePay"
+
+#: class/class-mpwp-gateway.php:158
+msgid "Title"
+msgstr "标题"
+
+#: class/class-mpwp-gateway.php:160
+msgid "This controls the title which the user sees during checkout."
+msgstr "此设置控制用户在结帐时看到的名称。"
+
+#: class/class-mpwp-gateway.php:165
+msgid "Description"
+msgstr "描述"
+
+#: class/class-mpwp-gateway.php:168
+msgid "This controls the description which the user sees during checkout."
+msgstr "这个控制用户在结帐时看到的描述。"
+
+#. Description of the plugin/theme
+#: class/class-mpwp-gateway.php:169
+msgid ""
+"MugglePay is a one-stop payment solution for merchants with an online "
+"payment need."
+msgstr "MugglePay是为有在线收款需求的商家提供的一站式收款解决方案。"
+
+#: class/class-mpwp-gateway.php:172
+msgid "Check Orders"
+msgstr "检查订单"
+
+#: class/class-mpwp-gateway.php:174
+msgid ""
+"The plugin automatically checks the order payment status by default and "
+"updates the order status every 5 minutes."
+msgstr "默认情况下,插件会自动检查订单付款状态,并每 5 分钟更新一次订单状态。"
+
+#: class/class-mpwp-gateway.php:182
+msgid "Setting"
+msgstr "设置"
+
+#: class/class-mpwp-gateway.php:187
+msgid "API Auth Token (API key) "
+msgstr "应用密钥 "
+
+#. translators: %s: URL
+#: class/class-mpwp-gateway.php:191
+#, php-format
+msgid ""
+"Register your MugglePay merchant accounts with your invitation code and get "
+"your API key at Merchants Portal. You "
+"will find your API Auth Token (API key) for authentication. MORE"
+msgstr ""
+"使用邀请代码注册您的MugglePay商户帐户,并在商家门户获取您的 API 密钥。您将找到用于身份验证的应用秘钥。 更多"
+
+#: class/class-mpwp-gateway.php:194
+msgid "Button Styles"
+msgstr "按钮样式"
+
+#. translators: %s: URL
+#: class/class-mpwp-gateway.php:198
+msgid ""
+"If the style of your payment page is not displayed properly, you can "
+"overwrite your new style here"
+msgstr "如果你在支付页面的样式显示不正常,你可以通过这里覆盖你的新样式"
+
+#: class/class-mpwp-gateway.php:201
+msgid "Debug log"
+msgstr "调试模式"
+
+#: class/class-mpwp-gateway.php:203
+msgid "Enable logging"
+msgstr "启用日志记录"
+
+#. translators: Description for 'Debug log' section of settings page.
+#: class/class-mpwp-gateway.php:206
+#, php-format
+msgid "Log MPWP API events inside %s"
+msgstr "日志文件存储于 %s"
+
+#: class/class-mpwp-gateway.php:209
+msgid "Payment Gateway"
+msgstr "支付网关"
+
+#: class/class-mpwp-gateway.php:262
+msgid "Refund failed."
+msgstr "退款失败。"
+
+#: class/class-mpwp-gateway.php:401
+#, php-format
+msgid "Payment order #%s"
+msgstr "付款单 #%s"
+
+#: class/class-mpwp-gateway.php:467
+msgid "Empty Response"
+msgstr "空响应"
+
+#: class/class-mpwp-gateway.php:500
+msgid "Authentication Token is not set or expired."
+msgstr "身份验证令牌未设置或过期。"
+
+#: class/class-mpwp-gateway.php:502
+msgid "Invoice does not exist."
+msgstr "凭证不存在。"
+
+#: class/class-mpwp-gateway.php:504
+msgid "It has been verified already."
+msgstr "已经验证了。"
+
+#: class/class-mpwp-gateway.php:506
+msgid "Invoice does not exist, or it cannot be canceled."
+msgstr "凭证不存在,或者无法取消。"
+
+#: class/class-mpwp-gateway.php:508
+msgid "Order does not exist or permission denied."
+msgstr "订单不存在或权限被拒绝。"
+
+#: class/class-mpwp-gateway.php:510
+msgid "Order does not exist, or it cannot be canceled."
+msgstr "订单不存在,或者无法取消。"
+
+#: class/class-mpwp-gateway.php:512
+msgid "Order does not exist, or it`s status is not refundable."
+msgstr "订单不存在,或者其状态不可退款。"
+
+#: class/class-mpwp-gateway.php:514
+msgid "Payment has been verified with payment already."
+msgstr "付款已通过付款进行验证。"
+
+#: class/class-mpwp-gateway.php:516
+msgid ""
+"Payment money does not match the order money, please double check the price."
+msgstr "付款金额与订单款不匹配,请仔细检查价格。"
+
+#: class/class-mpwp-gateway.php:518
+msgid ""
+"Payment money does not the order of current merchant , please double check "
+"the order."
+msgstr "付款钱不是当前商家的订单,请仔细检查订单。"
+
+#: class/class-mpwp-gateway.php:520
+msgid "Order id is not valid."
+msgstr "订单号无效。"
+
+#: class/class-mpwp-gateway.php:522
+msgid "Order not exist or is not paid yet."
+msgstr "订单不存在或尚未支付。"
+
+#: class/class-mpwp-gateway.php:524
+msgid "Order with same merchant_order_id exisits."
+msgstr "存在相同的订单信息。"
+
+#: class/class-mpwp-gateway.php:526
+msgid "The current order is not new, and payment method cannot be switched."
+msgstr "当前订单不是新的,无法切换付款方式。"
+
+#: class/class-mpwp-gateway.php:528
+msgid "The payment method is not working, please retry later."
+msgstr "该支付暂时无法使用,请先使用其他支付方式。"
+
+#: class/class-mpwp-gateway.php:530
+msgid "The current payment status not ready to send callback."
+msgstr "当前付款状态未准备好发送回调。"
+
+#: class/class-mpwp-gateway.php:532
+msgid "Missing parameters."
+msgstr "缺少参数。"
+
+#: class/class-mpwp-gateway.php:538
+msgid ""
+"The payment is temporarily unavailable, please use another payment method"
+msgstr "当前付款方式暂时不可用,请使用其他付款方式"
+
+#: class/class-mpwp-gateway.php:540
+msgid "Price amount or currency is not set correctly."
+msgstr "价格金额或货币设置不正确。"
+
+#: class/class-mpwp-gateway.php:542
+msgid "The email or password does not match."
+msgstr "电子邮件或密码不匹配。"
+
+#: class/class-mpwp-gateway.php:544
+msgid "The user does not exist or no permission."
+msgstr "用户不存在或没有权限。"
+
+#: class/class-mpwp-gateway.php:546
+msgid "The user operatioin failed."
+msgstr "用户操作失败。"
+
+#: class/class-mpwp-gateway.php:548
+msgid "The invitation code is not filled correctly."
+msgstr "邀请代码未正确填充。"
+
+#: class/class-mpwp-gateway.php:550
+msgid "Error."
+msgstr "错误."
+
+#: class/class-mpwp-gateway.php:552
+msgid "API credentials are not valid"
+msgstr "API 凭据无效"
+
+#: class/class-mpwp-gateway.php:554
+msgid "Page, action not found"
+msgstr "页面,未找到操作"
+
+#: class/class-mpwp-gateway.php:556
+msgid "API request limit is exceeded"
+msgstr "超过 API 请求限制"
+
+#: class/class-mpwp-gateway.php:558 class/class-mpwp-gateway.php:560
+msgid "Server error in MugglePay"
+msgstr "MugglePay 支付中的服务器错误"
+
+#: class/class-mpwp-gateway.php:581
+msgid "MugglePay acceptance mark"
+msgstr "MugglePay 标记"
+
+#: muggle-pay-for-wp.php:64
+msgid "Once every 5 minutes"
+msgstr "每5分钟一次"
+
+#: muggle-pay-for-wp.php:99
+msgid "MugglePay Payment Voucher"
+msgstr "MugglePay 支付凭证"
+
+#: muggle-pay-for-wp.php:102
+#, php-format
+msgid "Transaction ID: %s"
+msgstr "交易号: %s"
+
+#. Plugin Name of the plugin/theme
+msgid "MugglePay"
+msgstr ""
+
+#. Plugin URI of the plugin/theme
+msgid "https://mugglepay.com/"
+msgstr "https://mugglepay.com/"
+
+#. Author of the plugin/theme
+msgid "MugglePay"
+msgstr ""
+
+#. Author URI of the plugin/theme
+msgid "https://mugglepay.com/"
+msgstr ""
+
+#~ msgid ""
+#~ "MugglePay is a payment API for online merchants with high chargeback "
+#~ "rates to receive money safely and anonymously through accepting "
+#~ "cryptocurrency."
+#~ msgstr ""
+#~ "MugglePay 是一种支付 API,用于具有高退款率的在线商家,通过接受加密货币以"
+#~ "安全和匿名方式接收资金。"
+
+#~ msgid ""
+#~ "A payment gateway that allows your customers to pay with cryptocurrency "
+#~ "via MugglePay (https://MugglePay.com/)"
+#~ msgstr ""
+#~ "允许您的客户通过 MugglePay(https://MugglePay.com/) 使用加密货币付款的支"
+#~ "付网关"
+
+#~ msgid "DooFox,Inc."
+#~ msgstr "DooFox,Inc."
diff --git a/i18n/languages/mpwp.pot b/i18n/languages/mpwp.pot
new file mode 100644
index 0000000..d361e15
--- /dev/null
+++ b/i18n/languages/mpwp.pot
@@ -0,0 +1,369 @@
+#, fuzzy
+msgid ""
+msgstr ""
+"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
+"Project-Id-Version: MugglePayForWP\n"
+"POT-Creation-Date: 2021-07-22 17:11+0800\n"
+"PO-Revision-Date: 2020-12-08 02:43+0800\n"
+"Last-Translator: \n"
+"Language-Team: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 3.0\n"
+"X-Poedit-Basepath: ../..\n"
+"X-Poedit-Flags-xgettext: --add-comments=translators:\n"
+"X-Poedit-WPHeader: muggle-pay-for-wp.php\n"
+"X-Poedit-SourceCharset: UTF-8\n"
+"X-Poedit-KeywordsList: __;_e;_n:1,2;_x:1,2c;_ex:1,2c;_nx:4c,1,2;esc_attr__;"
+"esc_attr_e;esc_attr_x:1,2c;esc_html__;esc_html_e;esc_html_x:1,2c;"
+"_n_noop:1,2;_nx_noop:3c,1,2;__ngettext_noop:1,2\n"
+"X-Poedit-SearchPath-0: .\n"
+"X-Poedit-SearchPathExcluded-0: *.min.js\n"
+
+#: class/class-mpwp-gateway.php:33 class/class-mpwp-gateway.php:40
+msgid "Proceed to MugglePay"
+msgstr ""
+
+#: class/class-mpwp-gateway.php:34 class/class-mpwp-gateway.php:38
+#: class/class-mpwp-gateway.php:161
+msgid "MugglePay"
+msgstr ""
+
+#: class/class-mpwp-gateway.php:43
+msgid "Card"
+msgstr ""
+
+#: class/class-mpwp-gateway.php:45
+msgid "Proceed to Card"
+msgstr ""
+
+#: class/class-mpwp-gateway.php:48
+msgid "Alipay"
+msgstr ""
+
+#: class/class-mpwp-gateway.php:50
+msgid "Proceed to Alipay"
+msgstr ""
+
+#: class/class-mpwp-gateway.php:53
+msgid "Alipay Global"
+msgstr ""
+
+#: class/class-mpwp-gateway.php:55
+msgid "Proceed to Alipay Global"
+msgstr ""
+
+#: class/class-mpwp-gateway.php:58
+msgid "Wechat"
+msgstr ""
+
+#: class/class-mpwp-gateway.php:60
+msgid "Proceed to Wechat"
+msgstr ""
+
+#: class/class-mpwp-gateway.php:63
+msgid "BTC"
+msgstr ""
+
+#: class/class-mpwp-gateway.php:65
+msgid "Proceed to BTC"
+msgstr ""
+
+#: class/class-mpwp-gateway.php:68
+msgid "LTC"
+msgstr ""
+
+#: class/class-mpwp-gateway.php:70
+msgid "Proceed to LTC"
+msgstr ""
+
+#: class/class-mpwp-gateway.php:73
+msgid "ETH"
+msgstr ""
+
+#: class/class-mpwp-gateway.php:75
+msgid "Proceed to ETH"
+msgstr ""
+
+#: class/class-mpwp-gateway.php:78
+msgid "EOS"
+msgstr ""
+
+#: class/class-mpwp-gateway.php:80
+msgid "Proceed to EOS"
+msgstr ""
+
+#: class/class-mpwp-gateway.php:83
+msgid "BCH"
+msgstr ""
+
+#: class/class-mpwp-gateway.php:85
+msgid "Proceed to BCH"
+msgstr ""
+
+#: class/class-mpwp-gateway.php:88
+msgid "LBTC (for Lightening BTC)"
+msgstr ""
+
+#: class/class-mpwp-gateway.php:90
+msgid "Proceed to LBTC"
+msgstr ""
+
+#: class/class-mpwp-gateway.php:93
+msgid "CUSD (for Celo Dollars)"
+msgstr ""
+
+#: class/class-mpwp-gateway.php:95
+msgid "Proceed to CUSD"
+msgstr ""
+
+#: class/class-mpwp-gateway.php:152
+msgid "Enable/Disable"
+msgstr ""
+
+#: class/class-mpwp-gateway.php:154
+msgid "Enable MugglePay"
+msgstr ""
+
+#: class/class-mpwp-gateway.php:158
+msgid "Title"
+msgstr ""
+
+#: class/class-mpwp-gateway.php:160
+msgid "This controls the title which the user sees during checkout."
+msgstr ""
+
+#: class/class-mpwp-gateway.php:165
+msgid "Description"
+msgstr ""
+
+#: class/class-mpwp-gateway.php:168
+msgid "This controls the description which the user sees during checkout."
+msgstr ""
+
+#. Description of the plugin/theme
+#: class/class-mpwp-gateway.php:169
+msgid ""
+"MugglePay is a one-stop payment solution for merchants with an online "
+"payment need."
+msgstr ""
+
+#: class/class-mpwp-gateway.php:172
+msgid "Check Orders"
+msgstr ""
+
+#: class/class-mpwp-gateway.php:174
+msgid ""
+"The plugin automatically checks the order payment status by default and "
+"updates the order status every 5 minutes."
+msgstr ""
+
+#: class/class-mpwp-gateway.php:182
+msgid "Setting"
+msgstr ""
+
+#: class/class-mpwp-gateway.php:187
+msgid "API Auth Token (API key) "
+msgstr ""
+
+#. translators: %s: URL
+#: class/class-mpwp-gateway.php:191
+#, php-format
+msgid ""
+"Register your MugglePay merchant accounts with your invitation code and get "
+"your API key at Merchants Portal. You "
+"will find your API Auth Token (API key) for authentication. MORE"
+msgstr ""
+
+#: class/class-mpwp-gateway.php:194
+msgid "Button Styles"
+msgstr ""
+
+#. translators: %s: URL
+#: class/class-mpwp-gateway.php:198
+msgid ""
+"If the style of your payment page is not displayed properly, you can "
+"overwrite your new style here"
+msgstr ""
+
+#: class/class-mpwp-gateway.php:201
+msgid "Debug log"
+msgstr ""
+
+#: class/class-mpwp-gateway.php:203
+msgid "Enable logging"
+msgstr ""
+
+#. translators: Description for 'Debug log' section of settings page.
+#: class/class-mpwp-gateway.php:206
+#, php-format
+msgid "Log MPWP API events inside %s"
+msgstr ""
+
+#: class/class-mpwp-gateway.php:209
+msgid "Payment Gateway"
+msgstr ""
+
+#: class/class-mpwp-gateway.php:262
+msgid "Refund failed."
+msgstr ""
+
+#: class/class-mpwp-gateway.php:401
+#, php-format
+msgid "Payment order #%s"
+msgstr ""
+
+#: class/class-mpwp-gateway.php:467
+msgid "Empty Response"
+msgstr ""
+
+#: class/class-mpwp-gateway.php:500
+msgid "Authentication Token is not set or expired."
+msgstr ""
+
+#: class/class-mpwp-gateway.php:502
+msgid "Invoice does not exist."
+msgstr ""
+
+#: class/class-mpwp-gateway.php:504
+msgid "It has been verified already."
+msgstr ""
+
+#: class/class-mpwp-gateway.php:506
+msgid "Invoice does not exist, or it cannot be canceled."
+msgstr ""
+
+#: class/class-mpwp-gateway.php:508
+msgid "Order does not exist or permission denied."
+msgstr ""
+
+#: class/class-mpwp-gateway.php:510
+msgid "Order does not exist, or it cannot be canceled."
+msgstr ""
+
+#: class/class-mpwp-gateway.php:512
+msgid "Order does not exist, or it`s status is not refundable."
+msgstr ""
+
+#: class/class-mpwp-gateway.php:514
+msgid "Payment has been verified with payment already."
+msgstr ""
+
+#: class/class-mpwp-gateway.php:516
+msgid ""
+"Payment money does not match the order money, please double check the price."
+msgstr ""
+
+#: class/class-mpwp-gateway.php:518
+msgid ""
+"Payment money does not the order of current merchant , please double check "
+"the order."
+msgstr ""
+
+#: class/class-mpwp-gateway.php:520
+msgid "Order id is not valid."
+msgstr ""
+
+#: class/class-mpwp-gateway.php:522
+msgid "Order not exist or is not paid yet."
+msgstr ""
+
+#: class/class-mpwp-gateway.php:524
+msgid "Order with same merchant_order_id exisits."
+msgstr ""
+
+#: class/class-mpwp-gateway.php:526
+msgid "The current order is not new, and payment method cannot be switched."
+msgstr ""
+
+#: class/class-mpwp-gateway.php:528
+msgid "The payment method is not working, please retry later."
+msgstr ""
+
+#: class/class-mpwp-gateway.php:530
+msgid "The current payment status not ready to send callback."
+msgstr ""
+
+#: class/class-mpwp-gateway.php:532
+msgid "Missing parameters."
+msgstr ""
+
+#: class/class-mpwp-gateway.php:538
+msgid ""
+"The payment is temporarily unavailable, please use another payment method"
+msgstr ""
+
+#: class/class-mpwp-gateway.php:540
+msgid "Price amount or currency is not set correctly."
+msgstr ""
+
+#: class/class-mpwp-gateway.php:542
+msgid "The email or password does not match."
+msgstr ""
+
+#: class/class-mpwp-gateway.php:544
+msgid "The user does not exist or no permission."
+msgstr ""
+
+#: class/class-mpwp-gateway.php:546
+msgid "The user operatioin failed."
+msgstr ""
+
+#: class/class-mpwp-gateway.php:548
+msgid "The invitation code is not filled correctly."
+msgstr ""
+
+#: class/class-mpwp-gateway.php:550
+msgid "Error."
+msgstr ""
+
+#: class/class-mpwp-gateway.php:552
+msgid "API credentials are not valid"
+msgstr ""
+
+#: class/class-mpwp-gateway.php:554
+msgid "Page, action not found"
+msgstr ""
+
+#: class/class-mpwp-gateway.php:556
+msgid "API request limit is exceeded"
+msgstr ""
+
+#: class/class-mpwp-gateway.php:558 class/class-mpwp-gateway.php:560
+msgid "Server error in MugglePay"
+msgstr ""
+
+#: class/class-mpwp-gateway.php:581
+msgid "MugglePay acceptance mark"
+msgstr ""
+
+#: muggle-pay-for-wp.php:64
+msgid "Once every 5 minutes"
+msgstr ""
+
+#: muggle-pay-for-wp.php:99
+msgid "MugglePay Payment Voucher"
+msgstr ""
+
+#: muggle-pay-for-wp.php:102
+#, php-format
+msgid "Transaction ID: %s"
+msgstr ""
+
+#. Plugin Name of the plugin/theme
+msgid "MugglePay
+msgstr ""
+
+#. Plugin URI of the plugin/theme
+msgid "https://mugglepay.com/"
+msgstr ""
+
+#. Author of the plugin/theme
+msgid "MugglePay"
+msgstr ""
+
+#. Author URI of the plugin/theme
+msgid "https://mugglepay.com/"
+msgstr ""
diff --git a/muggle-pay.php b/muggle-pay.php
new file mode 100644
index 0000000..0e11f07
--- /dev/null
+++ b/muggle-pay.php
@@ -0,0 +1,202 @@
+register( new WC_Gateway_MPWP_Blocks_Support() );
+ }
+ );
+ }
+}
+add_action( 'woocommerce_blocks_loaded', 'wc_gateway_mpwp_woocommerce_block_support' );
+
+
+// Regiester Gateway To WooCommerce
+function mpwp_add_gateway_class($methods)
+{
+ $methods[] = 'WC_Gateway_MPWP';
+ return $methods;
+}
+
+/**
+ * Check All MugglePay Order Status
+ */
+function mpwp_wc_check_orders()
+{
+ $gateway = WC()->payment_gateways()->payment_gateways()['mpwp'];
+ return $gateway->check_orders();
+}
+
+/**
+ * Setup cron job.
+ */
+function mpwp_cron_schedules($schedules)
+{
+ if (!isset($schedules["5min"])) {
+ $schedules["5min"] = array(
+ 'interval' => 5 * 60,
+ 'display' => __('Once every 5 minutes', 'mpwp')
+ );
+ }
+ return $schedules;
+}
+add_filter('cron_schedules', 'mpwp_cron_schedules');
+
+function mpwp_activation()
+{
+ if (! wp_next_scheduled('mpwp_check_orders')) {
+ wp_schedule_event(time(), '5min', 'mpwp_check_orders');
+ }
+}
+function mpwp_deactivation()
+{
+ wp_clear_scheduled_hook('mpwp_check_orders');
+}
+register_activation_hook(__FILE__, 'mpwp_activation');
+register_deactivation_hook(__FILE__, 'mpwp_deactivation');
+
+
+/**
+ * Add order MugglePay meta after General and before Billing
+ *
+ * @see: https://rudrastyh.com/woocommerce/customize-order-details.html
+ *
+ * @param WC_Order $order WC order instance
+ */
+function mpwp_order_meta_general($order)
+{
+ $gateway = WC()->payment_gateways()->payment_gateways()['mpwp'];
+ if (isset($gateway->gateway_methods[$order->get_payment_method()])) {
+ ?>
+
+
+
getEnd()->getOperator() === '<' && $i+1 < $count) {
+ $nextInterval = $intervals['numeric'][$i+1];
+ if ($interval->getEnd()->getVersion() === $nextInterval->getStart()->getVersion() && $nextInterval->getStart()->getOperator() === '>') {
+ // only add a start if we didn't already do so, can be skipped if we're looking at second
+ // interval in [>=M, N,
P, =M, !=N] already and we only want to add !=P right now
+ if (\count($unEqualConstraints) === 0 && (string) $interval->getStart() !== (string) Interval::fromZero()) {
+ $unEqualConstraints[] = $interval->getStart();
+ }
+ $unEqualConstraints[] = new Constraint('!=', $interval->getEnd()->getVersion());
+ continue;
+ }
+ }
+
+ if (\count($unEqualConstraints) > 0) {
+ // this is where the end of the following interval of a != constraint is added as explained above
+ if ((string) $interval->getEnd() !== (string) Interval::untilPositiveInfinity()) {
+ $unEqualConstraints[] = $interval->getEnd();
+ }
+
+ // count is 1 if entire constraint is just one != expression
+ if (\count($unEqualConstraints) > 1) {
+ $constraints[] = new MultiConstraint($unEqualConstraints, true);
+ } else {
+ $constraints[] = $unEqualConstraints[0];
+ }
+
+ $unEqualConstraints = array();
+ continue;
+ }
+
+ // convert back >= x - <= x intervals to == x
+ if ($interval->getStart()->getVersion() === $interval->getEnd()->getVersion() && $interval->getStart()->getOperator() === '>=' && $interval->getEnd()->getOperator() === '<=') {
+ $constraints[] = new Constraint('==', $interval->getStart()->getVersion());
+ continue;
+ }
+
+ if ((string) $interval->getStart() === (string) Interval::fromZero()) {
+ $constraints[] = $interval->getEnd();
+ } elseif ((string) $interval->getEnd() === (string) Interval::untilPositiveInfinity()) {
+ $constraints[] = $interval->getStart();
+ } else {
+ $constraints[] = new MultiConstraint(array($interval->getStart(), $interval->getEnd()), true);
+ }
+ }
+ }
+
+ $devConstraints = array();
+
+ if (0 === \count($intervals['branches']['names'])) {
+ if ($intervals['branches']['exclude']) {
+ if ($hasNumericMatchAll) {
+ return new MatchAllConstraint;
+ }
+ // otherwise constraint should contain a != operator and already cover this
+ }
+ } else {
+ foreach ($intervals['branches']['names'] as $branchName) {
+ if ($intervals['branches']['exclude']) {
+ $devConstraints[] = new Constraint('!=', $branchName);
+ } else {
+ $devConstraints[] = new Constraint('==', $branchName);
+ }
+ }
+
+ // excluded branches, e.g. != dev-foo are conjunctive with the interval, so
+ // > 2.0 != dev-foo must return a conjunctive constraint
+ if ($intervals['branches']['exclude']) {
+ if (\count($constraints) > 1) {
+ return new MultiConstraint(array_merge(
+ array(new MultiConstraint($constraints, false)),
+ $devConstraints
+ ), true);
+ }
+
+ if (\count($constraints) === 1 && (string)$constraints[0] === (string)Interval::fromZero()) {
+ if (\count($devConstraints) > 1) {
+ return new MultiConstraint($devConstraints, true);
+ }
+ return $devConstraints[0];
+ }
+
+ return new MultiConstraint(array_merge($constraints, $devConstraints), true);
+ }
+
+ // otherwise devConstraints contains a list of == operators for branches which are disjunctive with the
+ // rest of the constraint
+ $constraints = array_merge($constraints, $devConstraints);
+ }
+
+ if (\count($constraints) > 1) {
+ return new MultiConstraint($constraints, false);
+ }
+
+ if (\count($constraints) === 1) {
+ return $constraints[0];
+ }
+
+ return new MatchNoneConstraint;
+ }
+
+ /**
+ * Creates an array of numeric intervals and branch constraints representing a given constraint
+ *
+ * if the returned numeric array is empty it means the constraint matches nothing in the numeric range (0 - +inf)
+ * if the returned branches array is empty it means no dev-* versions are matched
+ * if a constraint matches all possible dev-* versions, branches will contain Interval::anyDev()
+ *
+ * @return array
+ * @phpstan-return array{'numeric': Interval[], 'branches': array{'names': string[], 'exclude': bool}}
+ */
+ public static function get(ConstraintInterface $constraint)
+ {
+ $key = (string) $constraint;
+
+ if (!isset(self::$intervalsCache[$key])) {
+ self::$intervalsCache[$key] = self::generateIntervals($constraint);
+ }
+
+ return self::$intervalsCache[$key];
+ }
+
+ /**
+ * @phpstan-return array{'numeric': Interval[], 'branches': array{'names': string[], 'exclude': bool}}
+ */
+ private static function generateIntervals(ConstraintInterface $constraint, $stopOnFirstValidInterval = false)
+ {
+ if ($constraint instanceof MatchAllConstraint) {
+ return array('numeric' => array(new Interval(Interval::fromZero(), Interval::untilPositiveInfinity())), 'branches' => Interval::anyDev());
+ }
+
+ if ($constraint instanceof MatchNoneConstraint) {
+ return array('numeric' => array(), 'branches' => array('names' => array(), 'exclude' => false));
+ }
+
+ if ($constraint instanceof Constraint) {
+ return self::generateSingleConstraintIntervals($constraint);
+ }
+
+ if (!$constraint instanceof MultiConstraint) {
+ throw new \UnexpectedValueException('The constraint passed in should be an MatchAllConstraint, Constraint or MultiConstraint instance, got '.\get_class($constraint).'.');
+ }
+
+ $constraints = $constraint->getConstraints();
+
+ $numericGroups = array();
+ $constraintBranches = array();
+ foreach ($constraints as $c) {
+ $res = self::get($c);
+ $numericGroups[] = $res['numeric'];
+ $constraintBranches[] = $res['branches'];
+ }
+
+ if ($constraint->isDisjunctive()) {
+ $branches = Interval::noDev();
+ foreach ($constraintBranches as $b) {
+ if ($b['exclude']) {
+ if ($branches['exclude']) {
+ // disjunctive constraint, so only exclude what's excluded in all constraints
+ // !=a,!=b || !=b,!=c => !=b
+ $branches['names'] = array_intersect($branches['names'], $b['names']);
+ } else {
+ // disjunctive constraint so exclude all names which are not explicitly included in the alternative
+ // (==b || ==c) || !=a,!=b => !=a
+ $branches['exclude'] = true;
+ $branches['names'] = array_diff($b['names'], $branches['names']);
+ }
+ } else {
+ if ($branches['exclude']) {
+ // disjunctive constraint so exclude all names which are not explicitly included in the alternative
+ // !=a,!=b || (==b || ==c) => !=a
+ $branches['names'] = array_diff($branches['names'], $b['names']);
+ } else {
+ // disjunctive constraint, so just add all the other branches
+ // (==a || ==b) || ==c => ==a || ==b || ==c
+ $branches['names'] = array_merge($branches['names'], $b['names']);
+ }
+ }
+ }
+ } else {
+ $branches = Interval::anyDev();
+ foreach ($constraintBranches as $b) {
+ if ($b['exclude']) {
+ if ($branches['exclude']) {
+ // conjunctive, so just add all branch names to be excluded
+ // !=a && !=b => !=a,!=b
+ $branches['names'] = array_merge($branches['names'], $b['names']);
+ } else {
+ // conjunctive, so only keep included names which are not excluded
+ // (==a||==c) && !=a,!=b => ==c
+ $branches['names'] = array_diff($branches['names'], $b['names']);
+ }
+ } else {
+ if ($branches['exclude']) {
+ // conjunctive, so only keep included names which are not excluded
+ // !=a,!=b && (==a||==c) => ==c
+ $branches['names'] = array_diff($b['names'], $branches['names']);
+ $branches['exclude'] = false;
+ } else {
+ // conjunctive, so only keep names that are included in both
+ // (==a||==b) && (==a||==c) => ==a
+ $branches['names'] = array_intersect($branches['names'], $b['names']);
+ }
+ }
+ }
+ }
+
+ $branches['names'] = array_unique($branches['names']);
+
+ if (\count($numericGroups) === 1) {
+ return array('numeric' => $numericGroups[0], 'branches' => $branches);
+ }
+
+ $borders = array();
+ foreach ($numericGroups as $group) {
+ foreach ($group as $interval) {
+ $borders[] = array('version' => $interval->getStart()->getVersion(), 'operator' => $interval->getStart()->getOperator(), 'side' => 'start');
+ $borders[] = array('version' => $interval->getEnd()->getVersion(), 'operator' => $interval->getEnd()->getOperator(), 'side' => 'end');
+ }
+ }
+
+ $opSortOrder = self::$opSortOrder;
+ usort($borders, function ($a, $b) use ($opSortOrder) {
+ $order = version_compare($a['version'], $b['version']);
+ if ($order === 0) {
+ return $opSortOrder[$a['operator']] - $opSortOrder[$b['operator']];
+ }
+
+ return $order;
+ });
+
+ $activeIntervals = 0;
+ $intervals = array();
+ $index = 0;
+ $activationThreshold = $constraint->isConjunctive() ? \count($numericGroups) : 1;
+ $active = false;
+ $start = null;
+ foreach ($borders as $border) {
+ if ($border['side'] === 'start') {
+ $activeIntervals++;
+ } else {
+ $activeIntervals--;
+ }
+ if (!$active && $activeIntervals >= $activationThreshold) {
+ $start = new Constraint($border['operator'], $border['version']);
+ $active = true;
+ }
+ if ($active && $activeIntervals < $activationThreshold) {
+ $active = false;
+
+ // filter out invalid intervals like > x - <= x, or >= x - < x
+ if (
+ version_compare($start->getVersion(), $border['version'], '=')
+ && (
+ ($start->getOperator() === '>' && $border['operator'] === '<=')
+ || ($start->getOperator() === '>=' && $border['operator'] === '<')
+ )
+ ) {
+ unset($intervals[$index]);
+ } else {
+ $intervals[$index] = new Interval($start, new Constraint($border['operator'], $border['version']));
+ $index++;
+
+ if ($stopOnFirstValidInterval) {
+ break;
+ }
+ }
+
+ $start = null;
+ }
+ }
+
+ return array('numeric' => $intervals, 'branches' => $branches);
+ }
+
+ /**
+ * @phpstan-return array{'numeric': Interval[], 'branches': array{'names': string[], 'exclude': bool}}}
+ */
+ private static function generateSingleConstraintIntervals(Constraint $constraint)
+ {
+ $op = $constraint->getOperator();
+
+ // handle branch constraints first
+ if (strpos($constraint->getVersion(), 'dev-') === 0) {
+ $intervals = array();
+ $branches = array('names' => array(), 'exclude' => false);
+
+ // != dev-foo means any numeric version may match, we treat >/< like != they are not really defined for branches
+ if ($op === '!=') {
+ $intervals[] = new Interval(Interval::fromZero(), Interval::untilPositiveInfinity());
+ $branches = array('names' => array($constraint->getVersion()), 'exclude' => true);
+ } elseif ($op === '==') {
+ $branches['names'][] = $constraint->getVersion();
+ }
+
+ return array(
+ 'numeric' => $intervals,
+ 'branches' => $branches,
+ );
+ }
+
+ if ($op[0] === '>') { // > & >=
+ return array('numeric' => array(new Interval($constraint, Interval::untilPositiveInfinity())), 'branches' => Interval::noDev());
+ }
+ if ($op[0] === '<') { // < & <=
+ return array('numeric' => array(new Interval(Interval::fromZero(), $constraint)), 'branches' => Interval::noDev());
+ }
+ if ($op === '!=') {
+ // convert !=x to intervals of 0 - x - +inf + dev*
+ return array('numeric' => array(
+ new Interval(Interval::fromZero(), new Constraint('<', $constraint->getVersion())),
+ new Interval(new Constraint('>', $constraint->getVersion()), Interval::untilPositiveInfinity()),
+ ), 'branches' => Interval::anyDev());
+ }
+
+ // convert ==x to an interval of >=x - <=x
+ return array('numeric' => array(
+ new Interval(new Constraint('>=', $constraint->getVersion()), new Constraint('<=', $constraint->getVersion())),
+ ), 'branches' => Interval::noDev());
+ }
+}
diff --git a/tools/php-cs-fixer/vendor/composer/semver/src/Semver.php b/tools/php-cs-fixer/vendor/composer/semver/src/Semver.php
new file mode 100644
index 0000000..440aa10
--- /dev/null
+++ b/tools/php-cs-fixer/vendor/composer/semver/src/Semver.php
@@ -0,0 +1,129 @@
+
+ *
+ * For the full copyright and license information, please view
+ * the LICENSE file that was distributed with this source code.
+ */
+
+namespace Composer\Semver;
+
+use Composer\Semver\Constraint\Constraint;
+
+class Semver
+{
+ const SORT_ASC = 1;
+ const SORT_DESC = -1;
+
+ /** @var VersionParser */
+ private static $versionParser;
+
+ /**
+ * Determine if given version satisfies given constraints.
+ *
+ * @param string $version
+ * @param string $constraints
+ *
+ * @return bool
+ */
+ public static function satisfies($version, $constraints)
+ {
+ if (null === self::$versionParser) {
+ self::$versionParser = new VersionParser();
+ }
+
+ $versionParser = self::$versionParser;
+ $provider = new Constraint('==', $versionParser->normalize($version));
+ $parsedConstraints = $versionParser->parseConstraints($constraints);
+
+ return $parsedConstraints->matches($provider);
+ }
+
+ /**
+ * Return all versions that satisfy given constraints.
+ *
+ * @param array $versions
+ * @param string $constraints
+ *
+ * @return array
+ */
+ public static function satisfiedBy(array $versions, $constraints)
+ {
+ $versions = array_filter($versions, function ($version) use ($constraints) {
+ return Semver::satisfies($version, $constraints);
+ });
+
+ return array_values($versions);
+ }
+
+ /**
+ * Sort given array of versions.
+ *
+ * @param array $versions
+ *
+ * @return array
+ */
+ public static function sort(array $versions)
+ {
+ return self::usort($versions, self::SORT_ASC);
+ }
+
+ /**
+ * Sort given array of versions in reverse.
+ *
+ * @param array $versions
+ *
+ * @return array
+ */
+ public static function rsort(array $versions)
+ {
+ return self::usort($versions, self::SORT_DESC);
+ }
+
+ /**
+ * @param array $versions
+ * @param int $direction
+ *
+ * @return array
+ */
+ private static function usort(array $versions, $direction)
+ {
+ if (null === self::$versionParser) {
+ self::$versionParser = new VersionParser();
+ }
+
+ $versionParser = self::$versionParser;
+ $normalized = array();
+
+ // Normalize outside of usort() scope for minor performance increase.
+ // Creates an array of arrays: [[normalized, key], ...]
+ foreach ($versions as $key => $version) {
+ $normalizedVersion = $versionParser->normalize($version);
+ $normalizedVersion = $versionParser->normalizeDefaultBranch($normalizedVersion);
+ $normalized[] = array($normalizedVersion, $key);
+ }
+
+ usort($normalized, function (array $left, array $right) use ($direction) {
+ if ($left[0] === $right[0]) {
+ return 0;
+ }
+
+ if (Comparator::lessThan($left[0], $right[0])) {
+ return -$direction;
+ }
+
+ return $direction;
+ });
+
+ // Recreate input array, using the original indexes which are now in sorted order.
+ $sorted = array();
+ foreach ($normalized as $item) {
+ $sorted[] = $versions[$item[1]];
+ }
+
+ return $sorted;
+ }
+}
diff --git a/tools/php-cs-fixer/vendor/composer/semver/src/VersionParser.php b/tools/php-cs-fixer/vendor/composer/semver/src/VersionParser.php
new file mode 100644
index 0000000..5567950
--- /dev/null
+++ b/tools/php-cs-fixer/vendor/composer/semver/src/VersionParser.php
@@ -0,0 +1,573 @@
+
+ *
+ * For the full copyright and license information, please view
+ * the LICENSE file that was distributed with this source code.
+ */
+
+namespace Composer\Semver;
+
+use Composer\Semver\Constraint\ConstraintInterface;
+use Composer\Semver\Constraint\MatchAllConstraint;
+use Composer\Semver\Constraint\MultiConstraint;
+use Composer\Semver\Constraint\Constraint;
+
+/**
+ * Version parser.
+ *
+ * @author Jordi Boggiano
+ */
+class VersionParser
+{
+ /**
+ * Regex to match pre-release data (sort of).
+ *
+ * Due to backwards compatibility:
+ * - Instead of enforcing hyphen, an underscore, dot or nothing at all are also accepted.
+ * - Only stabilities as recognized by Composer are allowed to precede a numerical identifier.
+ * - Numerical-only pre-release identifiers are not supported, see tests.
+ *
+ * |--------------|
+ * [major].[minor].[patch] -[pre-release] +[build-metadata]
+ *
+ * @var string
+ */
+ private static $modifierRegex = '[._-]?(?:(stable|beta|b|RC|alpha|a|patch|pl|p)((?:[.-]?\d+)*+)?)?([.-]?dev)?';
+
+ /** @var string */
+ private static $stabilitiesRegex = 'stable|RC|beta|alpha|dev';
+
+ /**
+ * Returns the stability of a version.
+ *
+ * @param string $version
+ *
+ * @return string
+ */
+ public static function parseStability($version)
+ {
+ $version = preg_replace('{#.+$}', '', $version);
+
+ if (strpos($version, 'dev-') === 0 || '-dev' === substr($version, -4)) {
+ return 'dev';
+ }
+
+ preg_match('{' . self::$modifierRegex . '(?:\+.*)?$}i', strtolower($version), $match);
+
+ if (!empty($match[3])) {
+ return 'dev';
+ }
+
+ if (!empty($match[1])) {
+ if ('beta' === $match[1] || 'b' === $match[1]) {
+ return 'beta';
+ }
+ if ('alpha' === $match[1] || 'a' === $match[1]) {
+ return 'alpha';
+ }
+ if ('rc' === $match[1]) {
+ return 'RC';
+ }
+ }
+
+ return 'stable';
+ }
+
+ /**
+ * @param string $stability
+ *
+ * @return string
+ */
+ public static function normalizeStability($stability)
+ {
+ $stability = strtolower($stability);
+
+ return $stability === 'rc' ? 'RC' : $stability;
+ }
+
+ /**
+ * Normalizes a version string to be able to perform comparisons on it.
+ *
+ * @param string $version
+ * @param string $fullVersion optional complete version string to give more context
+ *
+ * @throws \UnexpectedValueException
+ *
+ * @return string
+ */
+ public function normalize($version, $fullVersion = null)
+ {
+ $version = trim($version);
+ $origVersion = $version;
+ if (null === $fullVersion) {
+ $fullVersion = $version;
+ }
+
+ // strip off aliasing
+ if (preg_match('{^([^,\s]++) ++as ++([^,\s]++)$}', $version, $match)) {
+ $version = $match[1];
+ }
+
+ // strip off stability flag
+ if (preg_match('{@(?:' . self::$stabilitiesRegex . ')$}i', $version, $match)) {
+ $version = substr($version, 0, strlen($version) - strlen($match[0]));
+ }
+
+ // normalize master/trunk/default branches to dev-name for BC with 1.x as these used to be valid constraints
+ if (\in_array($version, array('master', 'trunk', 'default'), true)) {
+ $version = 'dev-' . $version;
+ }
+
+ // if requirement is branch-like, use full name
+ if (stripos($version, 'dev-') === 0) {
+ return 'dev-' . substr($version, 4);
+ }
+
+ // strip off build metadata
+ if (preg_match('{^([^,\s+]++)\+[^\s]++$}', $version, $match)) {
+ $version = $match[1];
+ }
+
+ // match classical versioning
+ if (preg_match('{^v?(\d{1,5})(\.\d++)?(\.\d++)?(\.\d++)?' . self::$modifierRegex . '$}i', $version, $matches)) {
+ $version = $matches[1]
+ . (!empty($matches[2]) ? $matches[2] : '.0')
+ . (!empty($matches[3]) ? $matches[3] : '.0')
+ . (!empty($matches[4]) ? $matches[4] : '.0');
+ $index = 5;
+ // match date(time) based versioning
+ } elseif (preg_match('{^v?(\d{4}(?:[.:-]?\d{2}){1,6}(?:[.:-]?\d{1,3})?)' . self::$modifierRegex . '$}i', $version, $matches)) {
+ $version = preg_replace('{\D}', '.', $matches[1]);
+ $index = 2;
+ }
+
+ // add version modifiers if a version was matched
+ if (isset($index)) {
+ if (!empty($matches[$index])) {
+ if ('stable' === $matches[$index]) {
+ return $version;
+ }
+ $version .= '-' . $this->expandStability($matches[$index]) . (isset($matches[$index + 1]) && '' !== $matches[$index + 1] ? ltrim($matches[$index + 1], '.-') : '');
+ }
+
+ if (!empty($matches[$index + 2])) {
+ $version .= '-dev';
+ }
+
+ return $version;
+ }
+
+ // match dev branches
+ if (preg_match('{(.*?)[.-]?dev$}i', $version, $match)) {
+ try {
+ $normalized = $this->normalizeBranch($match[1]);
+ // a branch ending with -dev is only valid if it is numeric
+ // if it gets prefixed with dev- it means the branch name should
+ // have had a dev- prefix already when passed to normalize
+ if (strpos($normalized, 'dev-') === false) {
+ return $normalized;
+ }
+ } catch (\Exception $e) {
+ }
+ }
+
+ $extraMessage = '';
+ if (preg_match('{ +as +' . preg_quote($version) . '(?:@(?:'.self::$stabilitiesRegex.'))?$}', $fullVersion)) {
+ $extraMessage = ' in "' . $fullVersion . '", the alias must be an exact version';
+ } elseif (preg_match('{^' . preg_quote($version) . '(?:@(?:'.self::$stabilitiesRegex.'))? +as +}', $fullVersion)) {
+ $extraMessage = ' in "' . $fullVersion . '", the alias source must be an exact version, if it is a branch name you should prefix it with dev-';
+ }
+
+ throw new \UnexpectedValueException('Invalid version string "' . $origVersion . '"' . $extraMessage);
+ }
+
+ /**
+ * Extract numeric prefix from alias, if it is in numeric format, suitable for version comparison.
+ *
+ * @param string $branch Branch name (e.g. 2.1.x-dev)
+ *
+ * @return string|false Numeric prefix if present (e.g. 2.1.) or false
+ */
+ public function parseNumericAliasPrefix($branch)
+ {
+ if (preg_match('{^(?P(\d++\\.)*\d++)(?:\.x)?-dev$}i', $branch, $matches)) {
+ return $matches['version'] . '.';
+ }
+
+ return false;
+ }
+
+ /**
+ * Normalizes a branch name to be able to perform comparisons on it.
+ *
+ * @param string $name
+ *
+ * @return string
+ */
+ public function normalizeBranch($name)
+ {
+ $name = trim($name);
+
+ if (preg_match('{^v?(\d++)(\.(?:\d++|[xX*]))?(\.(?:\d++|[xX*]))?(\.(?:\d++|[xX*]))?$}i', $name, $matches)) {
+ $version = '';
+ for ($i = 1; $i < 5; ++$i) {
+ $version .= isset($matches[$i]) ? str_replace(array('*', 'X'), 'x', $matches[$i]) : '.x';
+ }
+
+ return str_replace('x', '9999999', $version) . '-dev';
+ }
+
+ return 'dev-' . $name;
+ }
+
+ /**
+ * Normalizes a default branch name (i.e. master on git) to 9999999-dev.
+ *
+ * @param string $name
+ *
+ * @return string
+ */
+ public function normalizeDefaultBranch($name)
+ {
+ if ($name === 'dev-master' || $name === 'dev-default' || $name === 'dev-trunk') {
+ return '9999999-dev';
+ }
+
+ return $name;
+ }
+
+ /**
+ * Parses a constraint string into MultiConstraint and/or Constraint objects.
+ *
+ * @param string $constraints
+ *
+ * @return ConstraintInterface
+ */
+ public function parseConstraints($constraints)
+ {
+ $prettyConstraint = $constraints;
+
+ $orConstraints = preg_split('{\s*\|\|?\s*}', trim($constraints));
+ $orGroups = array();
+
+ foreach ($orConstraints as $constraints) {
+ $andConstraints = preg_split('{(?< ,]) *(? 1) {
+ $constraintObjects = array();
+ foreach ($andConstraints as $constraint) {
+ foreach ($this->parseConstraint($constraint) as $parsedConstraint) {
+ $constraintObjects[] = $parsedConstraint;
+ }
+ }
+ } else {
+ $constraintObjects = $this->parseConstraint($andConstraints[0]);
+ }
+
+ if (1 === \count($constraintObjects)) {
+ $constraint = $constraintObjects[0];
+ } else {
+ $constraint = new MultiConstraint($constraintObjects);
+ }
+
+ $orGroups[] = $constraint;
+ }
+
+ $constraint = MultiConstraint::create($orGroups, false);
+
+ $constraint->setPrettyString($prettyConstraint);
+
+ return $constraint;
+ }
+
+ /**
+ * @param string $constraint
+ *
+ * @throws \UnexpectedValueException
+ *
+ * @return array
+ */
+ private function parseConstraint($constraint)
+ {
+ // strip off aliasing
+ if (preg_match('{^([^,\s]++) ++as ++([^,\s]++)$}', $constraint, $match)) {
+ $constraint = $match[1];
+ }
+
+ // strip @stability flags, and keep it for later use
+ if (preg_match('{^([^,\s]*?)@(' . self::$stabilitiesRegex . ')$}i', $constraint, $match)) {
+ $constraint = '' !== $match[1] ? $match[1] : '*';
+ if ($match[2] !== 'stable') {
+ $stabilityModifier = $match[2];
+ }
+ }
+
+ // get rid of #refs as those are used by composer only
+ if (preg_match('{^(dev-[^,\s@]+?|[^,\s@]+?\.x-dev)#.+$}i', $constraint, $match)) {
+ $constraint = $match[1];
+ }
+
+ if (preg_match('{^(v)?[xX*](\.[xX*])*$}i', $constraint, $match)) {
+ if (!empty($match[1]) || !empty($match[2])) {
+ return array(new Constraint('>=', '0.0.0.0-dev'));
+ }
+
+ return array(new MatchAllConstraint());
+ }
+
+ $versionRegex = 'v?(\d++)(?:\.(\d++))?(?:\.(\d++))?(?:\.(\d++))?(?:' . self::$modifierRegex . '|\.([xX*][.-]?dev))(?:\+[^\s]+)?';
+
+ // Tilde Range
+ //
+ // Like wildcard constraints, unsuffixed tilde constraints say that they must be greater than the previous
+ // version, to ensure that unstable instances of the current version are allowed. However, if a stability
+ // suffix is added to the constraint, then a >= match on the current version is used instead.
+ if (preg_match('{^~>?' . $versionRegex . '$}i', $constraint, $matches)) {
+ if (strpos($constraint, '~>') === 0) {
+ throw new \UnexpectedValueException(
+ 'Could not parse version constraint ' . $constraint . ': ' .
+ 'Invalid operator "~>", you probably meant to use the "~" operator'
+ );
+ }
+
+ // Work out which position in the version we are operating at
+ if (isset($matches[4]) && '' !== $matches[4] && null !== $matches[4]) {
+ $position = 4;
+ } elseif (isset($matches[3]) && '' !== $matches[3] && null !== $matches[3]) {
+ $position = 3;
+ } elseif (isset($matches[2]) && '' !== $matches[2] && null !== $matches[2]) {
+ $position = 2;
+ } else {
+ $position = 1;
+ }
+
+ // when matching 2.x-dev or 3.0.x-dev we have to shift the second or third number, despite no second/third number matching above
+ if (!empty($matches[8])) {
+ $position++;
+ }
+
+ // Calculate the stability suffix
+ $stabilitySuffix = '';
+ if (empty($matches[5]) && empty($matches[7]) && empty($matches[8])) {
+ $stabilitySuffix .= '-dev';
+ }
+
+ $lowVersion = $this->normalize(substr($constraint . $stabilitySuffix, 1));
+ $lowerBound = new Constraint('>=', $lowVersion);
+
+ // For upper bound, we increment the position of one more significance,
+ // but highPosition = 0 would be illegal
+ $highPosition = max(1, $position - 1);
+ $highVersion = $this->manipulateVersionString($matches, $highPosition, 1) . '-dev';
+ $upperBound = new Constraint('<', $highVersion);
+
+ return array(
+ $lowerBound,
+ $upperBound,
+ );
+ }
+
+ // Caret Range
+ //
+ // Allows changes that do not modify the left-most non-zero digit in the [major, minor, patch] tuple.
+ // In other words, this allows patch and minor updates for versions 1.0.0 and above, patch updates for
+ // versions 0.X >=0.1.0, and no updates for versions 0.0.X
+ if (preg_match('{^\^' . $versionRegex . '($)}i', $constraint, $matches)) {
+ // Work out which position in the version we are operating at
+ if ('0' !== $matches[1] || '' === $matches[2] || null === $matches[2]) {
+ $position = 1;
+ } elseif ('0' !== $matches[2] || '' === $matches[3] || null === $matches[3]) {
+ $position = 2;
+ } else {
+ $position = 3;
+ }
+
+ // Calculate the stability suffix
+ $stabilitySuffix = '';
+ if (empty($matches[5]) && empty($matches[7]) && empty($matches[8])) {
+ $stabilitySuffix .= '-dev';
+ }
+
+ $lowVersion = $this->normalize(substr($constraint . $stabilitySuffix, 1));
+ $lowerBound = new Constraint('>=', $lowVersion);
+
+ // For upper bound, we increment the position of one more significance,
+ // but highPosition = 0 would be illegal
+ $highVersion = $this->manipulateVersionString($matches, $position, 1) . '-dev';
+ $upperBound = new Constraint('<', $highVersion);
+
+ return array(
+ $lowerBound,
+ $upperBound,
+ );
+ }
+
+ // X Range
+ //
+ // Any of X, x, or * may be used to "stand in" for one of the numeric values in the [major, minor, patch] tuple.
+ // A partial version range is treated as an X-Range, so the special character is in fact optional.
+ if (preg_match('{^v?(\d++)(?:\.(\d++))?(?:\.(\d++))?(?:\.[xX*])++$}', $constraint, $matches)) {
+ if (isset($matches[3]) && '' !== $matches[3] && null !== $matches[3]) {
+ $position = 3;
+ } elseif (isset($matches[2]) && '' !== $matches[2] && null !== $matches[2]) {
+ $position = 2;
+ } else {
+ $position = 1;
+ }
+
+ $lowVersion = $this->manipulateVersionString($matches, $position) . '-dev';
+ $highVersion = $this->manipulateVersionString($matches, $position, 1) . '-dev';
+
+ if ($lowVersion === '0.0.0.0-dev') {
+ return array(new Constraint('<', $highVersion));
+ }
+
+ return array(
+ new Constraint('>=', $lowVersion),
+ new Constraint('<', $highVersion),
+ );
+ }
+
+ // Hyphen Range
+ //
+ // Specifies an inclusive set. If a partial version is provided as the first version in the inclusive range,
+ // then the missing pieces are replaced with zeroes. If a partial version is provided as the second version in
+ // the inclusive range, then all versions that start with the supplied parts of the tuple are accepted, but
+ // nothing that would be greater than the provided tuple parts.
+ if (preg_match('{^(?P' . $versionRegex . ') +- +(?P' . $versionRegex . ')($)}i', $constraint, $matches)) {
+ // Calculate the stability suffix
+ $lowStabilitySuffix = '';
+ if (empty($matches[6]) && empty($matches[8]) && empty($matches[9])) {
+ $lowStabilitySuffix = '-dev';
+ }
+
+ $lowVersion = $this->normalize($matches['from']);
+ $lowerBound = new Constraint('>=', $lowVersion . $lowStabilitySuffix);
+
+ $empty = function ($x) {
+ return ($x === 0 || $x === '0') ? false : empty($x);
+ };
+
+ if ((!$empty($matches[12]) && !$empty($matches[13])) || !empty($matches[15]) || !empty($matches[17]) || !empty($matches[18])) {
+ $highVersion = $this->normalize($matches['to']);
+ $upperBound = new Constraint('<=', $highVersion);
+ } else {
+ $highMatch = array('', $matches[11], $matches[12], $matches[13], $matches[14]);
+
+ // validate to version
+ $this->normalize($matches['to']);
+
+ $highVersion = $this->manipulateVersionString($highMatch, $empty($matches[12]) ? 1 : 2, 1) . '-dev';
+ $upperBound = new Constraint('<', $highVersion);
+ }
+
+ return array(
+ $lowerBound,
+ $upperBound,
+ );
+ }
+
+ // Basic Comparators
+ if (preg_match('{^(<>|!=|>=?|<=?|==?)?\s*(.*)}', $constraint, $matches)) {
+ try {
+ try {
+ $version = $this->normalize($matches[2]);
+ } catch (\UnexpectedValueException $e) {
+ // recover from an invalid constraint like foobar-dev which should be dev-foobar
+ // except if the constraint uses a known operator, in which case it must be a parse error
+ if (substr($matches[2], -4) === '-dev' && preg_match('{^[0-9a-zA-Z-./]+$}', $matches[2])) {
+ $version = $this->normalize('dev-'.substr($matches[2], 0, -4));
+ } else {
+ throw $e;
+ }
+ }
+
+ $op = $matches[1] ?: '=';
+
+ if ($op !== '==' && $op !== '=' && !empty($stabilityModifier) && self::parseStability($version) === 'stable') {
+ $version .= '-' . $stabilityModifier;
+ } elseif ('<' === $op || '>=' === $op) {
+ if (!preg_match('/-' . self::$modifierRegex . '$/', strtolower($matches[2]))) {
+ if (strpos($matches[2], 'dev-') !== 0) {
+ $version .= '-dev';
+ }
+ }
+ }
+
+ return array(new Constraint($matches[1] ?: '=', $version));
+ } catch (\Exception $e) {
+ }
+ }
+
+ $message = 'Could not parse version constraint ' . $constraint;
+ if (isset($e)) {
+ $message .= ': ' . $e->getMessage();
+ }
+
+ throw new \UnexpectedValueException($message);
+ }
+
+ /**
+ * Increment, decrement, or simply pad a version number.
+ *
+ * Support function for {@link parseConstraint()}
+ *
+ * @param array $matches Array with version parts in array indexes 1,2,3,4
+ * @param int $position 1,2,3,4 - which segment of the version to increment/decrement
+ * @param int $increment
+ * @param string $pad The string to pad version parts after $position
+ *
+ * @return string|null The new version
+ */
+ private function manipulateVersionString($matches, $position, $increment = 0, $pad = '0')
+ {
+ for ($i = 4; $i > 0; --$i) {
+ if ($i > $position) {
+ $matches[$i] = $pad;
+ } elseif ($i === $position && $increment) {
+ $matches[$i] += $increment;
+ // If $matches[$i] was 0, carry the decrement
+ if ($matches[$i] < 0) {
+ $matches[$i] = $pad;
+ --$position;
+
+ // Return null on a carry overflow
+ if ($i === 1) {
+ return null;
+ }
+ }
+ }
+ }
+
+ return $matches[1] . '.' . $matches[2] . '.' . $matches[3] . '.' . $matches[4];
+ }
+
+ /**
+ * Expand shorthand stability string to long version.
+ *
+ * @param string $stability
+ *
+ * @return string
+ */
+ private function expandStability($stability)
+ {
+ $stability = strtolower($stability);
+
+ switch ($stability) {
+ case 'a':
+ return 'alpha';
+ case 'b':
+ return 'beta';
+ case 'p':
+ case 'pl':
+ return 'patch';
+ case 'rc':
+ return 'RC';
+ default:
+ return $stability;
+ }
+ }
+}
diff --git a/tools/php-cs-fixer/vendor/composer/xdebug-handler/CHANGELOG.md b/tools/php-cs-fixer/vendor/composer/xdebug-handler/CHANGELOG.md
new file mode 100644
index 0000000..3776c61
--- /dev/null
+++ b/tools/php-cs-fixer/vendor/composer/xdebug-handler/CHANGELOG.md
@@ -0,0 +1,86 @@
+## [Unreleased]
+
+## [1.4.5] - 2020-11-13
+ * Fixed: support for stream_isatty and overall correct FD forwarding to the restarted process.
+
+## [1.4.4] - 2020-10-24
+ * Fix: exception if 'pcntl_signal' is disabled.
+
+## [1.4.3] - 2020-08-19
+ * Fixed: restore SIGINT to default handler in restarted process if no other handler exists.
+
+## [1.4.2] - 2020-06-04
+ * Fixed: ignore SIGINTs to let the restarted process handle them.
+
+## [1.4.1] - 2020-03-01
+ * Fixed: restart fails if an ini file is empty.
+
+## [1.4.0] - 2019-11-06
+ * Added: support for `NO_COLOR` environment variable: https://no-color.org
+ * Added: color support for Hyper terminal: https://github.com/zeit/hyper
+ * Fixed: correct capitalization of Xdebug (apparently).
+ * Fixed: improved handling for uopz extension.
+
+## [1.3.3] - 2019-05-27
+ * Fixed: add environment changes to `$_ENV` if it is being used.
+
+## [1.3.2] - 2019-01-28
+ * Fixed: exit call being blocked by uopz extension, resulting in application code running twice.
+
+## [1.3.1] - 2018-11-29
+ * Fixed: fail restart if `passthru` has been disabled in `disable_functions`.
+ * Fixed: fail restart if an ini file cannot be opened, otherwise settings will be missing.
+
+## [1.3.0] - 2018-08-31
+ * Added: `setPersistent` method to use environment variables for the restart.
+ * Fixed: improved debugging by writing output to stderr.
+ * Fixed: no restart when `php_ini_scanned_files` is not functional and is needed.
+
+## [1.2.1] - 2018-08-23
+ * Fixed: fatal error with apc, when using `apc.mmap_file_mask`.
+
+## [1.2.0] - 2018-08-16
+ * Added: debug information using `XDEBUG_HANDLER_DEBUG`.
+ * Added: fluent interface for setters.
+ * Added: `PhpConfig` helper class for calling PHP sub-processes.
+ * Added: `PHPRC` original value to restart stettings, for use in a restarted process.
+ * Changed: internal procedure to disable ini-scanning, using `-n` command-line option.
+ * Fixed: replaced `escapeshellarg` usage to avoid locale problems.
+ * Fixed: improved color-option handling to respect double-dash delimiter.
+ * Fixed: color-option handling regression from main script changes.
+ * Fixed: improved handling when checking main script.
+ * Fixed: handling for standard input, that never actually did anything.
+ * Fixed: fatal error when ctype extension is not available.
+
+## [1.1.0] - 2018-04-11
+ * Added: `getRestartSettings` method for calling PHP processes in a restarted process.
+ * Added: API definition and @internal class annotations.
+ * Added: protected `requiresRestart` method for extending classes.
+ * Added: `setMainScript` method for applications that change the working directory.
+ * Changed: private `tmpIni` variable to protected for extending classes.
+ * Fixed: environment variables not available in $_SERVER when restored in the restart.
+ * Fixed: relative path problems caused by Phar::interceptFileFuncs.
+ * Fixed: incorrect handling when script file cannot be found.
+
+## [1.0.0] - 2018-03-08
+ * Added: PSR3 logging for optional status output.
+ * Added: existing ini settings are merged to catch command-line overrides.
+ * Added: code, tests and other artefacts to decouple from Composer.
+ * Break: the following class was renamed:
+ - `Composer\XdebugHandler` -> `Composer\XdebugHandler\XdebugHandler`
+
+[Unreleased]: https://github.com/composer/xdebug-handler/compare/1.4.5...HEAD
+[1.4.5]: https://github.com/composer/xdebug-handler/compare/1.4.4...1.4.5
+[1.4.4]: https://github.com/composer/xdebug-handler/compare/1.4.3...1.4.4
+[1.4.3]: https://github.com/composer/xdebug-handler/compare/1.4.2...1.4.3
+[1.4.2]: https://github.com/composer/xdebug-handler/compare/1.4.1...1.4.2
+[1.4.1]: https://github.com/composer/xdebug-handler/compare/1.4.0...1.4.1
+[1.4.0]: https://github.com/composer/xdebug-handler/compare/1.3.3...1.4.0
+[1.3.3]: https://github.com/composer/xdebug-handler/compare/1.3.2...1.3.3
+[1.3.2]: https://github.com/composer/xdebug-handler/compare/1.3.1...1.3.2
+[1.3.1]: https://github.com/composer/xdebug-handler/compare/1.3.0...1.3.1
+[1.3.0]: https://github.com/composer/xdebug-handler/compare/1.2.1...1.3.0
+[1.2.1]: https://github.com/composer/xdebug-handler/compare/1.2.0...1.2.1
+[1.2.0]: https://github.com/composer/xdebug-handler/compare/1.1.0...1.2.0
+[1.1.0]: https://github.com/composer/xdebug-handler/compare/1.0.0...1.1.0
+[1.0.0]: https://github.com/composer/xdebug-handler/compare/d66f0d15cb57...1.0.0
diff --git a/tools/php-cs-fixer/vendor/composer/xdebug-handler/LICENSE b/tools/php-cs-fixer/vendor/composer/xdebug-handler/LICENSE
new file mode 100644
index 0000000..963618a
--- /dev/null
+++ b/tools/php-cs-fixer/vendor/composer/xdebug-handler/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2017 Composer
+
+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.
diff --git a/tools/php-cs-fixer/vendor/composer/xdebug-handler/README.md b/tools/php-cs-fixer/vendor/composer/xdebug-handler/README.md
new file mode 100644
index 0000000..57fb9ad
--- /dev/null
+++ b/tools/php-cs-fixer/vendor/composer/xdebug-handler/README.md
@@ -0,0 +1,293 @@
+# composer/xdebug-handler
+
+[![packagist](https://img.shields.io/packagist/v/composer/xdebug-handler.svg)](https://packagist.org/packages/composer/xdebug-handler)
+[![linux build](https://img.shields.io/travis/composer/xdebug-handler/master.svg?label=linux+build)](https://travis-ci.org/composer/xdebug-handler)
+[![windows build](https://img.shields.io/appveyor/ci/Seldaek/xdebug-handler/master.svg?label=windows+build)](https://ci.appveyor.com/project/Seldaek/xdebug-handler)
+![license](https://img.shields.io/github/license/composer/xdebug-handler.svg)
+![php](https://img.shields.io/packagist/php-v/composer/xdebug-handler.svg?colorB=8892BF&label=php)
+
+Restart a CLI process without loading the Xdebug extension.
+
+Originally written as part of [composer/composer](https://github.com/composer/composer),
+now extracted and made available as a stand-alone library.
+
+## Installation
+
+Install the latest version with:
+
+```bash
+$ composer require composer/xdebug-handler
+```
+
+## Requirements
+
+* PHP 5.3.2 minimum, although functionality is disabled below PHP 5.4.0. Using the latest PHP version is highly recommended.
+
+## Basic Usage
+```php
+use Composer\XdebugHandler\XdebugHandler;
+
+$xdebug = new XdebugHandler('myapp');
+$xdebug->check();
+unset($xdebug);
+```
+
+The constructor takes two parameters:
+
+#### _$envPrefix_
+This is used to create distinct environment variables and is upper-cased and prepended to default base values. The above example enables the use of:
+
+- `MYAPP_ALLOW_XDEBUG=1` to override automatic restart and allow Xdebug
+- `MYAPP_ORIGINAL_INIS` to obtain ini file locations in a restarted process
+
+#### _$colorOption_
+This optional value is added to the restart command-line and is needed to force color output in a piped child process. Only long-options are supported, for example `--ansi` or `--colors=always` etc.
+
+If the original command-line contains an argument that pattern matches this value (for example `--no-ansi`, `--colors=never`) then _$colorOption_ is ignored.
+
+If the pattern match ends with `=auto` (for example `--colors=auto`), the argument is replaced by _$colorOption_. Otherwise it is added at either the end of the command-line, or preceding the first double-dash `--` delimiter.
+
+## Advanced Usage
+
+* [How it works](#how-it-works)
+* [Limitations](#limitations)
+* [Helper methods](#helper-methods)
+* [Setter methods](#setter-methods)
+* [Process configuration](#process-configuration)
+* [Troubleshooting](#troubleshooting)
+* [Extending the library](#extending-the-library)
+
+### How it works
+
+A temporary ini file is created from the loaded (and scanned) ini files, with any references to the Xdebug extension commented out. Current ini settings are merged, so that most ini settings made on the command-line or by the application are included (see [Limitations](#limitations))
+
+* `MYAPP_ALLOW_XDEBUG` is set with internal data to flag and use in the restart.
+* The command-line and environment are [configured](#process-configuration) for the restart.
+* The application is restarted in a new process using `passthru`.
+ * The restart settings are stored in the environment.
+ * `MYAPP_ALLOW_XDEBUG` is unset.
+ * The application runs and exits.
+* The main process exits with the exit code from the restarted process.
+
+#### Signal handling
+From PHP 7.1 with the pcntl extension loaded, asynchronous signal handling is automatically enabled. `SIGINT` is set to `SIG_IGN` in the parent
+process and restored to `SIG_DFL` in the restarted process (if no other handler has been set).
+
+### Limitations
+There are a few things to be aware of when running inside a restarted process.
+
+* Extensions set on the command-line will not be loaded.
+* Ini file locations will be reported as per the restart - see [getAllIniFiles()](#getallinifiles).
+* Php sub-processes may be loaded with Xdebug enabled - see [Process configuration](#process-configuration).
+* On Windows `sapi_windows_set_ctrl_handler` handlers will not receive CTRL events.
+
+### Helper methods
+These static methods provide information from the current process, regardless of whether it has been restarted or not.
+
+#### _getAllIniFiles()_
+Returns an array of the original ini file locations. Use this instead of calling `php_ini_loaded_file` and `php_ini_scanned_files`, which will report the wrong values in a restarted process.
+
+```php
+use Composer\XdebugHandler\XdebugHandler;
+
+$files = XdebugHandler::getAllIniFiles();
+
+# $files[0] always exists, it could be an empty string
+$loadedIni = array_shift($files);
+$scannedInis = $files;
+```
+
+These locations are also available in the `MYAPP_ORIGINAL_INIS` environment variable. This is a path-separated string comprising the location returned from `php_ini_loaded_file`, which could be empty, followed by locations parsed from calling `php_ini_scanned_files`.
+
+#### _getRestartSettings()_
+Returns an array of settings that can be used with PHP [sub-processes](#sub-processes), or null if the process was not restarted.
+
+```php
+use Composer\XdebugHandler\XdebugHandler;
+
+$settings = XdebugHandler::getRestartSettings();
+/**
+ * $settings: array (if the current process was restarted,
+ * or called with the settings from a previous restart), or null
+ *
+ * 'tmpIni' => the temporary ini file used in the restart (string)
+ * 'scannedInis' => if there were any scanned inis (bool)
+ * 'scanDir' => the original PHP_INI_SCAN_DIR value (false|string)
+ * 'phprc' => the original PHPRC value (false|string)
+ * 'inis' => the original inis from getAllIniFiles (array)
+ * 'skipped' => the skipped version from getSkippedVersion (string)
+ */
+```
+
+#### _getSkippedVersion()_
+Returns the Xdebug version string that was skipped by the restart, or an empty value if there was no restart (or Xdebug is still loaded, perhaps by an extending class restarting for a reason other than removing Xdebug).
+
+```php
+use Composer\XdebugHandler\XdebugHandler;
+
+$version = XdebugHandler::getSkippedVersion();
+# $version: '2.6.0' (for example), or an empty string
+```
+
+### Setter methods
+These methods implement a fluent interface and must be called before the main `check()` method.
+
+#### _setLogger($logger)_
+Enables the output of status messages to an external PSR3 logger. All messages are reported with either `DEBUG` or `WARNING` log levels. For example (showing the level and message):
+
+```
+// Restart overridden
+DEBUG Checking MYAPP_ALLOW_XDEBUG
+DEBUG The Xdebug extension is loaded (2.5.0)
+DEBUG No restart (MYAPP_ALLOW_XDEBUG=1)
+
+// Failed restart
+DEBUG Checking MYAPP_ALLOW_XDEBUG
+DEBUG The Xdebug extension is loaded (2.5.0)
+WARNING No restart (Unable to create temp ini file at: ...)
+```
+
+Status messages can also be output with `XDEBUG_HANDLER_DEBUG`. See [Troubleshooting](#troubleshooting).
+
+#### _setMainScript($script)_
+Sets the location of the main script to run in the restart. This is only needed in more esoteric use-cases, or if the `argv[0]` location is inaccessible. The script name `--` is supported for standard input.
+
+#### _setPersistent()_
+Configures the restart using [persistent settings](#persistent-settings), so that Xdebug is not loaded in any sub-process.
+
+Use this method if your application invokes one or more PHP sub-process and the Xdebug extension is not needed. This avoids the overhead of implementing specific [sub-process](#sub-processes) strategies.
+
+Alternatively, this method can be used to set up a default _Xdebug-free_ environment which can be changed if a sub-process requires Xdebug, then restored afterwards:
+
+```php
+function SubProcessWithXdebug()
+{
+ $phpConfig = new Composer\XdebugHandler\PhpConfig();
+
+ # Set the environment to the original configuration
+ $phpConfig->useOriginal();
+
+ # run the process with Xdebug loaded
+ ...
+
+ # Restore Xdebug-free environment
+ $phpConfig->usePersistent();
+}
+```
+
+### Process configuration
+The library offers two strategies to invoke a new PHP process without loading Xdebug, using either _standard_ or _persistent_ settings. Note that this is only important if the application calls a PHP sub-process.
+
+#### Standard settings
+Uses command-line options to remove Xdebug from the new process only.
+
+* The -n option is added to the command-line. This tells PHP not to scan for additional inis.
+* The temporary ini is added to the command-line with the -c option.
+
+>_If the new process calls a PHP sub-process, Xdebug will be loaded in that sub-process (unless it implements xdebug-handler, in which case there will be another restart)._
+
+This is the default strategy used in the restart.
+
+#### Persistent settings
+Uses environment variables to remove Xdebug from the new process and persist these settings to any sub-process.
+
+* `PHP_INI_SCAN_DIR` is set to an empty string. This tells PHP not to scan for additional inis.
+* `PHPRC` is set to the temporary ini.
+
+>_If the new process calls a PHP sub-process, Xdebug will not be loaded in that sub-process._
+
+This strategy can be used in the restart by calling [setPersistent()](#setpersistent).
+
+#### Sub-processes
+The `PhpConfig` helper class makes it easy to invoke a PHP sub-process (with or without Xdebug loaded), regardless of whether there has been a restart.
+
+Each of its methods returns an array of PHP options (to add to the command-line) and sets up the environment for the required strategy. The [getRestartSettings()](#getrestartsettings) method is used internally.
+
+* `useOriginal()` - Xdebug will be loaded in the new process.
+* `useStandard()` - Xdebug will **not** be loaded in the new process - see [standard settings](#standard-settings).
+* `userPersistent()` - Xdebug will **not** be loaded in the new process - see [persistent settings](#persistent-settings)
+
+If there was no restart, an empty options array is returned and the environment is not changed.
+
+```php
+use Composer\XdebugHandler\PhpConfig;
+
+$config = new PhpConfig;
+
+$options = $config->useOriginal();
+# $options: empty array
+# environment: PHPRC and PHP_INI_SCAN_DIR set to original values
+
+$options = $config->useStandard();
+# $options: [-n, -c, tmpIni]
+# environment: PHPRC and PHP_INI_SCAN_DIR set to original values
+
+$options = $config->usePersistent();
+# $options: empty array
+# environment: PHPRC=tmpIni, PHP_INI_SCAN_DIR=''
+```
+
+### Troubleshooting
+The following environment settings can be used to troubleshoot unexpected behavior:
+
+* `XDEBUG_HANDLER_DEBUG=1` Outputs status messages to `STDERR`, if it is defined, irrespective of any PSR3 logger. Each message is prefixed `xdebug-handler[pid]`, where pid is the process identifier.
+
+* `XDEBUG_HANDLER_DEBUG=2` As above, but additionally saves the temporary ini file and reports its location in a status message.
+
+### Extending the library
+The API is defined by classes and their accessible elements that are not annotated as @internal. The main class has two protected methods that can be overridden to provide additional functionality:
+
+#### _requiresRestart($isLoaded)_
+By default the process will restart if Xdebug is loaded. Extending this method allows an application to decide, by returning a boolean (or equivalent) value. It is only called if `MYAPP_ALLOW_XDEBUG` is empty, so it will not be called in the restarted process (where this variable contains internal data), or if the restart has been overridden.
+
+Note that the [setMainScript()](#setmainscriptscript) and [setPersistent()](#setpersistent) setters can be used here, if required.
+
+#### _restart($command)_
+An application can extend this to modify the temporary ini file, its location given in the `tmpIni` property. New settings can be safely appended to the end of the data, which is `PHP_EOL` terminated.
+
+Note that the `$command` parameter is the escaped command-line string that will be used for the new process and must be treated accordingly.
+
+Remember to finish with `parent::restart($command)`.
+
+#### Example
+This example demonstrates two ways to extend basic functionality:
+
+* To avoid the overhead of spinning up a new process, the restart is skipped if a simple help command is requested.
+
+* The application needs write-access to phar files, so it will force a restart if `phar.readonly` is set (regardless of whether Xdebug is loaded) and change this value in the temporary ini file.
+
+```php
+use Composer\XdebugHandler\XdebugHandler;
+use MyApp\Command;
+
+class MyRestarter extends XdebugHandler
+{
+ private $required;
+
+ protected function requiresRestart($isLoaded)
+ {
+ if (Command::isHelp()) {
+ # No need to disable Xdebug for this
+ return false;
+ }
+
+ $this->required = (bool) ini_get('phar.readonly');
+ return $isLoaded || $this->required;
+ }
+
+ protected function restart($command)
+ {
+ if ($this->required) {
+ # Add required ini setting to tmpIni
+ $content = file_get_contents($this->tmpIni);
+ $content .= 'phar.readonly=0'.PHP_EOL;
+ file_put_contents($this->tmpIni, $content);
+ }
+
+ parent::restart($command);
+ }
+}
+```
+
+## License
+composer/xdebug-handler is licensed under the MIT License, see the LICENSE file for details.
diff --git a/tools/php-cs-fixer/vendor/composer/xdebug-handler/composer.json b/tools/php-cs-fixer/vendor/composer/xdebug-handler/composer.json
new file mode 100644
index 0000000..ea40a2f
--- /dev/null
+++ b/tools/php-cs-fixer/vendor/composer/xdebug-handler/composer.json
@@ -0,0 +1,40 @@
+{
+ "name": "composer/xdebug-handler",
+ "description": "Restarts a process without Xdebug.",
+ "type": "library",
+ "license": "MIT",
+ "keywords": [
+ "xdebug",
+ "performance"
+ ],
+ "authors": [
+ {
+ "name": "John Stevenson",
+ "email": "john-stevenson@blueyonder.co.uk"
+ }
+ ],
+ "support": {
+ "irc": "irc://irc.freenode.org/composer",
+ "issues": "https://github.com/composer/xdebug-handler/issues"
+ },
+ "require": {
+ "php": "^5.3.2 || ^7.0 || ^8.0",
+ "psr/log": "^1.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.8.35 || ^5.7 || 6.5 - 8"
+ },
+ "autoload": {
+ "psr-4": {
+ "Composer\\XdebugHandler\\": "src"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "Composer\\XdebugHandler\\": "tests"
+ }
+ },
+ "scripts": {
+ "test": "phpunit"
+ }
+}
diff --git a/tools/php-cs-fixer/vendor/composer/xdebug-handler/src/PhpConfig.php b/tools/php-cs-fixer/vendor/composer/xdebug-handler/src/PhpConfig.php
new file mode 100644
index 0000000..5535eca
--- /dev/null
+++ b/tools/php-cs-fixer/vendor/composer/xdebug-handler/src/PhpConfig.php
@@ -0,0 +1,73 @@
+
+ *
+ * For the full copyright and license information, please view
+ * the LICENSE file that was distributed with this source code.
+ */
+
+namespace Composer\XdebugHandler;
+
+/**
+ * @author John Stevenson
+ */
+class PhpConfig
+{
+ /**
+ * Use the original PHP configuration
+ *
+ * @return array PHP cli options
+ */
+ public function useOriginal()
+ {
+ $this->getDataAndReset();
+ return array();
+ }
+
+ /**
+ * Use standard restart settings
+ *
+ * @return array PHP cli options
+ */
+ public function useStandard()
+ {
+ if ($data = $this->getDataAndReset()) {
+ return array('-n', '-c', $data['tmpIni']);
+ }
+
+ return array();
+ }
+
+ /**
+ * Use environment variables to persist settings
+ *
+ * @return array PHP cli options
+ */
+ public function usePersistent()
+ {
+ if ($data = $this->getDataAndReset()) {
+ Process::setEnv('PHPRC', $data['tmpIni']);
+ Process::setEnv('PHP_INI_SCAN_DIR', '');
+ }
+
+ return array();
+ }
+
+ /**
+ * Returns restart data if available and resets the environment
+ *
+ * @return array|null
+ */
+ private function getDataAndReset()
+ {
+ if ($data = XdebugHandler::getRestartSettings()) {
+ Process::setEnv('PHPRC', $data['phprc']);
+ Process::setEnv('PHP_INI_SCAN_DIR', $data['scanDir']);
+ }
+
+ return $data;
+ }
+}
diff --git a/tools/php-cs-fixer/vendor/composer/xdebug-handler/src/Process.php b/tools/php-cs-fixer/vendor/composer/xdebug-handler/src/Process.php
new file mode 100644
index 0000000..eb2ad2b
--- /dev/null
+++ b/tools/php-cs-fixer/vendor/composer/xdebug-handler/src/Process.php
@@ -0,0 +1,181 @@
+
+ *
+ * For the full copyright and license information, please view
+ * the LICENSE file that was distributed with this source code.
+ */
+
+namespace Composer\XdebugHandler;
+
+/**
+ * Provides utility functions to prepare a child process command-line and set
+ * environment variables in that process.
+ *
+ * @author John Stevenson
+ * @internal
+ */
+class Process
+{
+ /**
+ * Returns an array of parameters, including a color option if required
+ *
+ * A color option is needed because child process output is piped.
+ *
+ * @param array $args The script parameters
+ * @param string $colorOption The long option to force color output
+ *
+ * @return array
+ */
+ public static function addColorOption(array $args, $colorOption)
+ {
+ if (!$colorOption
+ || in_array($colorOption, $args)
+ || !preg_match('/^--([a-z]+$)|(^--[a-z]+=)/', $colorOption, $matches)) {
+ return $args;
+ }
+
+ if (isset($matches[2])) {
+ // Handle --color(s)= options
+ if (false !== ($index = array_search($matches[2].'auto', $args))) {
+ $args[$index] = $colorOption;
+ return $args;
+ } elseif (preg_grep('/^'.$matches[2].'/', $args)) {
+ return $args;
+ }
+ } elseif (in_array('--no-'.$matches[1], $args)) {
+ return $args;
+ }
+
+ // Check for NO_COLOR variable (https://no-color.org/)
+ if (false !== getenv('NO_COLOR')) {
+ return $args;
+ }
+
+ if (false !== ($index = array_search('--', $args))) {
+ // Position option before double-dash delimiter
+ array_splice($args, $index, 0, $colorOption);
+ } else {
+ $args[] = $colorOption;
+ }
+
+ return $args;
+ }
+
+ /**
+ * Escapes a string to be used as a shell argument.
+ *
+ * From https://github.com/johnstevenson/winbox-args
+ * MIT Licensed (c) John Stevenson
+ *
+ * @param string $arg The argument to be escaped
+ * @param bool $meta Additionally escape cmd.exe meta characters
+ * @param bool $module The argument is the module to invoke
+ *
+ * @return string The escaped argument
+ */
+ public static function escape($arg, $meta = true, $module = false)
+ {
+ if (!defined('PHP_WINDOWS_VERSION_BUILD')) {
+ return "'".str_replace("'", "'\\''", $arg)."'";
+ }
+
+ $quote = strpbrk($arg, " \t") !== false || $arg === '';
+
+ $arg = preg_replace('/(\\\\*)"/', '$1$1\\"', $arg, -1, $dquotes);
+
+ if ($meta) {
+ $meta = $dquotes || preg_match('/%[^%]+%/', $arg);
+
+ if (!$meta) {
+ $quote = $quote || strpbrk($arg, '^&|<>()') !== false;
+ } elseif ($module && !$dquotes && $quote) {
+ $meta = false;
+ }
+ }
+
+ if ($quote) {
+ $arg = '"'.preg_replace('/(\\\\*)$/', '$1$1', $arg).'"';
+ }
+
+ if ($meta) {
+ $arg = preg_replace('/(["^&|<>()%])/', '^$1', $arg);
+ }
+
+ return $arg;
+ }
+
+ /**
+ * Returns true if the output stream supports colors
+ *
+ * This is tricky on Windows, because Cygwin, Msys2 etc emulate pseudo
+ * terminals via named pipes, so we can only check the environment.
+ *
+ * @param mixed $output A valid CLI output stream
+ *
+ * @return bool
+ */
+ public static function supportsColor($output)
+ {
+ if ('Hyper' === getenv('TERM_PROGRAM')) {
+ return true;
+ }
+
+ if (defined('PHP_WINDOWS_VERSION_BUILD')) {
+ return (function_exists('sapi_windows_vt100_support')
+ && sapi_windows_vt100_support($output))
+ || false !== getenv('ANSICON')
+ || 'ON' === getenv('ConEmuANSI')
+ || 'xterm' === getenv('TERM');
+ }
+
+ if (function_exists('stream_isatty')) {
+ return stream_isatty($output);
+ }
+
+ if (function_exists('posix_isatty')) {
+ return posix_isatty($output);
+ }
+
+ $stat = fstat($output);
+ // Check if formatted mode is S_IFCHR
+ return $stat ? 0020000 === ($stat['mode'] & 0170000) : false;
+ }
+
+ /**
+ * Makes putenv environment changes available in $_SERVER and $_ENV
+ *
+ * @param string $name
+ * @param string|false $value A false value unsets the variable
+ *
+ * @return bool Whether the environment variable was set
+ */
+ public static function setEnv($name, $value = false)
+ {
+ $unset = false === $value;
+
+ if (!putenv($unset ? $name : $name.'='.$value)) {
+ return false;
+ }
+
+ if ($unset) {
+ unset($_SERVER[$name]);
+ } else {
+ $_SERVER[$name] = $value;
+ }
+
+ // Update $_ENV if it is being used
+ if (false !== stripos((string) ini_get('variables_order'), 'E')) {
+ if ($unset) {
+ unset($_ENV[$name]);
+ } else {
+ $_ENV[$name] = $value;
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/tools/php-cs-fixer/vendor/composer/xdebug-handler/src/Status.php b/tools/php-cs-fixer/vendor/composer/xdebug-handler/src/Status.php
new file mode 100644
index 0000000..e714b1c
--- /dev/null
+++ b/tools/php-cs-fixer/vendor/composer/xdebug-handler/src/Status.php
@@ -0,0 +1,163 @@
+
+ *
+ * For the full copyright and license information, please view
+ * the LICENSE file that was distributed with this source code.
+ */
+
+namespace Composer\XdebugHandler;
+
+use Psr\Log\LoggerInterface;
+use Psr\Log\LogLevel;
+
+/**
+ * @author John Stevenson
+ * @internal
+ */
+class Status
+{
+ const ENV_RESTART = 'XDEBUG_HANDLER_RESTART';
+ const CHECK = 'Check';
+ const ERROR = 'Error';
+ const INFO = 'Info';
+ const NORESTART = 'NoRestart';
+ const RESTART = 'Restart';
+ const RESTARTING = 'Restarting';
+ const RESTARTED = 'Restarted';
+
+ private $debug;
+ private $envAllowXdebug;
+ private $loaded;
+ private $logger;
+ private $time;
+
+ /**
+ * Constructor
+ *
+ * @param string $envAllowXdebug Prefixed _ALLOW_XDEBUG name
+ * @param bool $debug Whether debug output is required
+ */
+ public function __construct($envAllowXdebug, $debug)
+ {
+ $start = getenv(self::ENV_RESTART);
+ Process::setEnv(self::ENV_RESTART);
+ $this->time = $start ? round((microtime(true) - $start) * 1000) : 0;
+
+ $this->envAllowXdebug = $envAllowXdebug;
+ $this->debug = $debug && defined('STDERR');
+ }
+
+ /**
+ * @param LoggerInterface $logger
+ */
+ public function setLogger(LoggerInterface $logger)
+ {
+ $this->logger = $logger;
+ }
+
+ /**
+ * Calls a handler method to report a message
+ *
+ * @param string $op The handler constant
+ * @param null|string $data Data required by the handler
+ */
+ public function report($op, $data)
+ {
+ if ($this->logger || $this->debug) {
+ call_user_func(array($this, 'report'.$op), $data);
+ }
+ }
+
+ /**
+ * Outputs a status message
+ *
+ * @param string $text
+ * @param string $level
+ */
+ private function output($text, $level = null)
+ {
+ if ($this->logger) {
+ $this->logger->log($level ?: LogLevel::DEBUG, $text);
+ }
+
+ if ($this->debug) {
+ fwrite(STDERR, sprintf('xdebug-handler[%d] %s', getmypid(), $text.PHP_EOL));
+ }
+ }
+
+ private function reportCheck($loaded)
+ {
+ $this->loaded = $loaded;
+ $this->output('Checking '.$this->envAllowXdebug);
+ }
+
+ private function reportError($error)
+ {
+ $this->output(sprintf('No restart (%s)', $error), LogLevel::WARNING);
+ }
+
+ private function reportInfo($info)
+ {
+ $this->output($info);
+ }
+
+ private function reportNoRestart()
+ {
+ $this->output($this->getLoadedMessage());
+
+ if ($this->loaded) {
+ $text = sprintf('No restart (%s)', $this->getEnvAllow());
+ if (!getenv($this->envAllowXdebug)) {
+ $text .= ' Allowed by application';
+ }
+ $this->output($text);
+ }
+ }
+
+ private function reportRestart()
+ {
+ $this->output($this->getLoadedMessage());
+ Process::setEnv(self::ENV_RESTART, (string) microtime(true));
+ }
+
+ private function reportRestarted()
+ {
+ $loaded = $this->getLoadedMessage();
+ $text = sprintf('Restarted (%d ms). %s', $this->time, $loaded);
+ $level = $this->loaded ? LogLevel::WARNING : null;
+ $this->output($text, $level);
+ }
+
+ private function reportRestarting($command)
+ {
+ $text = sprintf('Process restarting (%s)', $this->getEnvAllow());
+ $this->output($text);
+ $text = 'Running '.$command;
+ $this->output($text);
+ }
+
+ /**
+ * Returns the _ALLOW_XDEBUG environment variable as name=value
+ *
+ * @return string
+ */
+ private function getEnvAllow()
+ {
+ return $this->envAllowXdebug.'='.getenv($this->envAllowXdebug);
+ }
+
+ /**
+ * Returns the Xdebug status and version
+ *
+ * @return string
+ */
+ private function getLoadedMessage()
+ {
+ $loaded = $this->loaded ? sprintf('loaded (%s)', $this->loaded) : 'not loaded';
+ return 'The Xdebug extension is '.$loaded;
+ }
+}
diff --git a/tools/php-cs-fixer/vendor/composer/xdebug-handler/src/XdebugHandler.php b/tools/php-cs-fixer/vendor/composer/xdebug-handler/src/XdebugHandler.php
new file mode 100644
index 0000000..6267735
--- /dev/null
+++ b/tools/php-cs-fixer/vendor/composer/xdebug-handler/src/XdebugHandler.php
@@ -0,0 +1,615 @@
+
+ *
+ * For the full copyright and license information, please view
+ * the LICENSE file that was distributed with this source code.
+ */
+
+namespace Composer\XdebugHandler;
+
+use Psr\Log\LoggerInterface;
+
+/**
+ * @author John Stevenson
+ */
+class XdebugHandler
+{
+ const SUFFIX_ALLOW = '_ALLOW_XDEBUG';
+ const SUFFIX_INIS = '_ORIGINAL_INIS';
+ const RESTART_ID = 'internal';
+ const RESTART_SETTINGS = 'XDEBUG_HANDLER_SETTINGS';
+ const DEBUG = 'XDEBUG_HANDLER_DEBUG';
+
+ /** @var string|null */
+ protected $tmpIni;
+
+ private static $inRestart;
+ private static $name;
+ private static $skipped;
+
+ private $cli;
+ private $colorOption;
+ private $debug;
+ private $envAllowXdebug;
+ private $envOriginalInis;
+ private $loaded;
+ private $persistent;
+ private $script;
+ /** @var Status|null */
+ private $statusWriter;
+
+ /**
+ * Constructor
+ *
+ * The $envPrefix is used to create distinct environment variables. It is
+ * uppercased and prepended to the default base values. For example 'myapp'
+ * would result in MYAPP_ALLOW_XDEBUG and MYAPP_ORIGINAL_INIS.
+ *
+ * @param string $envPrefix Value used in environment variables
+ * @param string $colorOption Command-line long option to force color output
+ * @throws \RuntimeException If a parameter is invalid
+ */
+ public function __construct($envPrefix, $colorOption = '')
+ {
+ if (!is_string($envPrefix) || empty($envPrefix) || !is_string($colorOption)) {
+ throw new \RuntimeException('Invalid constructor parameter');
+ }
+
+ self::$name = strtoupper($envPrefix);
+ $this->envAllowXdebug = self::$name.self::SUFFIX_ALLOW;
+ $this->envOriginalInis = self::$name.self::SUFFIX_INIS;
+
+ $this->colorOption = $colorOption;
+
+ if (extension_loaded('xdebug')) {
+ $ext = new \ReflectionExtension('xdebug');
+ $this->loaded = $ext->getVersion() ?: 'unknown';
+ }
+
+ if ($this->cli = PHP_SAPI === 'cli') {
+ $this->debug = getenv(self::DEBUG);
+ }
+
+ $this->statusWriter = new Status($this->envAllowXdebug, (bool) $this->debug);
+ }
+
+ /**
+ * Activates status message output to a PSR3 logger
+ *
+ * @param LoggerInterface $logger
+ *
+ * @return $this
+ */
+ public function setLogger(LoggerInterface $logger)
+ {
+ $this->statusWriter->setLogger($logger);
+ return $this;
+ }
+
+ /**
+ * Sets the main script location if it cannot be called from argv
+ *
+ * @param string $script
+ *
+ * @return $this
+ */
+ public function setMainScript($script)
+ {
+ $this->script = $script;
+ return $this;
+ }
+
+ /**
+ * Persist the settings to keep Xdebug out of sub-processes
+ *
+ * @return $this
+ */
+ public function setPersistent()
+ {
+ $this->persistent = true;
+ return $this;
+ }
+
+ /**
+ * Checks if Xdebug is loaded and the process needs to be restarted
+ *
+ * This behaviour can be disabled by setting the MYAPP_ALLOW_XDEBUG
+ * environment variable to 1. This variable is used internally so that
+ * the restarted process is created only once.
+ */
+ public function check()
+ {
+ $this->notify(Status::CHECK, $this->loaded);
+ $envArgs = explode('|', (string) getenv($this->envAllowXdebug));
+
+ if (empty($envArgs[0]) && $this->requiresRestart((bool) $this->loaded)) {
+ // Restart required
+ $this->notify(Status::RESTART);
+
+ if ($this->prepareRestart()) {
+ $command = $this->getCommand();
+ $this->restart($command);
+ }
+ return;
+ }
+
+ if (self::RESTART_ID === $envArgs[0] && count($envArgs) === 5) {
+ // Restarted, so unset environment variable and use saved values
+ $this->notify(Status::RESTARTED);
+
+ Process::setEnv($this->envAllowXdebug);
+ self::$inRestart = true;
+
+ if (!$this->loaded) {
+ // Skipped version is only set if Xdebug is not loaded
+ self::$skipped = $envArgs[1];
+ }
+
+ $this->tryEnableSignals();
+
+ // Put restart settings in the environment
+ $this->setEnvRestartSettings($envArgs);
+ return;
+ }
+
+ $this->notify(Status::NORESTART);
+
+ if ($settings = self::getRestartSettings()) {
+ // Called with existing settings, so sync our settings
+ $this->syncSettings($settings);
+ }
+ }
+
+ /**
+ * Returns an array of php.ini locations with at least one entry
+ *
+ * The equivalent of calling php_ini_loaded_file then php_ini_scanned_files.
+ * The loaded ini location is the first entry and may be empty.
+ *
+ * @return array
+ */
+ public static function getAllIniFiles()
+ {
+ if (!empty(self::$name)) {
+ $env = getenv(self::$name.self::SUFFIX_INIS);
+
+ if (false !== $env) {
+ return explode(PATH_SEPARATOR, $env);
+ }
+ }
+
+ $paths = array((string) php_ini_loaded_file());
+
+ if ($scanned = php_ini_scanned_files()) {
+ $paths = array_merge($paths, array_map('trim', explode(',', $scanned)));
+ }
+
+ return $paths;
+ }
+
+ /**
+ * Returns an array of restart settings or null
+ *
+ * Settings will be available if the current process was restarted, or
+ * called with the settings from an existing restart.
+ *
+ * @return array|null
+ */
+ public static function getRestartSettings()
+ {
+ $envArgs = explode('|', (string) getenv(self::RESTART_SETTINGS));
+
+ if (count($envArgs) !== 6
+ || (!self::$inRestart && php_ini_loaded_file() !== $envArgs[0])) {
+ return;
+ }
+
+ return array(
+ 'tmpIni' => $envArgs[0],
+ 'scannedInis' => (bool) $envArgs[1],
+ 'scanDir' => '*' === $envArgs[2] ? false : $envArgs[2],
+ 'phprc' => '*' === $envArgs[3] ? false : $envArgs[3],
+ 'inis' => explode(PATH_SEPARATOR, $envArgs[4]),
+ 'skipped' => $envArgs[5],
+ );
+ }
+
+ /**
+ * Returns the Xdebug version that triggered a successful restart
+ *
+ * @return string
+ */
+ public static function getSkippedVersion()
+ {
+ return (string) self::$skipped;
+ }
+
+ /**
+ * Returns true if Xdebug is loaded, or as directed by an extending class
+ *
+ * @param bool $isLoaded Whether Xdebug is loaded
+ *
+ * @return bool
+ */
+ protected function requiresRestart($isLoaded)
+ {
+ return $isLoaded;
+ }
+
+ /**
+ * Allows an extending class to access the tmpIni
+ *
+ * @param string $command
+ */
+ protected function restart($command)
+ {
+ $this->doRestart($command);
+ }
+
+ /**
+ * Executes the restarted command then deletes the tmp ini
+ *
+ * @param string $command
+ */
+ private function doRestart($command)
+ {
+ $this->tryEnableSignals();
+ $this->notify(Status::RESTARTING, $command);
+
+ // Prefer proc_open to keep fds intact, because passthru pipes to stdout
+ if (function_exists('proc_open')) {
+ if (defined('PHP_WINDOWS_VERSION_BUILD') && PHP_VERSION_ID < 80000) {
+ $command = '"'.$command.'"';
+ }
+ $process = proc_open($command, array(), $pipes);
+ if (is_resource($process)) {
+ $exitCode = proc_close($process);
+ }
+ } else {
+ passthru($command, $exitCode);
+ }
+
+ if (!isset($exitCode)) {
+ // Unlikely that the default shell cannot be invoked
+ $this->notify(Status::ERROR, 'Unable to restart process');
+ $exitCode = -1;
+ } else {
+ $this->notify(Status::INFO, 'Restarted process exited '.$exitCode);
+ }
+
+ if ($this->debug === '2') {
+ $this->notify(Status::INFO, 'Temp ini saved: '.$this->tmpIni);
+ } else {
+ @unlink($this->tmpIni);
+ }
+
+ exit($exitCode);
+ }
+
+ /**
+ * Returns true if everything was written for the restart
+ *
+ * If any of the following fails (however unlikely) we must return false to
+ * stop potential recursion:
+ * - tmp ini file creation
+ * - environment variable creation
+ *
+ * @return bool
+ */
+ private function prepareRestart()
+ {
+ $error = '';
+ $iniFiles = self::getAllIniFiles();
+ $scannedInis = count($iniFiles) > 1;
+ $tmpDir = sys_get_temp_dir();
+
+ if (!$this->cli) {
+ $error = 'Unsupported SAPI: '.PHP_SAPI;
+ } elseif (!defined('PHP_BINARY')) {
+ $error = 'PHP version is too old: '.PHP_VERSION;
+ } elseif (!$this->checkConfiguration($info)) {
+ $error = $info;
+ } elseif (!$this->checkScanDirConfig()) {
+ $error = 'PHP version does not report scanned inis: '.PHP_VERSION;
+ } elseif (!$this->checkMainScript()) {
+ $error = 'Unable to access main script: '.$this->script;
+ } elseif (!$this->writeTmpIni($iniFiles, $tmpDir, $error)) {
+ $error = $error ?: 'Unable to create temp ini file at: '.$tmpDir;
+ } elseif (!$this->setEnvironment($scannedInis, $iniFiles)) {
+ $error = 'Unable to set environment variables';
+ }
+
+ if ($error) {
+ $this->notify(Status::ERROR, $error);
+ }
+
+ return empty($error);
+ }
+
+ /**
+ * Returns true if the tmp ini file was written
+ *
+ * @param array $iniFiles All ini files used in the current process
+ * @param string $tmpDir The system temporary directory
+ * @param string $error Set by method if ini file cannot be read
+ *
+ * @return bool
+ */
+ private function writeTmpIni(array $iniFiles, $tmpDir, &$error)
+ {
+ if (!$this->tmpIni = @tempnam($tmpDir, '')) {
+ return false;
+ }
+
+ // $iniFiles has at least one item and it may be empty
+ if (empty($iniFiles[0])) {
+ array_shift($iniFiles);
+ }
+
+ $content = '';
+ $regex = '/^\s*(zend_extension\s*=.*xdebug.*)$/mi';
+
+ foreach ($iniFiles as $file) {
+ // Check for inaccessible ini files
+ if (($data = @file_get_contents($file)) === false) {
+ $error = 'Unable to read ini: '.$file;
+ return false;
+ }
+ $content .= preg_replace($regex, ';$1', $data).PHP_EOL;
+ }
+
+ // Merge loaded settings into our ini content, if it is valid
+ if ($config = parse_ini_string($content)) {
+ $loaded = ini_get_all(null, false);
+ $content .= $this->mergeLoadedConfig($loaded, $config);
+ }
+
+ // Work-around for https://bugs.php.net/bug.php?id=75932
+ $content .= 'opcache.enable_cli=0'.PHP_EOL;
+
+ return @file_put_contents($this->tmpIni, $content);
+ }
+
+ /**
+ * Returns the restart command line
+ *
+ * @return string
+ */
+ private function getCommand()
+ {
+ $php = array(PHP_BINARY);
+ $args = array_slice($_SERVER['argv'], 1);
+
+ if (!$this->persistent) {
+ // Use command-line options
+ array_push($php, '-n', '-c', $this->tmpIni);
+ }
+
+ if (defined('STDOUT') && Process::supportsColor(STDOUT)) {
+ $args = Process::addColorOption($args, $this->colorOption);
+ }
+
+ $args = array_merge($php, array($this->script), $args);
+
+ $cmd = Process::escape(array_shift($args), true, true);
+ foreach ($args as $arg) {
+ $cmd .= ' '.Process::escape($arg);
+ }
+
+ return $cmd;
+ }
+
+ /**
+ * Returns true if the restart environment variables were set
+ *
+ * No need to update $_SERVER since this is set in the restarted process.
+ *
+ * @param bool $scannedInis Whether there were scanned ini files
+ * @param array $iniFiles All ini files used in the current process
+ *
+ * @return bool
+ */
+ private function setEnvironment($scannedInis, array $iniFiles)
+ {
+ $scanDir = getenv('PHP_INI_SCAN_DIR');
+ $phprc = getenv('PHPRC');
+
+ // Make original inis available to restarted process
+ if (!putenv($this->envOriginalInis.'='.implode(PATH_SEPARATOR, $iniFiles))) {
+ return false;
+ }
+
+ if ($this->persistent) {
+ // Use the environment to persist the settings
+ if (!putenv('PHP_INI_SCAN_DIR=') || !putenv('PHPRC='.$this->tmpIni)) {
+ return false;
+ }
+ }
+
+ // Flag restarted process and save values for it to use
+ $envArgs = array(
+ self::RESTART_ID,
+ $this->loaded,
+ (int) $scannedInis,
+ false === $scanDir ? '*' : $scanDir,
+ false === $phprc ? '*' : $phprc,
+ );
+
+ return putenv($this->envAllowXdebug.'='.implode('|', $envArgs));
+ }
+
+ /**
+ * Logs status messages
+ *
+ * @param string $op Status handler constant
+ * @param null|string $data Optional data
+ */
+ private function notify($op, $data = null)
+ {
+ $this->statusWriter->report($op, $data);
+ }
+
+ /**
+ * Returns default, changed and command-line ini settings
+ *
+ * @param array $loadedConfig All current ini settings
+ * @param array $iniConfig Settings from user ini files
+ *
+ * @return string
+ */
+ private function mergeLoadedConfig(array $loadedConfig, array $iniConfig)
+ {
+ $content = '';
+
+ foreach ($loadedConfig as $name => $value) {
+ // Value will either be null, string or array (HHVM only)
+ if (!is_string($value)
+ || strpos($name, 'xdebug') === 0
+ || $name === 'apc.mmap_file_mask') {
+ continue;
+ }
+
+ if (!isset($iniConfig[$name]) || $iniConfig[$name] !== $value) {
+ // Double-quote escape each value
+ $content .= $name.'="'.addcslashes($value, '\\"').'"'.PHP_EOL;
+ }
+ }
+
+ return $content;
+ }
+
+ /**
+ * Returns true if the script name can be used
+ *
+ * @return bool
+ */
+ private function checkMainScript()
+ {
+ if (null !== $this->script) {
+ // Allow an application to set -- for standard input
+ return file_exists($this->script) || '--' === $this->script;
+ }
+
+ if (file_exists($this->script = $_SERVER['argv'][0])) {
+ return true;
+ }
+
+ // Use a backtrace to resolve Phar and chdir issues
+ $options = PHP_VERSION_ID >= 50306 ? DEBUG_BACKTRACE_IGNORE_ARGS : false;
+ $trace = debug_backtrace($options);
+
+ if (($main = end($trace)) && isset($main['file'])) {
+ return file_exists($this->script = $main['file']);
+ }
+
+ return false;
+ }
+
+ /**
+ * Adds restart settings to the environment
+ *
+ * @param string $envArgs
+ */
+ private function setEnvRestartSettings($envArgs)
+ {
+ $settings = array(
+ php_ini_loaded_file(),
+ $envArgs[2],
+ $envArgs[3],
+ $envArgs[4],
+ getenv($this->envOriginalInis),
+ self::$skipped,
+ );
+
+ Process::setEnv(self::RESTART_SETTINGS, implode('|', $settings));
+ }
+
+ /**
+ * Syncs settings and the environment if called with existing settings
+ *
+ * @param array $settings
+ */
+ private function syncSettings(array $settings)
+ {
+ if (false === getenv($this->envOriginalInis)) {
+ // Called by another app, so make original inis available
+ Process::setEnv($this->envOriginalInis, implode(PATH_SEPARATOR, $settings['inis']));
+ }
+
+ self::$skipped = $settings['skipped'];
+ $this->notify(Status::INFO, 'Process called with existing restart settings');
+ }
+
+ /**
+ * Returns true if there are scanned inis and PHP is able to report them
+ *
+ * php_ini_scanned_files will fail when PHP_CONFIG_FILE_SCAN_DIR is empty.
+ * Fixed in 7.1.13 and 7.2.1
+ *
+ * @return bool
+ */
+ private function checkScanDirConfig()
+ {
+ return !(getenv('PHP_INI_SCAN_DIR')
+ && !PHP_CONFIG_FILE_SCAN_DIR
+ && (PHP_VERSION_ID < 70113
+ || PHP_VERSION_ID === 70200));
+ }
+
+ /**
+ * Returns true if there are no known configuration issues
+ *
+ * @param string $info Set by method
+ */
+ private function checkConfiguration(&$info)
+ {
+ if (false !== strpos(ini_get('disable_functions'), 'passthru')) {
+ $info = 'passthru function is disabled';
+ return false;
+ }
+
+ if (extension_loaded('uopz') && !ini_get('uopz.disable')) {
+ // uopz works at opcode level and disables exit calls
+ if (function_exists('uopz_allow_exit')) {
+ @uopz_allow_exit(true);
+ } else {
+ $info = 'uopz extension is not compatible';
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Enables async signals and control interrupts in the restarted process
+ *
+ * Only available on Unix PHP 7.1+ with the pcntl extension. To replicate on
+ * Windows would require PHP 7.4+ using proc_open rather than passthru.
+ */
+ private function tryEnableSignals()
+ {
+ if (!function_exists('pcntl_async_signals') || !function_exists('pcntl_signal')) {
+ return;
+ }
+
+ pcntl_async_signals(true);
+ $message = 'Async signals enabled';
+
+ if (!self::$inRestart) {
+ // Restarting, so ignore SIGINT in parent
+ pcntl_signal(SIGINT, SIG_IGN);
+ $message .= ' (SIGINT = SIG_IGN)';
+ } elseif (is_int(pcntl_signal_get_handler(SIGINT))) {
+ // Restarted, no handler set so force default action
+ pcntl_signal(SIGINT, SIG_DFL);
+ $message .= ' (SIGINT = SIG_DFL)';
+ }
+
+ $this->notify(Status::INFO, $message);
+ }
+}
diff --git a/tools/php-cs-fixer/vendor/doctrine/annotations/.doctrine-project.json b/tools/php-cs-fixer/vendor/doctrine/annotations/.doctrine-project.json
new file mode 100644
index 0000000..38553aa
--- /dev/null
+++ b/tools/php-cs-fixer/vendor/doctrine/annotations/.doctrine-project.json
@@ -0,0 +1,49 @@
+{
+ "active": true,
+ "name": "Annotations",
+ "slug": "annotations",
+ "docsSlug": "doctrine-annotations",
+ "versions": [
+ {
+ "name": "1.11",
+ "branchName": "master",
+ "slug": "latest",
+ "upcoming": true
+ },
+ {
+ "name": "1.10",
+ "branchName": "1.10.x",
+ "slug": "1.10",
+ "aliases": [
+ "current",
+ "stable"
+ ],
+ "current": true,
+ "maintained": true
+ },
+ {
+ "name": "1.9",
+ "branchName": "1.9.x",
+ "slug": "1.9",
+ "maintained": false
+ },
+ {
+ "name": "1.8",
+ "branchName": "1.8",
+ "slug": "1.8",
+ "maintained": false
+ },
+ {
+ "name": "1.7",
+ "branchName": "1.7",
+ "slug": "1.7",
+ "maintained": false
+ },
+ {
+ "name": "1.6",
+ "branchName": "1.6",
+ "slug": "1.6",
+ "maintained": false
+ }
+ ]
+}
diff --git a/tools/php-cs-fixer/vendor/doctrine/annotations/.github/workflows/coding-standards.yml b/tools/php-cs-fixer/vendor/doctrine/annotations/.github/workflows/coding-standards.yml
new file mode 100644
index 0000000..7df6889
--- /dev/null
+++ b/tools/php-cs-fixer/vendor/doctrine/annotations/.github/workflows/coding-standards.yml
@@ -0,0 +1,39 @@
+
+name: "Coding Standards"
+
+on: ["pull_request", "push"]
+
+jobs:
+ coding-standards:
+ name: "Coding Standards"
+ runs-on: "ubuntu-20.04"
+
+ strategy:
+ matrix:
+ php-version:
+ - "7.4"
+
+ steps:
+ - name: "Checkout"
+ uses: "actions/checkout@v2"
+
+ - name: "Install PHP"
+ uses: "shivammathur/setup-php@v2"
+ with:
+ coverage: "none"
+ php-version: "${{ matrix.php-version }}"
+ tools: "cs2pr"
+
+ - name: "Cache dependencies installed with Composer"
+ uses: "actions/cache@v2"
+ with:
+ path: "~/.composer/cache"
+ key: "php-${{ matrix.php-version }}-composer-locked-${{ hashFiles('composer.lock') }}"
+ restore-keys: "php-${{ matrix.php-version }}-composer-locked-"
+
+ - name: "Install dependencies with Composer"
+ run: "composer install --no-interaction --no-progress --no-suggest"
+
+ # https://github.com/doctrine/.github/issues/3
+ - name: "Run PHP_CodeSniffer"
+ run: "vendor/bin/phpcs -q --no-colors --report=checkstyle | cs2pr"
diff --git a/tools/php-cs-fixer/vendor/doctrine/annotations/.github/workflows/continuous-integration.yml b/tools/php-cs-fixer/vendor/doctrine/annotations/.github/workflows/continuous-integration.yml
new file mode 100644
index 0000000..d81974c
--- /dev/null
+++ b/tools/php-cs-fixer/vendor/doctrine/annotations/.github/workflows/continuous-integration.yml
@@ -0,0 +1,61 @@
+name: "Continuous Integration"
+
+on:
+ pull_request:
+ branches:
+ - "*.x"
+ - "master"
+ push:
+ branches:
+ - "*.x"
+ - "master"
+
+jobs:
+ phpunit:
+ name: "PHPUnit"
+ runs-on: "ubuntu-20.04"
+
+ strategy:
+ matrix:
+ php-version:
+ - "7.1"
+ - "7.2"
+ - "7.3"
+ - "7.4"
+ - "8.0"
+ deps:
+ - "normal"
+ include:
+ - deps: "low"
+ php-version: "7.2"
+
+ steps:
+ - name: "Checkout"
+ uses: "actions/checkout@v2"
+ with:
+ fetch-depth: 2
+
+ - name: "Install PHP"
+ uses: "shivammathur/setup-php@v2"
+ with:
+ php-version: "${{ matrix.php-version }}"
+ coverage: "pcov"
+ ini-values: "zend.assertions=1"
+
+ - name: "Cache dependencies installed with composer"
+ uses: "actions/cache@v2"
+ with:
+ path: "~/.composer/cache"
+ key: "php-${{ matrix.php-version }}-composer-locked-${{ hashFiles('composer.lock') }}"
+ restore-keys: "php-${{ matrix.php-version }}-composer-locked-"
+
+ - name: "Update dependencies with composer"
+ run: "composer update --no-interaction --no-progress --no-suggest"
+ if: "${{ matrix.deps == 'normal' }}"
+
+ - name: "Install lowest possible dependencies with composer"
+ run: "composer update --no-interaction --no-progress --no-suggest --prefer-dist --prefer-lowest"
+ if: "${{ matrix.deps == 'low' }}"
+
+ - name: "Run PHPUnit"
+ run: "vendor/bin/phpunit"
diff --git a/tools/php-cs-fixer/vendor/doctrine/annotations/.github/workflows/release-on-milestone-closed.yml b/tools/php-cs-fixer/vendor/doctrine/annotations/.github/workflows/release-on-milestone-closed.yml
new file mode 100644
index 0000000..70ccbdb
--- /dev/null
+++ b/tools/php-cs-fixer/vendor/doctrine/annotations/.github/workflows/release-on-milestone-closed.yml
@@ -0,0 +1,55 @@
+name: "Automatic Releases"
+
+on:
+ milestone:
+ types:
+ - "closed"
+
+jobs:
+ release:
+ name: "Git tag, release & create merge-up PR"
+ runs-on: "ubuntu-20.04"
+
+ steps:
+ - name: "Checkout"
+ uses: "actions/checkout@v2"
+
+ - name: "Release"
+ uses: "laminas/automatic-releases@v1"
+ with:
+ command-name: "laminas:automatic-releases:release"
+ env:
+ "GITHUB_TOKEN": ${{ secrets.GITHUB_TOKEN }}
+ "SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }}
+ "GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }}
+ "GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }}
+
+ - name: "Create Merge-Up Pull Request"
+ uses: "laminas/automatic-releases@v1"
+ with:
+ command-name: "laminas:automatic-releases:create-merge-up-pull-request"
+ env:
+ "GITHUB_TOKEN": ${{ secrets.GITHUB_TOKEN }}
+ "SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }}
+ "GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }}
+ "GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }}
+
+ - name: "Create and/or Switch to new Release Branch"
+ uses: "laminas/automatic-releases@v1"
+ with:
+ command-name: "laminas:automatic-releases:switch-default-branch-to-next-minor"
+ env:
+ "GITHUB_TOKEN": ${{ secrets.ORGANIZATION_ADMIN_TOKEN }}
+ "SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }}
+ "GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }}
+ "GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }}
+
+ - name: "Create new milestones"
+ uses: "laminas/automatic-releases@v1"
+ with:
+ command-name: "laminas:automatic-releases:create-milestones"
+ env:
+ "GITHUB_TOKEN": ${{ secrets.GITHUB_TOKEN }}
+ "SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }}
+ "GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }}
+ "GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }}
diff --git a/tools/php-cs-fixer/vendor/doctrine/annotations/.github/workflows/static-analysis.yml b/tools/php-cs-fixer/vendor/doctrine/annotations/.github/workflows/static-analysis.yml
new file mode 100644
index 0000000..eca2fe8
--- /dev/null
+++ b/tools/php-cs-fixer/vendor/doctrine/annotations/.github/workflows/static-analysis.yml
@@ -0,0 +1,45 @@
+name: "Static Analysis"
+
+on:
+ pull_request:
+ branches:
+ - "*.x"
+ - "master"
+ push:
+ branches:
+ - "*.x"
+ - "master"
+
+jobs:
+ static-analysis-phpstan:
+ name: "Static Analysis with PHPStan"
+ runs-on: "ubuntu-20.04"
+
+ strategy:
+ matrix:
+ php-version:
+ - "7.4"
+
+ steps:
+ - name: "Checkout code"
+ uses: "actions/checkout@v2"
+
+ - name: "Install PHP"
+ uses: "shivammathur/setup-php@v2"
+ with:
+ coverage: "none"
+ php-version: "${{ matrix.php-version }}"
+ tools: "cs2pr"
+
+ - name: "Cache dependencies installed with composer"
+ uses: "actions/cache@v2"
+ with:
+ path: "~/.composer/cache"
+ key: "php-${{ matrix.php-version }}-composer-locked-${{ hashFiles('composer.lock') }}"
+ restore-keys: "php-${{ matrix.php-version }}-composer-locked-"
+
+ - name: "Install dependencies with composer"
+ run: "composer install --no-interaction --no-progress --no-suggest"
+
+ - name: "Run a static analysis with phpstan/phpstan"
+ run: "vendor/bin/phpstan analyse -l 3 -c phpstan.neon --error-format=checkstyle lib/ tests/ | cs2pr"
diff --git a/tools/php-cs-fixer/vendor/doctrine/annotations/CHANGELOG.md b/tools/php-cs-fixer/vendor/doctrine/annotations/CHANGELOG.md
new file mode 100644
index 0000000..0b0ba1a
--- /dev/null
+++ b/tools/php-cs-fixer/vendor/doctrine/annotations/CHANGELOG.md
@@ -0,0 +1,162 @@
+## Changelog
+
+### 1.6.1
+
+This release fixes an issue in which annotations such as `@foo-bar`
+and `@foo-` were incorrectly recognised as valid, and both erroneously
+parsed as `@foo`.
+
+Any annotation with `@name-*` format will now silently be ignored,
+allowing vendor-specific annotations to be prefixed with the tool
+name.
+
+Total issues resolved: **3**
+
+- [165: Update the composer branch alias](https://github.com/doctrine/annotations/pull/165) thanks to @mikeSimonson
+- [209: Change Annotation::value typehint to mixed](https://github.com/doctrine/annotations/pull/209) thanks to @malarzm
+- [257: Skip parsing annotations containing dashes, such as `@Foo-bar`, or `@Foo-`](https://github.com/doctrine/annotations/pull/257) thanks to @Ocramius
+
+### 1.6.0
+
+This release brings a new endpoint that make sure that you can't shoot yourself in the foot by calling ```registerLoader``` multiple times and a few tests improvements.
+
+Total issues resolved: **7**
+
+- [145: Memory leak in AnnotationRegistry::registerLoader() when called multiple times](https://github.com/doctrine/annotations/issues/145) thanks to @TriAnMan
+- [146: Import error on @experimental Annotation](https://github.com/doctrine/annotations/issues/146) thanks to @aturki
+- [147: Ignoring @experimental annotation used by Symfony 3.3 CacheAdapter](https://github.com/doctrine/annotations/pull/147) thanks to @aturki
+- [151: Remove duplicate code in `DCOM58Test`](https://github.com/doctrine/annotations/pull/151) thanks to @tuanphpvn
+- [161: Prevent loading class_exists multiple times](https://github.com/doctrine/annotations/pull/161) thanks to @jrjohnson
+- [162: Add registerUniqueLoader to AnnotationRegistry](https://github.com/doctrine/annotations/pull/162) thanks to @jrjohnson
+- [163: Use assertDirectoryExists and assertDirectoryNotExists](https://github.com/doctrine/annotations/pull/163) thanks to @carusogabriel
+
+Thanks to everyone involved in this release.
+
+### 1.5.0
+
+This release increments the minimum supported PHP version to 7.1.0.
+
+Also, HHVM official support has been dropped.
+
+Some noticeable performance improvements to annotation autoloading
+have been applied, making failed annotation autoloading less heavy
+on the filesystem access.
+
+- [133: Add @throws annotation in AnnotationReader#__construct()](https://github.com/doctrine/annotations/issues/133) thanks to @SenseException
+- [134: Require PHP 7.1, drop HHVM support](https://github.com/doctrine/annotations/issues/134) thanks to @lcobucci
+- [135: Prevent the same loader from being registered twice](https://github.com/doctrine/annotations/issues/135) thanks to @jrjohnson
+- [137: #135 optimise multiple class load attempts in AnnotationRegistry](https://github.com/doctrine/annotations/issues/137) thanks to @Ocramius
+
+
+### 1.4.0
+
+This release fix an issue were some annotations could be not loaded if the namespace in the use statement started with a backslash.
+It also update the tests and drop the support for php 5.X
+
+- [115: Missing annotations with the latest composer version](https://github.com/doctrine/annotations/issues/115) thanks to @pascalporedda
+- [120: Missing annotations with the latest composer version](https://github.com/doctrine/annotations/pull/120) thanks to @gnat42
+- [121: Adding a more detailed explanation of the test](https://github.com/doctrine/annotations/pull/121) thanks to @mikeSimonson
+- [101: Test annotation parameters containing space](https://github.com/doctrine/annotations/pull/101) thanks to @mikeSimonson
+- [111: Cleanup: move to correct phpunit assertions](https://github.com/doctrine/annotations/pull/111) thanks to @Ocramius
+- [112: Removes support for PHP 5.x](https://github.com/doctrine/annotations/pull/112) thanks to @railto
+- [113: bumped phpunit version to 5.7](https://github.com/doctrine/annotations/pull/113) thanks to @gabbydgab
+- [114: Enhancement: Use SVG Travis build badge](https://github.com/doctrine/annotations/pull/114) thanks to @localheinz
+- [118: Integrating PHPStan](https://github.com/doctrine/annotations/pull/118) thanks to @ondrejmirtes
+
+### 1.3.1 - 2016-12-30
+
+This release fixes an issue with ignored annotations that were already
+autoloaded, causing the `SimpleAnnotationReader` to pick them up
+anyway. [#110](https://github.com/doctrine/annotations/pull/110)
+
+Additionally, an issue was fixed in the `CachedReader`, which was
+not correctly checking the freshness of cached annotations when
+traits were defined on a class. [#105](https://github.com/doctrine/annotations/pull/105)
+
+Total issues resolved: **2**
+
+- [105: Return single max timestamp](https://github.com/doctrine/annotations/pull/105)
+- [110: setIgnoreNotImportedAnnotations(true) didn’t work for existing classes](https://github.com/doctrine/annotations/pull/110)
+
+### 1.3.0
+
+This release introduces a PHP version bump. `doctrine/annotations` now requires PHP
+5.6 or later to be installed.
+
+A series of additional improvements have been introduced:
+
+ * support for PHP 7 "grouped use statements"
+ * support for ignoring entire namespace names
+ via `Doctrine\Common\Annotations\AnnotationReader::addGlobalIgnoredNamespace()` and
+ `Doctrine\Common\Annotations\DocParser::setIgnoredAnnotationNamespaces()`. This will
+ allow you to ignore annotations from namespaces that you cannot autoload
+ * testing all parent classes and interfaces when checking if the annotation cache
+ in the `CachedReader` is fresh
+ * simplifying the cache keys used by the `CachedReader`: keys are no longer artificially
+ namespaced, since `Doctrine\Common\Cache` already supports that
+ * corrected parsing of multibyte strings when `mbstring.func_overload` is enabled
+ * corrected parsing of annotations when `"\t"` is put before the first annotation
+ in a docblock
+ * allow skipping non-imported annotations when a custom `DocParser` is passed to
+ the `AnnotationReader` constructor
+
+Total issues resolved: **15**
+
+- [45: DocParser can now ignore whole namespaces](https://github.com/doctrine/annotations/pull/45)
+- [57: Switch to the docker-based infrastructure on Travis](https://github.com/doctrine/annotations/pull/57)
+- [59: opcache.load_comments has been removed from PHP 7](https://github.com/doctrine/annotations/pull/59)
+- [62: [CachedReader\ Test traits and parent class to see if cache is fresh](https://github.com/doctrine/annotations/pull/62)
+- [65: Remove cache salt making key unnecessarily long](https://github.com/doctrine/annotations/pull/65)
+- [66: Fix of incorrect parsing multibyte strings](https://github.com/doctrine/annotations/pull/66)
+- [68: Annotations that are indented by tab are not processed.](https://github.com/doctrine/annotations/issues/68)
+- [69: Support for Group Use Statements](https://github.com/doctrine/annotations/pull/69)
+- [70: Allow tab character before first annotation in DocBlock](https://github.com/doctrine/annotations/pull/70)
+- [74: Ignore not registered annotations fix](https://github.com/doctrine/annotations/pull/74)
+- [92: Added tests for AnnotationRegistry class.](https://github.com/doctrine/annotations/pull/92)
+- [96: Fix/#62 check trait and parent class ttl in annotations](https://github.com/doctrine/annotations/pull/96)
+- [97: Feature - #45 - allow ignoring entire namespaces](https://github.com/doctrine/annotations/pull/97)
+- [98: Enhancement/#65 remove cache salt from cached reader](https://github.com/doctrine/annotations/pull/98)
+- [99: Fix - #70 - allow tab character before first annotation in docblock](https://github.com/doctrine/annotations/pull/99)
+
+### 1.2.4
+
+Total issues resolved: **1**
+
+- [51: FileCacheReader::saveCacheFile::unlink fix](https://github.com/doctrine/annotations/pull/51)
+
+### 1.2.3
+
+Total issues resolved: [**2**](https://github.com/doctrine/annotations/milestones/v1.2.3)
+
+- [49: #46 - applying correct `chmod()` to generated cache file](https://github.com/doctrine/annotations/pull/49)
+- [50: Hotfix: match escaped quotes (revert #44)](https://github.com/doctrine/annotations/pull/50)
+
+### 1.2.2
+
+Total issues resolved: **4**
+
+- [43: Exclude files from distribution with .gitattributes](https://github.com/doctrine/annotations/pull/43)
+- [44: Update DocLexer.php](https://github.com/doctrine/annotations/pull/44)
+- [46: A plain "file_put_contents" can cause havoc](https://github.com/doctrine/annotations/pull/46)
+- [48: Deprecating the `FileCacheReader` in 1.2.2: will be removed in 2.0.0](https://github.com/doctrine/annotations/pull/48)
+
+### 1.2.1
+
+Total issues resolved: **4**
+
+- [38: fixes doctrine/common#326](https://github.com/doctrine/annotations/pull/38)
+- [39: Remove superfluous NS](https://github.com/doctrine/annotations/pull/39)
+- [41: Warn if load_comments is not enabled.](https://github.com/doctrine/annotations/pull/41)
+- [42: Clean up unused uses](https://github.com/doctrine/annotations/pull/42)
+
+### 1.2.0
+
+ * HHVM support
+ * Allowing dangling comma in annotations
+ * Excluded annotations are no longer autoloaded
+ * Importing namespaces also in traits
+ * Added support for `::class` 5.5-style constant, works also in 5.3 and 5.4
+
+### 1.1.0
+
+ * Add Exception when ZendOptimizer+ or Opcache is configured to drop comments
diff --git a/tools/php-cs-fixer/vendor/doctrine/annotations/LICENSE b/tools/php-cs-fixer/vendor/doctrine/annotations/LICENSE
new file mode 100644
index 0000000..5e781fc
--- /dev/null
+++ b/tools/php-cs-fixer/vendor/doctrine/annotations/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2006-2013 Doctrine Project
+
+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.
diff --git a/tools/php-cs-fixer/vendor/doctrine/annotations/README.md b/tools/php-cs-fixer/vendor/doctrine/annotations/README.md
new file mode 100644
index 0000000..bf07cd5
--- /dev/null
+++ b/tools/php-cs-fixer/vendor/doctrine/annotations/README.md
@@ -0,0 +1,22 @@
+# Doctrine Annotations
+
+[![Build Status](https://travis-ci.org/doctrine/annotations.svg?branch=master)](https://travis-ci.org/doctrine/annotations)
+[![Dependency Status](https://www.versioneye.com/package/php--doctrine--annotations/badge.png)](https://www.versioneye.com/package/php--doctrine--annotations)
+[![Reference Status](https://www.versioneye.com/php/doctrine:annotations/reference_badge.svg)](https://www.versioneye.com/php/doctrine:annotations/references)
+[![Total Downloads](https://poser.pugx.org/doctrine/annotations/downloads.png)](https://packagist.org/packages/doctrine/annotations)
+[![Latest Stable Version](https://poser.pugx.org/doctrine/annotations/v/stable.png)](https://packagist.org/packages/doctrine/annotations)
+
+Docblock Annotations Parser library (extracted from [Doctrine Common](https://github.com/doctrine/common)).
+
+## Documentation
+
+See the [doctrine-project website](https://www.doctrine-project.org/projects/doctrine-annotations/en/latest/index.html).
+
+## Contributing
+
+When making a pull request, make sure your changes follow the
+[Coding Standard Guidelines](https://www.doctrine-project.org/projects/doctrine-coding-standard/en/latest/reference/index.html#introduction).
+
+## Changelog
+
+See [CHANGELOG.md](CHANGELOG.md).
diff --git a/tools/php-cs-fixer/vendor/doctrine/annotations/composer.json b/tools/php-cs-fixer/vendor/doctrine/annotations/composer.json
new file mode 100644
index 0000000..2f3318a
--- /dev/null
+++ b/tools/php-cs-fixer/vendor/doctrine/annotations/composer.json
@@ -0,0 +1,46 @@
+{
+ "name": "doctrine/annotations",
+ "type": "library",
+ "description": "Docblock Annotations Parser",
+ "keywords": ["annotations", "docblock", "parser"],
+ "homepage": "https://www.doctrine-project.org/projects/annotations.html",
+ "license": "MIT",
+ "authors": [
+ {"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"},
+ {"name": "Roman Borschel", "email": "roman@code-factory.org"},
+ {"name": "Benjamin Eberlei", "email": "kontakt@beberlei.de"},
+ {"name": "Jonathan Wage", "email": "jonwage@gmail.com"},
+ {"name": "Johannes Schmitt", "email": "schmittjoh@gmail.com"}
+ ],
+ "require": {
+ "php": "^7.1 || ^8.0",
+ "ext-tokenizer": "*",
+ "doctrine/lexer": "1.*"
+ },
+ "require-dev": {
+ "doctrine/cache": "1.*",
+ "doctrine/coding-standard": "^6.0 || ^8.1",
+ "phpstan/phpstan": "^0.12.20",
+ "phpunit/phpunit": "^7.5 || ^9.1.5"
+ },
+ "config": {
+ "sort-packages": true
+ },
+ "autoload": {
+ "psr-4": { "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "Doctrine\\Performance\\Common\\Annotations\\": "tests/Doctrine/Performance/Common/Annotations",
+ "Doctrine\\Tests\\Common\\Annotations\\": "tests/Doctrine/Tests/Common/Annotations"
+ },
+ "files": [
+ "tests/Doctrine/Tests/Common/Annotations/Fixtures/SingleClassLOC1000.php"
+ ]
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.11.x-dev"
+ }
+ }
+}
diff --git a/tools/php-cs-fixer/vendor/doctrine/annotations/docs/en/annotations.rst b/tools/php-cs-fixer/vendor/doctrine/annotations/docs/en/annotations.rst
new file mode 100644
index 0000000..648ab26
--- /dev/null
+++ b/tools/php-cs-fixer/vendor/doctrine/annotations/docs/en/annotations.rst
@@ -0,0 +1,271 @@
+Handling Annotations
+====================
+
+There are several different approaches to handling annotations in PHP.
+Doctrine Annotations maps docblock annotations to PHP classes. Because
+not all docblock annotations are used for metadata purposes a filter is
+applied to ignore or skip classes that are not Doctrine annotations.
+
+Take a look at the following code snippet:
+
+.. code-block:: php
+
+ namespace MyProject\Entities;
+
+ use Doctrine\ORM\Mapping AS ORM;
+ use Symfony\Component\Validator\Constraints AS Assert;
+
+ /**
+ * @author Benjamin Eberlei
+ * @ORM\Entity
+ * @MyProject\Annotations\Foobarable
+ */
+ class User
+ {
+ /**
+ * @ORM\Id @ORM\Column @ORM\GeneratedValue
+ * @dummy
+ * @var int
+ */
+ private $id;
+
+ /**
+ * @ORM\Column(type="string")
+ * @Assert\NotEmpty
+ * @Assert\Email
+ * @var string
+ */
+ private $email;
+ }
+
+In this snippet you can see a variety of different docblock annotations:
+
+- Documentation annotations such as ``@var`` and ``@author``. These
+ annotations are ignored and never considered for throwing an
+ exception due to wrongly used annotations.
+- Annotations imported through use statements. The statement ``use
+ Doctrine\ORM\Mapping AS ORM`` makes all classes under that namespace
+ available as ``@ORM\ClassName``. Same goes for the import of
+ ``@Assert``.
+- The ``@dummy`` annotation. It is not a documentation annotation and
+ not ignored. For Doctrine Annotations it is not entirely clear how
+ to handle this annotation. Depending on the configuration an exception
+ (unknown annotation) will be thrown when parsing this annotation.
+- The fully qualified annotation ``@MyProject\Annotations\Foobarable``.
+ This is transformed directly into the given class name.
+
+How are these annotations loaded? From looking at the code you could
+guess that the ORM Mapping, Assert Validation and the fully qualified
+annotation can just be loaded using
+the defined PHP autoloaders. This is not the case however: For error
+handling reasons every check for class existence inside the
+``AnnotationReader`` sets the second parameter $autoload
+of ``class_exists($name, $autoload)`` to false. To work flawlessly the
+``AnnotationReader`` requires silent autoloaders which many autoloaders are
+not. Silent autoloading is NOT part of the `PSR-0 specification
+`_
+for autoloading.
+
+This is why Doctrine Annotations uses its own autoloading mechanism
+through a global registry. If you are wondering about the annotation
+registry being global, there is no other way to solve the architectural
+problems of autoloading annotation classes in a straightforward fashion.
+Additionally if you think about PHP autoloading then you recognize it is
+a global as well.
+
+To anticipate the configuration section, making the above PHP class work
+with Doctrine Annotations requires this setup:
+
+.. code-block:: php
+
+ use Doctrine\Common\Annotations\AnnotationReader;
+ use Doctrine\Common\Annotations\AnnotationRegistry;
+
+ AnnotationRegistry::registerFile("/path/to/doctrine/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php");
+ AnnotationRegistry::registerAutoloadNamespace("Symfony\Component\Validator\Constraint", "/path/to/symfony/src");
+ AnnotationRegistry::registerAutoloadNamespace("MyProject\Annotations", "/path/to/myproject/src");
+
+ $reader = new AnnotationReader();
+ AnnotationReader::addGlobalIgnoredName('dummy');
+
+The second block with the annotation registry calls registers all the
+three different annotation namespaces that are used.
+Doctrine Annotations saves all its annotations in a single file, that is
+why ``AnnotationRegistry#registerFile`` is used in contrast to
+``AnnotationRegistry#registerAutoloadNamespace`` which creates a PSR-0
+compatible loading mechanism for class to file names.
+
+In the third block, we create the actual ``AnnotationReader`` instance.
+Note that we also add ``dummy`` to the global list of ignored
+annotations for which we do not throw exceptions. Setting this is
+necessary in our example case, otherwise ``@dummy`` would trigger an
+exception to be thrown during the parsing of the docblock of
+``MyProject\Entities\User#id``.
+
+Setup and Configuration
+-----------------------
+
+To use the annotations library is simple, you just need to create a new
+``AnnotationReader`` instance:
+
+.. code-block:: php
+
+ $reader = new \Doctrine\Common\Annotations\AnnotationReader();
+
+This creates a simple annotation reader with no caching other than in
+memory (in php arrays). Since parsing docblocks can be expensive you
+should cache this process by using a caching reader.
+
+You can use a file caching reader, but please note it is deprecated to
+do so:
+
+.. code-block:: php
+
+ use Doctrine\Common\Annotations\FileCacheReader;
+ use Doctrine\Common\Annotations\AnnotationReader;
+
+ $reader = new FileCacheReader(
+ new AnnotationReader(),
+ "/path/to/cache",
+ $debug = true
+ );
+
+If you set the ``debug`` flag to ``true`` the cache reader will check
+for changes in the original files, which is very important during
+development. If you don't set it to ``true`` you have to delete the
+directory to clear the cache. This gives faster performance, however
+should only be used in production, because of its inconvenience during
+development.
+
+You can also use one of the ``Doctrine\Common\Cache\Cache`` cache
+implementations to cache the annotations:
+
+.. code-block:: php
+
+ use Doctrine\Common\Annotations\AnnotationReader;
+ use Doctrine\Common\Annotations\CachedReader;
+ use Doctrine\Common\Cache\ApcCache;
+
+ $reader = new CachedReader(
+ new AnnotationReader(),
+ new ApcCache(),
+ $debug = true
+ );
+
+The ``debug`` flag is used here as well to invalidate the cache files
+when the PHP class with annotations changed and should be used during
+development.
+
+.. warning ::
+
+ The ``AnnotationReader`` works and caches under the
+ assumption that all annotations of a doc-block are processed at
+ once. That means that annotation classes that do not exist and
+ aren't loaded and cannot be autoloaded (using the
+ AnnotationRegistry) would never be visible and not accessible if a
+ cache is used unless the cache is cleared and the annotations
+ requested again, this time with all annotations defined.
+
+By default the annotation reader returns a list of annotations with
+numeric indexes. If you want your annotations to be indexed by their
+class name you can wrap the reader in an ``IndexedReader``:
+
+.. code-block:: php
+
+ use Doctrine\Common\Annotations\AnnotationReader;
+ use Doctrine\Common\Annotations\IndexedReader;
+
+ $reader = new IndexedReader(new AnnotationReader());
+
+.. warning::
+
+ You should never wrap the indexed reader inside a cached reader,
+ only the other way around. This way you can re-use the cache with
+ indexed or numeric keys, otherwise your code may experience failures
+ due to caching in a numerical or indexed format.
+
+Registering Annotations
+~~~~~~~~~~~~~~~~~~~~~~~
+
+As explained in the introduction, Doctrine Annotations uses its own
+autoloading mechanism to determine if a given annotation has a
+corresponding PHP class that can be autoloaded. For annotation
+autoloading you have to configure the
+``Doctrine\Common\Annotations\AnnotationRegistry``. There are three
+different mechanisms to configure annotation autoloading:
+
+- Calling ``AnnotationRegistry#registerFile($file)`` to register a file
+ that contains one or more annotation classes.
+- Calling ``AnnotationRegistry#registerNamespace($namespace, $dirs =
+ null)`` to register that the given namespace contains annotations and
+ that their base directory is located at the given $dirs or in the
+ include path if ``NULL`` is passed. The given directories should *NOT*
+ be the directory where classes of the namespace are in, but the base
+ directory of the root namespace. The AnnotationRegistry uses a
+ namespace to directory separator approach to resolve the correct path.
+- Calling ``AnnotationRegistry#registerLoader($callable)`` to register
+ an autoloader callback. The callback accepts the class as first and
+ only parameter and has to return ``true`` if the corresponding file
+ was found and included.
+
+.. note::
+
+ Loaders have to fail silently, if a class is not found even if it
+ matches for example the namespace prefix of that loader. Never is a
+ loader to throw a warning or exception if the loading failed
+ otherwise parsing doc block annotations will become a huge pain.
+
+A sample loader callback could look like:
+
+.. code-block:: php
+
+ use Doctrine\Common\Annotations\AnnotationRegistry;
+ use Symfony\Component\ClassLoader\UniversalClassLoader;
+
+ AnnotationRegistry::registerLoader(function($class) {
+ $file = str_replace("\\", DIRECTORY_SEPARATOR, $class) . ".php";
+
+ if (file_exists("/my/base/path/" . $file)) {
+ // file_exists() makes sure that the loader fails silently
+ require "/my/base/path/" . $file;
+ }
+ });
+
+ $loader = new UniversalClassLoader();
+ AnnotationRegistry::registerLoader(array($loader, "loadClass"));
+
+
+Ignoring missing exceptions
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+By default an exception is thrown from the ``AnnotationReader`` if an
+annotation was found that:
+
+- is not part of the list of ignored "documentation annotations";
+- was not imported through a use statement;
+- is not a fully qualified class that exists.
+
+You can disable this behavior for specific names if your docblocks do
+not follow strict requirements:
+
+.. code-block:: php
+
+ $reader = new \Doctrine\Common\Annotations\AnnotationReader();
+ AnnotationReader::addGlobalIgnoredName('foo');
+
+PHP Imports
+~~~~~~~~~~~
+
+By default the annotation reader parses the use-statement of a php file
+to gain access to the import rules and register them for the annotation
+processing. Only if you are using PHP Imports can you validate the
+correct usage of annotations and throw exceptions if you misspelled an
+annotation. This mechanism is enabled by default.
+
+To ease the upgrade path, we still allow you to disable this mechanism.
+Note however that we will remove this in future versions:
+
+.. code-block:: php
+
+ $reader = new \Doctrine\Common\Annotations\AnnotationReader();
+ $reader->setEnabledPhpImports(false);
diff --git a/tools/php-cs-fixer/vendor/doctrine/annotations/docs/en/custom.rst b/tools/php-cs-fixer/vendor/doctrine/annotations/docs/en/custom.rst
new file mode 100644
index 0000000..8ea5f47
--- /dev/null
+++ b/tools/php-cs-fixer/vendor/doctrine/annotations/docs/en/custom.rst
@@ -0,0 +1,399 @@
+Custom Annotation Classes
+=========================
+
+If you want to define your own annotations, you just have to group them
+in a namespace and register this namespace in the ``AnnotationRegistry``.
+Annotation classes have to contain a class-level docblock with the text
+``@Annotation``:
+
+.. code-block:: php
+
+ namespace MyCompany\Annotations;
+
+ /** @Annotation */
+ class Bar
+ {
+ // some code
+ }
+
+Inject annotation values
+------------------------
+
+The annotation parser checks if the annotation constructor has arguments,
+if so then it will pass the value array, otherwise it will try to inject
+values into public properties directly:
+
+
+.. code-block:: php
+
+ namespace MyCompany\Annotations;
+
+ /**
+ * @Annotation
+ *
+ * Some Annotation using a constructor
+ */
+ class Bar
+ {
+ private $foo;
+
+ public function __construct(array $values)
+ {
+ $this->foo = $values['foo'];
+ }
+ }
+
+ /**
+ * @Annotation
+ *
+ * Some Annotation without a constructor
+ */
+ class Foo
+ {
+ public $bar;
+ }
+
+Optional: Constructors with Named Parameters
+--------------------------------------------
+
+Starting with Annotations v1.11 a new annotation instantiation strategy
+is available that aims at compatibility of Annotation classes with the PHP 8
+attribute feature.
+
+You can implement the
+``Doctrine\Common\Annotations\NamedArgumentConstructorAnnotation`` interface
+and then declare a constructor with regular parameter names that are matched
+from the named arguments in the annotation syntax.
+
+.. code-block:: php
+
+ namespace MyCompany\Annotations;
+
+ use Doctrine\Common\Annotations\NamedArgumentConstructorAnnotation;
+
+ /** @Annotation */
+ class Bar implements NamedArgumentConstructorAnnotation
+ {
+ private $foo;
+
+ public function __construct(string $foo)
+ {
+ $this->foo = $foo;
+ }
+ }
+
+ /** Useable with @Bar(foo="baz") */
+
+In combination with PHP 8's constructor property promotion feature
+you can simplify this to:
+
+.. code-block:: php
+
+ namespace MyCompany\Annotations;
+
+ use Doctrine\Common\Annotations\NamedArgumentConstructorAnnotation;
+
+ /** @Annotation */
+ class Bar implements NamedArgumentConstructorAnnotation
+ {
+ public function __construct(private string $foo) {}
+ }
+
+Annotation Target
+-----------------
+
+``@Target`` indicates the kinds of class elements to which an annotation
+type is applicable. Then you could define one or more targets:
+
+- ``CLASS`` Allowed in class docblocks
+- ``PROPERTY`` Allowed in property docblocks
+- ``METHOD`` Allowed in the method docblocks
+- ``ALL`` Allowed in class, property and method docblocks
+- ``ANNOTATION`` Allowed inside other annotations
+
+If the annotations is not allowed in the current context, an
+``AnnotationException`` is thrown.
+
+.. code-block:: php
+
+ namespace MyCompany\Annotations;
+
+ /**
+ * @Annotation
+ * @Target({"METHOD","PROPERTY"})
+ */
+ class Bar
+ {
+ // some code
+ }
+
+ /**
+ * @Annotation
+ * @Target("CLASS")
+ */
+ class Foo
+ {
+ // some code
+ }
+
+Attribute types
+---------------
+
+The annotation parser checks the given parameters using the phpdoc
+annotation ``@var``, The data type could be validated using the ``@var``
+annotation on the annotation properties or using the ``@Attributes`` and
+``@Attribute`` annotations.
+
+If the data type does not match you get an ``AnnotationException``
+
+.. code-block:: php
+
+ namespace MyCompany\Annotations;
+
+ /**
+ * @Annotation
+ * @Target({"METHOD","PROPERTY"})
+ */
+ class Bar
+ {
+ /** @var mixed */
+ public $mixed;
+
+ /** @var boolean */
+ public $boolean;
+
+ /** @var bool */
+ public $bool;
+
+ /** @var float */
+ public $float;
+
+ /** @var string */
+ public $string;
+
+ /** @var integer */
+ public $integer;
+
+ /** @var array */
+ public $array;
+
+ /** @var SomeAnnotationClass */
+ public $annotation;
+
+ /** @var array */
+ public $arrayOfIntegers;
+
+ /** @var array */
+ public $arrayOfAnnotations;
+ }
+
+ /**
+ * @Annotation
+ * @Target({"METHOD","PROPERTY"})
+ * @Attributes({
+ * @Attribute("stringProperty", type = "string"),
+ * @Attribute("annotProperty", type = "SomeAnnotationClass"),
+ * })
+ */
+ class Foo
+ {
+ public function __construct(array $values)
+ {
+ $this->stringProperty = $values['stringProperty'];
+ $this->annotProperty = $values['annotProperty'];
+ }
+
+ // some code
+ }
+
+Annotation Required
+-------------------
+
+``@Required`` indicates that the field must be specified when the
+annotation is used. If it is not used you get an ``AnnotationException``
+stating that this value can not be null.
+
+Declaring a required field:
+
+.. code-block:: php
+
+ /**
+ * @Annotation
+ * @Target("ALL")
+ */
+ class Foo
+ {
+ /** @Required */
+ public $requiredField;
+ }
+
+Usage:
+
+.. code-block:: php
+
+ /** @Foo(requiredField="value") */
+ public $direction; // Valid
+
+ /** @Foo */
+ public $direction; // Required field missing, throws an AnnotationException
+
+
+Enumerated values
+-----------------
+
+- An annotation property marked with ``@Enum`` is a field that accepts a
+ fixed set of scalar values.
+- You should use ``@Enum`` fields any time you need to represent fixed
+ values.
+- The annotation parser checks the given value and throws an
+ ``AnnotationException`` if the value does not match.
+
+
+Declaring an enumerated property:
+
+.. code-block:: php
+
+ /**
+ * @Annotation
+ * @Target("ALL")
+ */
+ class Direction
+ {
+ /**
+ * @Enum({"NORTH", "SOUTH", "EAST", "WEST"})
+ */
+ public $value;
+ }
+
+Annotation usage:
+
+.. code-block:: php
+
+ /** @Direction("NORTH") */
+ public $direction; // Valid value
+
+ /** @Direction("NORTHEAST") */
+ public $direction; // Invalid value, throws an AnnotationException
+
+
+Constants
+---------
+
+The use of constants and class constants is available on the annotations
+parser.
+
+The following usages are allowed:
+
+.. code-block:: php
+
+ namespace MyCompany\Entity;
+
+ use MyCompany\Annotations\Foo;
+ use MyCompany\Annotations\Bar;
+ use MyCompany\Entity\SomeClass;
+
+ /**
+ * @Foo(PHP_EOL)
+ * @Bar(Bar::FOO)
+ * @Foo({SomeClass::FOO, SomeClass::BAR})
+ * @Bar({SomeClass::FOO_KEY = SomeClass::BAR_VALUE})
+ */
+ class User
+ {
+ }
+
+
+Be careful with constants and the cache !
+
+.. note::
+
+ The cached reader will not re-evaluate each time an annotation is
+ loaded from cache. When a constant is changed the cache must be
+ cleaned.
+
+
+Usage
+-----
+
+Using the library API is simple. Using the annotations described in the
+previous section, you can now annotate other classes with your
+annotations:
+
+.. code-block:: php
+
+ namespace MyCompany\Entity;
+
+ use MyCompany\Annotations\Foo;
+ use MyCompany\Annotations\Bar;
+
+ /**
+ * @Foo(bar="foo")
+ * @Bar(foo="bar")
+ */
+ class User
+ {
+ }
+
+Now we can write a script to get the annotations above:
+
+.. code-block:: php
+
+ $reflClass = new ReflectionClass('MyCompany\Entity\User');
+ $classAnnotations = $reader->getClassAnnotations($reflClass);
+
+ foreach ($classAnnotations AS $annot) {
+ if ($annot instanceof \MyCompany\Annotations\Foo) {
+ echo $annot->bar; // prints "foo";
+ } else if ($annot instanceof \MyCompany\Annotations\Bar) {
+ echo $annot->foo; // prints "bar";
+ }
+ }
+
+You have a complete API for retrieving annotation class instances from a
+class, property or method docblock:
+
+
+Reader API
+~~~~~~~~~~
+
+Access all annotations of a class
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: php
+
+ public function getClassAnnotations(\ReflectionClass $class);
+
+Access one annotation of a class
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: php
+
+ public function getClassAnnotation(\ReflectionClass $class, $annotationName);
+
+Access all annotations of a method
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: php
+
+ public function getMethodAnnotations(\ReflectionMethod $method);
+
+Access one annotation of a method
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: php
+
+ public function getMethodAnnotation(\ReflectionMethod $method, $annotationName);
+
+Access all annotations of a property
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: php
+
+ public function getPropertyAnnotations(\ReflectionProperty $property);
+
+Access one annotation of a property
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: php
+
+ public function getPropertyAnnotation(\ReflectionProperty $property, $annotationName);
diff --git a/tools/php-cs-fixer/vendor/doctrine/annotations/docs/en/index.rst b/tools/php-cs-fixer/vendor/doctrine/annotations/docs/en/index.rst
new file mode 100644
index 0000000..b124f86
--- /dev/null
+++ b/tools/php-cs-fixer/vendor/doctrine/annotations/docs/en/index.rst
@@ -0,0 +1,100 @@
+Introduction
+============
+
+Doctrine Annotations allows to implement custom annotation
+functionality for PHP classes.
+
+.. code-block:: php
+
+ class Foo
+ {
+ /**
+ * @MyAnnotation(myProperty="value")
+ */
+ private $bar;
+ }
+
+Annotations aren't implemented in PHP itself which is why this component
+offers a way to use the PHP doc-blocks as a place for the well known
+annotation syntax using the ``@`` char.
+
+Annotations in Doctrine are used for the ORM configuration to build the
+class mapping, but it can be used in other projects for other purposes
+too.
+
+Installation
+============
+
+You can install the Annotation component with composer:
+
+.. code-block::
+
+ $ composer require doctrine/annotations
+
+Create an annotation class
+==========================
+
+An annotation class is a representation of the later used annotation
+configuration in classes. The annotation class of the previous example
+looks like this:
+
+.. code-block:: php
+
+ /**
+ * @Annotation
+ */
+ final class MyAnnotation
+ {
+ public $myProperty;
+ }
+
+The annotation class is declared as an annotation by ``@Annotation``.
+
+:ref:`Read more about custom annotations. `
+
+Reading annotations
+===================
+
+The access to the annotations happens by reflection of the class
+containing them. There are multiple reader-classes implementing the
+``Doctrine\Common\Annotations\Reader`` interface, that can access the
+annotations of a class. A common one is
+``Doctrine\Common\Annotations\AnnotationReader``:
+
+.. code-block:: php
+
+ use Doctrine\Common\Annotations\AnnotationReader;
+ use Doctrine\Common\Annotations\AnnotationRegistry;
+
+ // Deprecated and will be removed in 2.0 but currently needed
+ AnnotationRegistry::registerLoader('class_exists');
+
+ $reflectionClass = new ReflectionClass(Foo::class);
+ $property = $reflectionClass->getProperty('bar');
+
+ $reader = new AnnotationReader();
+ $myAnnotation = $reader->getPropertyAnnotation(
+ $property,
+ MyAnnotation::class
+ );
+
+ echo $myAnnotation->myProperty; // result: "value"
+
+Note that ``AnnotationRegistry::registerLoader('class_exists')`` only works
+if you already have an autoloader configured (i.e. composer autoloader).
+Otherwise, :ref:`please take a look to the other annotation autoload mechanisms `.
+
+A reader has multiple methods to access the annotations of a class.
+
+:ref:`Read more about handling annotations. `
+
+IDE Support
+-----------
+
+Some IDEs already provide support for annotations:
+
+- Eclipse via the `Symfony2 Plugin `_
+- PhpStorm via the `PHP Annotations Plugin `_ or the `Symfony Plugin `_
+
+.. _Read more about handling annotations.: annotations
+.. _Read more about custom annotations.: custom
diff --git a/tools/php-cs-fixer/vendor/doctrine/annotations/docs/en/sidebar.rst b/tools/php-cs-fixer/vendor/doctrine/annotations/docs/en/sidebar.rst
new file mode 100644
index 0000000..6f5d13c
--- /dev/null
+++ b/tools/php-cs-fixer/vendor/doctrine/annotations/docs/en/sidebar.rst
@@ -0,0 +1,6 @@
+.. toctree::
+ :depth: 3
+
+ index
+ annotations
+ custom
diff --git a/tools/php-cs-fixer/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation.php b/tools/php-cs-fixer/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation.php
new file mode 100644
index 0000000..750270e
--- /dev/null
+++ b/tools/php-cs-fixer/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation.php
@@ -0,0 +1,59 @@
+ $data Key-value for properties to be defined in this class.
+ */
+ final public function __construct(array $data)
+ {
+ foreach ($data as $key => $value) {
+ $this->$key = $value;
+ }
+ }
+
+ /**
+ * Error handler for unknown property accessor in Annotation class.
+ *
+ * @param string $name Unknown property name.
+ *
+ * @throws BadMethodCallException
+ */
+ public function __get($name)
+ {
+ throw new BadMethodCallException(
+ sprintf("Unknown property '%s' on annotation '%s'.", $name, static::class)
+ );
+ }
+
+ /**
+ * Error handler for unknown property mutator in Annotation class.
+ *
+ * @param string $name Unknown property name.
+ * @param mixed $value Property value.
+ *
+ * @throws BadMethodCallException
+ */
+ public function __set($name, $value)
+ {
+ throw new BadMethodCallException(
+ sprintf("Unknown property '%s' on annotation '%s'.", $name, static::class)
+ );
+ }
+}
diff --git a/tools/php-cs-fixer/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Attribute.php b/tools/php-cs-fixer/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Attribute.php
new file mode 100644
index 0000000..b1f8514
--- /dev/null
+++ b/tools/php-cs-fixer/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Attribute.php
@@ -0,0 +1,21 @@
+ */
+ public $value;
+}
diff --git a/tools/php-cs-fixer/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Enum.php b/tools/php-cs-fixer/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Enum.php
new file mode 100644
index 0000000..35d6410
--- /dev/null
+++ b/tools/php-cs-fixer/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Enum.php
@@ -0,0 +1,69 @@
+ */
+ public $value;
+
+ /**
+ * Literal target declaration.
+ *
+ * @var mixed[]
+ */
+ public $literal;
+
+ /**
+ * @throws InvalidArgumentException
+ *
+ * @phpstan-param array{literal?: mixed[], value: list} $values
+ */
+ public function __construct(array $values)
+ {
+ if (! isset($values['literal'])) {
+ $values['literal'] = [];
+ }
+
+ foreach ($values['value'] as $var) {
+ if (! is_scalar($var)) {
+ throw new InvalidArgumentException(sprintf(
+ '@Enum supports only scalar values "%s" given.',
+ is_object($var) ? get_class($var) : gettype($var)
+ ));
+ }
+ }
+
+ foreach ($values['literal'] as $key => $var) {
+ if (! in_array($key, $values['value'])) {
+ throw new InvalidArgumentException(sprintf(
+ 'Undefined enumerator value "%s" for literal "%s".',
+ $key,
+ $var
+ ));
+ }
+ }
+
+ $this->value = $values['value'];
+ $this->literal = $values['literal'];
+ }
+}
diff --git a/tools/php-cs-fixer/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/IgnoreAnnotation.php b/tools/php-cs-fixer/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/IgnoreAnnotation.php
new file mode 100644
index 0000000..ae60f7d
--- /dev/null
+++ b/tools/php-cs-fixer/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/IgnoreAnnotation.php
@@ -0,0 +1,43 @@
+ */
+ public $names;
+
+ /**
+ * @throws RuntimeException
+ *
+ * @phpstan-param array{value: string|list} $values
+ */
+ public function __construct(array $values)
+ {
+ if (is_string($values['value'])) {
+ $values['value'] = [$values['value']];
+ }
+
+ if (! is_array($values['value'])) {
+ throw new RuntimeException(sprintf(
+ '@IgnoreAnnotation expects either a string name, or an array of strings, but got %s.',
+ json_encode($values['value'])
+ ));
+ }
+
+ $this->names = $values['value'];
+ }
+}
diff --git a/tools/php-cs-fixer/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Required.php b/tools/php-cs-fixer/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Required.php
new file mode 100644
index 0000000..dee5857
--- /dev/null
+++ b/tools/php-cs-fixer/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Required.php
@@ -0,0 +1,13 @@
+ */
+ private static $map = [
+ 'ALL' => self::TARGET_ALL,
+ 'CLASS' => self::TARGET_CLASS,
+ 'METHOD' => self::TARGET_METHOD,
+ 'PROPERTY' => self::TARGET_PROPERTY,
+ 'ANNOTATION' => self::TARGET_ANNOTATION,
+ ];
+
+ /** @phpstan-var list */
+ public $value;
+
+ /**
+ * Targets as bitmask.
+ *
+ * @var int
+ */
+ public $targets;
+
+ /**
+ * Literal target declaration.
+ *
+ * @var string
+ */
+ public $literal;
+
+ /**
+ * @throws InvalidArgumentException
+ *
+ * @phpstan-param array{value?: string|list} $values
+ */
+ public function __construct(array $values)
+ {
+ if (! isset($values['value'])) {
+ $values['value'] = null;
+ }
+
+ if (is_string($values['value'])) {
+ $values['value'] = [$values['value']];
+ }
+
+ if (! is_array($values['value'])) {
+ throw new InvalidArgumentException(
+ sprintf(
+ '@Target expects either a string value, or an array of strings, "%s" given.',
+ is_object($values['value']) ? get_class($values['value']) : gettype($values['value'])
+ )
+ );
+ }
+
+ $bitmask = 0;
+ foreach ($values['value'] as $literal) {
+ if (! isset(self::$map[$literal])) {
+ throw new InvalidArgumentException(
+ sprintf(
+ 'Invalid Target "%s". Available targets: [%s]',
+ $literal,
+ implode(', ', array_keys(self::$map))
+ )
+ );
+ }
+
+ $bitmask |= self::$map[$literal];
+ }
+
+ $this->targets = $bitmask;
+ $this->value = $values['value'];
+ $this->literal = implode(', ', $this->value);
+ }
+}
diff --git a/tools/php-cs-fixer/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationException.php b/tools/php-cs-fixer/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationException.php
new file mode 100644
index 0000000..84d6660
--- /dev/null
+++ b/tools/php-cs-fixer/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationException.php
@@ -0,0 +1,171 @@
+ $available
+ */
+ public static function enumeratorError($attributeName, $annotationName, $context, $available, $given)
+ {
+ return new self(sprintf(
+ '[Enum Error] Attribute "%s" of @%s declared on %s accepts only [%s], but got %s.',
+ $attributeName,
+ $annotationName,
+ $context,
+ implode(', ', $available),
+ is_object($given) ? get_class($given) : $given
+ ));
+ }
+
+ /**
+ * @return AnnotationException
+ */
+ public static function optimizerPlusSaveComments()
+ {
+ return new self(
+ 'You have to enable opcache.save_comments=1 or zend_optimizerplus.save_comments=1.'
+ );
+ }
+
+ /**
+ * @return AnnotationException
+ */
+ public static function optimizerPlusLoadComments()
+ {
+ return new self(
+ 'You have to enable opcache.load_comments=1 or zend_optimizerplus.load_comments=1.'
+ );
+ }
+}
diff --git a/tools/php-cs-fixer/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationReader.php b/tools/php-cs-fixer/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationReader.php
new file mode 100644
index 0000000..6b1fe53
--- /dev/null
+++ b/tools/php-cs-fixer/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationReader.php
@@ -0,0 +1,342 @@
+
+ */
+ private static $globalImports = [
+ 'ignoreannotation' => Annotation\IgnoreAnnotation::class,
+ ];
+
+ /**
+ * A list with annotations that are not causing exceptions when not resolved to an annotation class.
+ *
+ * The names are case sensitive.
+ *
+ * @var array
+ */
+ private static $globalIgnoredNames = ImplicitlyIgnoredAnnotationNames::LIST;
+
+ /**
+ * A list with annotations that are not causing exceptions when not resolved to an annotation class.
+ *
+ * The names are case sensitive.
+ *
+ * @var array
+ */
+ private static $globalIgnoredNamespaces = [];
+
+ /**
+ * Add a new annotation to the globally ignored annotation names with regard to exception handling.
+ *
+ * @param string $name
+ */
+ public static function addGlobalIgnoredName($name)
+ {
+ self::$globalIgnoredNames[$name] = true;
+ }
+
+ /**
+ * Add a new annotation to the globally ignored annotation namespaces with regard to exception handling.
+ *
+ * @param string $namespace
+ */
+ public static function addGlobalIgnoredNamespace($namespace)
+ {
+ self::$globalIgnoredNamespaces[$namespace] = true;
+ }
+
+ /**
+ * Annotations parser.
+ *
+ * @var DocParser
+ */
+ private $parser;
+
+ /**
+ * Annotations parser used to collect parsing metadata.
+ *
+ * @var DocParser
+ */
+ private $preParser;
+
+ /**
+ * PHP parser used to collect imports.
+ *
+ * @var PhpParser
+ */
+ private $phpParser;
+
+ /**
+ * In-memory cache mechanism to store imported annotations per class.
+ *
+ * @var array>
+ */
+ private $imports = [];
+
+ /**
+ * In-memory cache mechanism to store ignored annotations per class.
+ *
+ * @var array>
+ */
+ private $ignoredAnnotationNames = [];
+
+ /**
+ * Initializes a new AnnotationReader.
+ *
+ * @throws AnnotationException
+ */
+ public function __construct(?DocParser $parser = null)
+ {
+ if (
+ extension_loaded('Zend Optimizer+') && (ini_get('zend_optimizerplus.save_comments') === '0' ||
+ ini_get('opcache.save_comments') === '0')
+ ) {
+ throw AnnotationException::optimizerPlusSaveComments();
+ }
+
+ if (extension_loaded('Zend OPcache') && ini_get('opcache.save_comments') === 0) {
+ throw AnnotationException::optimizerPlusSaveComments();
+ }
+
+ // Make sure that the IgnoreAnnotation annotation is loaded
+ class_exists(IgnoreAnnotation::class);
+
+ $this->parser = $parser ?: new DocParser();
+
+ $this->preParser = new DocParser();
+
+ $this->preParser->setImports(self::$globalImports);
+ $this->preParser->setIgnoreNotImportedAnnotations(true);
+ $this->preParser->setIgnoredAnnotationNames(self::$globalIgnoredNames);
+
+ $this->phpParser = new PhpParser();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function getClassAnnotations(ReflectionClass $class)
+ {
+ $this->parser->setTarget(Target::TARGET_CLASS);
+ $this->parser->setImports($this->getClassImports($class));
+ $this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class));
+ $this->parser->setIgnoredAnnotationNamespaces(self::$globalIgnoredNamespaces);
+
+ return $this->parser->parse($class->getDocComment(), 'class ' . $class->getName());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function getClassAnnotation(ReflectionClass $class, $annotationName)
+ {
+ $annotations = $this->getClassAnnotations($class);
+
+ foreach ($annotations as $annotation) {
+ if ($annotation instanceof $annotationName) {
+ return $annotation;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function getPropertyAnnotations(ReflectionProperty $property)
+ {
+ $class = $property->getDeclaringClass();
+ $context = 'property ' . $class->getName() . '::$' . $property->getName();
+
+ $this->parser->setTarget(Target::TARGET_PROPERTY);
+ $this->parser->setImports($this->getPropertyImports($property));
+ $this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class));
+ $this->parser->setIgnoredAnnotationNamespaces(self::$globalIgnoredNamespaces);
+
+ return $this->parser->parse($property->getDocComment(), $context);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function getPropertyAnnotation(ReflectionProperty $property, $annotationName)
+ {
+ $annotations = $this->getPropertyAnnotations($property);
+
+ foreach ($annotations as $annotation) {
+ if ($annotation instanceof $annotationName) {
+ return $annotation;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function getMethodAnnotations(ReflectionMethod $method)
+ {
+ $class = $method->getDeclaringClass();
+ $context = 'method ' . $class->getName() . '::' . $method->getName() . '()';
+
+ $this->parser->setTarget(Target::TARGET_METHOD);
+ $this->parser->setImports($this->getMethodImports($method));
+ $this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class));
+ $this->parser->setIgnoredAnnotationNamespaces(self::$globalIgnoredNamespaces);
+
+ return $this->parser->parse($method->getDocComment(), $context);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function getMethodAnnotation(ReflectionMethod $method, $annotationName)
+ {
+ $annotations = $this->getMethodAnnotations($method);
+
+ foreach ($annotations as $annotation) {
+ if ($annotation instanceof $annotationName) {
+ return $annotation;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the ignored annotations for the given class.
+ *
+ * @return array
+ */
+ private function getIgnoredAnnotationNames(ReflectionClass $class)
+ {
+ $name = $class->getName();
+ if (isset($this->ignoredAnnotationNames[$name])) {
+ return $this->ignoredAnnotationNames[$name];
+ }
+
+ $this->collectParsingMetadata($class);
+
+ return $this->ignoredAnnotationNames[$name];
+ }
+
+ /**
+ * Retrieves imports.
+ *
+ * @return array
+ */
+ private function getClassImports(ReflectionClass $class)
+ {
+ $name = $class->getName();
+ if (isset($this->imports[$name])) {
+ return $this->imports[$name];
+ }
+
+ $this->collectParsingMetadata($class);
+
+ return $this->imports[$name];
+ }
+
+ /**
+ * Retrieves imports for methods.
+ *
+ * @return array
+ */
+ private function getMethodImports(ReflectionMethod $method)
+ {
+ $class = $method->getDeclaringClass();
+ $classImports = $this->getClassImports($class);
+
+ $traitImports = [];
+
+ foreach ($class->getTraits() as $trait) {
+ if (
+ ! $trait->hasMethod($method->getName())
+ || $trait->getFileName() !== $method->getFileName()
+ ) {
+ continue;
+ }
+
+ $traitImports = array_merge($traitImports, $this->phpParser->parseClass($trait));
+ }
+
+ return array_merge($classImports, $traitImports);
+ }
+
+ /**
+ * Retrieves imports for properties.
+ *
+ * @return array
+ */
+ private function getPropertyImports(ReflectionProperty $property)
+ {
+ $class = $property->getDeclaringClass();
+ $classImports = $this->getClassImports($class);
+
+ $traitImports = [];
+
+ foreach ($class->getTraits() as $trait) {
+ if (! $trait->hasProperty($property->getName())) {
+ continue;
+ }
+
+ $traitImports = array_merge($traitImports, $this->phpParser->parseClass($trait));
+ }
+
+ return array_merge($classImports, $traitImports);
+ }
+
+ /**
+ * Collects parsing metadata for a given class.
+ */
+ private function collectParsingMetadata(ReflectionClass $class)
+ {
+ $ignoredAnnotationNames = self::$globalIgnoredNames;
+ $annotations = $this->preParser->parse($class->getDocComment(), 'class ' . $class->name);
+
+ foreach ($annotations as $annotation) {
+ if (! ($annotation instanceof IgnoreAnnotation)) {
+ continue;
+ }
+
+ foreach ($annotation->names as $annot) {
+ $ignoredAnnotationNames[$annot] = true;
+ }
+ }
+
+ $name = $class->getName();
+
+ $this->imports[$name] = array_merge(
+ self::$globalImports,
+ $this->phpParser->parseClass($class),
+ [
+ '__NAMESPACE__' => $class->getNamespaceName(),
+ 'self' => $name,
+ ]
+ );
+
+ $this->ignoredAnnotationNames[$name] = $ignoredAnnotationNames;
+ }
+}
diff --git a/tools/php-cs-fixer/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationRegistry.php b/tools/php-cs-fixer/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationRegistry.php
new file mode 100644
index 0000000..259d497
--- /dev/null
+++ b/tools/php-cs-fixer/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationRegistry.php
@@ -0,0 +1,190 @@
+|null $dirs
+ */
+ public static function registerAutoloadNamespace(string $namespace, $dirs = null): void
+ {
+ self::$autoloadNamespaces[$namespace] = $dirs;
+ }
+
+ /**
+ * Registers multiple namespaces.
+ *
+ * Loading of this namespaces will be done with a PSR-0 namespace loading algorithm.
+ *
+ * @deprecated This method is deprecated and will be removed in
+ * doctrine/annotations 2.0. Annotations will be autoloaded in 2.0.
+ *
+ * @param string[][]|string[]|null[] $namespaces indexed by namespace name
+ */
+ public static function registerAutoloadNamespaces(array $namespaces): void
+ {
+ self::$autoloadNamespaces = array_merge(self::$autoloadNamespaces, $namespaces);
+ }
+
+ /**
+ * Registers an autoloading callable for annotations, much like spl_autoload_register().
+ *
+ * NOTE: These class loaders HAVE to be silent when a class was not found!
+ * IMPORTANT: Loaders have to return true if they loaded a class that could contain the searched annotation class.
+ *
+ * @deprecated This method is deprecated and will be removed in
+ * doctrine/annotations 2.0. Annotations will be autoloaded in 2.0.
+ */
+ public static function registerLoader(callable $callable): void
+ {
+ // Reset our static cache now that we have a new loader to work with
+ self::$failedToAutoload = [];
+ self::$loaders[] = $callable;
+ }
+
+ /**
+ * Registers an autoloading callable for annotations, if it is not already registered
+ *
+ * @deprecated This method is deprecated and will be removed in
+ * doctrine/annotations 2.0. Annotations will be autoloaded in 2.0.
+ */
+ public static function registerUniqueLoader(callable $callable): void
+ {
+ if (in_array($callable, self::$loaders, true)) {
+ return;
+ }
+
+ self::registerLoader($callable);
+ }
+
+ /**
+ * Autoloads an annotation class silently.
+ */
+ public static function loadAnnotationClass(string $class): bool
+ {
+ if (class_exists($class, false)) {
+ return true;
+ }
+
+ if (array_key_exists($class, self::$failedToAutoload)) {
+ return false;
+ }
+
+ foreach (self::$autoloadNamespaces as $namespace => $dirs) {
+ if (strpos($class, $namespace) !== 0) {
+ continue;
+ }
+
+ $file = str_replace('\\', DIRECTORY_SEPARATOR, $class) . '.php';
+
+ if ($dirs === null) {
+ $path = stream_resolve_include_path($file);
+ if ($path) {
+ require $path;
+
+ return true;
+ }
+ } else {
+ foreach ((array) $dirs as $dir) {
+ if (is_file($dir . DIRECTORY_SEPARATOR . $file)) {
+ require $dir . DIRECTORY_SEPARATOR . $file;
+
+ return true;
+ }
+ }
+ }
+ }
+
+ foreach (self::$loaders as $loader) {
+ if ($loader($class) === true) {
+ return true;
+ }
+ }
+
+ if (
+ self::$loaders === [] &&
+ self::$autoloadNamespaces === [] &&
+ self::$registerFileUsed === false &&
+ class_exists($class)
+ ) {
+ return true;
+ }
+
+ self::$failedToAutoload[$class] = null;
+
+ return false;
+ }
+}
diff --git a/tools/php-cs-fixer/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/CachedReader.php b/tools/php-cs-fixer/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/CachedReader.php
new file mode 100644
index 0000000..91fbad0
--- /dev/null
+++ b/tools/php-cs-fixer/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/CachedReader.php
@@ -0,0 +1,264 @@
+> */
+ private $loadedAnnotations = [];
+
+ /** @var int[] */
+ private $loadedFilemtimes = [];
+
+ /**
+ * @param bool $debug
+ */
+ public function __construct(Reader $reader, Cache $cache, $debug = false)
+ {
+ $this->delegate = $reader;
+ $this->cache = $cache;
+ $this->debug = (bool) $debug;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function getClassAnnotations(ReflectionClass $class)
+ {
+ $cacheKey = $class->getName();
+
+ if (isset($this->loadedAnnotations[$cacheKey])) {
+ return $this->loadedAnnotations[$cacheKey];
+ }
+
+ $annots = $this->fetchFromCache($cacheKey, $class);
+ if ($annots === false) {
+ $annots = $this->delegate->getClassAnnotations($class);
+ $this->saveToCache($cacheKey, $annots);
+ }
+
+ return $this->loadedAnnotations[$cacheKey] = $annots;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function getClassAnnotation(ReflectionClass $class, $annotationName)
+ {
+ foreach ($this->getClassAnnotations($class) as $annot) {
+ if ($annot instanceof $annotationName) {
+ return $annot;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function getPropertyAnnotations(ReflectionProperty $property)
+ {
+ $class = $property->getDeclaringClass();
+ $cacheKey = $class->getName() . '$' . $property->getName();
+
+ if (isset($this->loadedAnnotations[$cacheKey])) {
+ return $this->loadedAnnotations[$cacheKey];
+ }
+
+ $annots = $this->fetchFromCache($cacheKey, $class);
+ if ($annots === false) {
+ $annots = $this->delegate->getPropertyAnnotations($property);
+ $this->saveToCache($cacheKey, $annots);
+ }
+
+ return $this->loadedAnnotations[$cacheKey] = $annots;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function getPropertyAnnotation(ReflectionProperty $property, $annotationName)
+ {
+ foreach ($this->getPropertyAnnotations($property) as $annot) {
+ if ($annot instanceof $annotationName) {
+ return $annot;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function getMethodAnnotations(ReflectionMethod $method)
+ {
+ $class = $method->getDeclaringClass();
+ $cacheKey = $class->getName() . '#' . $method->getName();
+
+ if (isset($this->loadedAnnotations[$cacheKey])) {
+ return $this->loadedAnnotations[$cacheKey];
+ }
+
+ $annots = $this->fetchFromCache($cacheKey, $class);
+ if ($annots === false) {
+ $annots = $this->delegate->getMethodAnnotations($method);
+ $this->saveToCache($cacheKey, $annots);
+ }
+
+ return $this->loadedAnnotations[$cacheKey] = $annots;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function getMethodAnnotation(ReflectionMethod $method, $annotationName)
+ {
+ foreach ($this->getMethodAnnotations($method) as $annot) {
+ if ($annot instanceof $annotationName) {
+ return $annot;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Clears loaded annotations.
+ *
+ * @return void
+ */
+ public function clearLoadedAnnotations()
+ {
+ $this->loadedAnnotations = [];
+ $this->loadedFilemtimes = [];
+ }
+
+ /**
+ * Fetches a value from the cache.
+ *
+ * @param string $cacheKey The cache key.
+ *
+ * @return mixed The cached value or false when the value is not in cache.
+ */
+ private function fetchFromCache($cacheKey, ReflectionClass $class)
+ {
+ $data = $this->cache->fetch($cacheKey);
+ if ($data !== false) {
+ if (! $this->debug || $this->isCacheFresh($cacheKey, $class)) {
+ return $data;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Saves a value to the cache.
+ *
+ * @param string $cacheKey The cache key.
+ * @param mixed $value The value.
+ *
+ * @return void
+ */
+ private function saveToCache($cacheKey, $value)
+ {
+ $this->cache->save($cacheKey, $value);
+ if (! $this->debug) {
+ return;
+ }
+
+ $this->cache->save('[C]' . $cacheKey, time());
+ }
+
+ /**
+ * Checks if the cache is fresh.
+ *
+ * @param string $cacheKey
+ *
+ * @return bool
+ */
+ private function isCacheFresh($cacheKey, ReflectionClass $class)
+ {
+ $lastModification = $this->getLastModification($class);
+ if ($lastModification === 0) {
+ return true;
+ }
+
+ return $this->cache->fetch('[C]' . $cacheKey) >= $lastModification;
+ }
+
+ /**
+ * Returns the time the class was last modified, testing traits and parents
+ */
+ private function getLastModification(ReflectionClass $class): int
+ {
+ $filename = $class->getFileName();
+
+ if (isset($this->loadedFilemtimes[$filename])) {
+ return $this->loadedFilemtimes[$filename];
+ }
+
+ $parent = $class->getParentClass();
+
+ $lastModification = max(array_merge(
+ [$filename ? filemtime($filename) : 0],
+ array_map(function (ReflectionClass $reflectionTrait): int {
+ return $this->getTraitLastModificationTime($reflectionTrait);
+ }, $class->getTraits()),
+ array_map(function (ReflectionClass $class): int {
+ return $this->getLastModification($class);
+ }, $class->getInterfaces()),
+ $parent ? [$this->getLastModification($parent)] : []
+ ));
+
+ assert($lastModification !== false);
+
+ return $this->loadedFilemtimes[$filename] = $lastModification;
+ }
+
+ private function getTraitLastModificationTime(ReflectionClass $reflectionTrait): int
+ {
+ $fileName = $reflectionTrait->getFileName();
+
+ if (isset($this->loadedFilemtimes[$fileName])) {
+ return $this->loadedFilemtimes[$fileName];
+ }
+
+ $lastModificationTime = max(array_merge(
+ [$fileName ? filemtime($fileName) : 0],
+ array_map(function (ReflectionClass $reflectionTrait): int {
+ return $this->getTraitLastModificationTime($reflectionTrait);
+ }, $reflectionTrait->getTraits())
+ ));
+
+ assert($lastModificationTime !== false);
+
+ return $this->loadedFilemtimes[$fileName] = $lastModificationTime;
+ }
+}
diff --git a/tools/php-cs-fixer/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/DocLexer.php b/tools/php-cs-fixer/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/DocLexer.php
new file mode 100644
index 0000000..f6567c5
--- /dev/null
+++ b/tools/php-cs-fixer/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/DocLexer.php
@@ -0,0 +1,129 @@
+= 100
+ public const T_IDENTIFIER = 100;
+ public const T_AT = 101;
+ public const T_CLOSE_CURLY_BRACES = 102;
+ public const T_CLOSE_PARENTHESIS = 103;
+ public const T_COMMA = 104;
+ public const T_EQUALS = 105;
+ public const T_FALSE = 106;
+ public const T_NAMESPACE_SEPARATOR = 107;
+ public const T_OPEN_CURLY_BRACES = 108;
+ public const T_OPEN_PARENTHESIS = 109;
+ public const T_TRUE = 110;
+ public const T_NULL = 111;
+ public const T_COLON = 112;
+ public const T_MINUS = 113;
+
+ /** @var array */
+ protected $noCase = [
+ '@' => self::T_AT,
+ ',' => self::T_COMMA,
+ '(' => self::T_OPEN_PARENTHESIS,
+ ')' => self::T_CLOSE_PARENTHESIS,
+ '{' => self::T_OPEN_CURLY_BRACES,
+ '}' => self::T_CLOSE_CURLY_BRACES,
+ '=' => self::T_EQUALS,
+ ':' => self::T_COLON,
+ '-' => self::T_MINUS,
+ '\\' => self::T_NAMESPACE_SEPARATOR,
+ ];
+
+ /** @var array */
+ protected $withCase = [
+ 'true' => self::T_TRUE,
+ 'false' => self::T_FALSE,
+ 'null' => self::T_NULL,
+ ];
+
+ /**
+ * Whether the next token starts immediately, or if there were
+ * non-captured symbols before that
+ */
+ public function nextTokenIsAdjacent(): bool
+ {
+ return $this->token === null
+ || ($this->lookahead !== null
+ && ($this->lookahead['position'] - $this->token['position']) === strlen($this->token['value']));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getCatchablePatterns()
+ {
+ return [
+ '[a-z_\\\][a-z0-9_\:\\\]*[a-z_][a-z0-9_]*',
+ '(?:[+-]?[0-9]+(?:[\.][0-9]+)*)(?:[eE][+-]?[0-9]+)?',
+ '"(?:""|[^"])*+"',
+ ];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getNonCatchablePatterns()
+ {
+ return ['\s+', '\*+', '(.)'];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getType(&$value)
+ {
+ $type = self::T_NONE;
+
+ if ($value[0] === '"') {
+ $value = str_replace('""', '"', substr($value, 1, strlen($value) - 2));
+
+ return self::T_STRING;
+ }
+
+ if (isset($this->noCase[$value])) {
+ return $this->noCase[$value];
+ }
+
+ if ($value[0] === '_' || $value[0] === '\\' || ctype_alpha($value[0])) {
+ return self::T_IDENTIFIER;
+ }
+
+ $lowerValue = strtolower($value);
+
+ if (isset($this->withCase[$lowerValue])) {
+ return $this->withCase[$lowerValue];
+ }
+
+ // Checking numeric value
+ if (is_numeric($value)) {
+ return strpos($value, '.') !== false || stripos($value, 'e') !== false
+ ? self::T_FLOAT : self::T_INTEGER;
+ }
+
+ return $type;
+ }
+}
diff --git a/tools/php-cs-fixer/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/DocParser.php b/tools/php-cs-fixer/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/DocParser.php
new file mode 100644
index 0000000..ed037d8
--- /dev/null
+++ b/tools/php-cs-fixer/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/DocParser.php
@@ -0,0 +1,1387 @@
+
+ */
+ private static $classIdentifiers = [
+ DocLexer::T_IDENTIFIER,
+ DocLexer::T_TRUE,
+ DocLexer::T_FALSE,
+ DocLexer::T_NULL,
+ ];
+
+ /**
+ * The lexer.
+ *
+ * @var DocLexer
+ */
+ private $lexer;
+
+ /**
+ * Current target context.
+ *
+ * @var int
+ */
+ private $target;
+
+ /**
+ * Doc parser used to collect annotation target.
+ *
+ * @var DocParser
+ */
+ private static $metadataParser;
+
+ /**
+ * Flag to control if the current annotation is nested or not.
+ *
+ * @var bool
+ */
+ private $isNestedAnnotation = false;
+
+ /**
+ * Hashmap containing all use-statements that are to be used when parsing
+ * the given doc block.
+ *
+ * @var array
+ */
+ private $imports = [];
+
+ /**
+ * This hashmap is used internally to cache results of class_exists()
+ * look-ups.
+ *
+ * @var array
+ */
+ private $classExists = [];
+
+ /**
+ * Whether annotations that have not been imported should be ignored.
+ *
+ * @var bool
+ */
+ private $ignoreNotImportedAnnotations = false;
+
+ /**
+ * An array of default namespaces if operating in simple mode.
+ *
+ * @var string[]
+ */
+ private $namespaces = [];
+
+ /**
+ * A list with annotations that are not causing exceptions when not resolved to an annotation class.
+ *
+ * The names must be the raw names as used in the class, not the fully qualified
+ *
+ * @var bool[] indexed by annotation name
+ */
+ private $ignoredAnnotationNames = [];
+
+ /**
+ * A list with annotations in namespaced format
+ * that are not causing exceptions when not resolved to an annotation class.
+ *
+ * @var bool[] indexed by namespace name
+ */
+ private $ignoredAnnotationNamespaces = [];
+
+ /** @var string */
+ private $context = '';
+
+ /**
+ * Hash-map for caching annotation metadata.
+ *
+ * @var array
+ */
+ private static $annotationMetadata = [
+ Annotation\Target::class => [
+ 'is_annotation' => true,
+ 'has_constructor' => true,
+ 'properties' => [],
+ 'targets_literal' => 'ANNOTATION_CLASS',
+ 'targets' => Target::TARGET_CLASS,
+ 'default_property' => 'value',
+ 'attribute_types' => [
+ 'value' => [
+ 'required' => false,
+ 'type' => 'array',
+ 'array_type' => 'string',
+ 'value' => 'array',
+ ],
+ ],
+ ],
+ Annotation\Attribute::class => [
+ 'is_annotation' => true,
+ 'has_constructor' => false,
+ 'targets_literal' => 'ANNOTATION_ANNOTATION',
+ 'targets' => Target::TARGET_ANNOTATION,
+ 'default_property' => 'name',
+ 'properties' => [
+ 'name' => 'name',
+ 'type' => 'type',
+ 'required' => 'required',
+ ],
+ 'attribute_types' => [
+ 'value' => [
+ 'required' => true,
+ 'type' => 'string',
+ 'value' => 'string',
+ ],
+ 'type' => [
+ 'required' => true,
+ 'type' => 'string',
+ 'value' => 'string',
+ ],
+ 'required' => [
+ 'required' => false,
+ 'type' => 'boolean',
+ 'value' => 'boolean',
+ ],
+ ],
+ ],
+ Annotation\Attributes::class => [
+ 'is_annotation' => true,
+ 'has_constructor' => false,
+ 'targets_literal' => 'ANNOTATION_CLASS',
+ 'targets' => Target::TARGET_CLASS,
+ 'default_property' => 'value',
+ 'properties' => ['value' => 'value'],
+ 'attribute_types' => [
+ 'value' => [
+ 'type' => 'array',
+ 'required' => true,
+ 'array_type' => Annotation\Attribute::class,
+ 'value' => 'array<' . Annotation\Attribute::class . '>',
+ ],
+ ],
+ ],
+ Annotation\Enum::class => [
+ 'is_annotation' => true,
+ 'has_constructor' => true,
+ 'targets_literal' => 'ANNOTATION_PROPERTY',
+ 'targets' => Target::TARGET_PROPERTY,
+ 'default_property' => 'value',
+ 'properties' => ['value' => 'value'],
+ 'attribute_types' => [
+ 'value' => [
+ 'type' => 'array',
+ 'required' => true,
+ ],
+ 'literal' => [
+ 'type' => 'array',
+ 'required' => false,
+ ],
+ ],
+ ],
+ ];
+
+ /**
+ * Hash-map for handle types declaration.
+ *
+ * @var array
+ */
+ private static $typeMap = [
+ 'float' => 'double',
+ 'bool' => 'boolean',
+ // allow uppercase Boolean in honor of George Boole
+ 'Boolean' => 'boolean',
+ 'int' => 'integer',
+ ];
+
+ /**
+ * Constructs a new DocParser.
+ */
+ public function __construct()
+ {
+ $this->lexer = new DocLexer();
+ }
+
+ /**
+ * Sets the annotation names that are ignored during the parsing process.
+ *
+ * The names are supposed to be the raw names as used in the class, not the
+ * fully qualified class names.
+ *
+ * @param bool[] $names indexed by annotation name
+ *
+ * @return void
+ */
+ public function setIgnoredAnnotationNames(array $names)
+ {
+ $this->ignoredAnnotationNames = $names;
+ }
+
+ /**
+ * Sets the annotation namespaces that are ignored during the parsing process.
+ *
+ * @param bool[] $ignoredAnnotationNamespaces indexed by annotation namespace name
+ *
+ * @return void
+ */
+ public function setIgnoredAnnotationNamespaces($ignoredAnnotationNamespaces)
+ {
+ $this->ignoredAnnotationNamespaces = $ignoredAnnotationNamespaces;
+ }
+
+ /**
+ * Sets ignore on not-imported annotations.
+ *
+ * @param bool $bool
+ *
+ * @return void
+ */
+ public function setIgnoreNotImportedAnnotations($bool)
+ {
+ $this->ignoreNotImportedAnnotations = (bool) $bool;
+ }
+
+ /**
+ * Sets the default namespaces.
+ *
+ * @param string $namespace
+ *
+ * @return void
+ *
+ * @throws RuntimeException
+ */
+ public function addNamespace($namespace)
+ {
+ if ($this->imports) {
+ throw new RuntimeException('You must either use addNamespace(), or setImports(), but not both.');
+ }
+
+ $this->namespaces[] = $namespace;
+ }
+
+ /**
+ * Sets the imports.
+ *
+ * @param array $imports
+ *
+ * @return void
+ *
+ * @throws RuntimeException
+ */
+ public function setImports(array $imports)
+ {
+ if ($this->namespaces) {
+ throw new RuntimeException('You must either use addNamespace(), or setImports(), but not both.');
+ }
+
+ $this->imports = $imports;
+ }
+
+ /**
+ * Sets current target context as bitmask.
+ *
+ * @param int $target
+ *
+ * @return void
+ */
+ public function setTarget($target)
+ {
+ $this->target = $target;
+ }
+
+ /**
+ * Parses the given docblock string for annotations.
+ *
+ * @param string $input The docblock string to parse.
+ * @param string $context The parsing context.
+ *
+ * @throws AnnotationException
+ * @throws ReflectionException
+ *
+ * @phpstan-return list