This commit is contained in:
Danny Houtworm 2023-08-14 01:01:26 +00:00
parent 2bce8efb23
commit e415309311
402 changed files with 0 additions and 37301 deletions

View File

@ -1,674 +0,0 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
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.
{one line to give the program's name and a brief idea of what it does.}
Copyright (C) {year} {name of author}
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 <http://www.gnu.org/licenses/>.
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:
{project} Copyright (C) {year} {fullname}
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
<http://www.gnu.org/licenses/>.
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
<http://www.gnu.org/philosophy/why-not-lgpl.html>.

View File

@ -1,196 +0,0 @@
#rediscache .content-column {
margin-bottom: 3em;
}
#rediscache .sidebar-column h6 {
margin: 5px 0;
padding: 10px 0;
font-size: 13px;
line-height: 19px;
border-bottom: 1px solid #ccc;
}
#rediscache .tab-pane {
display: none;
}
#rediscache .tab-pane.active {
display: block;
}
#rediscache .form-table th,
#rediscache .form-table td {
padding-top: 7px;
padding-bottom: 7px;
}
#rediscache .form-table .success {
color: #008a20;
}
#rediscache .form-table .warning {
color: #dba617;
}
#rediscache .form-table .error {
color: #d63638;
}
#rediscache .form-table td ul {
margin: 0;
}
#rediscache .form-table .description.is-notice {
max-width: 25rem;
color: #d54e21;
}
#rediscache .form-table .description.is-notice a {
color: #d54e21;
}
#rediscache .card ul {
list-style: circle;
padding-left: 25px;
}
#rediscache .tab-pane-metrics .card {
min-width: auto;
max-width: 100%;
padding: 0;
}
#rediscache .nav-tab-wrapper .nav-tab-disabled {
cursor: not-allowed;
background-color: #e5e5e5 !important;
}
#rediscache .tab-pane-diagnostics p {
margin-top: 20px;
}
#rediscache #diagnostics-pane .card {
width: 100%;
max-width: 100%;
}
#rediscache #diagnostics-pane pre {
height: 400px;
overflow: scroll;
}
#rediscache .compatibility {
display: flex;
max-width: 520px;
margin-bottom: 5px;
color: #666;
}
#rediscache .compatibility + ul {
margin-top: 0;
padding-left: 50px;
list-style: circle;
max-width: 470px;
color: #666;
}
#rediscache .compatibility .dashicons {
margin-right: 5px;
}
@media screen and (min-width: 1200px) {
#rediscache .columns {
display: flex;
}
#rediscache .content-column {
flex-grow: 1;
}
#rediscache .sidebar-column {
max-width: 400px;
margin-left: 20px;
}
}
#dashboard_rediscache .inside {
margin: 0;
padding: 0;
}
#widget-redis-stats ul {
margin: 11px 11px 0;
display: flex;
}
#widget-redis-stats ul li {
margin-bottom: 0;
}
#metrics-pane #widget-redis-stats ul {
display: block;
list-style-type: none;
text-align: center;
font-size: 14px;
}
#metrics-pane #widget-redis-stats ul li {
display: inline;
list-style-type: none;
}
#widget-redis-stats ul a {
padding: 0 7px;
text-decoration: none;
}
#widget-redis-stats ul a:hover {
color: #135e96;
}
#widget-redis-stats ul a.active {
color: #1d2327;
}
#redis-stats-chart .apexcharts-annotations {
display: none;
opacity: 0.75;
}
#redis-stats-chart .apexcharts-tooltip,
#redis-stats-chart .apexcharts-tooltip-title {
font-size: 13px;
}
#redis-stats-chart .apexcharts-tooltip {
display: flex;
flex-direction: row;
width: 100%;
justify-content: center;
border: none;
box-shadow: none;
background: #fff;
margin-top: 1px;
}
#redis-stats-chart .apexcharts-tooltip-title {
margin-bottom: 0;
padding: 0 15px;
border-bottom: none;
background-color: transparent;
}
#redis-stats-chart .apexcharts-tooltip-series-group {
display: flex;
padding-bottom: 0;
padding: 0 15px;
}
#redis-stats-chart .apexcharts-tooltip-y-group {
padding: 0;
}
#redis-stats-chart .apexcharts-tooltip-text-value {
margin-left: 0;
font-variant-numeric: tabular-nums;
}

View File

@ -1,488 +0,0 @@
( function ( $, root, undefined ) {
root.rediscache = root.rediscache || {};
var rediscache = root.rediscache;
$.extend( rediscache, {
metrics: {
computed: null,
},
chart: null,
chart_defaults: {
noData: {
text: root.rediscache_metrics
? rediscache.l10n.no_data
: rediscache.l10n.no_cache,
align: 'center',
verticalAlign: 'middle',
offsetY: -25,
style: {
color: '#72777c',
fontSize: '14px',
fontFamily: 'inherit',
}
},
stroke: {
width: [2, 2],
curve: 'smooth',
dashArray: [0, 8],
},
colors: [
'#0096dd',
'#72777c',
],
annotations: {
texts: [{
x: '15%',
y: '30%',
fontSize: '20px',
fontWeight: 600,
fontFamily: 'inherit',
foreColor: '#72777c',
}],
},
chart: {
type: 'line',
height: $( '#metrics-pane #widget-redis-stats' ).length ? '300px' : '100%',
toolbar: { show: false },
zoom: { enabled: false },
animations: { enabled: false },
},
dataLabels: {
enabled: false,
},
legend: {
show: false,
},
fill: {
opacity: [0.25, 1],
},
xaxis: {
type: 'datetime',
labels: {
format: 'HH:mm',
datetimeUTC: false,
style: { colors: '#72777c', fontSize: '13px', fontFamily: 'inherit' },
},
tooltip: { enabled: false },
},
yaxis: {
type: 'numeric',
tickAmount: 4,
min: 0,
labels: {
style: { colors: '#72777c', fontSize: '13px', fontFamily: 'inherit' },
formatter: function ( value ) {
return Math.round( value );
},
},
},
tooltip: {
fixed: {
enabled: true,
position: 'bottomLeft',
offsetY: 15,
offsetX: 0,
},
}
},
templates: {
tooltip_title: _.template(
'<div class="apexcharts-tooltip-title"><%- title %></div>'
),
series_group: _.template(
'<div class="apexcharts-tooltip-series-group">' +
' <span class="apexcharts-tooltip-marker" style="background-color: <%- color %>;"></span>' +
' <div class="apexcharts-tooltip-text">' +
' <div class="apexcharts-tooltip-y-group">' +
' <span class="apexcharts-tooltip-text-label"><%- name %>:</span>' +
' <span class="apexcharts-tooltip-text-value"><%- value %></span>' +
' </div>' +
' </div>' +
'</div>'
),
series_pro: _.template(
'<div class="apexcharts-tooltip-series-group">' +
' <span class="apexcharts-tooltip-marker" style="background-color: <%- color %>;"></span>' +
' <div class="apexcharts-tooltip-text">' +
' <div class="apexcharts-tooltip-y-group">' +
' <span class="apexcharts-tooltip-text-label"><%- name %></span>' +
' </div>' +
' </div>' +
'</div>'
),
}
} );
// Build the charts by deep extending the chart defaults
$.extend( rediscache, {
charts: {
time: $.extend( true, {}, rediscache.chart_defaults, {
yaxis: {
labels: {
formatter: function ( value ) {
return Math.round( value ) + ' ms';
},
},
},
tooltip: {
custom: function ({ series, seriesIndex, dataPointIndex, w }) {
return [
rediscache.templates.tooltip_title({
title: new Date( w.globals.seriesX[ seriesIndex ][ dataPointIndex ] )
.toTimeString().slice( 0, 5 ),
}),
rediscache.templates.series_group({
color: rediscache.chart_defaults.colors[0],
name: w.globals.seriesNames[0],
value: series[0][ dataPointIndex ].toFixed(2) + ' ms',
}),
rediscache.templates.series_pro({
color: rediscache.chart_defaults.colors[1],
name: rediscache.l10n.pro,
}),
].join('');
},
},
} ),
bytes: $.extend( true, {}, rediscache.chart_defaults, {
yaxis: {
labels: {
formatter: function ( value ) {
var i = value === 0 ? 0 : Math.floor( Math.log( value ) / Math.log( 1024 ) );
return parseFloat( (value / Math.pow( 1024, i ) ).toFixed( i ? 2 : 0 ) ) + ' ' + ['B', 'KB', 'MB', 'GB', 'TB'][i];
},
},
},
tooltip: {
custom: function ({ series, seriesIndex, dataPointIndex, w }) {
var value = series[0][ dataPointIndex ];
var i = value === 0 ? 0 : Math.floor( Math.log( value ) / Math.log( 1024 ) );
var bytes = parseFloat( (value / Math.pow( 1024, i ) ).toFixed( i ? 2 : 0 ) ) + ' ' + ['B', 'KB', 'MB', 'GB', 'TB'][i];
return [
rediscache.templates.tooltip_title({
title: new Date( w.globals.seriesX[ seriesIndex ][ dataPointIndex ] ).toTimeString().slice( 0, 5 ),
}),
rediscache.templates.series_group({
color: rediscache.chart_defaults.colors[0],
name: w.globals.seriesNames[0],
value: bytes,
}),
rediscache.templates.series_pro({
color: rediscache.chart_defaults.colors[1],
name: rediscache.l10n.pro,
}),
].join('');
},
},
} ),
ratio: $.extend( true, {}, rediscache.chart_defaults, {
yaxis: {
max: 100,
labels: {
formatter: function ( value ) {
return Math.round( value ) + '%';
},
},
},
tooltip: {
custom: function ({ series, seriesIndex, dataPointIndex, w }) {
return [
rediscache.templates.tooltip_title({
title: new Date( w.globals.seriesX[ seriesIndex ][ dataPointIndex ] )
.toTimeString().slice( 0, 5 ),
}),
rediscache.templates.series_group({
color: rediscache.chart_defaults.colors[0],
name: w.globals.seriesNames[0],
value: Math.round( series[0][ dataPointIndex ] * 100 ) / 100 + '%',
}),
].join('');
},
},
} ),
calls: $.extend( true, {}, rediscache.chart_defaults, {
tooltip: {
custom: function ({ series, seriesIndex, dataPointIndex, w }) {
return [
rediscache.templates.tooltip_title({
title: new Date( w.globals.seriesX[ seriesIndex ][ dataPointIndex ] )
.toTimeString().slice( 0, 5 ),
}),
rediscache.templates.series_group({
color: rediscache.chart_defaults.colors[0],
name: w.globals.seriesNames[0],
value: Math.round( series[0][ dataPointIndex ] ),
}),
rediscache.templates.series_pro({
color: rediscache.chart_defaults.colors[1],
name: rediscache.l10n.pro,
}),
].join('');
},
},
} ),
},
} );
var compute_metrics = function ( raw_metrics ) {
var metrics = {};
// parse raw metrics in blocks of minutes
for ( var entry in raw_metrics ) {
var values = {};
var timestamp = raw_metrics[ entry ].timestamp;
var minute = ( timestamp - timestamp % 60 ) * 1000;
for ( var key in raw_metrics[ entry ] ) {
if ( raw_metrics[ entry ].hasOwnProperty( key ) ) {
values[ key ] = Number( raw_metrics[ entry ][ key ] );
}
}
if ( ! metrics[ minute ] ) {
metrics[ minute ] = [];
}
metrics[ minute ].push( values );
}
// calculate median value for each block
for ( var entry in metrics ) {
if ( metrics[ entry ].length === 1 ) {
metrics[ entry ] = metrics[ entry ].shift();
continue;
}
var medians = {};
for ( var key in metrics[ entry ][0] ) {
medians[ key ] = compute_median(
metrics[ entry ].map(
function ( metric ) {
return metric[ key ];
}
)
);
}
metrics[ entry ] = medians;
}
var computed = [];
for ( var timestamp in metrics ) {
var entry = metrics[ timestamp ];
entry.date = Number( timestamp );
entry.time = entry.time * 1000;
computed.push( entry );
}
computed.sort(
function( a, b ) {
return a.date - b.date;
}
);
return computed.length < 2 ? [] : computed;
};
var compute_median = function ( numbers ) {
var median = 0;
var numsLen = numbers.length;
numbers.sort();
if ( numsLen % 2 === 0 ) {
median = ( numbers[ numsLen / 2 - 1 ] + numbers[ numsLen / 2 ] ) / 2;
} else {
median = numbers[ ( numsLen - 1 ) / 2 ];
}
return median;
};
var render_chart = function ( id ) {
if ( rediscache.chart ) {
rediscache.chart.updateOptions( rediscache.charts[ id ] );
return;
}
var chart = new ApexCharts(
document.querySelector( '#redis-stats-chart' ),
rediscache.charts[ id ]
);
chart.render();
root.rediscache.chart = chart;
};
var setup_charts = function () {
var metrics = {};
for ( var type in rediscache.charts ) {
if ( ! rediscache.charts.hasOwnProperty( type ) ) {
continue;
}
metrics[type] = rediscache.metrics.computed.map(
function ( entry ) {
return [ entry.date, entry[type] ];
}
);
rediscache.charts[type].series = [{
name: rediscache.l10n[type],
type: 'area',
data: metrics[type],
}];
}
if ( ! rediscache.disable_pro ) {
var pro_charts = {
time: function ( entry ) {
return [ entry[0], entry[1] * 0.5 ]
},
bytes: function ( entry ) {
return [ entry[0], entry[1] * 0.3 ]
},
calls: function ( entry ) {
return [ entry[0], Math.round( entry[1] / 50 ) + 5 ]
},
};
for ( var type in pro_charts ) {
if ( ! rediscache.charts[type] ) {
continue;
}
rediscache.charts[type].series.push({
name: rediscache.l10n.pro,
type: 'line',
data: metrics[type].map( pro_charts[type] ),
});
}
}
};
// executed on page load
$(function () {
var $tabs = $( '#rediscache .nav-tab-wrapper' );
var $panes = $( '#rediscache .content-column .tab-content' );
$tabs.find( 'a' ).on(
'click.redis-cache',
function ( event ) {
var toggle = $( this ).data( 'toggle' );
$( this ).blur();
show_tab( toggle );
if ( history.pushState ) {
history.pushState( null, null, '#' + toggle );
}
return false;
}
);
var firstRender = window.location.hash.indexOf('metrics') === -1;
var show_tab = function ( name ) {
$tabs.find( '.nav-tab-active' ).removeClass( 'nav-tab-active' );
$panes.find( '.tab-pane.active' ).removeClass( 'active' );
$( '#' + name + '-tab' ).addClass( 'nav-tab-active' );
$( '#' + name + '-pane' ).addClass( 'active' );
if (name === 'metrics' && firstRender) {
firstRender = false;
render_chart( 'time' );
}
};
var show_current_tab = function () {
var tabHash = window.location.hash.replace( '#', '' );
if ( tabHash !== '' && $( '#' + tabHash + '-tab' ) ) {
show_tab( tabHash );
}
};
show_current_tab();
$( window ).on( 'hashchange', show_current_tab );
if ( $( '#widget-redis-stats' ).length ) {
rediscache.metrics.computed = compute_metrics( root.rediscache_metrics );
setup_charts();
render_chart( 'time' );
}
$( '#widget-redis-stats ul a[data-chart]' ).on(
'click.redis-cache',
function ( event ) {
event.preventDefault();
$( '#widget-redis-stats .active' ).removeClass( 'active' );
$( this ).blur().addClass( 'active' );
render_chart(
$( event.target ).data( 'chart' )
);
}
);
$( '.notice.is-dismissible[data-dismissible]' ).on(
'click.roc-dismiss-notice',
'.notice-dismiss',
function ( event ) {
event.preventDefault();
var $parent = $( this ).parent();
$.post( ajaxurl, {
notice: $parent.data( 'dismissible' ),
action: 'roc_dismiss_notice',
_ajax_nonce: $parent.data( 'nonce' ),
} );
}
);
if ( $( '#redis-cache-copy-button' ).length ) {
if ( typeof ClipboardJS === 'undefined' ) {
$( '#redis-cache-copy-button' ).remove();
} else {
var successTimeout;
var clipboard = new ClipboardJS( '#redis-cache-copy-button .copy-button' );
clipboard.on( 'success', function( e ) {
var triggerElement = $( e.trigger ),
successElement = $( '.success', triggerElement.closest( 'div' ) );
e.clearSelection();
triggerElement.trigger( 'focus' );
clearTimeout( successTimeout );
successElement.removeClass( 'hidden' );
successTimeout = setTimeout( function() {
successElement.addClass( 'hidden' );
if ( clipboard.clipboardAction.fakeElem && clipboard.clipboardAction.removeFake ) {
clipboard.clipboardAction.removeFake();
}
}, 3000 );
} );
}
}
});
} ( window[ rediscache.jQuery ], window ) );

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -1,343 +0,0 @@
<?php
/**
* Credis, a Redis interface for the modest
*
* @author Justin Poliey <jdp34@njit.edu>
* @copyright 2009 Justin Poliey <jdp34@njit.edu>
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
* @package Credis
*/
/**
* A generalized Credis_Client interface for a cluster of Redis servers
*
* @deprecated
*/
class Credis_Cluster
{
/**
* Collection of Credis_Client objects attached to Redis servers
* @var Credis_Client[]
*/
protected $clients;
/**
* If a server is set as master, all write commands go to that one
* @var Credis_Client
*/
protected $masterClient;
/**
* Aliases of Credis_Client objects attached to Redis servers, used to route commands to specific servers
* @see Credis_Cluster::to
* @var array
*/
protected $aliases;
/**
* Hash ring of Redis server nodes
* @var array
*/
protected $ring;
/**
* Individual nodes of pointers to Redis servers on the hash ring
* @var array
*/
protected $nodes;
/**
* The commands that are not subject to hashing
* @var array
* @access protected
*/
protected $dont_hash;
/**
* Currently working cluster-wide database number.
* @var int
*/
protected $selectedDb = 0;
/**
* Creates an interface to a cluster of Redis servers
* Each server should be in the format:
* array(
* 'host' => hostname,
* 'port' => port,
* 'db' => db,
* 'password' => password,
* 'timeout' => timeout,
* 'alias' => alias,
* 'persistent' => persistence_identifier,
* 'master' => master
* 'write_only'=> true/false
* )
*
* @param array $servers The Redis servers in the cluster.
* @param int $replicas
* @param bool $standAlone
* @throws CredisException
*/
public function __construct($servers, $replicas = 128, $standAlone = false)
{
$this->clients = array();
$this->masterClient = null;
$this->aliases = array();
$this->ring = array();
$this->replicas = (int)$replicas;
$client = null;
foreach ($servers as $server)
{
if(is_array($server)){
$client = new Credis_Client(
$server['host'],
$server['port'],
isset($server['timeout']) ? $server['timeout'] : 2.5,
isset($server['persistent']) ? $server['persistent'] : '',
isset($server['db']) ? $server['db'] : 0,
isset($server['password']) ? $server['password'] : null
);
if (isset($server['alias'])) {
$this->aliases[$server['alias']] = $client;
}
if(isset($server['master']) && $server['master'] === true){
$this->masterClient = $client;
if(isset($server['write_only']) && $server['write_only'] === true){
continue;
}
}
} elseif($server instanceof Credis_Client){
$client = $server;
} else {
throw new CredisException('Server should either be an array or an instance of Credis_Client');
}
if($standAlone) {
$client->forceStandalone();
}
$this->clients[] = $client;
for ($replica = 0; $replica <= $this->replicas; $replica++) {
$md5num = hexdec(substr(md5($client->getHost().':'.$client->getPort().'-'.$replica),0,7));
$this->ring[$md5num] = count($this->clients)-1;
}
}
ksort($this->ring, SORT_NUMERIC);
$this->nodes = array_keys($this->ring);
$this->dont_hash = array_flip(array(
'RANDOMKEY', 'DBSIZE', 'PIPELINE', 'EXEC',
'SELECT', 'MOVE', 'FLUSHDB', 'FLUSHALL',
'SAVE', 'BGSAVE', 'LASTSAVE', 'SHUTDOWN',
'INFO', 'MONITOR', 'SLAVEOF'
));
if($this->masterClient !== null && count($this->clients()) == 0){
$this->clients[] = $this->masterClient;
for ($replica = 0; $replica <= $this->replicas; $replica++) {
$md5num = hexdec(substr(md5($this->masterClient->getHost().':'.$this->masterClient->getHost().'-'.$replica),0,7));
$this->ring[$md5num] = count($this->clients)-1;
}
$this->nodes = array_keys($this->ring);
}
}
/**
* @param Credis_Client $masterClient
* @param bool $writeOnly
* @return Credis_Cluster
*/
public function setMasterClient(Credis_Client $masterClient, $writeOnly=false)
{
if(!$masterClient instanceof Credis_Client){
throw new CredisException('Master client should be an instance of Credis_Client');
}
$this->masterClient = $masterClient;
if (!isset($this->aliases['master'])) {
$this->aliases['master'] = $masterClient;
}
if(!$writeOnly){
$this->clients[] = $this->masterClient;
for ($replica = 0; $replica <= $this->replicas; $replica++) {
$md5num = hexdec(substr(md5($this->masterClient->getHost().':'.$this->masterClient->getHost().'-'.$replica),0,7));
$this->ring[$md5num] = count($this->clients)-1;
}
$this->nodes = array_keys($this->ring);
}
return $this;
}
/**
* Get a client by index or alias.
*
* @param string|int $alias
* @throws CredisException
* @return Credis_Client
*/
public function client($alias)
{
if (is_int($alias) && isset($this->clients[$alias])) {
return $this->clients[$alias];
}
else if (isset($this->aliases[$alias])) {
return $this->aliases[$alias];
}
throw new CredisException("Client $alias does not exist.");
}
/**
* Get an array of all clients
*
* @return array|Credis_Client[]
*/
public function clients()
{
return $this->clients;
}
/**
* Execute a command on all clients
*
* @return array
*/
public function all()
{
$args = func_get_args();
$name = array_shift($args);
$results = array();
foreach($this->clients as $client) {
$results[] = call_user_func_array([$client, $name], $args);
}
return $results;
}
/**
* Get the client that the key would hash to.
*
* @param string $key
* @return \Credis_Client
*/
public function byHash($key)
{
return $this->clients[$this->hash($key)];
}
/**
* @param int $index
* @return void
*/
public function select($index)
{
$this->selectedDb = (int) $index;
}
/**
* Execute a Redis command on the cluster with automatic consistent hashing and read/write splitting
*
* @param string $name
* @param array $args
* @return mixed
*/
public function __call($name, $args)
{
if($this->masterClient !== null && !$this->isReadOnlyCommand($name)){
$client = $this->masterClient;
}elseif (count($this->clients()) == 1 || isset($this->dont_hash[strtoupper($name)]) || !isset($args[0])) {
$client = $this->clients[0];
}
else {
$hashKey = $args[0];
if (is_array($hashKey)) {
$hashKey = join('|', $hashKey);
}
$client = $this->byHash($hashKey);
}
// Ensure that current client is working on the same database as expected.
if ($client->getSelectedDb() != $this->selectedDb) {
$client->select($this->selectedDb);
}
return call_user_func_array([$client, $name], $args);
}
/**
* Get client index for a key by searching ring with binary search
*
* @param string $key The key to hash
* @return int The index of the client object associated with the hash of the key
*/
public function hash($key)
{
$needle = hexdec(substr(md5($key),0,7));
$server = $min = 0;
$max = count($this->nodes) - 1;
while ($max >= $min) {
$position = (int) (($min + $max) / 2);
$server = $this->nodes[$position];
if ($needle < $server) {
$max = $position - 1;
}
else if ($needle > $server) {
$min = $position + 1;
}
else {
break;
}
}
return $this->ring[$server];
}
public function isReadOnlyCommand($command)
{
static $readOnlyCommands = array(
'DBSIZE' => true,
'INFO' => true,
'MONITOR' => true,
'EXISTS' => true,
'TYPE' => true,
'KEYS' => true,
'SCAN' => true,
'RANDOMKEY' => true,
'TTL' => true,
'GET' => true,
'MGET' => true,
'SUBSTR' => true,
'STRLEN' => true,
'GETRANGE' => true,
'GETBIT' => true,
'LLEN' => true,
'LRANGE' => true,
'LINDEX' => true,
'SCARD' => true,
'SISMEMBER' => true,
'SINTER' => true,
'SUNION' => true,
'SDIFF' => true,
'SMEMBERS' => true,
'SSCAN' => true,
'SRANDMEMBER' => true,
'ZRANGE' => true,
'ZREVRANGE' => true,
'ZRANGEBYSCORE' => true,
'ZREVRANGEBYSCORE' => true,
'ZCARD' => true,
'ZSCORE' => true,
'ZCOUNT' => true,
'ZRANK' => true,
'ZREVRANK' => true,
'ZSCAN' => true,
'HGET' => true,
'HMGET' => true,
'HEXISTS' => true,
'HLEN' => true,
'HKEYS' => true,
'HVALS' => true,
'HGETALL' => true,
'HSCAN' => true,
'PING' => true,
'AUTH' => true,
'SELECT' => true,
'ECHO' => true,
'QUIT' => true,
'OBJECT' => true,
'BITCOUNT' => true,
'TIME' => true,
'SORT' => true,
);
return array_key_exists(strtoupper($command), $readOnlyCommands);
}
}

View File

@ -1,23 +0,0 @@
Copyright (c) 2009 Justin Poliey <jdp34@njit.edu>
Copyright (c) 2011 Colin Mollenhour <colin@mollenhour.com>
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.

View File

@ -1,68 +0,0 @@
<?php
/**
* Credis_Module
*
* Implements Redis Modules support. see http://redismodules.com
*
* @author Igor Veremchuk <igor.veremchuk@gmail.com>
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
* @package Credis_Module
*/
class Credis_Module
{
const MODULE_COUNTING_BLOOM_FILTER = 'CBF';
/** @var Credis_Client */
protected $client;
/** @var string */
protected $moduleName;
/**
* @param Credis_Client $client
* @param string $module
*/
public function __construct(Credis_Client $client, $module = null)
{
$client->forceStandalone(); // Redis Modules command not currently supported by phpredis
$this->client = $client;
if (isset($module)) {
$this->setModule($module);
}
}
/**
* Clean up client on destruct
*/
public function __destruct()
{
$this->client->close();
}
/**
* @param $moduleName
* @return $this
*/
public function setModule($moduleName)
{
$this->moduleName = (string) $moduleName;
return $this;
}
/**
* @param string $name
* @param string $args
* @return mixed
*/
public function __call($name, $args)
{
if ($this->moduleName === null) {
throw new \LogicException('Module must be set.');
}
return call_user_func(array($this->client, sprintf('%s.%s', $this->moduleName, $name)), $args);
}
}

View File

@ -1,214 +0,0 @@
![Build Status](https://github.com/colinmollenhour/credis/actions/workflows/ci.yml/badge.svg)
# Credis
Credis is a lightweight interface to the [Redis](http://redis.io/) key-value store which wraps the [phpredis](https://github.com/nicolasff/phpredis)
library when available for better performance. This project was forked from one of the many redisent forks.
## Getting Started
Credis_Client uses methods named the same as Redis commands, and translates return values to the appropriate
PHP equivalents.
```php
require 'Credis/Client.php';
$redis = new Credis_Client('localhost');
$redis->set('awesome', 'absolutely');
echo sprintf('Is Credis awesome? %s.\n', $redis->get('awesome'));
// When arrays are given as arguments they are flattened automatically
$redis->rpush('particles', array('proton','electron','neutron'));
$particles = $redis->lrange('particles', 0, -1);
```
Redis error responses will be wrapped in a CredisException class and thrown.
Credis_Client also supports transparent command renaming. Write code using the original command names and the
client will send the aliased commands to the server transparently. Specify the renamed commands using a prefix
for md5, a callable function, individual aliases, or an array map of aliases. See "Redis Security":http://redis.io/topics/security for more info.
## Supported connection string formats
```php
$redis = new Credis_Client(/* connection string */);
```
### Unix socket connection string
`unix:///path/to/redis.sock`
### TCP connection string
`tcp://host[:port][/persistence_identifier]`
### TLS connection string
`tls://host[:port][/persistence_identifier]`
or
`tlsv1.2://host[:port][/persistence_identifier]`
Before php 7.2, `tls://` only supports TLSv1.0, either `ssl://` or `tlsv1.2` can be used to force TLSv1.2 support.
Recent versions of redis do not support the protocols/cyphers that older versions of php default to, which may result in cryptic connection failures.
#### Enable transport level security (TLS)
Use TLS connection string `tls://127.0.0.1:6379` instead of TCP connection `tcp://127.0.0.1:6379` string in order to enable transport level security.
```php
require 'Credis/Client.php';
$redis = new Credis_Client('tls://127.0.0.1:6379');
$redis->set('awesome', 'absolutely');
echo sprintf('Is Credis awesome? %s.\n', $redis->get('awesome'));
// When arrays are given as arguments they are flattened automatically
$redis->rpush('particles', array('proton','electron','neutron'));
$particles = $redis->lrange('particles', 0, -1);
```
## Clustering your servers
Credis also includes a way for developers to fully utilize the scalability of Redis with multiple servers and [consistent hashing](http://en.wikipedia.org/wiki/Consistent_hashing).
Using the [Credis_Cluster](Cluster.php) class, you can use Credis the same way, except that keys will be hashed across multiple servers.
Here is how to set up a cluster:
### Basic clustering example
```php
<?php
require 'Credis/Client.php';
require 'Credis/Cluster.php';
$cluster = new Credis_Cluster(array(
array('host' => '127.0.0.1', 'port' => 6379, 'alias'=>'alpha'),
array('host' => '127.0.0.1', 'port' => 6380, 'alias'=>'beta')
));
$cluster->set('key','value');
echo "Alpha: ".$cluster->client('alpha')->get('key').PHP_EOL;
echo "Beta: ".$cluster->client('beta')->get('key').PHP_EOL;
```
### Explicit definition of replicas
The consistent hashing strategy stores keys on a so called "ring". The position of each key is relative to the position of its target node. The target node that has the closest position will be the selected node for that specific key.
To avoid an uneven distribution of keys (especially on small clusters), it is common to duplicate target nodes. Based on the number of replicas, each target node will exist *n times* on the "ring".
The following example explicitly sets the number of replicas to 5. Both Redis instances will have 5 copies. The default value is 128.
```php
<?php
require 'Credis/Client.php';
require 'Credis/Cluster.php';
$cluster = new Credis_Cluster(
array(
array('host' => '127.0.0.1', 'port' => 6379, 'alias'=>'alpha'),
array('host' => '127.0.0.1', 'port' => 6380, 'alias'=>'beta')
), 5
);
$cluster->set('key','value');
echo "Alpha: ".$cluster->client('alpha')->get('key').PHP_EOL;
echo "Beta: ".$cluster->client('beta')->get('key').PHP_EOL;
```
## Master/slave replication
The [Credis_Cluster](Cluster.php) class can also be used for [master/slave replication](http://redis.io/topics/replication).
Credis_Cluster will automatically perform *read/write splitting* and send the write requests exclusively to the master server.
Read requests will be handled by all servers unless you set the *write_only* flag to true in the connection string of the master server.
### Redis server settings for master/slave replication
Setting up master/slave replication is simple and only requires adding the following line to the config of the slave server:
```
slaveof 127.0.0.1 6379
```
### Basic master/slave example
```php
<?php
require 'Credis/Client.php';
require 'Credis/Cluster.php';
$cluster = new Credis_Cluster(array(
array('host' => '127.0.0.1', 'port' => 6379, 'alias'=>'master', 'master'=>true),
array('host' => '127.0.0.1', 'port' => 6380, 'alias'=>'slave')
));
$cluster->set('key','value');
echo $cluster->get('key').PHP_EOL;
echo $cluster->client('slave')->get('key').PHP_EOL;
$cluster->client('master')->set('key2','value');
echo $cluster->client('slave')->get('key2').PHP_EOL;
```
### No read on master
The following example illustrates how to disable reading on the master server. This will cause the master server only to be used for writing.
This should only happen when you have enough write calls to create a certain load on the master server. Otherwise this is an inefficient usage of server resources.
```php
<?php
require 'Credis/Client.php';
require 'Credis/Cluster.php';
$cluster = new Credis_Cluster(array(
array('host' => '127.0.0.1', 'port' => 6379, 'alias'=>'master', 'master'=>true, 'write_only'=>true),
array('host' => '127.0.0.1', 'port' => 6380, 'alias'=>'slave')
));
$cluster->set('key','value');
echo $cluster->get('key').PHP_EOL;
```
## Automatic failover with Sentinel
[Redis Sentinel](http://redis.io/topics/sentinel) is a system that can monitor Redis instances. You register master servers and Sentinel automatically detects its slaves.
When a master server dies, Sentinel will make sure one of the slaves is promoted to be the new master. This autofailover mechanism will also demote failed masters to avoid data inconsistency.
The [Credis_Sentinel](Sentinel.php) class interacts with the *Redis Sentinel* instance(s) and acts as a proxy. Sentinel will automatically create [Credis_Cluster](Cluster.php) objects and will set the master and slaves accordingly.
Sentinel uses the same protocol as Redis. In the example below we register the Sentinel server running on port *26379* and assign it to the [Credis_Sentinel](Sentinel.php) object.
We then ask Sentinel the hostname and port for the master server known as *mymaster*. By calling the *getCluster* method we immediately get a [Credis_Cluster](Cluster.php) object that allows us to perform basic Redis calls.
```php
<?php
require 'Credis/Client.php';
require 'Credis/Cluster.php';
require 'Credis/Sentinel.php';
$sentinel = new Credis_Sentinel(new Credis_Client('127.0.0.1',26379));
$masterAddress = $sentinel->getMasterAddressByName('mymaster');
$cluster = $sentinel->getCluster('mymaster');
echo 'Writing to master: '.$masterAddress[0].' on port '.$masterAddress[1].PHP_EOL;
$cluster->set('key','value');
echo $cluster->get('key').PHP_EOL;
```
### Additional parameters
Because [Credis_Sentinel](Sentinel.php) will create [Credis_Cluster](Cluster.php) objects using the *"getCluster"* or *"createCluster"* methods, additional parameters can be passed.
First of all there's the *"write_only"* flag. You can also define the selected database and the number of replicas. And finally there's a *"selectRandomSlave"* option.
The *"selectRandomSlave"* flag is used in setups for masters that have multiple slaves. The Credis_Sentinel will either select one random slave to be used when creating the Credis_Cluster object or to pass them all and use the built-in hashing.
The example below shows how to use these 3 options. It selects database 2, sets the number of replicas to 10, it doesn't select a random slave and doesn't allow reading on the master server.
```php
<?php
require 'Credis/Client.php';
require 'Credis/Cluster.php';
require 'Credis/Sentinel.php';
$sentinel = new Credis_Sentinel(new Credis_Client('127.0.0.1',26379));
$cluster = $sentinel->getCluster('mymaster',2,10,false,true);
$cluster->set('key','value');
echo $cluster->get('key').PHP_EOL;
```
## About
&copy; 2011 [Colin Mollenhour](http://colin.mollenhour.com)
&copy; 2009 [Justin Poliey](http://justinpoliey.com)

View File

@ -1,424 +0,0 @@
<?php
/**
* Credis_Sentinel
*
* Implements the Sentinel API as mentioned on http://redis.io/topics/sentinel.
* Sentinel is aware of master and slave nodes in a cluster and returns instances of Credis_Client accordingly.
*
* The complexity of read/write splitting can also be abstract by calling the createCluster() method which returns a
* Credis_Cluster object that contains both the master server and a random slave. Credis_Cluster takes care of the
* read/write splitting
*
* @author Thijs Feryn <thijs@feryn.eu>
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
* @package Credis_Sentinel
*/
class Credis_Sentinel
{
/**
* Contains a client that connects to a Sentinel node.
* Sentinel uses the same protocol as Redis which makes using Credis_Client convenient.
* @var Credis_Client
*/
protected $_client;
/**
* Contains an active instance of Credis_Cluster per master pool
* @var array
*/
protected $_cluster = array();
/**
* Contains an active instance of Credis_Client representing a master
* @var array
*/
protected $_master = array();
/**
* Contains an array Credis_Client objects representing all slaves per master pool
* @var array
*/
protected $_slaves = array();
/**
* Use the phpredis extension or the standalone implementation
* @var bool
* @deprecated
*/
protected $_standAlone = false;
/**
* Store the AUTH password used by Credis_Client instances
* @var string
*/
protected $_password = '';
/**
* Store the AUTH username used by Credis_Client instances (Redis v6+)
* @var string
*/
protected $_username = '';
/**
* @var null|float
*/
protected $_timeout;
/**
* @var string
*/
protected $_persistent;
/**
* @var int
*/
protected $_db;
/**
* @var string|null
*/
protected $_replicaCmd = null;
/**
* @var string|null
*/
protected $_redisVersion = null;
/**
* Connect with a Sentinel node. Sentinel will do the master and slave discovery
*
* @param Credis_Client $client
* @param string $password (deprecated - use setClientPassword)
* @throws CredisException
*/
public function __construct(Credis_Client $client, $password = NULL, $username = NULL)
{
$client->forceStandalone(); // SENTINEL command not currently supported by phpredis
$this->_client = $client;
$this->_password = $password;
$this->_username = $username;
$this->_timeout = NULL;
$this->_persistent = '';
$this->_db = 0;
}
/**
* Clean up client on destruct
*/
public function __destruct()
{
$this->_client->close();
}
/**
* @param float $timeout
* @return $this
*/
public function setClientTimeout($timeout)
{
$this->_timeout = $timeout;
return $this;
}
/**
* @param string $persistent
* @return $this
*/
public function setClientPersistent($persistent)
{
$this->_persistent = $persistent;
return $this;
}
/**
* @param int $db
* @return $this
*/
public function setClientDatabase($db)
{
$this->_db = $db;
return $this;
}
/**
* @param null|string $password
* @return $this
*/
public function setClientPassword($password)
{
$this->_password = $password;
return $this;
}
/**
* @param null|string $username
* @return $this
*/
public function setClientUsername($username)
{
$this->_username = $username;
return $this;
}
/**
* @param null|string $replicaCmd
* @return $this
*/
public function setReplicaCommand($replicaCmd)
{
$this->_replicaCmd = $replicaCmd;
return $this;
}
public function detectRedisVersion()
{
if ($this->_redisVersion !== null && $this->_replicaCmd !== null) {
return;
}
$serverInfo = $this->info('server');
$this->_redisVersion = $serverInfo['redis_version'];
// Redis v7+ renames the replica command to 'replicas' instead of 'slaves'
$this->_replicaCmd = version_compare($this->_redisVersion, '7.0.0', '>=') ? 'replicas' : 'slaves';
}
/**
* @return Credis_Sentinel
* @deprecated
*/
public function forceStandalone()
{
$this->_standAlone = true;
return $this;
}
/**
* Discover the master node automatically and return an instance of Credis_Client that connects to the master
*
* @param string $name
* @return Credis_Client
* @throws CredisException
*/
public function createMasterClient($name)
{
$master = $this->getMasterAddressByName($name);
if(!isset($master[0]) || !isset($master[1])){
throw new CredisException('Master not found');
}
return new Credis_Client($master[0], $master[1], $this->_timeout, $this->_persistent, $this->_db, $this->_password, $this->_username);
}
/**
* If a Credis_Client object exists for a master, return it. Otherwise create one and return it
* @param string $name
* @return Credis_Client
*/
public function getMasterClient($name)
{
if(!isset($this->_master[$name])){
$this->_master[$name] = $this->createMasterClient($name);
}
return $this->_master[$name];
}
/**
* Discover the slave nodes automatically and return an array of Credis_Client objects
*
* @param string $name
* @return Credis_Client[]
* @throws CredisException
*/
public function createSlaveClients($name)
{
$slaves = $this->slaves($name);
$workingSlaves = array();
foreach($slaves as $slave) {
if(!isset($slave[9])){
throw new CredisException('Can\' retrieve slave status');
}
if(!strstr($slave[9],'s_down') && !strstr($slave[9],'disconnected')) {
$workingSlaves[] = new Credis_Client($slave[3], $slave[5], $this->_timeout, $this->_persistent, $this->_db, $this->_password, $this->_username);
}
}
return $workingSlaves;
}
/**
* If an array of Credis_Client objects exist for a set of slaves, return them. Otherwise create and return them
* @param string $name
* @return Credis_Client[]
*/
public function getSlaveClients($name)
{
if(!isset($this->_slaves[$name])){
$this->_slaves[$name] = $this->createSlaveClients($name);
}
return $this->_slaves[$name];
}
/**
* Returns a Redis cluster object containing a random slave and the master
* When $selectRandomSlave is true, only one random slave is passed.
* When $selectRandomSlave is false, all clients are passed and hashing is applied in Credis_Cluster
* When $writeOnly is false, the master server will also be used for read commands.
* When $masterOnly is true, only the master server will also be used for both read and write commands. $writeOnly will be ignored and forced to set to false.
* @param string $name
* @param int $db
* @param int $replicas
* @param bool $selectRandomSlave
* @param bool $writeOnly
* @param bool $masterOnly
* @return Credis_Cluster
* @throws CredisException
* @deprecated
*/
public function createCluster($name, $db=0, $replicas=128, $selectRandomSlave=true, $writeOnly=false, $masterOnly=false)
{
$clients = array();
$workingClients = array();
$master = $this->master($name);
if(strstr($master[9],'s_down') || strstr($master[9],'disconnected')) {
throw new CredisException('The master is down');
}
if (!$masterOnly) {
$slaves = $this->slaves($name);
foreach($slaves as $slave){
if(!strstr($slave[9],'s_down') && !strstr($slave[9],'disconnected')) {
$workingClients[] = array('host'=>$slave[3],'port'=>$slave[5],'master'=>false,'db'=>$db,'password'=>$this->_password);
}
}
if(count($workingClients)>0){
if($selectRandomSlave){
if(!$writeOnly){
$workingClients[] = array('host'=>$master[3],'port'=>$master[5],'master'=>false,'db'=>$db,'password'=>$this->_password);
}
$clients[] = $workingClients[rand(0,count($workingClients)-1)];
} else {
$clients = $workingClients;
}
}
} else {
$writeOnly = false;
}
$clients[] = array('host'=>$master[3],'port'=>$master[5], 'db'=>$db ,'master'=>true,'write_only'=>$writeOnly,'password'=>$this->_password);
return new Credis_Cluster($clients,$replicas,$this->_standAlone);
}
/**
* If a Credis_Cluster object exists, return it. Otherwise create one and return it.
* @param string $name
* @param int $db
* @param int $replicas
* @param bool $selectRandomSlave
* @param bool $writeOnly
* @param bool $masterOnly
* @return Credis_Cluster
* @throws CredisException
* @deprecated
*/
public function getCluster($name, $db=0, $replicas=128, $selectRandomSlave=true, $writeOnly=false, $masterOnly=false)
{
if(!isset($this->_cluster[$name])){
$this->_cluster[$name] = $this->createCluster($name, $db, $replicas, $selectRandomSlave, $writeOnly, $masterOnly);
}
return $this->_cluster[$name];
}
/**
* Catch-all method
* @param string $name
* @param array $args
* @return mixed
*/
public function __call($name, $args)
{
array_unshift($args,$name);
return call_user_func(array($this->_client,'sentinel'),$args);
}
/**
* get information block for the sentinel instance
*
* @param string|NUll $section
*
* @return array
*/
public function info($section = null)
{
if ($section)
{
return $this->_client->info($section);
}
return $this->_client->info();
}
/**
* Return information about all registered master servers
* @return mixed
*/
public function masters()
{
return $this->_client->sentinel('masters');
}
/**
* Return all information for slaves that are associated with a single master
* @param string $name
* @return mixed
*/
public function slaves($name)
{
if ($this->_replicaCmd === null) {
$this->detectRedisVersion();
}
return $this->_client->sentinel($this->_replicaCmd,$name);
}
/**
* Get the information for a specific master
* @param string $name
* @return mixed
*/
public function master($name)
{
return $this->_client->sentinel('master',$name);
}
/**
* Get the hostname and port for a specific master
* @param string $name
* @return mixed
*/
public function getMasterAddressByName($name)
{
return $this->_client->sentinel('get-master-addr-by-name',$name);
}
/**
* Check if the Sentinel is still responding
* @return string|Credis_Client
*/
public function ping()
{
return $this->_client->ping();
}
/**
* Perform an auto-failover which will re-elect another master and make the current master a slave
* @param string $name
* @return mixed
*/
public function failover($name)
{
return $this->_client->sentinel('failover',$name);
}
/**
* @return string
*/
public function getHost()
{
return $this->_client->getHost();
}
/**
* @return int
*/
public function getPort()
{
return $this->_client->getPort();
}
}

View File

@ -1,27 +0,0 @@
{
"name": "colinmollenhour/credis",
"type": "library",
"description": "Credis is a lightweight interface to the Redis key-value store which wraps the phpredis library when available for better performance.",
"homepage": "https://github.com/colinmollenhour/credis",
"license": "MIT",
"authors": [
{
"name": "Colin Mollenhour",
"email": "colin@mollenhour.com"
}
],
"require": {
"php": ">=5.6.0"
},
"suggest": {
"ext-redis": "Improved performance for communicating with redis"
},
"autoload": {
"classmap": [
"Client.php",
"Cluster.php",
"Sentinel.php",
"Module.php"
]
}
}

View File

@ -1,26 +0,0 @@
#!/usr/bin/env bash
# This script runs unit tests locally in environment similar to Travis-CI
# It runs tests in different PHP versions with suitable PHPUnite version.
#
# You can see results of unit tests execution in console.
# Also all execution logs are saved to files phpunit_<date-time>.log
#
# Prerequisites for running unit tests on local machine:
# - docker
# - docker-compose
#
# You can find definition of all test environments in folder testenv/
# This folder is not automatically synced with .travis.yml
# If you add new PHP version to .travis.yml then you'll need to adjust files in testenv/
cd testenv
# build containers and run tests
docker-compose build && docker-compose up
# save logs to log file
docker-compose logs --no-color --timestamps | sort >"../phpunit_$(date '+%Y%m%d-%H%M%S').log"
# remove containers
docker-compose rm -f

View File

@ -1,32 +0,0 @@
version: '2'
services:
php-56:
build: env/php-5.6/
volumes:
- ../:/src/
php-70:
build: env/php-7.0/
volumes:
- ../:/src/
php-71:
build: env/php-7.1/
volumes:
- ../:/src/
php-72:
build: env/php-7.2/
volumes:
- ../:/src/
php-73:
build: env/php-7.3/
volumes:
- ../:/src/
php-74:
build: env/php-7.4/
volumes:
- ../:/src/

View File

@ -1,24 +0,0 @@
FROM php:5.6
ENV phpunit_verison 5.7
ENV redis_version 4.0.11
RUN apt-get update && \
apt-get install -y wget
RUN wget https://phar.phpunit.de/phpunit-${phpunit_verison}.phar && \
chmod +x phpunit-${phpunit_verison}.phar && \
mv phpunit-${phpunit_verison}.phar /usr/local/bin/phpunit
# install php extension
RUN yes '' | pecl install -f redis-4.3.0 && \
docker-php-ext-enable redis
# install redis server
RUN wget http://download.redis.io/releases/redis-${redis_version}.tar.gz && \
tar -xzf redis-${redis_version}.tar.gz && \
make -s -C redis-${redis_version} -j
CMD PATH=$PATH:/usr/local/bin/:/redis-${redis_version}/src/ && \
cp -rp /src /app && \
cd /app && \
phpunit

View File

@ -1,25 +0,0 @@
FROM php:7.0
ENV phpunit_verison 6.5
ENV redis_version 6.0.8
RUN apt-get update && \
apt-get install -y wget libssl-dev
RUN wget https://phar.phpunit.de/phpunit-${phpunit_verison}.phar && \
chmod +x phpunit-${phpunit_verison}.phar && \
mv phpunit-${phpunit_verison}.phar /usr/local/bin/phpunit
# install php extension
RUN yes '' | pecl install -f redis && \
docker-php-ext-enable redis
# install redis server
RUN wget http://download.redis.io/releases/redis-${redis_version}.tar.gz && \
tar -xzf redis-${redis_version}.tar.gz && \
export BUILD_TLS=yes && \
make -s -C redis-${redis_version} -j
CMD PATH=$PATH:/usr/local/bin/:/redis-${redis_version}/src/ && \
cp -rp /src /app && \
cd /app && \
phpunit

View File

@ -1,25 +0,0 @@
FROM php:7.1
ENV phpunit_verison 7.5
ENV redis_version 6.0.8
RUN apt-get update && \
apt-get install -y wget libssl-dev
RUN wget https://phar.phpunit.de/phpunit-${phpunit_verison}.phar && \
chmod +x phpunit-${phpunit_verison}.phar && \
mv phpunit-${phpunit_verison}.phar /usr/local/bin/phpunit
# install php extension
RUN yes '' | pecl install -f redis && \
docker-php-ext-enable redis
# install redis server
RUN wget http://download.redis.io/releases/redis-${redis_version}.tar.gz && \
tar -xzf redis-${redis_version}.tar.gz && \
export BUILD_TLS=yes && \
make -s -C redis-${redis_version} -j
CMD PATH=$PATH:/usr/local/bin/:/redis-${redis_version}/src/ && \
cp -rp /src /app && \
cd /app && \
phpunit

View File

@ -1,25 +0,0 @@
FROM php:7.2
ENV phpunit_verison 7.5
ENV redis_version 6.0.8
RUN apt-get update && \
apt-get install -y wget libssl-dev
RUN wget https://phar.phpunit.de/phpunit-${phpunit_verison}.phar && \
chmod +x phpunit-${phpunit_verison}.phar && \
mv phpunit-${phpunit_verison}.phar /usr/local/bin/phpunit
# install php extension
RUN yes '' | pecl install -f redis && \
docker-php-ext-enable redis
# install redis server
RUN wget http://download.redis.io/releases/redis-${redis_version}.tar.gz && \
tar -xzf redis-${redis_version}.tar.gz && \
export BUILD_TLS=yes && \
make -s -C redis-${redis_version} -j
CMD PATH=$PATH:/usr/local/bin/:/redis-${redis_version}/src/ && \
cp -rp /src /app && \
cd /app && \
phpunit

View File

@ -1,25 +0,0 @@
FROM php:7.3
ENV phpunit_verison 7.5
ENV redis_version 6.0.8
RUN apt-get update && \
apt-get install -y wget libssl-dev
RUN wget https://phar.phpunit.de/phpunit-${phpunit_verison}.phar && \
chmod +x phpunit-${phpunit_verison}.phar && \
mv phpunit-${phpunit_verison}.phar /usr/local/bin/phpunit
# install php extension
RUN yes '' | pecl install -f redis && \
docker-php-ext-enable redis
# install redis server
RUN wget http://download.redis.io/releases/redis-${redis_version}.tar.gz && \
tar -xzf redis-${redis_version}.tar.gz && \
export BUILD_TLS=yes && \
make -s -C redis-${redis_version} -j
CMD PATH=$PATH:/usr/local/bin/:/redis-${redis_version}/src/ && \
cp -rp /src /app && \
cd /app && \
phpunit

View File

@ -1,25 +0,0 @@
FROM php:7.4
ENV phpunit_verison 7.5
ENV redis_version 6.0.8
RUN apt-get update && \
apt-get install -y wget libssl-dev
RUN wget https://phar.phpunit.de/phpunit-${phpunit_verison}.phar && \
chmod +x phpunit-${phpunit_verison}.phar && \
mv phpunit-${phpunit_verison}.phar /usr/local/bin/phpunit
# install php extension
RUN yes '' | pecl install -f redis && \
docker-php-ext-enable redis
# install redis server
RUN wget http://download.redis.io/releases/redis-${redis_version}.tar.gz && \
tar -xzf redis-${redis_version}.tar.gz && \
export BUILD_TLS=yes && \
make -s -C redis-${redis_version} -j
CMD PATH=$PATH:/usr/local/bin/:/redis-${redis_version}/src/ && \
cp -rp /src /app && \
cd /app && \
phpunit

View File

@ -1,22 +0,0 @@
MIT License
Copyright (c) 2009-2020 Daniele Alessandri (original work)
Copyright (c) 2021-2023 Till Krüss (modified work)
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.

View File

@ -1,473 +0,0 @@
# Predis #
[![Software license][ico-license]](LICENSE)
[![Latest stable][ico-version-stable]][link-releases]
[![Latest development][ico-version-dev]][link-releases]
[![Monthly installs][ico-downloads-monthly]][link-downloads]
[![Build status][ico-build]][link-actions]
[![Coverage Status][ico-coverage]][link-coverage]
A flexible and feature-complete [Redis](http://redis.io) client for PHP 7.2 and newer.
More details about this project can be found on the [frequently asked questions](FAQ.md).
## Main features ##
- Support for Redis from __3.0__ to __7.0__.
- Support for clustering using client-side sharding and pluggable keyspace distributors.
- Support for [redis-cluster](http://redis.io/topics/cluster-tutorial) (Redis >= 3.0).
- Support for master-slave replication setups and [redis-sentinel](http://redis.io/topics/sentinel).
- Transparent key prefixing of keys using a customizable prefix strategy.
- Command pipelining on both single nodes and clusters (client-side sharding only).
- Abstraction for Redis transactions (Redis >= 2.0) and CAS operations (Redis >= 2.2).
- Abstraction for Lua scripting (Redis >= 2.6) and automatic switching between `EVALSHA` or `EVAL`.
- Abstraction for `SCAN`, `SSCAN`, `ZSCAN` and `HSCAN` (Redis >= 2.8) based on PHP iterators.
- Connections are established lazily by the client upon the first command and can be persisted.
- Connections can be established via TCP/IP (also TLS/SSL-encrypted) or UNIX domain sockets.
- Support for custom connection classes for providing different network or protocol backends.
- Flexible system for defining custom commands and override the default ones.
## How to _install_ and use Predis ##
This library can be found on [Packagist](http://packagist.org/packages/predis/predis) for an easier
management of projects dependencies using [Composer](http://packagist.org/about-composer).
Compressed archives of each release are [available on GitHub](https://github.com/predis/predis/releases).
```shell
composer require predis/predis
```
### Loading the library ###
Predis relies on the autoloading features of PHP to load its files when needed and complies with the
[PSR-4 standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader.md).
Autoloading is handled automatically when dependencies are managed through Composer, but it is also
possible to leverage its own autoloader in projects or scripts lacking any autoload facility:
```php
// Prepend a base path if Predis is not available in your "include_path".
require 'Predis/Autoloader.php';
Predis\Autoloader::register();
```
### Connecting to Redis ###
When creating a client instance without passing any connection parameter, Predis assumes `127.0.0.1`
and `6379` as default host and port. The default timeout for the `connect()` operation is 5 seconds:
```php
$client = new Predis\Client();
$client->set('foo', 'bar');
$value = $client->get('foo');
```
Connection parameters can be supplied either in the form of URI strings or named arrays. The latter
is the preferred way to supply parameters, but URI strings can be useful when parameters are read
from non-structured or partially-structured sources:
```php
// Parameters passed using a named array:
$client = new Predis\Client([
'scheme' => 'tcp',
'host' => '10.0.0.1',
'port' => 6379,
]);
// Same set of parameters, passed using an URI string:
$client = new Predis\Client('tcp://10.0.0.1:6379');
```
Password protected servers can be accessed by adding `password` to the parameters set. When ACLs are
enabled on Redis >= 6.0, both `username` and `password` are required for user authentication.
It is also possible to connect to local instances of Redis using UNIX domain sockets, in this case
the parameters must use the `unix` scheme and specify a path for the socket file:
```php
$client = new Predis\Client(['scheme' => 'unix', 'path' => '/path/to/redis.sock']);
$client = new Predis\Client('unix:/path/to/redis.sock');
```
The client can leverage TLS/SSL encryption to connect to secured remote Redis instances without the
need to configure an SSL proxy like stunnel. This can be useful when connecting to nodes running on
various cloud hosting providers. Encryption can be enabled with using the `tls` scheme and an array
of suitable [options](http://php.net/manual/context.ssl.php) passed via the `ssl` parameter:
```php
// Named array of connection parameters:
$client = new Predis\Client([
'scheme' => 'tls',
'ssl' => ['cafile' => 'private.pem', 'verify_peer' => true],
]);
// Same set of parameters, but using an URI string:
$client = new Predis\Client('tls://127.0.0.1?ssl[cafile]=private.pem&ssl[verify_peer]=1');
```
The connection schemes [`redis`](http://www.iana.org/assignments/uri-schemes/prov/redis) (alias of
`tcp`) and [`rediss`](http://www.iana.org/assignments/uri-schemes/prov/rediss) (alias of `tls`) are
also supported, with the difference that URI strings containing these schemes are parsed following
the rules described on their respective IANA provisional registration documents.
The actual list of supported connection parameters can vary depending on each connection backend so
it is recommended to refer to their specific documentation or implementation for details.
Predis can aggregate multiple connections when providing an array of connection parameters and the
appropriate option to instruct the client about how to aggregate them (clustering, replication or a
custom aggregation logic). Named arrays and URI strings can be mixed when providing configurations
for each node:
```php
$client = new Predis\Client([
'tcp://10.0.0.1?alias=first-node', ['host' => '10.0.0.2', 'alias' => 'second-node'],
], [
'cluster' => 'predis',
]);
```
See the [aggregate connections](#aggregate-connections) section of this document for more details.
Connections to Redis are lazy meaning that the client connects to a server only if and when needed.
While it is recommended to let the client do its own stuff under the hood, there may be times when
it is still desired to have control of when the connection is opened or closed: this can easily be
achieved by invoking `$client->connect()` and `$client->disconnect()`. Please note that the effect
of these methods on aggregate connections may differ depending on each specific implementation.
### Client configuration ###
Many aspects and behaviors of the client can be configured by passing specific client options to the
second argument of `Predis\Client::__construct()`:
```php
$client = new Predis\Client($parameters, ['prefix' => 'sample:']);
```
Options are managed using a mini DI-alike container and their values can be lazily initialized only
when needed. The client options supported by default in Predis are:
- `prefix`: prefix string applied to every key found in commands.
- `exceptions`: whether the client should throw or return responses upon Redis errors.
- `connections`: list of connection backends or a connection factory instance.
- `cluster`: specifies a cluster backend (`predis`, `redis` or callable).
- `replication`: specifies a replication backend (`predis`, `sentinel` or callable).
- `aggregate`: configures the client with a custom aggregate connection (callable).
- `parameters`: list of default connection parameters for aggregate connections.
- `commands`: specifies a command factory instance to use through the library.
Users can also provide custom options with values or callable objects (for lazy initialization) that
are stored in the options container for later use through the library.
### Aggregate connections ###
Aggregate connections are the foundation upon which Predis implements clustering and replication and
they are used to group multiple connections to single Redis nodes and hide the specific logic needed
to handle them properly depending on the context. Aggregate connections usually require an array of
connection parameters along with the appropriate client option when creating a new client instance.
#### Cluster ####
Predis can be configured to work in clustering mode with a traditional client-side sharding approach
to create a cluster of independent nodes and distribute the keyspace among them. This approach needs
some sort of external health monitoring of nodes and requires the keyspace to be rebalanced manually
when nodes are added or removed:
```php
$parameters = ['tcp://10.0.0.1', 'tcp://10.0.0.2', 'tcp://10.0.0.3'];
$options = ['cluster' => 'predis'];
$client = new Predis\Client($parameters);
```
Along with Redis 3.0, a new supervised and coordinated type of clustering was introduced in the form
of [redis-cluster](http://redis.io/topics/cluster-tutorial). This kind of approach uses a different
algorithm to distribute the keyspaces, with Redis nodes coordinating themselves by communicating via
a gossip protocol to handle health status, rebalancing, nodes discovery and request redirection. In
order to connect to a cluster managed by redis-cluster, the client requires a list of its nodes (not
necessarily complete since it will automatically discover new nodes if necessary) and the `cluster`
client options set to `redis`:
```php
$parameters = ['tcp://10.0.0.1', 'tcp://10.0.0.2', 'tcp://10.0.0.3'];
$options = ['cluster' => 'redis'];
$client = new Predis\Client($parameters, $options);
```
#### Replication ####
The client can be configured to operate in a single master / multiple slaves setup to provide better
service availability. When using replication, Predis recognizes read-only commands and sends them to
a random slave in order to provide some sort of load-balancing and switches to the master as soon as
it detects a command that performs any kind of operation that would end up modifying the keyspace or
the value of a key. Instead of raising a connection error when a slave fails, the client attempts to
fall back to a different slave among the ones provided in the configuration.
The basic configuration needed to use the client in replication mode requires one Redis server to be
identified as the master (this can be done via connection parameters by setting the `role` parameter
to `master`) and one or more slaves (in this case setting `role` to `slave` for slaves is optional):
```php
$parameters = ['tcp://10.0.0.1?role=master', 'tcp://10.0.0.2', 'tcp://10.0.0.3'];
$options = ['replication' => 'predis'];
$client = new Predis\Client($parameters, $options);
```
The above configuration has a static list of servers and relies entirely on the client's logic, but
it is possible to rely on [`redis-sentinel`](http://redis.io/topics/sentinel) for a more robust HA
environment with sentinel servers acting as a source of authority for clients for service discovery.
The minimum configuration required by the client to work with redis-sentinel is a list of connection
parameters pointing to a bunch of sentinel instances, the `replication` option set to `sentinel` and
the `service` option set to the name of the service:
```php
$sentinels = ['tcp://10.0.0.1', 'tcp://10.0.0.2', 'tcp://10.0.0.3'];
$options = ['replication' => 'sentinel', 'service' => 'mymaster'];
$client = new Predis\Client($sentinels, $options);
```
If the master and slave nodes are configured to require an authentication from clients, a password
must be provided via the global `parameters` client option. This option can also be used to specify
a different database index. The client options array would then look like this:
```php
$options = [
'replication' => 'sentinel',
'service' => 'mymaster',
'parameters' => [
'password' => $secretpassword,
'database' => 10,
],
];
```
While Predis is able to distinguish commands performing write and read-only operations, `EVAL` and
`EVALSHA` represent a corner case in which the client switches to the master node because it cannot
tell when a Lua script is safe to be executed on slaves. While this is indeed the default behavior,
when certain Lua scripts do not perform write operations it is possible to provide an hint to tell
the client to stick with slaves for their execution:
```php
$parameters = ['tcp://10.0.0.1?role=master', 'tcp://10.0.0.2', 'tcp://10.0.0.3'];
$options = ['replication' => function () {
// Set scripts that won't trigger a switch from a slave to the master node.
$strategy = new Predis\Replication\ReplicationStrategy();
$strategy->setScriptReadOnly($LUA_SCRIPT);
return new Predis\Connection\Replication\MasterSlaveReplication($strategy);
}];
$client = new Predis\Client($parameters, $options);
$client->eval($LUA_SCRIPT, 0); // Sticks to slave using `eval`...
$client->evalsha(sha1($LUA_SCRIPT), 0); // ... and `evalsha`, too.
```
The [`examples`](examples/) directory contains a few scripts that demonstrate how the client can be
configured and used to leverage replication in both basic and complex scenarios.
### Command pipelines ###
Pipelining can help with performances when many commands need to be sent to a server by reducing the
latency introduced by network round-trip timings. Pipelining also works with aggregate connections.
The client can execute the pipeline inside a callable block or return a pipeline instance with the
ability to chain commands thanks to its fluent interface:
```php
// Executes a pipeline inside the given callable block:
$responses = $client->pipeline(function ($pipe) {
for ($i = 0; $i < 1000; $i++) {
$pipe->set("key:$i", str_pad($i, 4, '0', 0));
$pipe->get("key:$i");
}
});
// Returns a pipeline that can be chained thanks to its fluent interface:
$responses = $client->pipeline()->set('foo', 'bar')->get('foo')->execute();
```
### Transactions ###
The client provides an abstraction for Redis transactions based on `MULTI` and `EXEC` with a similar
interface to command pipelines:
```php
// Executes a transaction inside the given callable block:
$responses = $client->transaction(function ($tx) {
$tx->set('foo', 'bar');
$tx->get('foo');
});
// Returns a transaction that can be chained thanks to its fluent interface:
$responses = $client->transaction()->set('foo', 'bar')->get('foo')->execute();
```
This abstraction can perform check-and-set operations thanks to `WATCH` and `UNWATCH` and provides
automatic retries of transactions aborted by Redis when `WATCH`ed keys are touched. For an example
of a transaction using CAS you can see [the following example](examples/transaction_using_cas.php).
### Adding new commands ###
While we try to update Predis to stay up to date with all the commands available in Redis, you might
prefer to stick with an old version of the library or provide a different way to filter arguments or
parse responses for specific commands. To achieve that, Predis provides the ability to implement new
command classes to define or override commands in the default command factory used by the client:
```php
// Define a new command by extending Predis\Command\Command:
class BrandNewRedisCommand extends Predis\Command\Command
{
public function getId()
{
return 'NEWCMD';
}
}
// Inject your command in the current command factory:
$client = new Predis\Client($parameters, [
'commands' => [
'newcmd' => 'BrandNewRedisCommand',
],
]);
$response = $client->newcmd();
```
There is also a method to send raw commands without filtering their arguments or parsing responses.
Users must provide the list of arguments for the command as an array, following the signatures as
defined by the [Redis documentation for commands](http://redis.io/commands):
```php
$response = $client->executeRaw(['SET', 'foo', 'bar']);
```
### Script commands ###
While it is possible to leverage [Lua scripting](http://redis.io/commands/eval) on Redis 2.6+ using
directly [`EVAL`](http://redis.io/commands/eval) and [`EVALSHA`](http://redis.io/commands/evalsha),
Predis offers script commands as an higher level abstraction built upon them to make things simple.
Script commands can be registered in the command factory used by the client and are accessible as if
they were plain Redis commands, but they define Lua scripts that get transmitted to the server for
remote execution. Internally they use [`EVALSHA`](http://redis.io/commands/evalsha) by default and
identify a script by its SHA1 hash to save bandwidth, but [`EVAL`](http://redis.io/commands/eval)
is used as a fall back when needed:
```php
// Define a new script command by extending Predis\Command\ScriptCommand:
class ListPushRandomValue extends Predis\Command\ScriptCommand
{
public function getKeysCount()
{
return 1;
}
public function getScript()
{
return <<<LUA
math.randomseed(ARGV[1])
local rnd = tostring(math.random())
redis.call('lpush', KEYS[1], rnd)
return rnd
LUA;
}
}
// Inject the script command in the current command factory:
$client = new Predis\Client($parameters, [
'commands' => [
'lpushrand' => 'ListPushRandomValue',
],
]);
$response = $client->lpushrand('random_values', $seed = mt_rand());
```
### Customizable connection backends ###
Developers can create their own connection classes to support whole new network backends, extend
existing classes or provide completely different implementations. Connection classes must implement
`Predis\Connection\NodeConnectionInterface` or extend `Predis\Connection\AbstractConnection`:
```php
class MyConnectionClass implements Predis\Connection\NodeConnectionInterface
{
// Implementation goes here...
}
// Use MyConnectionClass to handle connections for the `tcp` scheme:
$client = new Predis\Client('tcp://127.0.0.1', [
'connections' => ['tcp' => 'MyConnectionClass'],
]);
```
For a more in-depth insight on how to create new connection backends you can refer to the actual
implementation of the standard connection classes available in the `Predis\Connection` namespace.
## Development ##
### Reporting bugs and contributing code ###
Contributions to Predis are highly appreciated either in the form of pull requests for new features,
bug fixes, or just bug reports. We only ask you to adhere to issue and pull request templates.
### Test suite ###
__ATTENTION__: Do not ever run the test suite shipped with Predis against instances of Redis running
in production environments or containing data you are interested in!
Predis has a comprehensive test suite covering every aspect of the library and that can optionally
perform integration tests against a running instance of Redis (required >= 2.4.0 in order to verify
the correct behavior of the implementation of each command. Integration tests for unsupported Redis
commands are automatically skipped. If you do not have Redis up and running, integration tests can
be disabled. See [the tests README](tests/README.md) for more details about testing this library.
Predis uses GitHub Actions for continuous integration and the history for past and current builds can be
found [on its actions page](https://github.com/predis/predis/actions).
## Other ##
### Project related links ###
- [Source code](https://github.com/predis/predis)
- [Wiki](https://github.com/predis/predis/wiki)
- [Issue tracker](https://github.com/predis/predis/issues)
### Author ###
- [Till Krüss](https://till.im) ([Twitter](http://twitter.com/tillkruss))
- [Daniele Alessandri](mailto:suppakilla@gmail.com) ([twitter](http://twitter.com/JoL1hAHN))
### License ###
The code for Predis is distributed under the terms of the MIT license (see [LICENSE](LICENSE)).
[ico-license]: https://img.shields.io/github/license/predis/predis.svg?style=flat-square
[ico-version-stable]: https://img.shields.io/github/v/tag/predis/predis?label=stable&style=flat-square
[ico-version-dev]: https://img.shields.io/github/v/tag/predis/predis?include_prereleases&label=pre-release&style=flat-square
[ico-downloads-monthly]: https://img.shields.io/packagist/dm/predis/predis.svg?style=flat-square
[ico-build]: https://img.shields.io/github/actions/workflow/status/predis/predis/tests.yml?branch=main&style=flat-square
[ico-coverage]: https://img.shields.io/coverallsCoverage/github/predis/predis?style=flat-square
[link-releases]: https://github.com/predis/predis/releases
[link-actions]: https://github.com/predis/predis/actions
[link-downloads]: https://packagist.org/packages/predis/predis/stats
[link-coverage]: https://coveralls.io/github/predis/predis

View File

@ -1,12 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
require __DIR__.'/src/Autoloader.php';
Predis\Autoloader::register();

View File

@ -1,274 +0,0 @@
#!/usr/bin/env php
<?php
/*
* This file is part of the Predis package.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
// -------------------------------------------------------------------------- //
// This script can be used to automatically generate a file with the scheleton
// of a test case to test a Redis command by specifying the name of the class
// in the Predis\Command namespace (only classes in this namespace are valid).
// For example, to generate a test case for SET (which is represented by the
// Predis\Command\Redis\StringSet class):
//
// $ ./bin/generate-command-test --class=StringSet
//
// Here is a list of optional arguments:
//
// --realm: each command has its own realm (commands that operate on strings,
// lists, sets and such) but while this realm is usually inferred from the name
// of the specified class, sometimes it can be useful to override it with a
// custom one.
//
// --output: write the generated test case to the specified path instead of
// the default one.
//
// --overwrite: pre-existing test files are not overwritten unless this option
// is explicitly specified.
// -------------------------------------------------------------------------- //
use Predis\Command\CommandInterface;
use Predis\Command\PrefixableCommandInterface;
class CommandTestCaseGenerator
{
private $options;
public function __construct(array $options)
{
if (!isset($options['class'])) {
throw new RuntimeException("Missing 'class' option.");
}
if (!isset($options['realm'])) {
throw new RuntimeException("Missing 'realm' option.");
}
$this->options = $options;
}
public static function fromCommandLine()
{
$parameters = array(
'c:' => 'class:',
'r::' => 'realm::',
'o::' => 'output::',
'x::' => 'overwrite::'
);
$getops = getopt(implode(array_keys($parameters)), $parameters);
$options = array(
'overwrite' => false,
'tests' => __DIR__.'/../tests/Predis',
);
foreach ($getops as $option => $value) {
switch ($option) {
case 'c':
case 'class':
$options['class'] = $value;
break;
case 'r':
case 'realm':
$options['realm'] = $value;
break;
case 'o':
case 'output':
$options['output'] = $value;
break;
case 'x':
case 'overwrite':
$options['overwrite'] = true;
break;
}
}
if (!isset($options['class'])) {
throw new RuntimeException("Missing 'class' option.");
}
if (!isset($options['realm'])) {
throw new RuntimeException("Missing 'realm' option.");
}
$options['fqn'] = "Predis\\Command\\Redis\\{$options['class']}";
$options['path'] = "Command/Redis/{$options['class']}.php";
$source = __DIR__.'/../src/'.$options['path'];
if (!file_exists($source)) {
throw new RuntimeException("Cannot find class file for {$options['fqn']} in $source.");
}
if (!isset($options['output'])) {
$options['output'] = sprintf("%s/%s", $options['tests'], str_replace('.php', '_Test.php', $options['path']));
}
return new self($options);
}
protected function getTestRealm()
{
if (empty($this->options['realm'])) {
throw new RuntimeException('Invalid value for realm has been specified (empty).');
}
return $this->options['realm'];
}
public function generate()
{
$reflection = new ReflectionClass($class = $this->options['fqn']);
if (!$reflection->isInstantiable()) {
throw new RuntimeException("Class $class must be instantiable, abstract classes or interfaces are not allowed.");
}
if (!$reflection->implementsInterface('Predis\Command\CommandInterface')) {
throw new RuntimeException("Class $class must implement Predis\Command\CommandInterface.");
}
/*
* @var CommandInterface
*/
$instance = $reflection->newInstance();
$buffer = $this->getTestCaseBuffer($instance);
return $buffer;
}
public function save()
{
$options = $this->options;
if (file_exists($options['output']) && !$options['overwrite']) {
throw new RuntimeException("File {$options['output']} already exist. Specify the --overwrite option to overwrite the existing file.");
}
file_put_contents($options['output'], $this->generate());
}
protected function getTestCaseBuffer(CommandInterface $instance)
{
$id = $instance->getId();
$fqn = get_class($instance);
$fqnParts = explode('\\', $fqn);
$class = array_pop($fqnParts) . "Test";
$realm = $this->getTestRealm();
$buffer =<<<PHP
<?php
/*
* This file is part of the Predis package.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
/**
* @group commands
* @group realm-$realm
*/
class $class extends PredisCommandTestCase
{
/**
* {@inheritdoc}
*/
protected function getExpectedCommand(): string
{
return '$fqn';
}
/**
* {@inheritdoc}
*/
protected function getExpectedId(): string
{
return '$id';
}
/**
* @group disconnected
*/
public function testFilterArguments(): void
{
\$this->markTestIncomplete('This test has not been implemented yet.');
\$arguments = array(/* add arguments */);
\$expected = array(/* add arguments */);
\$command = \$this->getCommand();
\$command->setArguments(\$arguments);
\$this->assertSame(\$expected, \$command->getArguments());
}
/**
* @group disconnected
*/
public function testParseResponse(): void
{
\$this->markTestIncomplete('This test has not been implemented yet.');
\$raw = null;
\$expected = null;
\$command = \$this->getCommand();
\$this->assertSame(\$expected, \$command->parseResponse(\$raw));
}
PHP;
if ($instance instanceof PrefixableCommandInterface) {
$buffer .=<<<PHP
/**
* @group disconnected
*/
public function testPrefixKeys(): void
{
\$this->markTestIncomplete('This test has not been implemented yet.');
\$arguments = array(/* add arguments */);
\$expected = array(/* add arguments */);
\$command = \$this->getCommandWithArgumentsArray(\$arguments);
\$command->prefixKeys('prefix:');
\$this->assertSame(\$expected, \$command->getArguments());
}
/**
* @group disconnected
*/
public function testPrefixKeysIgnoredOnEmptyArguments(): void
{
\$command = \$this->getCommand();
\$command->prefixKeys('prefix:');
\$this->assertSame(array(), \$command->getArguments());
}
PHP;
}
return "$buffer}\n";
}
}
// ------------------------------------------------------------------------- //
require __DIR__.'/../autoload.php';
$generator = CommandTestCaseGenerator::fromCommandLine();
$generator->save();

View File

@ -1,48 +0,0 @@
{
"name": "predis/predis",
"type": "library",
"description": "A flexible and feature-complete Redis client for PHP.",
"keywords": ["nosql", "redis", "predis"],
"homepage": "http://github.com/predis/predis",
"license": "MIT",
"support": {
"issues": "https://github.com/predis/predis/issues"
},
"authors": [
{
"name": "Till Krüss",
"homepage": "https://till.im",
"role": "Maintainer"
}
],
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/tillkruss"
}
],
"require": {
"php": "^7.2 || ^8.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.3",
"phpstan/phpstan": "^1.9",
"phpunit/phpunit": "^8.0 || ~9.4.4"
},
"scripts": {
"phpstan": "phpstan analyse",
"style": "php-cs-fixer fix --diff --dry-run",
"style:fix": "php-cs-fixer fix"
},
"autoload": {
"psr-4": {
"Predis\\": "src/"
}
},
"config": {
"sort-packages": true,
"preferred-install": "dist"
},
"minimum-stability": "dev",
"prefer-stable": true
}

View File

@ -1,64 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis;
/**
* Implements a lightweight PSR-0 compliant autoloader for Predis.
*
* @author Eric Naeseth <eric@thumbtack.com>
* @author Daniele Alessandri <suppakilla@gmail.com>
* @codeCoverageIgnore
*/
class Autoloader
{
private $directory;
private $prefix;
private $prefixLength;
/**
* @param string $baseDirectory Base directory where the source files are located.
*/
public function __construct($baseDirectory = __DIR__)
{
$this->directory = $baseDirectory;
$this->prefix = __NAMESPACE__ . '\\';
$this->prefixLength = strlen($this->prefix);
}
/**
* Registers the autoloader class with the PHP SPL autoloader.
*
* @param bool $prepend Prepend the autoloader on the stack instead of appending it.
*/
public static function register($prepend = false)
{
spl_autoload_register([new self(), 'autoload'], true, $prepend);
}
/**
* Loads a class from a file using its fully qualified name.
*
* @param string $className Fully qualified name of a class.
*/
public function autoload($className)
{
if (0 === strpos($className, $this->prefix)) {
$parts = explode('\\', substr($className, $this->prefixLength));
$filepath = $this->directory . DIRECTORY_SEPARATOR . implode(DIRECTORY_SEPARATOR, $parts) . '.php';
if (is_file($filepath)) {
require $filepath;
}
}
}
}

View File

@ -1,566 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis;
use ArrayIterator;
use InvalidArgumentException;
use IteratorAggregate;
use Predis\Command\CommandInterface;
use Predis\Command\RawCommand;
use Predis\Command\Redis\Container\ContainerFactory;
use Predis\Command\Redis\Container\ContainerInterface;
use Predis\Command\ScriptCommand;
use Predis\Configuration\Options;
use Predis\Configuration\OptionsInterface;
use Predis\Connection\ConnectionInterface;
use Predis\Connection\Parameters;
use Predis\Connection\ParametersInterface;
use Predis\Monitor\Consumer as MonitorConsumer;
use Predis\Pipeline\Pipeline;
use Predis\PubSub\Consumer as PubSubConsumer;
use Predis\Response\ErrorInterface as ErrorResponseInterface;
use Predis\Response\ResponseInterface;
use Predis\Response\ServerException;
use Predis\Transaction\MultiExec as MultiExecTransaction;
use ReturnTypeWillChange;
use RuntimeException;
use Traversable;
/**
* Client class used for connecting and executing commands on Redis.
*
* This is the main high-level abstraction of Predis upon which various other
* abstractions are built. Internally it aggregates various other classes each
* one with its own responsibility and scope.
*
* @template-implements \IteratorAggregate<string, static>
*/
class Client implements ClientInterface, IteratorAggregate
{
public const VERSION = '2.1.2';
/** @var OptionsInterface */
private $options;
/** @var ConnectionInterface */
private $connection;
/** @var Command\FactoryInterface */
private $commands;
/**
* @param mixed $parameters Connection parameters for one or more servers.
* @param mixed $options Options to configure some behaviours of the client.
*/
public function __construct($parameters = null, $options = null)
{
$this->options = static::createOptions($options ?? new Options());
$this->connection = static::createConnection($this->options, $parameters ?? new Parameters());
$this->commands = $this->options->commands;
}
/**
* Creates a new set of client options for the client.
*
* @param array|OptionsInterface $options Set of client options
*
* @return OptionsInterface
* @throws InvalidArgumentException
*/
protected static function createOptions($options)
{
if (is_array($options)) {
return new Options($options);
} elseif ($options instanceof OptionsInterface) {
return $options;
} else {
throw new InvalidArgumentException('Invalid type for client options');
}
}
/**
* Creates single or aggregate connections from supplied arguments.
*
* This method accepts the following types to create a connection instance:
*
* - Array (dictionary: single connection, indexed: aggregate connections)
* - String (URI for a single connection)
* - Callable (connection initializer callback)
* - Instance of Predis\Connection\ParametersInterface (used as-is)
* - Instance of Predis\Connection\ConnectionInterface (returned as-is)
*
* When a callable is passed, it receives the original set of client options
* and must return an instance of Predis\Connection\ConnectionInterface.
*
* Connections are created using the connection factory (in case of single
* connections) or a specialized aggregate connection initializer (in case
* of cluster and replication) retrieved from the supplied client options.
*
* @param OptionsInterface $options Client options container
* @param mixed $parameters Connection parameters
*
* @return ConnectionInterface
* @throws InvalidArgumentException
*/
protected static function createConnection(OptionsInterface $options, $parameters)
{
if ($parameters instanceof ConnectionInterface) {
return $parameters;
}
if ($parameters instanceof ParametersInterface || is_string($parameters)) {
return $options->connections->create($parameters);
}
if (is_array($parameters)) {
if (!isset($parameters[0])) {
return $options->connections->create($parameters);
} elseif ($options->defined('cluster') && $initializer = $options->cluster) {
return $initializer($parameters, true);
} elseif ($options->defined('replication') && $initializer = $options->replication) {
return $initializer($parameters, true);
} elseif ($options->defined('aggregate') && $initializer = $options->aggregate) {
return $initializer($parameters, false);
} else {
throw new InvalidArgumentException(
'Array of connection parameters requires `cluster`, `replication` or `aggregate` client option'
);
}
}
if (is_callable($parameters)) {
$connection = call_user_func($parameters, $options);
if (!$connection instanceof ConnectionInterface) {
throw new InvalidArgumentException('Callable parameters must return a valid connection');
}
return $connection;
}
throw new InvalidArgumentException('Invalid type for connection parameters');
}
/**
* {@inheritdoc}
*/
public function getCommandFactory()
{
return $this->commands;
}
/**
* {@inheritdoc}
*/
public function getOptions()
{
return $this->options;
}
/**
* Creates a new client using a specific underlying connection.
*
* This method allows to create a new client instance by picking a specific
* connection out of an aggregate one, with the same options of the original
* client instance.
*
* The specified selector defines which logic to use to look for a suitable
* connection by the specified value. Supported selectors are:
*
* - `id`
* - `key`
* - `slot`
* - `command`
* - `alias`
* - `role`
*
* Internally the client relies on duck-typing and follows this convention:
*
* $selector string => getConnectionBy$selector($value) method
*
* This means that support for specific selectors may vary depending on the
* actual logic implemented by connection classes and there is no interface
* binding a connection class to implement any of these.
*
* @param string $selector Type of selector.
* @param mixed $value Value to be used by the selector.
*
* @return ClientInterface
*/
public function getClientBy($selector, $value)
{
$selector = strtolower($selector);
if (!in_array($selector, ['id', 'key', 'slot', 'role', 'alias', 'command'])) {
throw new InvalidArgumentException("Invalid selector type: `$selector`");
}
if (!method_exists($this->connection, $method = "getConnectionBy$selector")) {
$class = get_class($this->connection);
throw new InvalidArgumentException("Selecting connection by $selector is not supported by $class");
}
if (!$connection = $this->connection->$method($value)) {
throw new InvalidArgumentException("Cannot find a connection by $selector matching `$value`");
}
return new static($connection, $this->getOptions());
}
/**
* Opens the underlying connection and connects to the server.
*/
public function connect()
{
$this->connection->connect();
}
/**
* Closes the underlying connection and disconnects from the server.
*/
public function disconnect()
{
$this->connection->disconnect();
}
/**
* Closes the underlying connection and disconnects from the server.
*
* This is the same as `Client::disconnect()` as it does not actually send
* the `QUIT` command to Redis, but simply closes the connection.
*/
public function quit()
{
$this->disconnect();
}
/**
* Returns the current state of the underlying connection.
*
* @return bool
*/
public function isConnected()
{
return $this->connection->isConnected();
}
/**
* {@inheritdoc}
*/
public function getConnection()
{
return $this->connection;
}
/**
* Executes a command without filtering its arguments, parsing the response,
* applying any prefix to keys or throwing exceptions on Redis errors even
* regardless of client options.
*
* It is possible to identify Redis error responses from normal responses
* using the second optional argument which is populated by reference.
*
* @param array $arguments Command arguments as defined by the command signature.
* @param bool $error Set to TRUE when Redis returned an error response.
*
* @return mixed
*/
public function executeRaw(array $arguments, &$error = null)
{
$error = false;
$commandID = array_shift($arguments);
$response = $this->connection->executeCommand(
new RawCommand($commandID, $arguments)
);
if ($response instanceof ResponseInterface) {
if ($response instanceof ErrorResponseInterface) {
$error = true;
}
return (string) $response;
}
return $response;
}
/**
* {@inheritdoc}
*/
public function __call($commandID, $arguments)
{
return $this->executeCommand(
$this->createCommand($commandID, $arguments)
);
}
/**
* {@inheritdoc}
*/
public function createCommand($commandID, $arguments = [])
{
return $this->commands->create($commandID, $arguments);
}
/**
* @param $name
* @return ContainerInterface
*/
public function __get($name)
{
return ContainerFactory::create($this, $name);
}
/**
* @param $name
* @param $value
* @return mixed
*/
public function __set($name, $value)
{
throw new RuntimeException('Not allowed');
}
/**
* @param $name
* @return mixed
*/
public function __isset($name)
{
throw new RuntimeException('Not allowed');
}
/**
* {@inheritdoc}
*/
public function executeCommand(CommandInterface $command)
{
$response = $this->connection->executeCommand($command);
if ($response instanceof ResponseInterface) {
if ($response instanceof ErrorResponseInterface) {
$response = $this->onErrorResponse($command, $response);
}
return $response;
}
return $command->parseResponse($response);
}
/**
* Handles -ERR responses returned by Redis.
*
* @param CommandInterface $command Redis command that generated the error.
* @param ErrorResponseInterface $response Instance of the error response.
*
* @return mixed
* @throws ServerException
*/
protected function onErrorResponse(CommandInterface $command, ErrorResponseInterface $response)
{
if ($command instanceof ScriptCommand && $response->getErrorType() === 'NOSCRIPT') {
$response = $this->executeCommand($command->getEvalCommand());
if (!$response instanceof ResponseInterface) {
$response = $command->parseResponse($response);
}
return $response;
}
if ($this->options->exceptions) {
throw new ServerException($response->getMessage());
}
return $response;
}
/**
* Executes the specified initializer method on `$this` by adjusting the
* actual invocation depending on the arity (0, 1 or 2 arguments). This is
* simply an utility method to create Redis contexts instances since they
* follow a common initialization path.
*
* @param string $initializer Method name.
* @param array $argv Arguments for the method.
*
* @return mixed
*/
private function sharedContextFactory($initializer, $argv = null)
{
switch (count($argv)) {
case 0:
return $this->$initializer();
case 1:
return is_array($argv[0])
? $this->$initializer($argv[0])
: $this->$initializer(null, $argv[0]);
case 2:
[$arg0, $arg1] = $argv;
return $this->$initializer($arg0, $arg1);
default:
return $this->$initializer($this, $argv);
}
}
/**
* Creates a new pipeline context and returns it, or returns the results of
* a pipeline executed inside the optionally provided callable object.
*
* @param mixed ...$arguments Array of options, a callable for execution, or both.
*
* @return Pipeline|array
*/
public function pipeline(...$arguments)
{
return $this->sharedContextFactory('createPipeline', func_get_args());
}
/**
* Actual pipeline context initializer method.
*
* @param array $options Options for the context.
* @param mixed $callable Optional callable used to execute the context.
*
* @return Pipeline|array
*/
protected function createPipeline(array $options = null, $callable = null)
{
if (isset($options['atomic']) && $options['atomic']) {
$class = 'Predis\Pipeline\Atomic';
} elseif (isset($options['fire-and-forget']) && $options['fire-and-forget']) {
$class = 'Predis\Pipeline\FireAndForget';
} else {
$class = 'Predis\Pipeline\Pipeline';
}
/*
* @var ClientContextInterface
*/
$pipeline = new $class($this);
if (isset($callable)) {
return $pipeline->execute($callable);
}
return $pipeline;
}
/**
* Creates a new transaction context and returns it, or returns the results
* of a transaction executed inside the optionally provided callable object.
*
* @param mixed ...$arguments Array of options, a callable for execution, or both.
*
* @return MultiExecTransaction|array
*/
public function transaction(...$arguments)
{
return $this->sharedContextFactory('createTransaction', func_get_args());
}
/**
* Actual transaction context initializer method.
*
* @param array $options Options for the context.
* @param mixed $callable Optional callable used to execute the context.
*
* @return MultiExecTransaction|array
*/
protected function createTransaction(array $options = null, $callable = null)
{
$transaction = new MultiExecTransaction($this, $options);
if (isset($callable)) {
return $transaction->execute($callable);
}
return $transaction;
}
/**
* Creates a new publish/subscribe context and returns it, or starts its loop
* inside the optionally provided callable object.
*
* @param mixed ...$arguments Array of options, a callable for execution, or both.
*
* @return PubSubConsumer|null
*/
public function pubSubLoop(...$arguments)
{
return $this->sharedContextFactory('createPubSub', func_get_args());
}
/**
* Actual publish/subscribe context initializer method.
*
* @param array $options Options for the context.
* @param mixed $callable Optional callable used to execute the context.
*
* @return PubSubConsumer|null
*/
protected function createPubSub(array $options = null, $callable = null)
{
$pubsub = new PubSubConsumer($this, $options);
if (!isset($callable)) {
return $pubsub;
}
foreach ($pubsub as $message) {
if (call_user_func($callable, $pubsub, $message) === false) {
$pubsub->stop();
}
}
return null;
}
/**
* Creates a new monitor consumer and returns it.
*
* @return MonitorConsumer
*/
public function monitor()
{
return new MonitorConsumer($this);
}
/**
* @return Traversable<string, static>
*/
#[ReturnTypeWillChange]
public function getIterator()
{
$clients = [];
$connection = $this->getConnection();
if (!$connection instanceof Traversable) {
return new ArrayIterator([
(string) $connection => new static($connection, $this->getOptions()),
]);
}
foreach ($connection as $node) {
$clients[(string) $node] = new static($node, $this->getOptions());
}
return new ArrayIterator($clients);
}
}

View File

@ -1,238 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis;
use Predis\Command\Argument\Geospatial\ByInterface;
use Predis\Command\Argument\Geospatial\FromInterface;
use Predis\Command\Argument\Server\LimitOffsetCount;
use Predis\Command\Argument\Server\To;
use Predis\Command\CommandInterface;
use Predis\Command\Redis\Container\FunctionContainer;
/**
* Interface defining a client-side context such as a pipeline or transaction.
*
* @method $this copy(string $source, string $destination, int $db = -1, bool $replace = false)
* @method $this del(array|string $keys)
* @method $this dump($key)
* @method $this exists($key)
* @method $this expire($key, $seconds, string $expireOption = '')
* @method $this expireat($key, $timestamp, string $expireOption = '')
* @method $this expiretime(string $key)
* @method $this keys($pattern)
* @method $this move($key, $db)
* @method $this object($subcommand, $key)
* @method $this persist($key)
* @method $this pexpire($key, $milliseconds)
* @method $this pexpireat($key, $timestamp)
* @method $this pttl($key)
* @method $this randomkey()
* @method $this rename($key, $target)
* @method $this renamenx($key, $target)
* @method $this scan($cursor, array $options = null)
* @method $this sort($key, array $options = null)
* @method $this sort_ro(string $key, ?string $byPattern = null, ?LimitOffsetCount $limit = null, array $getPatterns = [], ?string $sorting = null, bool $alpha = false)
* @method $this ttl($key)
* @method $this type($key)
* @method $this append($key, $value)
* @method $this bitcount($key, $start = null, $end = null, string $index = 'byte')
* @method $this bitop($operation, $destkey, $key)
* @method $this bitfield($key, $subcommand, ...$subcommandArg)
* @method $this bitpos($key, $bit, $start = null, $end = null, string $index = 'byte')
* @method $this blmpop(int $timeout, array $keys, string $modifier = 'left', int $count = 1)
* @method $this bzpopmax(array $keys, int $timeout)
* @method $this bzpopmin(array $keys, int $timeout)
* @method $this bzmpop(int $timeout, array $keys, string $modifier = 'min', int $count = 1)
* @method $this decr($key)
* @method $this decrby($key, $decrement)
* @method $this failover(?To $to = null, bool $abort = false, int $timeout = -1)
* @method $this fcall(string $function, array $keys, ...$args)
* @method $this get($key)
* @method $this getbit($key, $offset)
* @method $this getex(string $key, $modifier = '', $value = false)
* @method $this getrange($key, $start, $end)
* @method $this getdel(string $key)
* @method $this getset($key, $value)
* @method $this incr($key)
* @method $this incrby($key, $increment)
* @method $this incrbyfloat($key, $increment)
* @method $this mget(array $keys)
* @method $this mset(array $dictionary)
* @method $this msetnx(array $dictionary)
* @method $this psetex($key, $milliseconds, $value)
* @method $this set($key, $value, $expireResolution = null, $expireTTL = null, $flag = null)
* @method $this setbit($key, $offset, $value)
* @method $this setex($key, $seconds, $value)
* @method $this setnx($key, $value)
* @method $this setrange($key, $offset, $value)
* @method $this strlen($key)
* @method $this hdel($key, array $fields)
* @method $this hexists($key, $field)
* @method $this hget($key, $field)
* @method $this hgetall($key)
* @method $this hincrby($key, $field, $increment)
* @method $this hincrbyfloat($key, $field, $increment)
* @method $this hkeys($key)
* @method $this hlen($key)
* @method $this hmget($key, array $fields)
* @method $this hmset($key, array $dictionary)
* @method $this hrandfield(string $key, int $count = 1, bool $withValues = false)
* @method $this hscan($key, $cursor, array $options = null)
* @method $this hset($key, $field, $value)
* @method $this hsetnx($key, $field, $value)
* @method $this hvals($key)
* @method $this hstrlen($key, $field)
* @method $this blmove(string $source, string $destination, string $where, string $to, int $timeout)
* @method $this blpop(array|string $keys, $timeout)
* @method $this brpop(array|string $keys, $timeout)
* @method $this brpoplpush($source, $destination, $timeout)
* @method $this lcs(string $key1, string $key2, bool $len = false, bool $idx = false, int $minMatchLen = 0, bool $withMatchLen = false)
* @method $this lindex($key, $index)
* @method $this linsert($key, $whence, $pivot, $value)
* @method $this llen($key)
* @method $this lmove(string $source, string $destination, string $where, string $to)
* @method $this lmpop(array $keys, string $modifier = 'left', int $count = 1)
* @method $this lpop($key)
* @method $this lpush($key, array $values)
* @method $this lpushx($key, array $values)
* @method $this lrange($key, $start, $stop)
* @method $this lrem($key, $count, $value)
* @method $this lset($key, $index, $value)
* @method $this ltrim($key, $start, $stop)
* @method $this rpop($key)
* @method $this rpoplpush($source, $destination)
* @method $this rpush($key, array $values)
* @method $this rpushx($key, array $values)
* @method $this sadd($key, array $members)
* @method $this scard($key)
* @method $this sdiff(array|string $keys)
* @method $this sdiffstore($destination, array|string $keys)
* @method $this sinter(array|string $keys)
* @method $this sintercard(array $keys, int $limit = 0)
* @method $this sinterstore($destination, array|string $keys)
* @method $this sismember($key, $member)
* @method $this smembers($key)
* @method $this smismember(string $key, string ...$members)
* @method $this smove($source, $destination, $member)
* @method $this spop($key, $count = null)
* @method $this srandmember($key, $count = null)
* @method $this srem($key, $member)
* @method $this sscan($key, $cursor, array $options = null)
* @method $this sunion(array|string $keys)
* @method $this sunionstore($destination, array|string $keys)
* @method $this zadd($key, array $membersAndScoresDictionary)
* @method $this zcard($key)
* @method $this zcount($key, $min, $max)
* @method $this zdiff(array $keys, bool $withScores = false)
* @method $this zdiffstore(string $destination, array $keys)
* @method $this zincrby($key, $increment, $member)
* @method $this zintercard(array $keys, int $limit = 0)
* @method $this zinterstore(string $destination, array $keys, int[] $weights = [], string $aggregate = 'sum')
* @method $this zinter(array $keys, int[] $weights = [], string $aggregate = 'sum', bool $withScores = false)
* @method $this zmpop(array $keys, string $modifier = 'min', int $count = 1)
* @method $this zmscore(string $key, string ...$member)
* @method $this zrandmember(string $key, int $count = 1, bool $withScores = false)
* @method $this zrange($key, $start, $stop, array $options = null)
* @method $this zrangebyscore($key, $min, $max, array $options = null)
* @method $this zrangestore(string $destination, string $source, int|string $min, string|int $max, string|bool $by = false, bool $reversed = false, bool $limit = false, int $offset = 0, int $count = 0)
* @method $this zrank($key, $member)
* @method $this zrem($key, $member)
* @method $this zremrangebyrank($key, $start, $stop)
* @method $this zremrangebyscore($key, $min, $max)
* @method $this zrevrange($key, $start, $stop, array $options = null)
* @method $this zrevrangebyscore($key, $max, $min, array $options = null)
* @method $this zrevrank($key, $member)
* @method $this zunion(array $keys, int[] $weights = [], string $aggregate = 'sum', bool $withScores = false)
* @method $this zunionstore(string $destination, array $keys, int[] $weights = [], string $aggregate = 'sum')
* @method $this zscore($key, $member)
* @method $this zscan($key, $cursor, array $options = null)
* @method $this zrangebylex($key, $start, $stop, array $options = null)
* @method $this zrevrangebylex($key, $start, $stop, array $options = null)
* @method $this zremrangebylex($key, $min, $max)
* @method $this zlexcount($key, $min, $max)
* @method $this pexpiretime(string $key)
* @method $this pfadd($key, array $elements)
* @method $this pfmerge($destinationKey, array|string $sourceKeys)
* @method $this pfcount(array|string $keys)
* @method $this pubsub($subcommand, $argument)
* @method $this publish($channel, $message)
* @method $this discard()
* @method $this exec()
* @method $this multi()
* @method $this unwatch()
* @method $this watch($key)
* @method $this eval($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null)
* @method $this eval_ro(string $script, array $keys, ...$argument)
* @method $this evalsha($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null)
* @method $this evalsha_ro(string $sha1, array $keys, ...$argument)
* @method $this script($subcommand, $argument = null)
* @method $this auth($password)
* @method $this echo($message)
* @method $this ping($message = null)
* @method $this select($database)
* @method $this bgrewriteaof()
* @method $this bgsave()
* @method $this client($subcommand, $argument = null)
* @method $this config($subcommand, $argument = null)
* @method $this dbsize()
* @method $this flushall()
* @method $this flushdb()
* @method $this info($section = null)
* @method $this lastsave()
* @method $this save()
* @method $this slaveof($host, $port)
* @method $this slowlog($subcommand, $argument = null)
* @method $this time()
* @method $this command()
* @method $this geoadd($key, $longitude, $latitude, $member)
* @method $this geohash($key, array $members)
* @method $this geopos($key, array $members)
* @method $this geodist($key, $member1, $member2, $unit = null)
* @method $this georadius($key, $longitude, $latitude, $radius, $unit, array $options = null)
* @method $this georadiusbymember($key, $member, $radius, $unit, array $options = null)
* @method $this geosearch(string $key, FromInterface $from, ByInterface $by, ?string $sorting = null, int $count = -1, bool $any = false, bool $withCoord = false, bool $withDist = false, bool $withHash = false)
* @method $this geosearchstore(string $destination, string $source, FromInterface $from, ByInterface $by, ?string $sorting = null, int $count = -1, bool $any = false, bool $storeDist = false)
*
* Container commands
* @property FunctionContainer $function
*/
interface ClientContextInterface
{
/**
* Sends the specified command instance to Redis.
*
* @param CommandInterface $command Command instance.
*
* @return mixed
*/
public function executeCommand(CommandInterface $command);
/**
* Sends the specified command with its arguments to Redis.
*
* @param string $method Command ID.
* @param array $arguments Arguments for the command.
*
* @return mixed
*/
public function __call($method, $arguments);
/**
* Starts the execution of the context.
*
* @param mixed $callable Optional callback for execution.
*
* @return array
*/
public function execute($callable = null);
}

View File

@ -1,20 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis;
/**
* Exception class that identifies client-side errors.
*/
class ClientException extends PredisException
{
}

View File

@ -1,289 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis;
use Predis\Command\Argument\Geospatial\ByInterface;
use Predis\Command\Argument\Geospatial\FromInterface;
use Predis\Command\Argument\Server\LimitOffsetCount;
use Predis\Command\Argument\Server\To;
use Predis\Command\CommandInterface;
use Predis\Command\FactoryInterface;
use Predis\Command\Redis\Container\FunctionContainer;
use Predis\Configuration\OptionsInterface;
use Predis\Connection\ConnectionInterface;
use Predis\Response\Status;
/**
* Interface defining a client able to execute commands against Redis.
*
* All the commands exposed by the client generally have the same signature as
* described by the Redis documentation, but some of them offer an additional
* and more friendly interface to ease programming which is described in the
* following list of methods:
*
* @method int copy(string $source, string $destination, int $db = -1, bool $replace = false)
* @method int del(string[]|string $keyOrKeys, string ...$keys = null)
* @method string|null dump(string $key)
* @method int exists(string $key)
* @method int expire(string $key, int $seconds, string $expireOption = '')
* @method int expireat(string $key, int $timestamp, string $expireOption = '')
* @method int expiretime(string $key)
* @method array keys(string $pattern)
* @method int move(string $key, int $db)
* @method mixed object($subcommand, string $key)
* @method int persist(string $key)
* @method int pexpire(string $key, int $milliseconds)
* @method int pexpireat(string $key, int $timestamp)
* @method int pttl(string $key)
* @method string|null randomkey()
* @method mixed rename(string $key, string $target)
* @method int renamenx(string $key, string $target)
* @method array scan($cursor, array $options = null)
* @method array sort(string $key, array $options = null)
* @method array sort_ro(string $key, ?string $byPattern = null, ?LimitOffsetCount $limit = null, array $getPatterns = [], ?string $sorting = null, bool $alpha = false)
* @method int ttl(string $key)
* @method mixed type(string $key)
* @method int append(string $key, $value)
* @method int bitcount(string $key, $start = null, $end = null, string $index = 'byte')
* @method int bitop($operation, $destkey, $key)
* @method array|null bitfield(string $key, $subcommand, ...$subcommandArg)
* @method int bitpos(string $key, $bit, $start = null, $end = null, string $index = 'byte')
* @method array blmpop(int $timeout, array $keys, string $modifier = 'left', int $count = 1)
* @method array bzpopmax(array $keys, int $timeout)
* @method array bzpopmin(array $keys, int $timeout)
* @method array bzmpop(int $timeout, array $keys, string $modifier = 'min', int $count = 1)
* @method int decr(string $key)
* @method int decrby(string $key, int $decrement)
* @method Status failover(?To $to = null, bool $abort = false, int $timeout = -1)
* @method mixed fcall(string $function, array $keys, ...$args)
* @method string|null get(string $key)
* @method int getbit(string $key, $offset)
* @method int|null getex(string $key, $modifier = '', $value = false)
* @method string getrange(string $key, $start, $end)
* @method string getdel(string $key)
* @method string|null getset(string $key, $value)
* @method int incr(string $key)
* @method int incrby(string $key, int $increment)
* @method string incrbyfloat(string $key, int|float $increment)
* @method array mget(string[]|string $keyOrKeys, string ...$keys = null)
* @method mixed mset(array $dictionary)
* @method int msetnx(array $dictionary)
* @method Status psetex(string $key, $milliseconds, $value)
* @method Status set(string $key, $value, $expireResolution = null, $expireTTL = null, $flag = null)
* @method int setbit(string $key, $offset, $value)
* @method Status setex(string $key, $seconds, $value)
* @method int setnx(string $key, $value)
* @method int setrange(string $key, $offset, $value)
* @method int strlen(string $key)
* @method int hdel(string $key, array $fields)
* @method int hexists(string $key, string $field)
* @method string|null hget(string $key, string $field)
* @method array hgetall(string $key)
* @method int hincrby(string $key, string $field, int $increment)
* @method string hincrbyfloat(string $key, string $field, int|float $increment)
* @method array hkeys(string $key)
* @method int hlen(string $key)
* @method array hmget(string $key, array $fields)
* @method mixed hmset(string $key, array $dictionary)
* @method array hrandfield(string $key, int $count = 1, bool $withValues = false)
* @method array hscan(string $key, $cursor, array $options = null)
* @method int hset(string $key, string $field, string $value)
* @method int hsetnx(string $key, string $field, string $value)
* @method array hvals(string $key)
* @method int hstrlen(string $key, string $field)
* @method string blmove(string $source, string $destination, string $where, string $to, int $timeout)
* @method array|null blpop(array|string $keys, int|float $timeout)
* @method array|null brpop(array|string $keys, int|float $timeout)
* @method string|null brpoplpush(string $source, string $destination, int|float $timeout)
* @method mixed lcs(string $key1, string $key2, bool $len = false, bool $idx = false, int $minMatchLen = 0, bool $withMatchLen = false)
* @method string|null lindex(string $key, int $index)
* @method int linsert(string $key, $whence, $pivot, $value)
* @method int llen(string $key)
* @method string lmove(string $source, string $destination, string $where, string $to)
* @method array|null lmpop(array $keys, string $modifier = 'left', int $count = 1)
* @method string|null lpop(string $key)
* @method int lpush(string $key, array $values)
* @method int lpushx(string $key, array $values)
* @method string[] lrange(string $key, int $start, int $stop)
* @method int lrem(string $key, int $count, string $value)
* @method mixed lset(string $key, int $index, string $value)
* @method mixed ltrim(string $key, int $start, int $stop)
* @method string|null rpop(string $key)
* @method string|null rpoplpush(string $source, string $destination)
* @method int rpush(string $key, array $values)
* @method int rpushx(string $key, array $values)
* @method int sadd(string $key, array $members)
* @method int scard(string $key)
* @method string[] sdiff(array|string $keys)
* @method int sdiffstore(string $destination, array|string $keys)
* @method string[] sinter(array|string $keys)
* @method int sintercard(array $keys, int $limit = 0)
* @method int sinterstore(string $destination, array|string $keys)
* @method int sismember(string $key, string $member)
* @method string[] smembers(string $key)
* @method array smismember(string $key, string ...$members)
* @method int smove(string $source, string $destination, string $member)
* @method string|array|null spop(string $key, int $count = null)
* @method string|null srandmember(string $key, int $count = null)
* @method int srem(string $key, array|string $member)
* @method array sscan(string $key, int $cursor, array $options = null)
* @method string[] sunion(array|string $keys)
* @method int sunionstore(string $destination, array|string $keys)
* @method int touch(string[]|string $keyOrKeys, string ...$keys = null)
* @method string xadd(string $key, array $dictionary, string $id = '*', array $options = null)
* @method int xdel(string $key, string ...$id)
* @method int xlen(string $key)
* @method array xrevrange(string $key, string $end, string $start, ?int $count = null)
* @method array xrange(string $key, string $start, string $end, ?int $count = null)
* @method string xtrim(string $key, array|string $strategy, string $threshold, array $options = null)
* @method int zadd(string $key, array $membersAndScoresDictionary)
* @method int zcard(string $key)
* @method string zcount(string $key, int|string $min, int|string $max)
* @method array zdiff(array $keys, bool $withScores = false)
* @method int zdiffstore(string $destination, array $keys)
* @method string zincrby(string $key, int $increment, string $member)
* @method int zintercard(array $keys, int $limit = 0)
* @method int zinterstore(string $destination, array $keys, int[] $weights = [], string $aggregate = 'sum')
* @method array zinter(array $keys, int[] $weights = [], string $aggregate = 'sum', bool $withScores = false)
* @method array zmpop(array $keys, string $modifier = 'min', int $count = 1)
* @method array zmscore(string $key, string ...$member)
* @method array zpopmin(string $key, int $count = 1)
* @method array zpopmax(string $key, int $count = 1)
* @method mixed zrandmember(string $key, int $count = 1, bool $withScores = false)
* @method array zrange(string $key, int|string $start, int|string $stop, array $options = null)
* @method array zrangebyscore(string $key, int|string $min, int|string $max, array $options = null)
* @method int zrangestore(string $destination, string $source, int|string $min, int|string $max, string|bool $by = false, bool $reversed = false, bool $limit = false, int $offset = 0, int $count = 0)
* @method int|null zrank(string $key, string $member)
* @method int zrem(string $key, string ...$member)
* @method int zremrangebyrank(string $key, int|string $start, int|string $stop)
* @method int zremrangebyscore(string $key, int|string $min, int|string $max)
* @method array zrevrange(string $key, int|string $start, int|string $stop, array $options = null)
* @method array zrevrangebyscore(string $key, int|string $max, int|string $min, array $options = null)
* @method int|null zrevrank(string $key, string $member)
* @method array zunion(array $keys, int[] $weights = [], string $aggregate = 'sum', bool $withScores = false)
* @method int zunionstore(string $destination, array $keys, int[] $weights = [], string $aggregate = 'sum')
* @method string|null zscore(string $key, string $member)
* @method array zscan(string $key, int $cursor, array $options = null)
* @method array zrangebylex(string $key, string $start, string $stop, array $options = null)
* @method array zrevrangebylex(string $key, string $start, string $stop, array $options = null)
* @method int zremrangebylex(string $key, string $min, string $max)
* @method int zlexcount(string $key, string $min, string $max)
* @method int pexpiretime(string $key)
* @method int pfadd(string $key, array $elements)
* @method mixed pfmerge(string $destinationKey, array|string $sourceKeys)
* @method int pfcount(string[]|string $keyOrKeys, string ...$keys = null)
* @method mixed pubsub($subcommand, $argument)
* @method int publish($channel, $message)
* @method mixed discard()
* @method array|null exec()
* @method mixed multi()
* @method mixed unwatch()
* @method mixed watch(string $key)
* @method mixed eval(string $script, int $numkeys, string ...$keyOrArg = null)
* @method mixed eval_ro(string $script, array $keys, ...$argument)
* @method mixed evalsha(string $script, int $numkeys, string ...$keyOrArg = null)
* @method mixed evalsha_ro(string $sha1, array $keys, ...$argument)
* @method mixed script($subcommand, $argument = null)
* @method mixed auth(string $password)
* @method string echo(string $message)
* @method mixed ping(string $message = null)
* @method mixed select(int $database)
* @method mixed bgrewriteaof()
* @method mixed bgsave()
* @method mixed client($subcommand, $argument = null)
* @method mixed config($subcommand, $argument = null)
* @method int dbsize()
* @method mixed flushall()
* @method mixed flushdb()
* @method array info($section = null)
* @method int lastsave()
* @method mixed save()
* @method mixed slaveof(string $host, int $port)
* @method mixed slowlog($subcommand, $argument = null)
* @method array time()
* @method array command()
* @method int geoadd(string $key, $longitude, $latitude, $member)
* @method array geohash(string $key, array $members)
* @method array geopos(string $key, array $members)
* @method string|null geodist(string $key, $member1, $member2, $unit = null)
* @method array georadius(string $key, $longitude, $latitude, $radius, $unit, array $options = null)
* @method array georadiusbymember(string $key, $member, $radius, $unit, array $options = null)
* @method array geosearch(string $key, FromInterface $from, ByInterface $by, ?string $sorting = null, int $count = -1, bool $any = false, bool $withCoord = false, bool $withDist = false, bool $withHash = false)
* @method int geosearchstore(string $destination, string $source, FromInterface $from, ByInterface $by, ?string $sorting = null, int $count = -1, bool $any = false, bool $storeDist = false)
*
* Container commands
* @property FunctionContainer $function
*/
interface ClientInterface
{
/**
* Returns the command factory used by the client.
*
* @return FactoryInterface
*/
public function getCommandFactory();
/**
* Returns the client options specified upon initialization.
*
* @return OptionsInterface
*/
public function getOptions();
/**
* Opens the underlying connection to the server.
*/
public function connect();
/**
* Closes the underlying connection from the server.
*/
public function disconnect();
/**
* Returns the underlying connection instance.
*
* @return ConnectionInterface
*/
public function getConnection();
/**
* Creates a new instance of the specified Redis command.
*
* @param string $method Command ID.
* @param array $arguments Arguments for the command.
*
* @return CommandInterface
*/
public function createCommand($method, $arguments = []);
/**
* Executes the specified Redis command.
*
* @param CommandInterface $command Command instance.
*
* @return mixed
*/
public function executeCommand(CommandInterface $command);
/**
* Creates a Redis command with the specified arguments and sends a request
* to the server.
*
* @param string $method Command ID.
* @param array $arguments Arguments for the command.
*
* @return mixed
*/
public function __call($method, $arguments);
}

View File

@ -1,476 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Cluster;
use InvalidArgumentException;
use Predis\Command\CommandInterface;
use Predis\Command\ScriptCommand;
/**
* Common class implementing the logic needed to support clustering strategies.
*/
abstract class ClusterStrategy implements StrategyInterface
{
protected $commands;
public function __construct()
{
$this->commands = $this->getDefaultCommands();
}
/**
* Returns the default map of supported commands with their handlers.
*
* @return array
*/
protected function getDefaultCommands()
{
$getKeyFromFirstArgument = [$this, 'getKeyFromFirstArgument'];
$getKeyFromAllArguments = [$this, 'getKeyFromAllArguments'];
return [
/* commands operating on the key space */
'EXISTS' => $getKeyFromAllArguments,
'DEL' => $getKeyFromAllArguments,
'TYPE' => $getKeyFromFirstArgument,
'EXPIRE' => $getKeyFromFirstArgument,
'EXPIREAT' => $getKeyFromFirstArgument,
'PERSIST' => $getKeyFromFirstArgument,
'PEXPIRE' => $getKeyFromFirstArgument,
'PEXPIREAT' => $getKeyFromFirstArgument,
'TTL' => $getKeyFromFirstArgument,
'PTTL' => $getKeyFromFirstArgument,
'SORT' => [$this, 'getKeyFromSortCommand'],
'DUMP' => $getKeyFromFirstArgument,
'RESTORE' => $getKeyFromFirstArgument,
/* commands operating on string values */
'APPEND' => $getKeyFromFirstArgument,
'DECR' => $getKeyFromFirstArgument,
'DECRBY' => $getKeyFromFirstArgument,
'GET' => $getKeyFromFirstArgument,
'GETBIT' => $getKeyFromFirstArgument,
'MGET' => $getKeyFromAllArguments,
'SET' => $getKeyFromFirstArgument,
'GETRANGE' => $getKeyFromFirstArgument,
'GETSET' => $getKeyFromFirstArgument,
'INCR' => $getKeyFromFirstArgument,
'INCRBY' => $getKeyFromFirstArgument,
'INCRBYFLOAT' => $getKeyFromFirstArgument,
'SETBIT' => $getKeyFromFirstArgument,
'SETEX' => $getKeyFromFirstArgument,
'MSET' => [$this, 'getKeyFromInterleavedArguments'],
'MSETNX' => [$this, 'getKeyFromInterleavedArguments'],
'SETNX' => $getKeyFromFirstArgument,
'SETRANGE' => $getKeyFromFirstArgument,
'STRLEN' => $getKeyFromFirstArgument,
'SUBSTR' => $getKeyFromFirstArgument,
'BITOP' => [$this, 'getKeyFromBitOp'],
'BITCOUNT' => $getKeyFromFirstArgument,
'BITFIELD' => $getKeyFromFirstArgument,
/* commands operating on lists */
'LINSERT' => $getKeyFromFirstArgument,
'LINDEX' => $getKeyFromFirstArgument,
'LLEN' => $getKeyFromFirstArgument,
'LPOP' => $getKeyFromFirstArgument,
'RPOP' => $getKeyFromFirstArgument,
'RPOPLPUSH' => $getKeyFromAllArguments,
'BLPOP' => [$this, 'getKeyFromBlockingListCommands'],
'BRPOP' => [$this, 'getKeyFromBlockingListCommands'],
'BRPOPLPUSH' => [$this, 'getKeyFromBlockingListCommands'],
'LPUSH' => $getKeyFromFirstArgument,
'LPUSHX' => $getKeyFromFirstArgument,
'RPUSH' => $getKeyFromFirstArgument,
'RPUSHX' => $getKeyFromFirstArgument,
'LRANGE' => $getKeyFromFirstArgument,
'LREM' => $getKeyFromFirstArgument,
'LSET' => $getKeyFromFirstArgument,
'LTRIM' => $getKeyFromFirstArgument,
/* commands operating on sets */
'SADD' => $getKeyFromFirstArgument,
'SCARD' => $getKeyFromFirstArgument,
'SDIFF' => $getKeyFromAllArguments,
'SDIFFSTORE' => $getKeyFromAllArguments,
'SINTER' => $getKeyFromAllArguments,
'SINTERSTORE' => $getKeyFromAllArguments,
'SUNION' => $getKeyFromAllArguments,
'SUNIONSTORE' => $getKeyFromAllArguments,
'SISMEMBER' => $getKeyFromFirstArgument,
'SMEMBERS' => $getKeyFromFirstArgument,
'SSCAN' => $getKeyFromFirstArgument,
'SPOP' => $getKeyFromFirstArgument,
'SRANDMEMBER' => $getKeyFromFirstArgument,
'SREM' => $getKeyFromFirstArgument,
/* commands operating on sorted sets */
'ZADD' => $getKeyFromFirstArgument,
'ZCARD' => $getKeyFromFirstArgument,
'ZCOUNT' => $getKeyFromFirstArgument,
'ZINCRBY' => $getKeyFromFirstArgument,
'ZINTERSTORE' => [$this, 'getKeyFromZsetAggregationCommands'],
'ZRANGE' => $getKeyFromFirstArgument,
'ZRANGEBYSCORE' => $getKeyFromFirstArgument,
'ZRANK' => $getKeyFromFirstArgument,
'ZREM' => $getKeyFromFirstArgument,
'ZREMRANGEBYRANK' => $getKeyFromFirstArgument,
'ZREMRANGEBYSCORE' => $getKeyFromFirstArgument,
'ZREVRANGE' => $getKeyFromFirstArgument,
'ZREVRANGEBYSCORE' => $getKeyFromFirstArgument,
'ZREVRANK' => $getKeyFromFirstArgument,
'ZSCORE' => $getKeyFromFirstArgument,
'ZUNIONSTORE' => [$this, 'getKeyFromZsetAggregationCommands'],
'ZSCAN' => $getKeyFromFirstArgument,
'ZLEXCOUNT' => $getKeyFromFirstArgument,
'ZRANGEBYLEX' => $getKeyFromFirstArgument,
'ZREMRANGEBYLEX' => $getKeyFromFirstArgument,
'ZREVRANGEBYLEX' => $getKeyFromFirstArgument,
/* commands operating on hashes */
'HDEL' => $getKeyFromFirstArgument,
'HEXISTS' => $getKeyFromFirstArgument,
'HGET' => $getKeyFromFirstArgument,
'HGETALL' => $getKeyFromFirstArgument,
'HMGET' => $getKeyFromFirstArgument,
'HMSET' => $getKeyFromFirstArgument,
'HINCRBY' => $getKeyFromFirstArgument,
'HINCRBYFLOAT' => $getKeyFromFirstArgument,
'HKEYS' => $getKeyFromFirstArgument,
'HLEN' => $getKeyFromFirstArgument,
'HSET' => $getKeyFromFirstArgument,
'HSETNX' => $getKeyFromFirstArgument,
'HVALS' => $getKeyFromFirstArgument,
'HSCAN' => $getKeyFromFirstArgument,
'HSTRLEN' => $getKeyFromFirstArgument,
/* commands operating on HyperLogLog */
'PFADD' => $getKeyFromFirstArgument,
'PFCOUNT' => $getKeyFromAllArguments,
'PFMERGE' => $getKeyFromAllArguments,
/* scripting */
'EVAL' => [$this, 'getKeyFromScriptingCommands'],
'EVALSHA' => [$this, 'getKeyFromScriptingCommands'],
/* commands performing geospatial operations */
'GEOADD' => $getKeyFromFirstArgument,
'GEOHASH' => $getKeyFromFirstArgument,
'GEOPOS' => $getKeyFromFirstArgument,
'GEODIST' => $getKeyFromFirstArgument,
'GEORADIUS' => [$this, 'getKeyFromGeoradiusCommands'],
'GEORADIUSBYMEMBER' => [$this, 'getKeyFromGeoradiusCommands'],
];
}
/**
* Returns the list of IDs for the supported commands.
*
* @return array
*/
public function getSupportedCommands()
{
return array_keys($this->commands);
}
/**
* Sets an handler for the specified command ID.
*
* The signature of the callback must have a single parameter of type
* Predis\Command\CommandInterface.
*
* When the callback argument is omitted or NULL, the previously associated
* handler for the specified command ID is removed.
*
* @param string $commandID Command ID.
* @param mixed $callback A valid callable object, or NULL to unset the handler.
*
* @throws InvalidArgumentException
*/
public function setCommandHandler($commandID, $callback = null)
{
$commandID = strtoupper($commandID);
if (!isset($callback)) {
unset($this->commands[$commandID]);
return;
}
if (!is_callable($callback)) {
throw new InvalidArgumentException(
'The argument must be a callable object or NULL.'
);
}
$this->commands[$commandID] = $callback;
}
/**
* Extracts the key from the first argument of a command instance.
*
* @param CommandInterface $command Command instance.
*
* @return string
*/
protected function getKeyFromFirstArgument(CommandInterface $command)
{
return $command->getArgument(0);
}
/**
* Extracts the key from a command with multiple keys only when all keys in
* the arguments array produce the same hash.
*
* @param CommandInterface $command Command instance.
*
* @return string|null
*/
protected function getKeyFromAllArguments(CommandInterface $command)
{
$arguments = $command->getArguments();
if (!$this->checkSameSlotForKeys($arguments)) {
return null;
}
return $arguments[0];
}
/**
* Extracts the key from a command with multiple keys only when all keys in
* the arguments array produce the same hash.
*
* @param CommandInterface $command Command instance.
*
* @return string|null
*/
protected function getKeyFromInterleavedArguments(CommandInterface $command)
{
$arguments = $command->getArguments();
$keys = [];
for ($i = 0; $i < count($arguments); $i += 2) {
$keys[] = $arguments[$i];
}
if (!$this->checkSameSlotForKeys($keys)) {
return null;
}
return $arguments[0];
}
/**
* Extracts the key from SORT command.
*
* @param CommandInterface $command Command instance.
*
* @return string|null
*/
protected function getKeyFromSortCommand(CommandInterface $command)
{
$arguments = $command->getArguments();
$firstKey = $arguments[0];
if (1 === $argc = count($arguments)) {
return $firstKey;
}
$keys = [$firstKey];
for ($i = 1; $i < $argc; ++$i) {
if (strtoupper($arguments[$i]) === 'STORE') {
$keys[] = $arguments[++$i];
}
}
if (!$this->checkSameSlotForKeys($keys)) {
return null;
}
return $firstKey;
}
/**
* Extracts the key from BLPOP and BRPOP commands.
*
* @param CommandInterface $command Command instance.
*
* @return string|null
*/
protected function getKeyFromBlockingListCommands(CommandInterface $command)
{
$arguments = $command->getArguments();
if (!$this->checkSameSlotForKeys(array_slice($arguments, 0, count($arguments) - 1))) {
return null;
}
return $arguments[0];
}
/**
* Extracts the key from BITOP command.
*
* @param CommandInterface $command Command instance.
*
* @return string|null
*/
protected function getKeyFromBitOp(CommandInterface $command)
{
$arguments = $command->getArguments();
if (!$this->checkSameSlotForKeys(array_slice($arguments, 1, count($arguments)))) {
return null;
}
return $arguments[1];
}
/**
* Extracts the key from GEORADIUS and GEORADIUSBYMEMBER commands.
*
* @param CommandInterface $command Command instance.
*
* @return string|null
*/
protected function getKeyFromGeoradiusCommands(CommandInterface $command)
{
$arguments = $command->getArguments();
$argc = count($arguments);
$startIndex = $command->getId() === 'GEORADIUS' ? 5 : 4;
if ($argc > $startIndex) {
$keys = [$arguments[0]];
for ($i = $startIndex; $i < $argc; ++$i) {
$argument = strtoupper($arguments[$i]);
if ($argument === 'STORE' || $argument === 'STOREDIST') {
$keys[] = $arguments[++$i];
}
}
if (!$this->checkSameSlotForKeys($keys)) {
return null;
}
}
return $arguments[0];
}
/**
* Extracts the key from ZINTERSTORE and ZUNIONSTORE commands.
*
* @param CommandInterface $command Command instance.
*
* @return string|null
*/
protected function getKeyFromZsetAggregationCommands(CommandInterface $command)
{
$arguments = $command->getArguments();
$keys = array_merge([$arguments[0]], array_slice($arguments, 2, $arguments[1]));
if (!$this->checkSameSlotForKeys($keys)) {
return null;
}
return $arguments[0];
}
/**
* Extracts the key from EVAL and EVALSHA commands.
*
* @param CommandInterface $command Command instance.
*
* @return string|null
*/
protected function getKeyFromScriptingCommands(CommandInterface $command)
{
$keys = $command instanceof ScriptCommand
? $command->getKeys()
: array_slice($args = $command->getArguments(), 2, $args[1]);
if (!$keys || !$this->checkSameSlotForKeys($keys)) {
return null;
}
return $keys[0];
}
/**
* {@inheritdoc}
*/
public function getSlot(CommandInterface $command)
{
$slot = $command->getSlot();
if (!isset($slot) && isset($this->commands[$cmdID = $command->getId()])) {
$key = call_user_func($this->commands[$cmdID], $command);
if (isset($key)) {
$slot = $this->getSlotByKey($key);
$command->setSlot($slot);
}
}
return $slot;
}
/**
* Checks if the specified array of keys will generate the same hash.
*
* @param array $keys Array of keys.
*
* @return bool
*/
protected function checkSameSlotForKeys(array $keys)
{
if (!$count = count($keys)) {
return false;
}
$currentSlot = $this->getSlotByKey($keys[0]);
for ($i = 1; $i < $count; ++$i) {
$nextSlot = $this->getSlotByKey($keys[$i]);
if ($currentSlot !== $nextSlot) {
return false;
}
$currentSlot = $nextSlot;
}
return true;
}
/**
* Returns only the hashable part of a key (delimited by "{...}"), or the
* whole key if a key tag is not found in the string.
*
* @param string $key A key.
*
* @return string
*/
protected function extractKeyTag($key)
{
if (false !== $start = strpos($key, '{')) {
if (false !== ($end = strpos($key, '}', $start)) && $end !== ++$start) {
$key = substr($key, $start, $end - $start);
}
}
return $key;
}
}

View File

@ -1,81 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Cluster\Distributor;
use Predis\Cluster\Hash\HashGeneratorInterface;
/**
* A distributor implements the logic to automatically distribute keys among
* several nodes for client-side sharding.
*/
interface DistributorInterface
{
/**
* Adds a node to the distributor with an optional weight.
*
* @param mixed $node Node object.
* @param int $weight Weight for the node.
*/
public function add($node, $weight = null);
/**
* Removes a node from the distributor.
*
* @param mixed $node Node object.
*/
public function remove($node);
/**
* Returns the corresponding slot of a node from the distributor using the
* computed hash of a key.
*
* @param mixed $hash
*
* @return mixed
*/
public function getSlot($hash);
/**
* Returns a node from the distributor using its assigned slot ID.
*
* @param mixed $slot
*
* @return mixed|null
*/
public function getBySlot($slot);
/**
* Returns a node from the distributor using the computed hash of a key.
*
* @param mixed $hash
*
* @return mixed
*/
public function getByHash($hash);
/**
* Returns a node from the distributor mapping to the specified value.
*
* @param string $value
*
* @return mixed
*/
public function get($value);
/**
* Returns the underlying hash generator instance.
*
* @return HashGeneratorInterface
*/
public function getHashGenerator();
}

View File

@ -1,22 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Cluster\Distributor;
use Exception;
/**
* Exception class that identifies empty rings.
*/
class EmptyRingException extends Exception
{
}

View File

@ -1,268 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Cluster\Distributor;
use Predis\Cluster\Hash\HashGeneratorInterface;
/**
* This class implements an hashring-based distributor that uses the same
* algorithm of memcache to distribute keys in a cluster using client-side
* sharding.
* @author Lorenzo Castelli <lcastelli@gmail.com>
*/
class HashRing implements DistributorInterface, HashGeneratorInterface
{
public const DEFAULT_REPLICAS = 128;
public const DEFAULT_WEIGHT = 100;
private $ring;
private $ringKeys;
private $ringKeysCount;
private $replicas;
private $nodeHashCallback;
private $nodes = [];
/**
* @param int $replicas Number of replicas in the ring.
* @param mixed $nodeHashCallback Callback returning a string used to calculate the hash of nodes.
*/
public function __construct($replicas = self::DEFAULT_REPLICAS, $nodeHashCallback = null)
{
$this->replicas = $replicas;
$this->nodeHashCallback = $nodeHashCallback;
}
/**
* Adds a node to the ring with an optional weight.
*
* @param mixed $node Node object.
* @param int $weight Weight for the node.
*/
public function add($node, $weight = null)
{
// In case of collisions in the hashes of the nodes, the node added
// last wins, thus the order in which nodes are added is significant.
$this->nodes[] = [
'object' => $node,
'weight' => (int) $weight ?: $this::DEFAULT_WEIGHT,
];
$this->reset();
}
/**
* {@inheritdoc}
*/
public function remove($node)
{
// A node is removed by resetting the ring so that it's recreated from
// scratch, in order to reassign possible hashes with collisions to the
// right node according to the order in which they were added in the
// first place.
for ($i = 0; $i < count($this->nodes); ++$i) {
if ($this->nodes[$i]['object'] === $node) {
array_splice($this->nodes, $i, 1);
$this->reset();
break;
}
}
}
/**
* Resets the distributor.
*/
private function reset()
{
unset(
$this->ring,
$this->ringKeys,
$this->ringKeysCount
);
}
/**
* Returns the initialization status of the distributor.
*
* @return bool
*/
private function isInitialized()
{
return isset($this->ringKeys);
}
/**
* Calculates the total weight of all the nodes in the distributor.
*
* @return int
*/
private function computeTotalWeight()
{
$totalWeight = 0;
foreach ($this->nodes as $node) {
$totalWeight += $node['weight'];
}
return $totalWeight;
}
/**
* Initializes the distributor.
*/
private function initialize()
{
if ($this->isInitialized()) {
return;
}
if (!$this->nodes) {
throw new EmptyRingException('Cannot initialize an empty hashring.');
}
$this->ring = [];
$totalWeight = $this->computeTotalWeight();
$nodesCount = count($this->nodes);
foreach ($this->nodes as $node) {
$weightRatio = $node['weight'] / $totalWeight;
$this->addNodeToRing($this->ring, $node, $nodesCount, $this->replicas, $weightRatio);
}
ksort($this->ring, SORT_NUMERIC);
$this->ringKeys = array_keys($this->ring);
$this->ringKeysCount = count($this->ringKeys);
}
/**
* Implements the logic needed to add a node to the hashring.
*
* @param array $ring Source hashring.
* @param mixed $node Node object to be added.
* @param int $totalNodes Total number of nodes.
* @param int $replicas Number of replicas in the ring.
* @param float $weightRatio Weight ratio for the node.
*/
protected function addNodeToRing(&$ring, $node, $totalNodes, $replicas, $weightRatio)
{
$nodeObject = $node['object'];
$nodeHash = $this->getNodeHash($nodeObject);
$replicas = (int) round($weightRatio * $totalNodes * $replicas);
for ($i = 0; $i < $replicas; ++$i) {
$key = $this->hash("$nodeHash:$i");
$ring[$key] = $nodeObject;
}
}
/**
* {@inheritdoc}
*/
protected function getNodeHash($nodeObject)
{
if (!isset($this->nodeHashCallback)) {
return (string) $nodeObject;
}
return call_user_func($this->nodeHashCallback, $nodeObject);
}
/**
* {@inheritdoc}
*/
public function hash($value)
{
return crc32($value);
}
/**
* {@inheritdoc}
*/
public function getByHash($hash)
{
return $this->ring[$this->getSlot($hash)];
}
/**
* {@inheritdoc}
*/
public function getBySlot($slot)
{
$this->initialize();
if (isset($this->ring[$slot])) {
return $this->ring[$slot];
}
}
/**
* {@inheritdoc}
*/
public function getSlot($hash)
{
$this->initialize();
$ringKeys = $this->ringKeys;
$upper = $this->ringKeysCount - 1;
$lower = 0;
while ($lower <= $upper) {
$index = ($lower + $upper) >> 1;
$item = $ringKeys[$index];
if ($item > $hash) {
$upper = $index - 1;
} elseif ($item < $hash) {
$lower = $index + 1;
} else {
return $item;
}
}
return $ringKeys[$this->wrapAroundStrategy($upper, $lower, $this->ringKeysCount)];
}
/**
* {@inheritdoc}
*/
public function get($value)
{
$hash = $this->hash($value);
return $this->getByHash($hash);
}
/**
* Implements a strategy to deal with wrap-around errors during binary searches.
*
* @param int $upper
* @param int $lower
* @param int $ringKeysCount
*
* @return int
*/
protected function wrapAroundStrategy($upper, $lower, $ringKeysCount)
{
// Binary search for the last item in ringkeys with a value less or
// equal to the key. If no such item exists, return the last item.
return $upper >= 0 ? $upper : $ringKeysCount - 1;
}
/**
* {@inheritdoc}
*/
public function getHashGenerator()
{
return $this;
}
}

View File

@ -1,70 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Cluster\Distributor;
/**
* This class implements an hashring-based distributor that uses the same
* algorithm of libketama to distribute keys in a cluster using client-side
* sharding.
* @author Lorenzo Castelli <lcastelli@gmail.com>
*/
class KetamaRing extends HashRing
{
public const DEFAULT_REPLICAS = 160;
/**
* @param mixed $nodeHashCallback Callback returning a string used to calculate the hash of nodes.
*/
public function __construct($nodeHashCallback = null)
{
parent::__construct($this::DEFAULT_REPLICAS, $nodeHashCallback);
}
/**
* {@inheritdoc}
*/
protected function addNodeToRing(&$ring, $node, $totalNodes, $replicas, $weightRatio)
{
$nodeObject = $node['object'];
$nodeHash = $this->getNodeHash($nodeObject);
$replicas = (int) floor($weightRatio * $totalNodes * ($replicas / 4));
for ($i = 0; $i < $replicas; ++$i) {
$unpackedDigest = unpack('V4', md5("$nodeHash-$i", true));
foreach ($unpackedDigest as $key) {
$ring[$key] = $nodeObject;
}
}
}
/**
* {@inheritdoc}
*/
public function hash($value)
{
$hash = unpack('V', md5($value, true));
return $hash[1];
}
/**
* {@inheritdoc}
*/
protected function wrapAroundStrategy($upper, $lower, $ringKeysCount)
{
// Binary search for the first item in ringkeys with a value greater
// or equal to the key. If no such item exists, return the first item.
return $lower < $ringKeysCount ? $lower : 0;
}
}

View File

@ -1,73 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Cluster\Hash;
/**
* Hash generator implementing the CRC-CCITT-16 algorithm used by redis-cluster.
*/
class CRC16 implements HashGeneratorInterface
{
private static $CCITT_16 = [
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0,
];
/**
* {@inheritdoc}
*/
public function hash($value)
{
// CRC-CCITT-16 algorithm
$crc = 0;
$CCITT_16 = self::$CCITT_16;
$value = (string) $value;
$strlen = strlen($value);
for ($i = 0; $i < $strlen; ++$i) {
$crc = (($crc << 8) ^ $CCITT_16[($crc >> 8) ^ ord($value[$i])]) & 0xFFFF;
}
return $crc;
}
}

View File

@ -1,29 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Cluster\Hash;
/**
* An hash generator implements the logic used to calculate the hash of a key to
* distribute operations among Redis nodes.
*/
interface HashGeneratorInterface
{
/**
* Generates an hash from a string to be used for distribution.
*
* @param string $value String value.
*
* @return int
*/
public function hash($value);
}

View File

@ -1,42 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Cluster\Hash;
use Predis\NotSupportedException;
/**
* Hash generator implementing the CRC-CCITT-16 algorithm used by redis-cluster.
*
* @deprecated 2.1.2
*/
class PhpiredisCRC16 implements HashGeneratorInterface
{
public function __construct()
{
if (!function_exists('phpiredis_utils_crc16')) {
// @codeCoverageIgnoreStart
throw new NotSupportedException(
'This hash generator requires a compatible version of ext-phpiredis'
);
// @codeCoverageIgnoreEnd
}
}
/**
* {@inheritdoc}
*/
public function hash($value)
{
return phpiredis_utils_crc16($value);
}
}

View File

@ -1,77 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Cluster;
use Predis\Cluster\Distributor\DistributorInterface;
use Predis\Cluster\Distributor\HashRing;
/**
* Default cluster strategy used by Predis to handle client-side sharding.
*/
class PredisStrategy extends ClusterStrategy
{
protected $distributor;
/**
* @param DistributorInterface $distributor Optional distributor instance.
*/
public function __construct(DistributorInterface $distributor = null)
{
parent::__construct();
$this->distributor = $distributor ?: new HashRing();
}
/**
* {@inheritdoc}
*/
public function getSlotByKey($key)
{
$key = $this->extractKeyTag($key);
$hash = $this->distributor->hash($key);
return $this->distributor->getSlot($hash);
}
/**
* {@inheritdoc}
*/
protected function checkSameSlotForKeys(array $keys)
{
if (!$count = count($keys)) {
return false;
}
$currentKey = $this->extractKeyTag($keys[0]);
for ($i = 1; $i < $count; ++$i) {
$nextKey = $this->extractKeyTag($keys[$i]);
if ($currentKey !== $nextKey) {
return false;
}
$currentKey = $nextKey;
}
return true;
}
/**
* {@inheritdoc}
*/
public function getDistributor()
{
return $this->distributor;
}
}

View File

@ -1,55 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Cluster;
use Predis\Cluster\Hash\CRC16;
use Predis\Cluster\Hash\HashGeneratorInterface;
use Predis\NotSupportedException;
/**
* Default class used by Predis to calculate hashes out of keys of
* commands supported by redis-cluster.
*/
class RedisStrategy extends ClusterStrategy
{
protected $hashGenerator;
/**
* @param HashGeneratorInterface $hashGenerator Hash generator instance.
*/
public function __construct(HashGeneratorInterface $hashGenerator = null)
{
parent::__construct();
$this->hashGenerator = $hashGenerator ?: new CRC16();
}
/**
* {@inheritdoc}
*/
public function getSlotByKey($key)
{
$key = $this->extractKeyTag($key);
return $this->hashGenerator->hash($key) & 0x3FFF;
}
/**
* {@inheritdoc}
*/
public function getDistributor()
{
$class = get_class($this);
throw new NotSupportedException("$class does not provide an external distributor");
}
}

View File

@ -1,208 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Cluster;
use ArrayAccess;
use ArrayIterator;
use Countable;
use IteratorAggregate;
use OutOfBoundsException;
use Predis\Connection\NodeConnectionInterface;
use ReturnTypeWillChange;
/**
* Slot map for redis-cluster.
*/
class SlotMap implements ArrayAccess, IteratorAggregate, Countable
{
private $slots = [];
/**
* Checks if the given slot is valid.
*
* @param int $slot Slot index.
*
* @return bool
*/
public static function isValid($slot)
{
return $slot >= 0x0000 && $slot <= 0x3FFF;
}
/**
* Checks if the given slot range is valid.
*
* @param int $first Initial slot of the range.
* @param int $last Last slot of the range.
*
* @return bool
*/
public static function isValidRange($first, $last)
{
return $first >= 0x0000 && $first <= 0x3FFF && $last >= 0x0000 && $last <= 0x3FFF && $first <= $last;
}
/**
* Resets the slot map.
*/
public function reset()
{
$this->slots = [];
}
/**
* Checks if the slot map is empty.
*
* @return bool
*/
public function isEmpty()
{
return empty($this->slots);
}
/**
* Returns the current slot map as a dictionary of $slot => $node.
*
* The order of the slots in the dictionary is not guaranteed.
*
* @return array
*/
public function toArray()
{
return $this->slots;
}
/**
* Returns the list of unique nodes in the slot map.
*
* @return array
*/
public function getNodes()
{
return array_keys(array_flip($this->slots));
}
/**
* Assigns the specified slot range to a node.
*
* @param int $first Initial slot of the range.
* @param int $last Last slot of the range.
* @param NodeConnectionInterface|string $connection ID or connection instance.
*
* @throws OutOfBoundsException
*/
public function setSlots($first, $last, $connection)
{
if (!static::isValidRange($first, $last)) {
throw new OutOfBoundsException("Invalid slot range $first-$last for `$connection`");
}
$this->slots += array_fill($first, $last - $first + 1, (string) $connection);
}
/**
* Returns the specified slot range.
*
* @param int $first Initial slot of the range.
* @param int $last Last slot of the range.
*
* @return array
*/
public function getSlots($first, $last)
{
if (!static::isValidRange($first, $last)) {
throw new OutOfBoundsException("Invalid slot range $first-$last");
}
return array_intersect_key($this->slots, array_fill($first, $last - $first + 1, null));
}
/**
* Checks if the specified slot is assigned.
*
* @param int $slot Slot index.
*
* @return bool
*/
#[ReturnTypeWillChange]
public function offsetExists($slot)
{
return isset($this->slots[$slot]);
}
/**
* Returns the node assigned to the specified slot.
*
* @param int $slot Slot index.
*
* @return string|null
*/
#[ReturnTypeWillChange]
public function offsetGet($slot)
{
return $this->slots[$slot] ?? null;
}
/**
* Assigns the specified slot to a node.
*
* @param int $slot Slot index.
* @param NodeConnectionInterface|string $connection ID or connection instance.
*
* @return void
*/
#[ReturnTypeWillChange]
public function offsetSet($slot, $connection)
{
if (!static::isValid($slot)) {
throw new OutOfBoundsException("Invalid slot $slot for `$connection`");
}
$this->slots[(int) $slot] = (string) $connection;
}
/**
* Returns the node assigned to the specified slot.
*
* @param int $slot Slot index.
*
* @return void
*/
#[ReturnTypeWillChange]
public function offsetUnset($slot)
{
unset($this->slots[$slot]);
}
/**
* Returns the current number of assigned slots.
*
* @return int
*/
#[ReturnTypeWillChange]
public function count()
{
return count($this->slots);
}
/**
* Returns an iterator over the slot map.
*
* @return ArrayIterator
*/
#[ReturnTypeWillChange]
public function getIterator()
{
return new ArrayIterator($this->slots);
}
}

View File

@ -1,52 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Cluster;
use Predis\Cluster\Distributor\DistributorInterface;
use Predis\Command\CommandInterface;
/**
* Interface for classes defining the strategy used to calculate an hash out of
* keys extracted from supported commands.
*
* This is mostly useful to support clustering via client-side sharding.
*/
interface StrategyInterface
{
/**
* Returns a slot for the given command used for clustering distribution or
* NULL when this is not possible.
*
* @param CommandInterface $command Command instance.
*
* @return int|null
*/
public function getSlot(CommandInterface $command);
/**
* Returns a slot for the given key used for clustering distribution or NULL
* when this is not possible.
*
* @param string $key Key string.
*
* @return int|null
*/
public function getSlotByKey($key);
/**
* Returns a distributor instance to be used by the cluster.
*
* @return DistributorInterface
*/
public function getDistributor();
}

View File

@ -1,196 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Collection\Iterator;
use Iterator;
use Predis\ClientInterface;
use Predis\NotSupportedException;
use ReturnTypeWillChange;
/**
* Provides the base implementation for a fully-rewindable PHP iterator that can
* incrementally iterate over cursor-based collections stored on Redis using the
* commands in the `SCAN` family.
*
* Given their incremental nature with multiple fetches, these kind of iterators
* offer limited guarantees about the returned elements because the collection
* can change several times during the iteration process.
*
* @see http://redis.io/commands/scan
*/
abstract class CursorBasedIterator implements Iterator
{
protected $client;
protected $match;
protected $count;
protected $valid;
protected $fetchmore;
protected $elements;
protected $cursor;
protected $position;
protected $current;
/**
* @param ClientInterface $client Client connected to Redis.
* @param string $match Pattern to match during the server-side iteration.
* @param int $count Hint used by Redis to compute the number of results per iteration.
*/
public function __construct(ClientInterface $client, $match = null, $count = null)
{
$this->client = $client;
$this->match = $match;
$this->count = $count;
$this->reset();
}
/**
* Ensures that the client supports the specified Redis command required to
* fetch elements from the server to perform the iteration.
*
* @param ClientInterface $client Client connected to Redis.
* @param string $commandID Command ID.
*
* @throws NotSupportedException
*/
protected function requiredCommand(ClientInterface $client, $commandID)
{
if (!$client->getCommandFactory()->supports($commandID)) {
throw new NotSupportedException("'$commandID' is not supported by the current command factory.");
}
}
/**
* Resets the inner state of the iterator.
*/
protected function reset()
{
$this->valid = true;
$this->fetchmore = true;
$this->elements = [];
$this->cursor = 0;
$this->position = -1;
$this->current = null;
}
/**
* Returns an array of options for the `SCAN` command.
*
* @return array
*/
protected function getScanOptions()
{
$options = [];
if (strlen(strval($this->match)) > 0) {
$options['MATCH'] = $this->match;
}
if ($this->count > 0) {
$options['COUNT'] = $this->count;
}
return $options;
}
/**
* Fetches a new set of elements from the remote collection, effectively
* advancing the iteration process.
*
* @return array
*/
abstract protected function executeCommand();
/**
* Populates the local buffer of elements fetched from the server during
* the iteration.
*/
protected function fetch()
{
[$cursor, $elements] = $this->executeCommand();
if (!$cursor) {
$this->fetchmore = false;
}
$this->cursor = $cursor;
$this->elements = $elements;
}
/**
* Extracts next values for key() and current().
*/
protected function extractNext()
{
++$this->position;
$this->current = array_shift($this->elements);
}
/**
* {@inheritdoc}
*/
#[ReturnTypeWillChange]
public function rewind()
{
$this->reset();
$this->next();
}
/**
* {@inheritdoc}
*/
#[ReturnTypeWillChange]
public function current()
{
return $this->current;
}
/**
* {@inheritdoc}
*/
#[ReturnTypeWillChange]
public function key()
{
return $this->position;
}
/**
* {@inheritdoc}
*/
#[ReturnTypeWillChange]
public function next()
{
tryFetch:
if (!$this->elements && $this->fetchmore) {
$this->fetch();
}
if ($this->elements) {
$this->extractNext();
} elseif ($this->cursor) {
goto tryFetch;
} else {
$this->valid = false;
}
}
/**
* {@inheritdoc}
*/
#[ReturnTypeWillChange]
public function valid()
{
return $this->valid;
}
}

View File

@ -1,57 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Collection\Iterator;
use Predis\ClientInterface;
/**
* Abstracts the iteration of fields and values of an hash by leveraging the
* HSCAN command (Redis >= 2.8) wrapped in a fully-rewindable PHP iterator.
*
* @see http://redis.io/commands/scan
*/
class HashKey extends CursorBasedIterator
{
protected $key;
/**
* {@inheritdoc}
*/
public function __construct(ClientInterface $client, $key, $match = null, $count = null)
{
$this->requiredCommand($client, 'HSCAN');
parent::__construct($client, $match, $count);
$this->key = $key;
}
/**
* {@inheritdoc}
*/
protected function executeCommand()
{
return $this->client->hscan($this->key, $this->cursor, $this->getScanOptions());
}
/**
* {@inheritdoc}
*/
protected function extractNext()
{
$this->position = key($this->elements);
$this->current = current($this->elements);
unset($this->elements[$this->position]);
}
}

View File

@ -1,42 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Collection\Iterator;
use Predis\ClientInterface;
/**
* Abstracts the iteration of the keyspace on a Redis instance by leveraging the
* SCAN command (Redis >= 2.8) wrapped in a fully-rewindable PHP iterator.
*
* @see http://redis.io/commands/scan
*/
class Keyspace extends CursorBasedIterator
{
/**
* {@inheritdoc}
*/
public function __construct(ClientInterface $client, $match = null, $count = null)
{
$this->requiredCommand($client, 'SCAN');
parent::__construct($client, $match, $count);
}
/**
* {@inheritdoc}
*/
protected function executeCommand()
{
return $this->client->scan($this->cursor, $this->getScanOptions());
}
}

View File

@ -1,183 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Collection\Iterator;
use InvalidArgumentException;
use Iterator;
use Predis\ClientInterface;
use Predis\NotSupportedException;
use ReturnTypeWillChange;
/**
* Abstracts the iteration of items stored in a list by leveraging the LRANGE
* command wrapped in a fully-rewindable PHP iterator.
*
* This iterator tries to emulate the behaviour of cursor-based iterators based
* on the SCAN-family of commands introduced in Redis <= 2.8, meaning that due
* to its incremental nature with multiple fetches it can only offer limited
* guarantees on the returned elements because the collection can change several
* times (trimmed, deleted, overwritten) during the iteration process.
*
* @see http://redis.io/commands/lrange
*/
class ListKey implements Iterator
{
protected $client;
protected $count;
protected $key;
protected $valid;
protected $fetchmore;
protected $elements;
protected $position;
protected $current;
/**
* @param ClientInterface $client Client connected to Redis.
* @param string $key Redis list key.
* @param int $count Number of items retrieved on each fetch operation.
*
* @throws InvalidArgumentException
*/
public function __construct(ClientInterface $client, $key, $count = 10)
{
$this->requiredCommand($client, 'LRANGE');
if ((false === $count = filter_var($count, FILTER_VALIDATE_INT)) || $count < 0) {
throw new InvalidArgumentException('The $count argument must be a positive integer.');
}
$this->client = $client;
$this->key = $key;
$this->count = $count;
$this->reset();
}
/**
* Ensures that the client instance supports the specified Redis command
* required to fetch elements from the server to perform the iteration.
*
* @param ClientInterface $client Client connected to Redis.
* @param string $commandID Command ID.
*
* @throws NotSupportedException
*/
protected function requiredCommand(ClientInterface $client, $commandID)
{
if (!$client->getCommandFactory()->supports($commandID)) {
throw new NotSupportedException("'$commandID' is not supported by the current command factory.");
}
}
/**
* Resets the inner state of the iterator.
*/
protected function reset()
{
$this->valid = true;
$this->fetchmore = true;
$this->elements = [];
$this->position = -1;
$this->current = null;
}
/**
* Fetches a new set of elements from the remote collection, effectively
* advancing the iteration process.
*
* @return array
*/
protected function executeCommand()
{
return $this->client->lrange($this->key, $this->position + 1, $this->position + $this->count);
}
/**
* Populates the local buffer of elements fetched from the server during the
* iteration.
*/
protected function fetch()
{
$elements = $this->executeCommand();
if (count($elements) < $this->count) {
$this->fetchmore = false;
}
$this->elements = $elements;
}
/**
* Extracts next values for key() and current().
*/
protected function extractNext()
{
++$this->position;
$this->current = array_shift($this->elements);
}
/**
* {@inheritdoc}
*/
#[ReturnTypeWillChange]
public function rewind()
{
$this->reset();
$this->next();
}
/**
* {@inheritdoc}
*/
#[ReturnTypeWillChange]
public function current()
{
return $this->current;
}
/**
* {@inheritdoc}
*/
#[ReturnTypeWillChange]
public function key()
{
return $this->position;
}
/**
* {@inheritdoc}
*/
#[ReturnTypeWillChange]
public function next()
{
if (!$this->elements && $this->fetchmore) {
$this->fetch();
}
if ($this->elements) {
$this->extractNext();
} else {
$this->valid = false;
}
}
/**
* {@inheritdoc}
*/
#[ReturnTypeWillChange]
public function valid()
{
return $this->valid;
}
}

View File

@ -1,46 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Collection\Iterator;
use Predis\ClientInterface;
/**
* Abstracts the iteration of members stored in a set by leveraging the SSCAN
* command (Redis >= 2.8) wrapped in a fully-rewindable PHP iterator.
*
* @see http://redis.io/commands/scan
*/
class SetKey extends CursorBasedIterator
{
protected $key;
/**
* {@inheritdoc}
*/
public function __construct(ClientInterface $client, $key, $match = null, $count = null)
{
$this->requiredCommand($client, 'SSCAN');
parent::__construct($client, $match, $count);
$this->key = $key;
}
/**
* {@inheritdoc}
*/
protected function executeCommand()
{
return $this->client->sscan($this->key, $this->cursor, $this->getScanOptions());
}
}

View File

@ -1,57 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Collection\Iterator;
use Predis\ClientInterface;
/**
* Abstracts the iteration of members stored in a sorted set by leveraging the
* ZSCAN command (Redis >= 2.8) wrapped in a fully-rewindable PHP iterator.
*
* @see http://redis.io/commands/scan
*/
class SortedSetKey extends CursorBasedIterator
{
protected $key;
/**
* {@inheritdoc}
*/
public function __construct(ClientInterface $client, $key, $match = null, $count = null)
{
$this->requiredCommand($client, 'ZSCAN');
parent::__construct($client, $match, $count);
$this->key = $key;
}
/**
* {@inheritdoc}
*/
protected function executeCommand()
{
return $this->client->zscan($this->key, $this->cursor, $this->getScanOptions());
}
/**
* {@inheritdoc}
*/
protected function extractNext()
{
$this->position = key($this->elements);
$this->current = current($this->elements);
unset($this->elements[$this->position]);
}
}

View File

@ -1,26 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Argument;
/**
* Allows to use object-oriented approach to handle complex conditional arguments.
*/
interface ArrayableArgument
{
/**
* Get the instance as an array.
*
* @return array
*/
public function toArray(): array;
}

View File

@ -1,46 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Argument\Geospatial;
use UnexpectedValueException;
abstract class AbstractBy implements ByInterface
{
/**
* @var string[]
*/
private static $unitEnum = ['m', 'km', 'ft', 'mi'];
/**
* @var string
*/
protected $unit;
/**
* {@inheritDoc}
*/
abstract public function toArray(): array;
/**
* @param string $unit
* @return void
*/
protected function setUnit(string $unit): void
{
if (!in_array($unit, self::$unitEnum, true)) {
throw new UnexpectedValueException('Wrong value given for unit');
}
$this->unit = $unit;
}
}

View File

@ -1,43 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Argument\Geospatial;
class ByBox extends AbstractBy
{
private const KEYWORD = 'BYBOX';
/**
* @var int
*/
private $width;
/**
* @var int
*/
private $height;
public function __construct(int $width, int $height, string $unit)
{
$this->width = $width;
$this->height = $height;
$this->setUnit($unit);
}
/**
* {@inheritDoc}
*/
public function toArray(): array
{
return [self::KEYWORD, $this->width, $this->height, $this->unit];
}
}

View File

@ -1,19 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Argument\Geospatial;
use Predis\Command\Argument\ArrayableArgument;
interface ByInterface extends ArrayableArgument
{
}

View File

@ -1,37 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Argument\Geospatial;
class ByRadius extends AbstractBy
{
private const KEYWORD = 'BYRADIUS';
/**
* @var int
*/
private $radius;
public function __construct(int $radius, string $unit)
{
$this->radius = $radius;
$this->setUnit($unit);
}
/**
* {@inheritDoc}
*/
public function toArray(): array
{
return [self::KEYWORD, $this->radius, $this->unit];
}
}

View File

@ -1,19 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Argument\Geospatial;
use Predis\Command\Argument\ArrayableArgument;
interface FromInterface extends ArrayableArgument
{
}

View File

@ -1,42 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Argument\Geospatial;
class FromLonLat implements FromInterface
{
private const KEYWORD = 'FROMLONLAT';
/**
* @var float
*/
private $longitude;
/**
* @var float
*/
private $latitude;
public function __construct(float $longitude, float $latitude)
{
$this->longitude = $longitude;
$this->latitude = $latitude;
}
/**
* {@inheritDoc}
*/
public function toArray(): array
{
return [self::KEYWORD, $this->longitude, $this->latitude];
}
}

View File

@ -1,36 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Argument\Geospatial;
class FromMember implements FromInterface
{
private const KEYWORD = 'FROMMEMBER';
/**
* @var string
*/
private $member;
public function __construct(string $member)
{
$this->member = $member;
}
/**
* {@inheritDoc}
*/
public function toArray(): array
{
return [self::KEYWORD, $this->member];
}
}

View File

@ -1,19 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Argument\Server;
use Predis\Command\Argument\ArrayableArgument;
interface LimitInterface extends ArrayableArgument
{
}

View File

@ -1,42 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Argument\Server;
class LimitOffsetCount implements LimitInterface
{
private const KEYWORD = 'LIMIT';
/**
* @var int
*/
private $offset;
/**
* @var int
*/
private $count;
public function __construct(int $offset, int $count)
{
$this->offset = $offset;
$this->count = $count;
}
/**
* {@inheritDoc}
*/
public function toArray(): array
{
return [self::KEYWORD, $this->offset, $this->count];
}
}

View File

@ -1,57 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Argument\Server;
use Predis\Command\Argument\ArrayableArgument;
class To implements ArrayableArgument
{
private const KEYWORD = 'TO';
private const FORCE_KEYWORD = 'FORCE';
/**
* @var string
*/
private $host;
/**
* @var int
*/
private $port;
/**
* @var bool
*/
private $isForce;
public function __construct(string $host, int $port, bool $isForce = false)
{
$this->host = $host;
$this->port = $port;
$this->isForce = $isForce;
}
/**
* {@inheritDoc}
*/
public function toArray(): array
{
$arguments = [self::KEYWORD, $this->host, $this->port];
if ($this->isForce) {
$arguments[] = self::FORCE_KEYWORD;
}
return $arguments;
}
}

View File

@ -1,126 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* Base class for Redis commands.
*/
abstract class Command implements CommandInterface
{
private $slot;
private $arguments = [];
/**
* {@inheritdoc}
*/
public function setArguments(array $arguments)
{
$this->arguments = $arguments;
unset($this->slot);
}
/**
* {@inheritdoc}
*/
public function setRawArguments(array $arguments)
{
$this->arguments = $arguments;
unset($this->slot);
}
/**
* {@inheritdoc}
*/
public function getArguments()
{
return $this->arguments;
}
/**
* {@inheritdoc}
*/
public function getArgument($index)
{
if (isset($this->arguments[$index])) {
return $this->arguments[$index];
}
}
/**
* {@inheritdoc}
*/
public function setSlot($slot)
{
$this->slot = $slot;
}
/**
* {@inheritdoc}
*/
public function getSlot()
{
return $this->slot ?? null;
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
return $data;
}
/**
* Normalizes the arguments array passed to a Redis command.
*
* @param array $arguments Arguments for a command.
*
* @return array
*/
public static function normalizeArguments(array $arguments)
{
if (count($arguments) === 1 && isset($arguments[0]) && is_array($arguments[0])) {
return $arguments[0];
}
return $arguments;
}
/**
* Normalizes the arguments array passed to a variadic Redis command.
*
* @param array $arguments Arguments for a command.
*
* @return array
*/
public static function normalizeVariadic(array $arguments)
{
if (count($arguments) === 2 && is_array($arguments[1])) {
return array_merge([$arguments[0]], $arguments[1]);
}
return $arguments;
}
/**
* Remove all false values from arguments.
*
* @return void
*/
public function filterArguments(): void
{
$this->arguments = array_filter($this->arguments, static function ($argument) {
return $argument !== false && $argument !== null;
});
}
}

View File

@ -1,80 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* Defines an abstraction representing a Redis command.
*/
interface CommandInterface
{
/**
* Returns the ID of the Redis command. By convention, command identifiers
* must always be uppercase.
*
* @return string
*/
public function getId();
/**
* Assign the specified slot to the command for clustering distribution.
*
* @param int $slot Slot ID.
*/
public function setSlot($slot);
/**
* Returns the assigned slot of the command for clustering distribution.
*
* @return int|null
*/
public function getSlot();
/**
* Sets the arguments for the command.
*
* @param array $arguments List of arguments.
*/
public function setArguments(array $arguments);
/**
* Sets the raw arguments for the command without processing them.
*
* @param array $arguments List of arguments.
*/
public function setRawArguments(array $arguments);
/**
* Gets the arguments of the command.
*
* @return array
*/
public function getArguments();
/**
* Gets the argument of the command at the specified index.
*
* @param int $index Index of the desired argument.
*
* @return mixed|null
*/
public function getArgument($index);
/**
* Parses a raw response and returns a PHP object.
*
* @param string|array|null $data Binary string containing the whole response.
*
* @return mixed
*/
public function parseResponse($data);
}

View File

@ -1,143 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
use InvalidArgumentException;
use Predis\ClientException;
use Predis\Command\Processor\ProcessorInterface;
/**
* Base command factory class.
*
* This class provides all of the common functionalities required for a command
* factory to create new instances of Redis commands objects. It also allows to
* define or undefine command handler classes for each command ID.
*/
abstract class Factory implements FactoryInterface
{
protected $commands = [];
protected $processor;
/**
* {@inheritdoc}
*/
public function supports(string ...$commandIDs): bool
{
foreach ($commandIDs as $commandID) {
if ($this->getCommandClass($commandID) === null) {
return false;
}
}
return true;
}
/**
* Returns the FQCN of a class that represents the specified command ID.
*
* @codeCoverageIgnore
*
* @param string $commandID Command ID
*
* @return string|null
*/
public function getCommandClass(string $commandID): ?string
{
return $this->commands[strtoupper($commandID)] ?? null;
}
/**
* {@inheritdoc}
*/
public function create(string $commandID, array $arguments = []): CommandInterface
{
if (!$commandClass = $this->getCommandClass($commandID)) {
$commandID = strtoupper($commandID);
throw new ClientException("Command `$commandID` is not a registered Redis command.");
}
$command = new $commandClass();
$command->setArguments($arguments);
if (isset($this->processor)) {
$this->processor->process($command);
}
return $command;
}
/**
* Defines a command in the factory.
*
* Only classes implementing Predis\Command\CommandInterface are allowed to
* handle a command. If the command specified by its ID is already handled
* by the factory, the underlying command class is replaced by the new one.
*
* @param string $commandID Command ID
* @param string $commandClass FQCN of a class implementing Predis\Command\CommandInterface
*
* @throws InvalidArgumentException
*/
public function define(string $commandID, string $commandClass): void
{
if (!is_a($commandClass, 'Predis\Command\CommandInterface', true)) {
throw new InvalidArgumentException(
"Class $commandClass must implement Predis\Command\CommandInterface"
);
}
$this->commands[strtoupper($commandID)] = $commandClass;
}
/**
* Undefines a command in the factory.
*
* When the factory already has a class handler associated to the specified
* command ID it is removed from the map of known commands. Nothing happens
* when the command is not handled by the factory.
*
* @param string $commandID Command ID
*/
public function undefine(string $commandID): void
{
unset($this->commands[strtoupper($commandID)]);
}
/**
* Sets a command processor for processing command arguments.
*
* Command processors are used to process and transform arguments of Redis
* commands before their newly created instances are returned to the caller
* of "create()".
*
* A NULL value can be used to effectively unset any processor if previously
* set for the command factory.
*
* @param ProcessorInterface|null $processor Command processor or NULL value.
*/
public function setProcessor(?ProcessorInterface $processor): void
{
$this->processor = $processor;
}
/**
* Returns the current command processor.
*
* @return ProcessorInterface|null
*/
public function getProcessor(): ?ProcessorInterface
{
return $this->processor;
}
}

View File

@ -1,42 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* Command factory interface.
*
* A command factory is used through the library to create instances of commands
* classes implementing Predis\Command\CommandInterface mapped to Redis commands
* by their command ID string (SET, GET, etc...).
*/
interface FactoryInterface
{
/**
* Checks if the command factory supports the specified list of commands.
*
* @param string ...$commandIDs List of command IDs
*
* @return bool
*/
public function supports(string ...$commandIDs): bool;
/**
* Creates a new command instance.
*
* @param string $commandID Command ID
* @param array $arguments Arguments for the command
*
* @return CommandInterface
*/
public function create(string $commandID, array $arguments = []): CommandInterface;
}

View File

@ -1,26 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* Defines a command whose keys can be prefixed.
*/
interface PrefixableCommandInterface extends CommandInterface
{
/**
* Prefixes all the keys found in the arguments of the command.
*
* @param string $prefix String used to prefix the keys.
*/
public function prefixKeys($prefix);
}

View File

@ -1,467 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Processor;
use InvalidArgumentException;
use Predis\Command\CommandInterface;
use Predis\Command\PrefixableCommandInterface;
/**
* Command processor capable of prefixing keys stored in the arguments of Redis
* commands supported.
*/
class KeyPrefixProcessor implements ProcessorInterface
{
private $prefix;
private $commands;
/**
* @param string $prefix Prefix for the keys.
*/
public function __construct($prefix)
{
$this->prefix = $prefix;
$prefixFirst = static::class . '::first';
$prefixAll = static::class . '::all';
$prefixInterleaved = static::class . '::interleaved';
$prefixSkipFirst = static::class . '::skipFirst';
$prefixSkipLast = static::class . '::skipLast';
$prefixSort = static::class . '::sort';
$prefixEvalKeys = static::class . '::evalKeys';
$prefixZsetStore = static::class . '::zsetStore';
$prefixMigrate = static::class . '::migrate';
$prefixGeoradius = static::class . '::georadius';
$this->commands = [
/* ---------------- Redis 1.2 ---------------- */
'EXISTS' => $prefixAll,
'DEL' => $prefixAll,
'TYPE' => $prefixFirst,
'KEYS' => $prefixFirst,
'RENAME' => $prefixAll,
'RENAMENX' => $prefixAll,
'EXPIRE' => $prefixFirst,
'EXPIREAT' => $prefixFirst,
'TTL' => $prefixFirst,
'MOVE' => $prefixFirst,
'SORT' => $prefixSort,
'DUMP' => $prefixFirst,
'RESTORE' => $prefixFirst,
'SET' => $prefixFirst,
'SETNX' => $prefixFirst,
'MSET' => $prefixInterleaved,
'MSETNX' => $prefixInterleaved,
'GET' => $prefixFirst,
'MGET' => $prefixAll,
'GETSET' => $prefixFirst,
'INCR' => $prefixFirst,
'INCRBY' => $prefixFirst,
'DECR' => $prefixFirst,
'DECRBY' => $prefixFirst,
'RPUSH' => $prefixFirst,
'LPUSH' => $prefixFirst,
'LLEN' => $prefixFirst,
'LRANGE' => $prefixFirst,
'LTRIM' => $prefixFirst,
'LINDEX' => $prefixFirst,
'LSET' => $prefixFirst,
'LREM' => $prefixFirst,
'LPOP' => $prefixFirst,
'RPOP' => $prefixFirst,
'RPOPLPUSH' => $prefixAll,
'SADD' => $prefixFirst,
'SREM' => $prefixFirst,
'SPOP' => $prefixFirst,
'SMOVE' => $prefixSkipLast,
'SCARD' => $prefixFirst,
'SISMEMBER' => $prefixFirst,
'SINTER' => $prefixAll,
'SINTERSTORE' => $prefixAll,
'SUNION' => $prefixAll,
'SUNIONSTORE' => $prefixAll,
'SDIFF' => $prefixAll,
'SDIFFSTORE' => $prefixAll,
'SMEMBERS' => $prefixFirst,
'SRANDMEMBER' => $prefixFirst,
'ZADD' => $prefixFirst,
'ZINCRBY' => $prefixFirst,
'ZREM' => $prefixFirst,
'ZRANGE' => $prefixFirst,
'ZREVRANGE' => $prefixFirst,
'ZRANGEBYSCORE' => $prefixFirst,
'ZCARD' => $prefixFirst,
'ZSCORE' => $prefixFirst,
'ZREMRANGEBYSCORE' => $prefixFirst,
/* ---------------- Redis 2.0 ---------------- */
'SETEX' => $prefixFirst,
'APPEND' => $prefixFirst,
'SUBSTR' => $prefixFirst,
'BLPOP' => $prefixSkipLast,
'BRPOP' => $prefixSkipLast,
'ZUNIONSTORE' => $prefixZsetStore,
'ZINTERSTORE' => $prefixZsetStore,
'ZCOUNT' => $prefixFirst,
'ZRANK' => $prefixFirst,
'ZREVRANK' => $prefixFirst,
'ZREMRANGEBYRANK' => $prefixFirst,
'HSET' => $prefixFirst,
'HSETNX' => $prefixFirst,
'HMSET' => $prefixFirst,
'HINCRBY' => $prefixFirst,
'HGET' => $prefixFirst,
'HMGET' => $prefixFirst,
'HDEL' => $prefixFirst,
'HEXISTS' => $prefixFirst,
'HLEN' => $prefixFirst,
'HKEYS' => $prefixFirst,
'HVALS' => $prefixFirst,
'HGETALL' => $prefixFirst,
'SUBSCRIBE' => $prefixAll,
'UNSUBSCRIBE' => $prefixAll,
'PSUBSCRIBE' => $prefixAll,
'PUNSUBSCRIBE' => $prefixAll,
'PUBLISH' => $prefixFirst,
/* ---------------- Redis 2.2 ---------------- */
'PERSIST' => $prefixFirst,
'STRLEN' => $prefixFirst,
'SETRANGE' => $prefixFirst,
'GETRANGE' => $prefixFirst,
'SETBIT' => $prefixFirst,
'GETBIT' => $prefixFirst,
'RPUSHX' => $prefixFirst,
'LPUSHX' => $prefixFirst,
'LINSERT' => $prefixFirst,
'BRPOPLPUSH' => $prefixSkipLast,
'ZREVRANGEBYSCORE' => $prefixFirst,
'WATCH' => $prefixAll,
/* ---------------- Redis 2.6 ---------------- */
'PTTL' => $prefixFirst,
'PEXPIRE' => $prefixFirst,
'PEXPIREAT' => $prefixFirst,
'PSETEX' => $prefixFirst,
'INCRBYFLOAT' => $prefixFirst,
'BITOP' => $prefixSkipFirst,
'BITCOUNT' => $prefixFirst,
'HINCRBYFLOAT' => $prefixFirst,
'EVAL' => $prefixEvalKeys,
'EVALSHA' => $prefixEvalKeys,
'MIGRATE' => $prefixMigrate,
/* ---------------- Redis 2.8 ---------------- */
'SSCAN' => $prefixFirst,
'ZSCAN' => $prefixFirst,
'HSCAN' => $prefixFirst,
'PFADD' => $prefixFirst,
'PFCOUNT' => $prefixAll,
'PFMERGE' => $prefixAll,
'ZLEXCOUNT' => $prefixFirst,
'ZRANGEBYLEX' => $prefixFirst,
'ZREMRANGEBYLEX' => $prefixFirst,
'ZREVRANGEBYLEX' => $prefixFirst,
'BITPOS' => $prefixFirst,
/* ---------------- Redis 3.2 ---------------- */
'HSTRLEN' => $prefixFirst,
'BITFIELD' => $prefixFirst,
'GEOADD' => $prefixFirst,
'GEOHASH' => $prefixFirst,
'GEOPOS' => $prefixFirst,
'GEODIST' => $prefixFirst,
'GEORADIUS' => $prefixGeoradius,
'GEORADIUSBYMEMBER' => $prefixGeoradius,
/* ---------------- Redis 5.0 ---------------- */
'XADD' => $prefixFirst,
'XRANGE' => $prefixFirst,
'XDEL' => $prefixFirst,
'XLEN' => $prefixFirst,
'XACK' => $prefixFirst,
];
}
/**
* Sets a prefix that is applied to all the keys.
*
* @param string $prefix Prefix for the keys.
*/
public function setPrefix($prefix)
{
$this->prefix = $prefix;
}
/**
* Gets the current prefix.
*
* @return string
*/
public function getPrefix()
{
return $this->prefix;
}
/**
* {@inheritdoc}
*/
public function process(CommandInterface $command)
{
if ($command instanceof PrefixableCommandInterface) {
$command->prefixKeys($this->prefix);
} elseif (isset($this->commands[$commandID = strtoupper($command->getId())])) {
$this->commands[$commandID]($command, $this->prefix);
}
}
/**
* Sets an handler for the specified command ID.
*
* The callback signature must have 2 parameters of the following types:
*
* - Predis\Command\CommandInterface (command instance)
* - String (prefix)
*
* When the callback argument is omitted or NULL, the previously
* associated handler for the specified command ID is removed.
*
* @param string $commandID The ID of the command to be handled.
* @param mixed $callback A valid callable object or NULL.
*
* @throws InvalidArgumentException
*/
public function setCommandHandler($commandID, $callback = null)
{
$commandID = strtoupper($commandID);
if (!isset($callback)) {
unset($this->commands[$commandID]);
return;
}
if (!is_callable($callback)) {
throw new InvalidArgumentException(
'Callback must be a valid callable object or NULL'
);
}
$this->commands[$commandID] = $callback;
}
/**
* {@inheritdoc}
*/
public function __toString()
{
return $this->getPrefix();
}
/**
* Applies the specified prefix only the first argument.
*
* @param CommandInterface $command Command instance.
* @param string $prefix Prefix string.
*/
public static function first(CommandInterface $command, $prefix)
{
if ($arguments = $command->getArguments()) {
$arguments[0] = "$prefix{$arguments[0]}";
$command->setRawArguments($arguments);
}
}
/**
* Applies the specified prefix to all the arguments.
*
* @param CommandInterface $command Command instance.
* @param string $prefix Prefix string.
*/
public static function all(CommandInterface $command, $prefix)
{
if ($arguments = $command->getArguments()) {
foreach ($arguments as &$key) {
$key = "$prefix$key";
}
$command->setRawArguments($arguments);
}
}
/**
* Applies the specified prefix only to even arguments in the list.
*
* @param CommandInterface $command Command instance.
* @param string $prefix Prefix string.
*/
public static function interleaved(CommandInterface $command, $prefix)
{
if ($arguments = $command->getArguments()) {
$length = count($arguments);
for ($i = 0; $i < $length; $i += 2) {
$arguments[$i] = "$prefix{$arguments[$i]}";
}
$command->setRawArguments($arguments);
}
}
/**
* Applies the specified prefix to all the arguments but the first one.
*
* @param CommandInterface $command Command instance.
* @param string $prefix Prefix string.
*/
public static function skipFirst(CommandInterface $command, $prefix)
{
if ($arguments = $command->getArguments()) {
$length = count($arguments);
for ($i = 1; $i < $length; ++$i) {
$arguments[$i] = "$prefix{$arguments[$i]}";
}
$command->setRawArguments($arguments);
}
}
/**
* Applies the specified prefix to all the arguments but the last one.
*
* @param CommandInterface $command Command instance.
* @param string $prefix Prefix string.
*/
public static function skipLast(CommandInterface $command, $prefix)
{
if ($arguments = $command->getArguments()) {
$length = count($arguments);
for ($i = 0; $i < $length - 1; ++$i) {
$arguments[$i] = "$prefix{$arguments[$i]}";
}
$command->setRawArguments($arguments);
}
}
/**
* Applies the specified prefix to the keys of a SORT command.
*
* @param CommandInterface $command Command instance.
* @param string $prefix Prefix string.
*/
public static function sort(CommandInterface $command, $prefix)
{
if ($arguments = $command->getArguments()) {
$arguments[0] = "$prefix{$arguments[0]}";
if (($count = count($arguments)) > 1) {
for ($i = 1; $i < $count; ++$i) {
switch (strtoupper($arguments[$i])) {
case 'BY':
case 'STORE':
$arguments[$i] = "$prefix{$arguments[++$i]}";
break;
case 'GET':
$value = $arguments[++$i];
if ($value !== '#') {
$arguments[$i] = "$prefix$value";
}
break;
case 'LIMIT':
$i += 2;
break;
}
}
}
$command->setRawArguments($arguments);
}
}
/**
* Applies the specified prefix to the keys of an EVAL-based command.
*
* @param CommandInterface $command Command instance.
* @param string $prefix Prefix string.
*/
public static function evalKeys(CommandInterface $command, $prefix)
{
if ($arguments = $command->getArguments()) {
for ($i = 2; $i < $arguments[1] + 2; ++$i) {
$arguments[$i] = "$prefix{$arguments[$i]}";
}
$command->setRawArguments($arguments);
}
}
/**
* Applies the specified prefix to the keys of Z[INTERSECTION|UNION]STORE.
*
* @param CommandInterface $command Command instance.
* @param string $prefix Prefix string.
*/
public static function zsetStore(CommandInterface $command, $prefix)
{
if ($arguments = $command->getArguments()) {
$arguments[0] = "$prefix{$arguments[0]}";
$length = ((int) $arguments[1]) + 2;
for ($i = 2; $i < $length; ++$i) {
$arguments[$i] = "$prefix{$arguments[$i]}";
}
$command->setRawArguments($arguments);
}
}
/**
* Applies the specified prefix to the key of a MIGRATE command.
*
* @param CommandInterface $command Command instance.
* @param string $prefix Prefix string.
*/
public static function migrate(CommandInterface $command, $prefix)
{
if ($arguments = $command->getArguments()) {
$arguments[2] = "$prefix{$arguments[2]}";
$command->setRawArguments($arguments);
}
}
/**
* Applies the specified prefix to the key of a GEORADIUS command.
*
* @param CommandInterface $command Command instance.
* @param string $prefix Prefix string.
*/
public static function georadius(CommandInterface $command, $prefix)
{
if ($arguments = $command->getArguments()) {
$arguments[0] = "$prefix{$arguments[0]}";
$startIndex = $command->getId() === 'GEORADIUS' ? 5 : 4;
if (($count = count($arguments)) > $startIndex) {
for ($i = $startIndex; $i < $count; ++$i) {
switch (strtoupper($arguments[$i])) {
case 'STORE':
case 'STOREDIST':
$arguments[$i] = "$prefix{$arguments[++$i]}";
break;
}
}
}
$command->setRawArguments($arguments);
}
}
}

View File

@ -1,137 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Processor;
use ArrayAccess;
use ArrayIterator;
use InvalidArgumentException;
use Predis\Command\CommandInterface;
use ReturnTypeWillChange;
use Traversable;
/**
* Default implementation of a command processors chain.
*/
class ProcessorChain implements ArrayAccess, ProcessorInterface
{
private $processors = [];
/**
* @param array $processors List of instances of ProcessorInterface.
*/
public function __construct($processors = [])
{
foreach ($processors as $processor) {
$this->add($processor);
}
}
/**
* {@inheritdoc}
*/
public function add(ProcessorInterface $processor)
{
$this->processors[] = $processor;
}
/**
* {@inheritdoc}
*/
public function remove(ProcessorInterface $processor)
{
if (false !== $index = array_search($processor, $this->processors, true)) {
unset($this[$index]);
}
}
/**
* {@inheritdoc}
*/
public function process(CommandInterface $command)
{
for ($i = 0; $i < $count = count($this->processors); ++$i) {
$this->processors[$i]->process($command);
}
}
/**
* {@inheritdoc}
*/
public function getProcessors()
{
return $this->processors;
}
/**
* Returns an iterator over the list of command processor in the chain.
*
* @return Traversable<int, ProcessorInterface>
*/
public function getIterator()
{
return new ArrayIterator($this->processors);
}
/**
* Returns the number of command processors in the chain.
*
* @return int
*/
public function count()
{
return count($this->processors);
}
/**
* {@inheritdoc}
*/
#[ReturnTypeWillChange]
public function offsetExists($index)
{
return isset($this->processors[$index]);
}
/**
* {@inheritdoc}
*/
#[ReturnTypeWillChange]
public function offsetGet($index)
{
return $this->processors[$index];
}
/**
* {@inheritdoc}
*/
#[ReturnTypeWillChange]
public function offsetSet($index, $processor)
{
if (!$processor instanceof ProcessorInterface) {
throw new InvalidArgumentException(
'Processor chain accepts only instances of `Predis\Command\Processor\ProcessorInterface`'
);
}
$this->processors[$index] = $processor;
}
/**
* {@inheritdoc}
*/
#[ReturnTypeWillChange]
public function offsetUnset($index)
{
unset($this->processors[$index]);
$this->processors = array_values($this->processors);
}
}

View File

@ -1,28 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Processor;
use Predis\Command\CommandInterface;
/**
* A command processor processes Redis commands before they are sent to Redis.
*/
interface ProcessorInterface
{
/**
* Processes the given Redis command.
*
* @param CommandInterface $command Command instance.
*/
public function process(CommandInterface $command);
}

View File

@ -1,123 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* Class representing a generic Redis command.
*
* Arguments and responses for these commands are not normalized and they follow
* what is defined by the Redis documentation.
*
* Raw commands can be useful when implementing higher level abstractions on top
* of Predis\Client or managing internals like Redis Sentinel or Cluster as they
* are not potentially subject to hijacking from third party libraries when they
* override command handlers for standard Redis commands.
*/
final class RawCommand implements CommandInterface
{
private $slot;
private $commandID;
private $arguments;
/**
* @param string $commandID Command ID
* @param array $arguments Command arguments
*/
public function __construct($commandID, array $arguments = [])
{
$this->commandID = strtoupper($commandID);
$this->setArguments($arguments);
}
/**
* Creates a new raw command using a variadic method.
*
* @param string $commandID Redis command ID
* @param string ...$args Arguments list for the command
*
* @return CommandInterface
*/
public static function create($commandID, ...$args)
{
$arguments = func_get_args();
return new static(array_shift($arguments), $arguments);
}
/**
* {@inheritdoc}
*/
public function getId()
{
return $this->commandID;
}
/**
* {@inheritdoc}
*/
public function setArguments(array $arguments)
{
$this->arguments = $arguments;
unset($this->slot);
}
/**
* {@inheritdoc}
*/
public function setRawArguments(array $arguments)
{
$this->setArguments($arguments);
}
/**
* {@inheritdoc}
*/
public function getArguments()
{
return $this->arguments;
}
/**
* {@inheritdoc}
*/
public function getArgument($index)
{
if (isset($this->arguments[$index])) {
return $this->arguments[$index];
}
}
/**
* {@inheritdoc}
*/
public function setSlot($slot)
{
$this->slot = $slot;
}
/**
* {@inheritdoc}
*/
public function getSlot()
{
return $this->slot ?? null;
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
return $data;
}
}

View File

@ -1,43 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* Command factory creating raw command instances out of command IDs.
*
* Any command ID will produce a command instance even for unknown commands that
* are not implemented by Redis (the server will return a "-ERR unknown command"
* error responses).
*
* When using this factory the client does not process arguments before sending
* commands to Redis and server responses are not further processed before being
* returned to the caller.
*/
class RawFactory implements FactoryInterface
{
/**
* {@inheritdoc}
*/
public function supports(string ...$commandIDs): bool
{
return true;
}
/**
* {@inheritdoc}
*/
public function create(string $commandID, array $arguments = []): CommandInterface
{
return new RawCommand($commandID, $arguments);
}
}

View File

@ -1,29 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @see http://redis.io/commands/append
*/
class APPEND extends RedisCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'APPEND';
}
}

View File

@ -1,29 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @see http://redis.io/commands/auth
*/
class AUTH extends RedisCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'AUTH';
}
}

View File

@ -1,43 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis\AbstractCommand;
use Predis\Command\Command as RedisCommand;
use Predis\Command\Traits\Keys;
abstract class BZPOPBase extends RedisCommand
{
use Keys {
Keys::setArguments as setKeys;
}
protected static $keysArgumentPositionOffset = 0;
abstract public function getId(): string;
public function setArguments(array $arguments)
{
$this->setKeys($arguments, false);
}
public function parseResponse($data)
{
$key = array_shift($data);
if (null === $key) {
return [$key];
}
return array_combine([$key], [[$data[0] => $data[1]]]);
}
}

View File

@ -1,37 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @see http://redis.io/commands/bgrewriteaof
*/
class BGREWRITEAOF extends RedisCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'BGREWRITEAOF';
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
return $data == 'Background append only file rewriting started';
}
}

View File

@ -1,37 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @see http://redis.io/commands/bgsave
*/
class BGSAVE extends RedisCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'BGSAVE';
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
return $data === 'Background saving started' ? true : $data;
}
}

View File

@ -1,34 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
use Predis\Command\Traits\BitByte;
/**
* @see http://redis.io/commands/bitcount
*
* Count the number of set bits (population counting) in a string.
*/
class BITCOUNT extends RedisCommand
{
use BitByte;
/**
* {@inheritdoc}
*/
public function getId()
{
return 'BITCOUNT';
}
}

View File

@ -1,29 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @see http://redis.io/commands/bitfield
*/
class BITFIELD extends RedisCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'BITFIELD';
}
}

View File

@ -1,43 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @see http://redis.io/commands/bitop
*/
class BITOP extends RedisCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'BITOP';
}
/**
* {@inheritdoc}
*/
public function setArguments(array $arguments)
{
if (count($arguments) === 3 && is_array($arguments[2])) {
[$operation, $destination] = $arguments;
$arguments = $arguments[2];
array_unshift($arguments, $operation, $destination);
}
parent::setArguments($arguments);
}
}

View File

@ -1,34 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
use Predis\Command\Traits\BitByte;
/**
* @see http://redis.io/commands/bitpos
*
* Return the position of the first bit set to 1 or 0 in a string.
*/
class BITPOS extends RedisCommand
{
use BitByte;
/**
* {@inheritdoc}
*/
public function getId()
{
return 'BITPOS';
}
}

View File

@ -1,21 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
class BLMOVE extends LMOVE
{
public function getId()
{
return 'BLMOVE';
}
}

View File

@ -1,25 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
class BLMPOP extends LMPOP
{
protected static $keysArgumentPositionOffset = 1;
protected static $leftRightArgumentPositionOffset = 2;
protected static $countArgumentPositionOffset = 3;
public function getId()
{
return 'BLMPOP';
}
}

View File

@ -1,42 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @see http://redis.io/commands/blpop
*/
class BLPOP extends RedisCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'BLPOP';
}
/**
* {@inheritdoc}
*/
public function setArguments(array $arguments)
{
if (count($arguments) === 2 && is_array($arguments[0])) {
[$arguments, $timeout] = $arguments;
array_push($arguments, $timeout);
}
parent::setArguments($arguments);
}
}

View File

@ -1,42 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @see http://redis.io/commands/brpop
*/
class BRPOP extends RedisCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'BRPOP';
}
/**
* {@inheritdoc}
*/
public function setArguments(array $arguments)
{
if (count($arguments) === 2 && is_array($arguments[0])) {
[$arguments, $timeout] = $arguments;
array_push($arguments, $timeout);
}
parent::setArguments($arguments);
}
}

View File

@ -1,29 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @see http://redis.io/commands/brpoplpush
*/
class BRPOPLPUSH extends RedisCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'BRPOPLPUSH';
}
}

View File

@ -1,30 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
/**
* @see https://redis.io/commands/bzmpop/
*
* BZMPOP is the blocking variant of ZMPOP.
*/
class BZMPOP extends ZMPOP
{
protected static $keysArgumentPositionOffset = 1;
protected static $countArgumentPositionOffset = 3;
protected static $modifierArgumentPositionOffset = 2;
public function getId()
{
return 'BZMPOP';
}
}

View File

@ -1,33 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Redis\AbstractCommand\BZPOPBase;
/**
* @see https://redis.io/commands/bzpopmax/
*
* BZPOPMAX is the blocking variant of the sorted set ZPOPMAX primitive.
*
* It is the blocking version because it blocks the connection when there are
* no members to pop from any of the given sorted sets.
* A member with the highest score is popped from first sorted set that is non-empty,
* with the given keys being checked in the order that they are given.
*/
class BZPOPMAX extends BZPOPBase
{
public function getId(): string
{
return 'BZPOPMAX';
}
}

View File

@ -1,33 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Redis\AbstractCommand\BZPOPBase;
/**
* @see https://redis.io/commands/bzpopmin/
*
* BZPOPMIN is the blocking variant of the sorted set ZPOPMIN primitive.
*
* It is the blocking version because it blocks the connection when there are
* no members to pop from any of the given sorted sets.
* A member with the lowest score is popped from first sorted set that is non-empty,
* with the given keys being checked in the order that they are given.
*/
class BZPOPMIN extends BZPOPBase
{
public function getId(): string
{
return 'BZPOPMIN';
}
}

View File

@ -1,75 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @see http://redis.io/commands/client-list
* @see http://redis.io/commands/client-kill
* @see http://redis.io/commands/client-getname
* @see http://redis.io/commands/client-setname
*/
class CLIENT extends RedisCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'CLIENT';
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
$args = array_change_key_case($this->getArguments(), CASE_UPPER);
switch (strtoupper($args[0])) {
case 'LIST':
return $this->parseClientList($data);
case 'KILL':
case 'GETNAME':
case 'SETNAME':
default:
return $data;
} // @codeCoverageIgnore
}
/**
* Parses the response to CLIENT LIST and returns a structured list.
*
* @param string $data Response buffer.
*
* @return array
*/
protected function parseClientList($data)
{
$clients = [];
foreach (explode("\n", $data, -1) as $clientData) {
$client = [];
foreach (explode(' ', $clientData) as $kv) {
@[$k, $v] = explode('=', $kv);
$client[$k] = $v;
}
$clients[] = $client;
}
return $clients;
}
}

View File

@ -1,29 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as BaseCommand;
/**
* @see http://redis.io/commands/command
*/
class COMMAND extends BaseCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'COMMAND';
}
}

View File

@ -1,50 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @see http://redis.io/commands/config-set
* @see http://redis.io/commands/config-get
* @see http://redis.io/commands/config-resetstat
* @see http://redis.io/commands/config-rewrite
*/
class CONFIG extends RedisCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'CONFIG';
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
if (is_array($data)) {
$result = [];
for ($i = 0; $i < count($data); ++$i) {
$result[$data[$i]] = $data[++$i];
}
return $result;
}
return $data;
}
}

View File

@ -1,47 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
use Predis\Command\Traits\DB;
use Predis\Command\Traits\Replace;
/**
* @see https://redis.io/commands/copy/
*
* This command copies the value stored at the source key to the destination key.
*/
class COPY extends RedisCommand
{
use DB {
DB::setArguments as setDB;
}
use Replace {
Replace::setArguments as setReplace;
}
protected static $dbArgumentPositionOffset = 2;
public function getId()
{
return 'COPY';
}
public function setArguments(array $arguments)
{
$this->setDB($arguments);
$arguments = $this->getArguments();
$this->setReplace($arguments);
}
}

View File

@ -1,42 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis\Container;
use Predis\ClientInterface;
abstract class AbstractContainer implements ContainerInterface
{
/**
* @var ClientInterface
*/
protected $client;
public function __construct(ClientInterface $client)
{
$this->client = $client;
}
/**
* {@inheritDoc}
*/
public function __call($subcommandID, $arguments)
{
array_unshift($arguments, strtoupper($subcommandID));
return $this->client->executeCommand(
$this->client->createCommand($this->getContainerCommandId(), $arguments)
);
}
abstract public function getContainerCommandId(): string;
}

View File

@ -1,54 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis\Container;
use Predis\ClientInterface;
use UnexpectedValueException;
class ContainerFactory
{
private const CONTAINER_NAMESPACE = "Predis\Command\Redis\Container";
/**
* Mappings for class names that corresponds to PHP reserved words.
*
* @var array
*/
private static $specialMappings = [
'FUNCTION' => FunctionContainer::class,
];
/**
* Creates container command.
*
* @param ClientInterface $client
* @param string $containerCommandID
* @return ContainerInterface
*/
public static function create(ClientInterface $client, string $containerCommandID): ContainerInterface
{
$containerCommandID = strtoupper($containerCommandID);
if (class_exists($containerClass = self::CONTAINER_NAMESPACE . '\\' . $containerCommandID)) {
return new $containerClass($client);
}
if (array_key_exists($containerCommandID, self::$specialMappings)) {
$containerClass = self::$specialMappings[$containerCommandID];
return new $containerClass($client);
}
throw new UnexpectedValueException('Given command is not supported.');
}
}

View File

@ -1,33 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis\Container;
interface ContainerInterface
{
/**
* Creates Redis container command with subcommand as virtual method name
* and sends a request to the server.
*
* @param $subcommandID
* @param $arguments
* @return mixed
*/
public function __call($subcommandID, $arguments);
/**
* Returns containerCommandId of specific container command.
*
* @return string
*/
public function getContainerCommandId(): string;
}

View File

@ -1,27 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis\Container;
use Predis\Response\Status;
/**
* @method string load(string $functionCode, bool $replace = 'false')
* @method Status delete(string $libraryName)
*/
class FunctionContainer extends AbstractContainer
{
public function getContainerCommandId(): string
{
return 'functions';
}
}

View File

@ -1,29 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @see http://redis.io/commands/dbsize
*/
class DBSIZE extends RedisCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'DBSIZE';
}
}

View File

@ -1,29 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @see http://redis.io/commands/decr
*/
class DECR extends RedisCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'DECR';
}
}

View File

@ -1,29 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @see http://redis.io/commands/decrby
*/
class DECRBY extends RedisCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'DECRBY';
}
}

View File

@ -1,39 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @see http://redis.io/commands/del
*/
class DEL extends RedisCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'DEL';
}
/**
* {@inheritdoc}
*/
public function setArguments(array $arguments)
{
$arguments = self::normalizeArguments($arguments);
parent::setArguments($arguments);
}
}

View File

@ -1,29 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @see http://redis.io/commands/discard
*/
class DISCARD extends RedisCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'DISCARD';
}
}

View File

@ -1,29 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @see http://redis.io/commands/dump
*/
class DUMP extends RedisCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'DUMP';
}
}

View File

@ -1,29 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @see http://redis.io/commands/echo
*/
class ECHO_ extends RedisCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'ECHO';
}
}

Some files were not shown because too many files have changed in this diff Show More