added redis contrib module
This commit is contained in:
		
							
								
								
									
										1
									
								
								sites/all/modules/contrib/dev/redis/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								sites/all/modules/contrib/dev/redis/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
predis
 | 
			
		||||
							
								
								
									
										339
									
								
								sites/all/modules/contrib/dev/redis/LICENSE.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										339
									
								
								sites/all/modules/contrib/dev/redis/LICENSE.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,339 @@
 | 
			
		||||
                    GNU GENERAL PUBLIC LICENSE
 | 
			
		||||
                       Version 2, June 1991
 | 
			
		||||
 | 
			
		||||
 Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
 | 
			
		||||
 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 | 
			
		||||
 Everyone is permitted to copy and distribute verbatim copies
 | 
			
		||||
 of this license document, but changing it is not allowed.
 | 
			
		||||
 | 
			
		||||
                            Preamble
 | 
			
		||||
 | 
			
		||||
  The licenses for most software are designed to take away your
 | 
			
		||||
freedom to share and change it.  By contrast, the GNU General Public
 | 
			
		||||
License is intended to guarantee your freedom to share and change free
 | 
			
		||||
software--to make sure the software is free for all its users.  This
 | 
			
		||||
General Public License applies to most of the Free Software
 | 
			
		||||
Foundation's software and to any other program whose authors commit to
 | 
			
		||||
using it.  (Some other Free Software Foundation software is covered by
 | 
			
		||||
the GNU Lesser General Public License instead.)  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
 | 
			
		||||
this service 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 make restrictions that forbid
 | 
			
		||||
anyone to deny you these rights or to ask you to surrender the rights.
 | 
			
		||||
These restrictions translate to certain responsibilities for you if you
 | 
			
		||||
distribute copies of the software, or if you modify it.
 | 
			
		||||
 | 
			
		||||
  For example, if you distribute copies of such a program, whether
 | 
			
		||||
gratis or for a fee, you must give the recipients all the rights that
 | 
			
		||||
you have.  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.
 | 
			
		||||
 | 
			
		||||
  We protect your rights with two steps: (1) copyright the software, and
 | 
			
		||||
(2) offer you this license which gives you legal permission to copy,
 | 
			
		||||
distribute and/or modify the software.
 | 
			
		||||
 | 
			
		||||
  Also, for each author's protection and ours, we want to make certain
 | 
			
		||||
that everyone understands that there is no warranty for this free
 | 
			
		||||
software.  If the software is modified by someone else and passed on, we
 | 
			
		||||
want its recipients to know that what they have is not the original, so
 | 
			
		||||
that any problems introduced by others will not reflect on the original
 | 
			
		||||
authors' reputations.
 | 
			
		||||
 | 
			
		||||
  Finally, any free program is threatened constantly by software
 | 
			
		||||
patents.  We wish to avoid the danger that redistributors of a free
 | 
			
		||||
program will individually obtain patent licenses, in effect making the
 | 
			
		||||
program proprietary.  To prevent this, we have made it clear that any
 | 
			
		||||
patent must be licensed for everyone's free use or not licensed at all.
 | 
			
		||||
 | 
			
		||||
  The precise terms and conditions for copying, distribution and
 | 
			
		||||
modification follow.
 | 
			
		||||
 | 
			
		||||
                    GNU GENERAL PUBLIC LICENSE
 | 
			
		||||
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
 | 
			
		||||
 | 
			
		||||
  0. This License applies to any program or other work which contains
 | 
			
		||||
a notice placed by the copyright holder saying it may be distributed
 | 
			
		||||
under the terms of this General Public License.  The "Program", below,
 | 
			
		||||
refers to any such program or work, and a "work based on the Program"
 | 
			
		||||
means either the Program or any derivative work under copyright law:
 | 
			
		||||
that is to say, a work containing the Program or a portion of it,
 | 
			
		||||
either verbatim or with modifications and/or translated into another
 | 
			
		||||
language.  (Hereinafter, translation is included without limitation in
 | 
			
		||||
the term "modification".)  Each licensee is addressed as "you".
 | 
			
		||||
 | 
			
		||||
Activities other than copying, distribution and modification are not
 | 
			
		||||
covered by this License; they are outside its scope.  The act of
 | 
			
		||||
running the Program is not restricted, and the output from the Program
 | 
			
		||||
is covered only if its contents constitute a work based on the
 | 
			
		||||
Program (independent of having been made by running the Program).
 | 
			
		||||
Whether that is true depends on what the Program does.
 | 
			
		||||
 | 
			
		||||
  1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the
 | 
			
		||||
notices that refer to this License and to the absence of any warranty;
 | 
			
		||||
and give any other recipients of the Program a copy of this License
 | 
			
		||||
along with the Program.
 | 
			
		||||
 | 
			
		||||
You may charge a fee for the physical act of transferring a copy, and
 | 
			
		||||
you may at your option offer warranty protection in exchange for a fee.
 | 
			
		||||
 | 
			
		||||
  2. You may modify your copy or copies of the Program or any portion
 | 
			
		||||
of it, thus forming a work based on the Program, and copy and
 | 
			
		||||
distribute such modifications or work under the terms of Section 1
 | 
			
		||||
above, provided that you also meet all of these conditions:
 | 
			
		||||
 | 
			
		||||
    a) You must cause the modified files to carry prominent notices
 | 
			
		||||
    stating that you changed the files and the date of any change.
 | 
			
		||||
 | 
			
		||||
    b) You must cause any work that you distribute or publish, that in
 | 
			
		||||
    whole or in part contains or is derived from the Program or any
 | 
			
		||||
    part thereof, to be licensed as a whole at no charge to all third
 | 
			
		||||
    parties under the terms of this License.
 | 
			
		||||
 | 
			
		||||
    c) If the modified program normally reads commands interactively
 | 
			
		||||
    when run, you must cause it, when started running for such
 | 
			
		||||
    interactive use in the most ordinary way, to print or display an
 | 
			
		||||
    announcement including an appropriate copyright notice and a
 | 
			
		||||
    notice that there is no warranty (or else, saying that you provide
 | 
			
		||||
    a warranty) and that users may redistribute the program under
 | 
			
		||||
    these conditions, and telling the user how to view a copy of this
 | 
			
		||||
    License.  (Exception: if the Program itself is interactive but
 | 
			
		||||
    does not normally print such an announcement, your work based on
 | 
			
		||||
    the Program is not required to print an announcement.)
 | 
			
		||||
 | 
			
		||||
These requirements apply to the modified work as a whole.  If
 | 
			
		||||
identifiable sections of that work are not derived from the Program,
 | 
			
		||||
and can be reasonably considered independent and separate works in
 | 
			
		||||
themselves, then this License, and its terms, do not apply to those
 | 
			
		||||
sections when you distribute them as separate works.  But when you
 | 
			
		||||
distribute the same sections as part of a whole which is a work based
 | 
			
		||||
on the Program, the distribution of the whole must be on the terms of
 | 
			
		||||
this License, whose permissions for other licensees extend to the
 | 
			
		||||
entire whole, and thus to each and every part regardless of who wrote it.
 | 
			
		||||
 | 
			
		||||
Thus, it is not the intent of this section to claim rights or contest
 | 
			
		||||
your rights to work written entirely by you; rather, the intent is to
 | 
			
		||||
exercise the right to control the distribution of derivative or
 | 
			
		||||
collective works based on the Program.
 | 
			
		||||
 | 
			
		||||
In addition, mere aggregation of another work not based on the Program
 | 
			
		||||
with the Program (or with a work based on the Program) on a volume of
 | 
			
		||||
a storage or distribution medium does not bring the other work under
 | 
			
		||||
the scope of this License.
 | 
			
		||||
 | 
			
		||||
  3. You may copy and distribute the Program (or a work based on it,
 | 
			
		||||
under Section 2) in object code or executable form under the terms of
 | 
			
		||||
Sections 1 and 2 above provided that you also do one of the following:
 | 
			
		||||
 | 
			
		||||
    a) Accompany it with the complete corresponding machine-readable
 | 
			
		||||
    source code, which must be distributed under the terms of Sections
 | 
			
		||||
    1 and 2 above on a medium customarily used for software interchange; or,
 | 
			
		||||
 | 
			
		||||
    b) Accompany it with a written offer, valid for at least three
 | 
			
		||||
    years, to give any third party, for a charge no more than your
 | 
			
		||||
    cost of physically performing source distribution, a complete
 | 
			
		||||
    machine-readable copy of the corresponding source code, to be
 | 
			
		||||
    distributed under the terms of Sections 1 and 2 above on a medium
 | 
			
		||||
    customarily used for software interchange; or,
 | 
			
		||||
 | 
			
		||||
    c) Accompany it with the information you received as to the offer
 | 
			
		||||
    to distribute corresponding source code.  (This alternative is
 | 
			
		||||
    allowed only for noncommercial distribution and only if you
 | 
			
		||||
    received the program in object code or executable form with such
 | 
			
		||||
    an offer, in accord with Subsection b above.)
 | 
			
		||||
 | 
			
		||||
The source code for a work means the preferred form of the work for
 | 
			
		||||
making modifications to it.  For an executable work, complete source
 | 
			
		||||
code means all the source code for all modules it contains, plus any
 | 
			
		||||
associated interface definition files, plus the scripts used to
 | 
			
		||||
control compilation and installation of the executable.  However, as a
 | 
			
		||||
special exception, the source code distributed need not include
 | 
			
		||||
anything that is normally distributed (in either source or binary
 | 
			
		||||
form) with the major components (compiler, kernel, and so on) of the
 | 
			
		||||
operating system on which the executable runs, unless that component
 | 
			
		||||
itself accompanies the executable.
 | 
			
		||||
 | 
			
		||||
If distribution of executable or object code is made by offering
 | 
			
		||||
access to copy from a designated place, then offering equivalent
 | 
			
		||||
access to copy the source code from the same place counts as
 | 
			
		||||
distribution of the source code, even though third parties are not
 | 
			
		||||
compelled to copy the source along with the object code.
 | 
			
		||||
 | 
			
		||||
  4. You may not copy, modify, sublicense, or distribute the Program
 | 
			
		||||
except as expressly provided under this License.  Any attempt
 | 
			
		||||
otherwise to copy, modify, sublicense or distribute the Program is
 | 
			
		||||
void, and will automatically terminate your rights under this License.
 | 
			
		||||
However, parties who have received copies, or rights, from you under
 | 
			
		||||
this License will not have their licenses terminated so long as such
 | 
			
		||||
parties remain in full compliance.
 | 
			
		||||
 | 
			
		||||
  5. You are not required to accept this License, since you have not
 | 
			
		||||
signed it.  However, nothing else grants you permission to modify or
 | 
			
		||||
distribute the Program or its derivative works.  These actions are
 | 
			
		||||
prohibited by law if you do not accept this License.  Therefore, by
 | 
			
		||||
modifying or distributing the Program (or any work based on the
 | 
			
		||||
Program), you indicate your acceptance of this License to do so, and
 | 
			
		||||
all its terms and conditions for copying, distributing or modifying
 | 
			
		||||
the Program or works based on it.
 | 
			
		||||
 | 
			
		||||
  6. Each time you redistribute the Program (or any work based on the
 | 
			
		||||
Program), the recipient automatically receives a license from the
 | 
			
		||||
original licensor to copy, distribute or modify the Program subject to
 | 
			
		||||
these terms and conditions.  You may not impose any further
 | 
			
		||||
restrictions on the recipients' exercise of the rights granted herein.
 | 
			
		||||
You are not responsible for enforcing compliance by third parties to
 | 
			
		||||
this License.
 | 
			
		||||
 | 
			
		||||
  7. If, as a consequence of a court judgment or allegation of patent
 | 
			
		||||
infringement or for any other reason (not limited to patent issues),
 | 
			
		||||
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
 | 
			
		||||
distribute so as to satisfy simultaneously your obligations under this
 | 
			
		||||
License and any other pertinent obligations, then as a consequence you
 | 
			
		||||
may not distribute the Program at all.  For example, if a patent
 | 
			
		||||
license would not permit royalty-free redistribution of the Program by
 | 
			
		||||
all those who receive copies directly or indirectly through you, then
 | 
			
		||||
the only way you could satisfy both it and this License would be to
 | 
			
		||||
refrain entirely from distribution of the Program.
 | 
			
		||||
 | 
			
		||||
If any portion of this section is held invalid or unenforceable under
 | 
			
		||||
any particular circumstance, the balance of the section is intended to
 | 
			
		||||
apply and the section as a whole is intended to apply in other
 | 
			
		||||
circumstances.
 | 
			
		||||
 | 
			
		||||
It is not the purpose of this section to induce you to infringe any
 | 
			
		||||
patents or other property right claims or to contest validity of any
 | 
			
		||||
such claims; this section has the sole purpose of protecting the
 | 
			
		||||
integrity of the free software distribution system, which is
 | 
			
		||||
implemented by public license practices.  Many people have made
 | 
			
		||||
generous contributions to the wide range of software distributed
 | 
			
		||||
through that system in reliance on consistent application of that
 | 
			
		||||
system; it is up to the author/donor to decide if he or she is willing
 | 
			
		||||
to distribute software through any other system and a licensee cannot
 | 
			
		||||
impose that choice.
 | 
			
		||||
 | 
			
		||||
This section is intended to make thoroughly clear what is believed to
 | 
			
		||||
be a consequence of the rest of this License.
 | 
			
		||||
 | 
			
		||||
  8. If the distribution and/or use of the Program is restricted in
 | 
			
		||||
certain countries either by patents or by copyrighted interfaces, the
 | 
			
		||||
original copyright holder who places the Program under this License
 | 
			
		||||
may add an explicit geographical distribution limitation excluding
 | 
			
		||||
those countries, so that distribution is permitted only in or among
 | 
			
		||||
countries not thus excluded.  In such case, this License incorporates
 | 
			
		||||
the limitation as if written in the body of this License.
 | 
			
		||||
 | 
			
		||||
  9. The Free Software Foundation may publish revised and/or new versions
 | 
			
		||||
of the 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 a version number of this License which applies to it and "any
 | 
			
		||||
later version", you have the option of following the terms and conditions
 | 
			
		||||
either of that version or of any later version published by the Free
 | 
			
		||||
Software Foundation.  If the Program does not specify a version number of
 | 
			
		||||
this License, you may choose any version ever published by the Free Software
 | 
			
		||||
Foundation.
 | 
			
		||||
 | 
			
		||||
  10. If you wish to incorporate parts of the Program into other free
 | 
			
		||||
programs whose distribution conditions are different, write to the author
 | 
			
		||||
to ask for permission.  For software which is copyrighted by the Free
 | 
			
		||||
Software Foundation, write to the Free Software Foundation; we sometimes
 | 
			
		||||
make exceptions for this.  Our decision will be guided by the two goals
 | 
			
		||||
of preserving the free status of all derivatives of our free software and
 | 
			
		||||
of promoting the sharing and reuse of software generally.
 | 
			
		||||
 | 
			
		||||
                            NO WARRANTY
 | 
			
		||||
 | 
			
		||||
  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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.
 | 
			
		||||
 | 
			
		||||
  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
 | 
			
		||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
 | 
			
		||||
REDISTRIBUTE 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.
 | 
			
		||||
 | 
			
		||||
                     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
 | 
			
		||||
convey 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 2 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, write to the Free Software Foundation, Inc.,
 | 
			
		||||
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 | 
			
		||||
 | 
			
		||||
Also add information on how to contact you by electronic and paper mail.
 | 
			
		||||
 | 
			
		||||
If the program is interactive, make it output a short notice like this
 | 
			
		||||
when it starts in an interactive mode:
 | 
			
		||||
 | 
			
		||||
    Gnomovision version 69, Copyright (C) year name of author
 | 
			
		||||
    Gnomovision 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, the commands you use may
 | 
			
		||||
be called something other than `show w' and `show c'; they could even be
 | 
			
		||||
mouse-clicks or menu items--whatever suits your program.
 | 
			
		||||
 | 
			
		||||
You should also get your employer (if you work as a programmer) or your
 | 
			
		||||
school, if any, to sign a "copyright disclaimer" for the program, if
 | 
			
		||||
necessary.  Here is a sample; alter the names:
 | 
			
		||||
 | 
			
		||||
  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
 | 
			
		||||
  `Gnomovision' (which makes passes at compilers) written by James Hacker.
 | 
			
		||||
 | 
			
		||||
  <signature of Ty Coon>, 1 April 1989
 | 
			
		||||
  Ty Coon, President of Vice
 | 
			
		||||
 | 
			
		||||
This 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.
 | 
			
		||||
							
								
								
									
										37
									
								
								sites/all/modules/contrib/dev/redis/README.PhpRedis.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								sites/all/modules/contrib/dev/redis/README.PhpRedis.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,37 @@
 | 
			
		||||
PhpRedis cache backend
 | 
			
		||||
======================
 | 
			
		||||
 | 
			
		||||
This client, for now, is only able to use the PhpRedis extension.
 | 
			
		||||
 | 
			
		||||
Get PhpRedis
 | 
			
		||||
------------
 | 
			
		||||
 | 
			
		||||
You can download this library at:
 | 
			
		||||
 | 
			
		||||
  https://github.com/phpredis/phpredis
 | 
			
		||||
 | 
			
		||||
Most common Linux distribution should now have packages for this PHP extension
 | 
			
		||||
however if that's not the case for the one you use, use the above link to find
 | 
			
		||||
the release source download links and follow the provided instructions in order
 | 
			
		||||
to compile it for your system.
 | 
			
		||||
 | 
			
		||||
Default behavior is to connect via tcp://localhost:6379 but you might want to
 | 
			
		||||
connect differently.
 | 
			
		||||
 | 
			
		||||
Connect via UNIX socket
 | 
			
		||||
-----------------------
 | 
			
		||||
 | 
			
		||||
Just add this line to your settings.php file:
 | 
			
		||||
 | 
			
		||||
  $conf['redis_cache_socket'] = '/tmp/redis.sock';
 | 
			
		||||
 | 
			
		||||
Don't forget to change the path depending on your operating system and Redis
 | 
			
		||||
server configuration.
 | 
			
		||||
 | 
			
		||||
Connect to a remote host and database
 | 
			
		||||
-------------------------------------
 | 
			
		||||
 | 
			
		||||
See README.txt file.
 | 
			
		||||
 | 
			
		||||
For this particular implementation, host settings are overridden by the
 | 
			
		||||
UNIX socket parameter.
 | 
			
		||||
							
								
								
									
										60
									
								
								sites/all/modules/contrib/dev/redis/README.Predis.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								sites/all/modules/contrib/dev/redis/README.Predis.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,60 @@
 | 
			
		||||
Predis cache backend
 | 
			
		||||
====================
 | 
			
		||||
 | 
			
		||||
This module will work with the Predis 1.x version. Any earlier versions
 | 
			
		||||
are unsupported.
 | 
			
		||||
 | 
			
		||||
This client, for now, is only able to use the Predis PHP library.
 | 
			
		||||
 | 
			
		||||
The Predis library requires PHP 5.3 minimum. If your hosted environment does
 | 
			
		||||
not ships with at least PHP 5.3, please do not use this cache backend.
 | 
			
		||||
 | 
			
		||||
Please consider using an OPCode cache such as APC. Predis is a good and fully
 | 
			
		||||
featured API, the cost is that the code is a lot more than a single file in
 | 
			
		||||
opposition to some other backends such as the APC one.
 | 
			
		||||
 | 
			
		||||
Get Predis
 | 
			
		||||
----------
 | 
			
		||||
 | 
			
		||||
You can download this library at:
 | 
			
		||||
 | 
			
		||||
  https://github.com/nrk/predis
 | 
			
		||||
 | 
			
		||||
This file explains how to install the Predis library and the Drupal cache
 | 
			
		||||
backend. If you are an advanced Drupal integrator, please consider the fact
 | 
			
		||||
that you can easily change all the pathes. Pathes used in this file are
 | 
			
		||||
likely to be default for non advanced users.
 | 
			
		||||
 | 
			
		||||
Download and install library
 | 
			
		||||
----------------------------
 | 
			
		||||
 | 
			
		||||
Once done, you either have to clone it into:
 | 
			
		||||
 | 
			
		||||
  sites/all/libraries/predis
 | 
			
		||||
 | 
			
		||||
So that you have the following directory tree:
 | 
			
		||||
 | 
			
		||||
  sites/all/libraries/predis/src/ # Where the PHP code stands
 | 
			
		||||
 | 
			
		||||
Or, any other place in order to share it:
 | 
			
		||||
For example, from your install profiles libraries folder:
 | 
			
		||||
 | 
			
		||||
  profiles/example/libraries/predis
 | 
			
		||||
 | 
			
		||||
If you choose this solution, you have to alter a bit your $conf array into
 | 
			
		||||
the settings.php file as this:
 | 
			
		||||
 | 
			
		||||
  define('PREDIS_BASE_PATH', DRUPAL_ROOT . '/profiles/example/libraries/predis/');
 | 
			
		||||
 | 
			
		||||
Connect to a remote host and database
 | 
			
		||||
-------------------------------------
 | 
			
		||||
 | 
			
		||||
See README.txt file.
 | 
			
		||||
 | 
			
		||||
Advanced configuration (PHP expert)
 | 
			
		||||
-----------------------------------
 | 
			
		||||
 | 
			
		||||
Best solution is, whatever is the place where you put the Predis library, that
 | 
			
		||||
you set up a fully working autoloader able to use it. The one being used by the
 | 
			
		||||
Redis module is a default fallback and will naturally being appened to the SPL
 | 
			
		||||
autoloader stack.
 | 
			
		||||
							
								
								
									
										40
									
								
								sites/all/modules/contrib/dev/redis/README.testing.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								sites/all/modules/contrib/dev/redis/README.testing.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,40 @@
 | 
			
		||||
Redis module testing
 | 
			
		||||
====================
 | 
			
		||||
 | 
			
		||||
Unit tests won't cover the cache backend until the 8.x series.
 | 
			
		||||
 | 
			
		||||
This won't be fixed, by design. Drupal 8.x now ships a complete cache backend
 | 
			
		||||
unit tests suite which will be used when this module will be upgraded.
 | 
			
		||||
 | 
			
		||||
7.x testing
 | 
			
		||||
===========
 | 
			
		||||
 | 
			
		||||
7.x testing will keep it to the bare minimum and will test some minor bugs such
 | 
			
		||||
as admin UI or autoloading related bugs.
 | 
			
		||||
 | 
			
		||||
Cleanup environment
 | 
			
		||||
===================
 | 
			
		||||
 | 
			
		||||
php -f scripts/run-tests.sh -- --clean
 | 
			
		||||
 | 
			
		||||
Run common tests
 | 
			
		||||
================
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
php -f scripts/run-tests.sh -- --verbose --color \
 | 
			
		||||
    --url "http://yoursite" \
 | 
			
		||||
    --class "Redis_Tests_Client_UnitTestCase"
 | 
			
		||||
 | 
			
		||||
Run all PhpRedis tests
 | 
			
		||||
======================
 | 
			
		||||
 | 
			
		||||
php -f scripts/run-tests.sh -- --verbose --color \
 | 
			
		||||
    --url "http://laborange.net" \
 | 
			
		||||
    --class "Redis_Tests_Cache_PhpRedisFixesUnitTestCase,Redis_Tests_Cache_PhpRedisFlushUnitTestCase,Redis_Tests_Cache_PhpRedisShardedFixesUnitTestCase,Redis_Tests_Cache_PhpRedisShardedFlushUnitTestCase,Redis_Tests_Cache_PhpRedisShardedWithPipelineFixesUnitTestCase,Redis_Tests_Lock_PhpRedisLockingUnitTestCase,Redis_Tests_Path_PhpRedisPathUnitTestCase,Redis_Tests_Queue_PhpRedisQueueUnitTestCase"
 | 
			
		||||
 | 
			
		||||
Run all Predis tests
 | 
			
		||||
======================
 | 
			
		||||
 | 
			
		||||
php -f scripts/run-tests.sh -- --verbose --color \
 | 
			
		||||
    --url "http://yoursite" \
 | 
			
		||||
    --class "Redis_Tests_Cache_PredisFixesUnitTestCase,Redis_Tests_Cache_PredisFlushUnitTestCase,Redis_Tests_Cache_PredisShardedFixesUnitTestCase,Redis_Tests_Cache_PredisShardedFlushUnitTestCase,Redis_Tests_Cache_PredisShardedWithPipelineFixesUnitTestCase,Redis_Tests_Lock_PredisLockingUnitTestCase,Redis_Tests_Path_PredisPathUnitTestCase"
 | 
			
		||||
							
								
								
									
										474
									
								
								sites/all/modules/contrib/dev/redis/README.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										474
									
								
								sites/all/modules/contrib/dev/redis/README.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,474 @@
 | 
			
		||||
Redis cache backends
 | 
			
		||||
====================
 | 
			
		||||
 | 
			
		||||
This package provides two different Redis cache backends. If you want to use
 | 
			
		||||
Redis as cache backend, you have to choose one of the two, but you cannot use
 | 
			
		||||
both at the same time. Well, it will be technically possible, but it would be
 | 
			
		||||
quite a dumb thing to do.
 | 
			
		||||
 | 
			
		||||
Predis
 | 
			
		||||
------
 | 
			
		||||
 | 
			
		||||
This implementation uses the Predis PHP library. It is compatible PHP 5.3
 | 
			
		||||
only.
 | 
			
		||||
 | 
			
		||||
PhpRedis
 | 
			
		||||
--------
 | 
			
		||||
 | 
			
		||||
This implementation uses the PhpRedis PHP extention. In order to use it, you
 | 
			
		||||
probably will need to compile the extension yourself.
 | 
			
		||||
 | 
			
		||||
Redis version
 | 
			
		||||
-------------
 | 
			
		||||
 | 
			
		||||
This module requires Redis version to be 2.6.0 or later with LUA scrpting
 | 
			
		||||
enabled due to the EVAL command usage.
 | 
			
		||||
 | 
			
		||||
If you can't upgrade you Redis server:
 | 
			
		||||
 | 
			
		||||
  - 3.x release will only officially support Redis server <= 2.8 and 3.x
 | 
			
		||||
    nevertheless you may use it with Redis 2.4 if you configure your cache
 | 
			
		||||
    backend to operate in sharding mode.
 | 
			
		||||
 | 
			
		||||
  - For Redis 2.4 use the latest 2.x release of this module or use the
 | 
			
		||||
    3.x release with sharding mode enabled.
 | 
			
		||||
 | 
			
		||||
  - For Redis <=2.3 use any version of this module <=2.6
 | 
			
		||||
 | 
			
		||||
Notes
 | 
			
		||||
-----
 | 
			
		||||
 | 
			
		||||
Both backends provide the exact same functionalities. The major difference is
 | 
			
		||||
because PhpRedis uses a PHP extension, and not PHP code, it will performe a
 | 
			
		||||
lot better (Predis needs PHP userland code to be loaded).
 | 
			
		||||
 | 
			
		||||
Difference is not that visible, it's really a few millisec on my testing box,
 | 
			
		||||
in case you attempt to profile the code, traces will be a lot bigger.
 | 
			
		||||
 | 
			
		||||
Note that most of the settings are shared. See next sections.
 | 
			
		||||
 | 
			
		||||
Getting started
 | 
			
		||||
===============
 | 
			
		||||
 | 
			
		||||
Quick setup
 | 
			
		||||
-----------
 | 
			
		||||
 | 
			
		||||
Here is a simple yet working easy way to setup the module.
 | 
			
		||||
This method will allow Drupal to use Redis for all caches and locks
 | 
			
		||||
and path alias cache replacement.
 | 
			
		||||
 | 
			
		||||
  $conf['redis_client_interface'] = 'PhpRedis'; // Can be "Predis".
 | 
			
		||||
  $conf['redis_client_host']      = '1.2.3.4';  // Your Redis instance hostname.
 | 
			
		||||
  $conf['lock_inc']               = 'sites/all/modules/redis/redis.lock.inc';
 | 
			
		||||
  $conf['path_inc']               = 'sites/all/modules/redis/redis.path.inc';
 | 
			
		||||
  $conf['cache_backends'][]       = 'sites/all/modules/redis/redis.autoload.inc';
 | 
			
		||||
  $conf['cache_default_class']    = 'Redis_Cache';
 | 
			
		||||
 | 
			
		||||
See next chapters for more information.
 | 
			
		||||
 | 
			
		||||
Is there any cache bins that should *never* go into Redis?
 | 
			
		||||
----------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
TL;DR: No. Except for 'cache_form' if you use Redis with LRU eviction.
 | 
			
		||||
 | 
			
		||||
Redis has been maturing a lot over time, and will apply different sensible
 | 
			
		||||
settings for different bins; It's today very stable.
 | 
			
		||||
 | 
			
		||||
Advanced configuration
 | 
			
		||||
======================
 | 
			
		||||
 | 
			
		||||
Use the compressed cache
 | 
			
		||||
------------------------
 | 
			
		||||
 | 
			
		||||
Please note this is for now an experimental feature. As a personnal note
 | 
			
		||||
from the module author, it should be safe to use.
 | 
			
		||||
 | 
			
		||||
Use this cache class setting to enable compression. This will save usually
 | 
			
		||||
about 80% RAM at the cost of some milliseconds server time.
 | 
			
		||||
 | 
			
		||||
  $conf['cache_default_class'] = 'Redis_CacheCompressed';
 | 
			
		||||
 | 
			
		||||
Additionnaly, you can alter the default size compression threshold, under which
 | 
			
		||||
entries will not be compressed (size is in bytes, set 0 to always compress):
 | 
			
		||||
 | 
			
		||||
  $conf['cache_compression_size_threshold'] = 100;
 | 
			
		||||
 | 
			
		||||
You can also change the compression level, which an positive integer between
 | 
			
		||||
1 and 9, 1 being the lowest but fastest compression ratio, 9 being the most
 | 
			
		||||
aggressive compression but is a lot slower. From testing, setting it to the
 | 
			
		||||
lower level (1) gives 80% memory usage decrease, which is more than enough.
 | 
			
		||||
 | 
			
		||||
  $conf['cache_compression_ratio'] = 5;
 | 
			
		||||
 | 
			
		||||
Please note that those settings are global and not on a cache bin basis, you can
 | 
			
		||||
already control whenever the compression is to be used or not by selecting a
 | 
			
		||||
different cache class on per cache bin basis.
 | 
			
		||||
 | 
			
		||||
If you switch from the standard default backend (without compression) to the
 | 
			
		||||
compressed cache backend, it will recover transparently uncompressed data and
 | 
			
		||||
proceed normally without additional cache eviction, it safe to upgrade.
 | 
			
		||||
Donwgrading from compressed data to uncompressed data won't work, but the
 | 
			
		||||
cache backend will just give you cache hit miss and it will work seamlessly
 | 
			
		||||
too without any danger for the site.
 | 
			
		||||
 | 
			
		||||
Choose the Redis client library to use
 | 
			
		||||
--------------------------------------
 | 
			
		||||
 | 
			
		||||
Add into your settings.php file:
 | 
			
		||||
 | 
			
		||||
  $conf['redis_client_interface']      = 'PhpRedis';
 | 
			
		||||
 | 
			
		||||
You can replace 'PhpRedis' with 'Predis', depending on the library you chose.
 | 
			
		||||
 | 
			
		||||
Note that this is optional but recommended. If you don't set this variable the
 | 
			
		||||
module will proceed to class lookups and attempt to choose the best client
 | 
			
		||||
available, having always a preference for the Predis one.
 | 
			
		||||
 | 
			
		||||
Tell Drupal to use the cache backend
 | 
			
		||||
------------------------------------
 | 
			
		||||
 | 
			
		||||
Usual cache backend configuration, as follows, to add into your settings.php
 | 
			
		||||
file like any other backend:
 | 
			
		||||
 | 
			
		||||
  $conf['cache_backends'][]            = 'sites/all/modules/redis/redis.autoload.inc';
 | 
			
		||||
  $conf['cache_class_cache']           = 'Redis_Cache';
 | 
			
		||||
  $conf['cache_class_cache_menu']      = 'Redis_Cache';
 | 
			
		||||
  $conf['cache_class_cache_bootstrap'] = 'Redis_Cache';
 | 
			
		||||
  // ... Any other bins.
 | 
			
		||||
 | 
			
		||||
Tell Drupal to use the lock backend
 | 
			
		||||
-----------------------------------
 | 
			
		||||
 | 
			
		||||
Usual lock backend override, update you settings.php file as this:
 | 
			
		||||
 | 
			
		||||
  $conf['lock_inc'] = 'sites/all/modules/redis/redis.lock.inc';
 | 
			
		||||
 | 
			
		||||
Tell Drupal to use the path alias backend
 | 
			
		||||
-----------------------------------------
 | 
			
		||||
 | 
			
		||||
Usual path backend override, update you settings.php file as this:
 | 
			
		||||
 | 
			
		||||
  $conf['path_inc'] = 'sites/all/modules/redis/redis.path.inc';
 | 
			
		||||
 | 
			
		||||
Notice that there is an additional variable for path handling that is set
 | 
			
		||||
per default which will ignore any path that is an admin path, gaining a few
 | 
			
		||||
SQL queries. If you want to be able to set aliases on admin path and restore
 | 
			
		||||
an almost default Drupal core behavior, you should add this line into your
 | 
			
		||||
settings.php file:
 | 
			
		||||
 | 
			
		||||
  $conf['path_alias_admin_blacklist'] = FALSE;
 | 
			
		||||
 | 
			
		||||
Drupal 6 and lock backend
 | 
			
		||||
-------------------------
 | 
			
		||||
 | 
			
		||||
Considering this is a Drupal 7 module only downloading it in Drupal 6 will make
 | 
			
		||||
the module UI telling you this module is unsupported yet you can use the lock
 | 
			
		||||
backend on Drupal 6.
 | 
			
		||||
 | 
			
		||||
Read your Drupal 6 core documentation and use the redis.lock.inc file as
 | 
			
		||||
lock_inc replacement the same way its being done for Drupal 7 and it should
 | 
			
		||||
work. Note that this is untested by the module maintainer (feedback will be
 | 
			
		||||
greatly appreciated).
 | 
			
		||||
 | 
			
		||||
Common settings
 | 
			
		||||
===============
 | 
			
		||||
 | 
			
		||||
Connect throught a UNIX socket
 | 
			
		||||
------------------------------
 | 
			
		||||
 | 
			
		||||
All you have to do is specify this line:
 | 
			
		||||
 | 
			
		||||
  $conf['redis_client_socket'] = '/some/path/redis.sock';
 | 
			
		||||
 | 
			
		||||
Both drivers support it.
 | 
			
		||||
 | 
			
		||||
Connect to a remote host
 | 
			
		||||
------------------------
 | 
			
		||||
 | 
			
		||||
If your Redis instance is remote, you can use this syntax:
 | 
			
		||||
 | 
			
		||||
  $conf['redis_client_host'] = '1.2.3.4';
 | 
			
		||||
  $conf['redis_client_port'] = 1234;
 | 
			
		||||
 | 
			
		||||
Port is optional, default is 6379 (default Redis port).
 | 
			
		||||
 | 
			
		||||
Using a specific database
 | 
			
		||||
-------------------------
 | 
			
		||||
 | 
			
		||||
Per default, Redis ships the database "0". All default connections will be use
 | 
			
		||||
this one if nothing is specified.
 | 
			
		||||
 | 
			
		||||
Depending on you OS or OS distribution, you might have numerous database. To
 | 
			
		||||
use one in particular, just add to your settings.php file:
 | 
			
		||||
 | 
			
		||||
  $conf['redis_client_base'] = 12;
 | 
			
		||||
 | 
			
		||||
Please note that if you are working in shard mode, you should never set this
 | 
			
		||||
variable.
 | 
			
		||||
 | 
			
		||||
Connection to a password protected instance
 | 
			
		||||
-------------------------------------------
 | 
			
		||||
 | 
			
		||||
If you are using a password protected instance, specify the password this way:
 | 
			
		||||
 | 
			
		||||
  $conf['redis_client_password'] = "mypassword";
 | 
			
		||||
 | 
			
		||||
Depending on the backend, using a wrong auth will behave differently:
 | 
			
		||||
 | 
			
		||||
 - Predis will throw an exception and make Drupal fail during early boostrap.
 | 
			
		||||
 | 
			
		||||
 - PhpRedis will make Redis calls silent and creates some PHP warnings, thus
 | 
			
		||||
   Drupal will behave as if it was running with a null cache backend (no cache
 | 
			
		||||
   at all).
 | 
			
		||||
 | 
			
		||||
Prefixing site cache entries (avoiding sites name collision)
 | 
			
		||||
------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
If you need to differenciate multiple sites using the same Redis instance and
 | 
			
		||||
database, you will need to specify a prefix for your site cache entries.
 | 
			
		||||
 | 
			
		||||
Important note: most people don't need that feature since that when no prefix
 | 
			
		||||
is specified, the Redis module will attempt to use the a hash of the database
 | 
			
		||||
credentials in order to provide a multisite safe default behavior. This means
 | 
			
		||||
that the module will also safely work in CLI scripts.
 | 
			
		||||
 | 
			
		||||
Cache prefix configuration attemps to use a unified variable accross contrib
 | 
			
		||||
backends that support this feature. This variable name is 'cache_prefix'.
 | 
			
		||||
 | 
			
		||||
This variable is polymorphic, the simplest version is to provide a raw string
 | 
			
		||||
that will be the default prefix for all cache bins:
 | 
			
		||||
 | 
			
		||||
  $conf['cache_prefix'] = 'mysite_';
 | 
			
		||||
 | 
			
		||||
Alternatively, to provide the same functionality, you can provide the variable
 | 
			
		||||
as an array:
 | 
			
		||||
 | 
			
		||||
  $conf['cache_prefix']['default'] = 'mysite_';
 | 
			
		||||
 | 
			
		||||
This allows you to provide different prefix depending on the bin name. Common
 | 
			
		||||
usage is that each key inside the 'cache_prefix' array is a bin name, the value
 | 
			
		||||
the associated prefix. If the value is explicitely FALSE, then no prefix is
 | 
			
		||||
used for this bin.
 | 
			
		||||
 | 
			
		||||
The 'default' meta bin name is provided to define the default prefix for non
 | 
			
		||||
specified bins. It behaves like the other names, which means that an explicit
 | 
			
		||||
FALSE will order the backend not to provide any prefix for any non specified
 | 
			
		||||
bin.
 | 
			
		||||
 | 
			
		||||
Here is a complex sample:
 | 
			
		||||
 | 
			
		||||
  // Default behavior for all bins, prefix is 'mysite_'.
 | 
			
		||||
  $conf['cache_prefix']['default'] = 'mysite_';
 | 
			
		||||
 | 
			
		||||
  // Set no prefix explicitely for 'cache' and 'cache_bootstrap' bins.
 | 
			
		||||
  $conf['cache_prefix']['cache'] = FALSE;
 | 
			
		||||
  $conf['cache_prefix']['cache_bootstrap'] = FALSE;
 | 
			
		||||
 | 
			
		||||
  // Set another prefix for 'cache_menu' bin.
 | 
			
		||||
  $conf['cache_prefix']['cache_menu'] = 'menumysite_';
 | 
			
		||||
 | 
			
		||||
Note that this last notice is Redis only specific, because per default Redis
 | 
			
		||||
server will not namespace data, thus sharing an instance for multiple sites
 | 
			
		||||
will create conflicts. This is not true for every contributed backends.
 | 
			
		||||
 | 
			
		||||
Sharding vs normal mode
 | 
			
		||||
-----------------------
 | 
			
		||||
 | 
			
		||||
Per default the Redis cache backend will be in "normal" mode, meaning that
 | 
			
		||||
every flush call will trigger and EVAL lua script that will proceed to cache
 | 
			
		||||
wipeout and cleanup the Redis database from stalled entries.
 | 
			
		||||
 | 
			
		||||
Nevertheless, if you are working with a Redis server < 2.6 or in a sharded
 | 
			
		||||
environment, you cannot multiple keys per command nor proceed to EVAL'ed
 | 
			
		||||
scripts, you will then need to switch to the sharded mode.
 | 
			
		||||
 | 
			
		||||
Sharded mode will never delete entries on flush calls, but register a key
 | 
			
		||||
with the current flush time instead. Cache entries will then be deleted on
 | 
			
		||||
read if the entry checksum does not match or is older than the latest flush
 | 
			
		||||
call. Note that this mode is fast and safe, but must be used accordingly
 | 
			
		||||
with the default lifetime for permanent items, else your Redis server might
 | 
			
		||||
keep stalled entries into its database forever.
 | 
			
		||||
 | 
			
		||||
In order to enable the sharded mode, set into your settings.php file:
 | 
			
		||||
 | 
			
		||||
    $conf['redis_flush_mode'] = 3;
 | 
			
		||||
 | 
			
		||||
Please note that the value 3 is there to keep backward compatibility with
 | 
			
		||||
older versions of the Redis module and will not change.
 | 
			
		||||
 | 
			
		||||
Note that previous Redis module version allowed to set a per-bin setting for
 | 
			
		||||
the clear mode value; nevertheless the clear mode is not a valid setting
 | 
			
		||||
anymore and the past issues have been resolved. Only the global value will
 | 
			
		||||
work as of now.
 | 
			
		||||
 | 
			
		||||
Sharding and pipelining
 | 
			
		||||
-----------------------
 | 
			
		||||
 | 
			
		||||
Whe using this module with sharding mode you may have a sharding proxy able to
 | 
			
		||||
do command pipelining. If that is the case, you should switch to "sharding with
 | 
			
		||||
pipelining" mode instead:
 | 
			
		||||
 | 
			
		||||
    $conf['redis_flush_mode'] = 4;
 | 
			
		||||
 | 
			
		||||
Note that if you use the sharding mode because you use an older version of the
 | 
			
		||||
Redis server, you should always use this mode to ensure the best performances.
 | 
			
		||||
 | 
			
		||||
Default lifetime for permanent items
 | 
			
		||||
------------------------------------
 | 
			
		||||
 | 
			
		||||
Redis when reaching its maximum memory limit will stop writing data in its
 | 
			
		||||
storage engine: this is a feature that avoid the Redis server crashing when
 | 
			
		||||
there is no memory left on the machine.
 | 
			
		||||
 | 
			
		||||
As a workaround, Redis can be configured as a LRU cache for both volatile or
 | 
			
		||||
permanent items, which means it can behave like Memcache; Problem is that if
 | 
			
		||||
you use Redis as a permanent storage for other business matters than this
 | 
			
		||||
module you cannot possibly configure it to drop permanent items or you'll
 | 
			
		||||
loose data.
 | 
			
		||||
 | 
			
		||||
This workaround allows you to explicity set a very long or configured default
 | 
			
		||||
lifetime for CACHE_PERMANENT items (that would normally be permanent) which
 | 
			
		||||
will mark them as being volatile in Redis storage engine: this then allows you
 | 
			
		||||
to configure a LRU behavior for volatile keys without engaging the permenent
 | 
			
		||||
business stuff in a dangerous LRU mechanism; Cache items even if permament will
 | 
			
		||||
be dropped when unused using this.
 | 
			
		||||
 | 
			
		||||
Per default the TTL for permanent items will set to safe-enough value which is
 | 
			
		||||
one year; No matter how Redis will be configured default configuration or lazy
 | 
			
		||||
admin will inherit from a safe module behavior with zero-conf.
 | 
			
		||||
 | 
			
		||||
For advanturous people, you can manage the TTL on a per bin basis and change
 | 
			
		||||
the default one:
 | 
			
		||||
 | 
			
		||||
    // Make CACHE_PERMANENT items being permanent once again
 | 
			
		||||
    // 0 is a special value usable for all bins to explicitely tell the
 | 
			
		||||
    // cache items will not be volatile in Redis.
 | 
			
		||||
    $conf['redis_perm_ttl'] = 0;
 | 
			
		||||
 | 
			
		||||
    // Make them being volatile with a default lifetime of 1 year.
 | 
			
		||||
    $conf['redis_perm_ttl'] = "1 year";
 | 
			
		||||
 | 
			
		||||
    // You can override on a per-bin basis;
 | 
			
		||||
    // For example make cached field values live only 3 monthes:
 | 
			
		||||
    $conf['redis_perm_ttl_cache_field'] = "3 months";
 | 
			
		||||
 | 
			
		||||
    // But you can also put a timestamp in there; In this case the
 | 
			
		||||
    // value must be a STRICTLY TYPED integer:
 | 
			
		||||
    $conf['redis_perm_ttl_cache_field'] = 2592000; // 30 days.
 | 
			
		||||
 | 
			
		||||
Time interval string will be parsed using DateInterval::createFromDateString
 | 
			
		||||
please refer to its documentation:
 | 
			
		||||
 | 
			
		||||
    http://www.php.net/manual/en/dateinterval.createfromdatestring.php
 | 
			
		||||
 | 
			
		||||
Please also be careful about the fact that those settings are overriden by
 | 
			
		||||
the 'cache_lifetime' Drupal variable, which should always be set to 0.
 | 
			
		||||
Moreover, this setting will affect all cache entries without exception so
 | 
			
		||||
be careful and never set values too low if you don't want this setting to
 | 
			
		||||
override default expire value given by modules on temporary cache entries.
 | 
			
		||||
 | 
			
		||||
Lock backends
 | 
			
		||||
-------------
 | 
			
		||||
 | 
			
		||||
Both implementations provides a Redis lock backend. Redis lock backend proved to
 | 
			
		||||
be faster than the default SQL based one when using both servers on the same box.
 | 
			
		||||
 | 
			
		||||
Both backends, thanks to the Redis WATCH, MULTI and EXEC commands provides a
 | 
			
		||||
real race condition free mutexes by using Redis transactions.
 | 
			
		||||
 | 
			
		||||
Queue backend
 | 
			
		||||
-------------
 | 
			
		||||
 | 
			
		||||
This module provides an experimental queue backend. It is for now implemented
 | 
			
		||||
only using the PhpRedis driver, any attempt to use it using Predis will result
 | 
			
		||||
in runtime errors.
 | 
			
		||||
 | 
			
		||||
If you want to change the queue driver system wide, set this into your
 | 
			
		||||
setting.php file:
 | 
			
		||||
 | 
			
		||||
    $conf['queue_default_class'] = 'Redis_Queue';
 | 
			
		||||
    $conf['queue_default_reliable_class'] = 'Redis_Queue';
 | 
			
		||||
 | 
			
		||||
Note that some queue implementations such as the batch queue are hardcoded
 | 
			
		||||
within Drupal and will always use a database dependent implementation.
 | 
			
		||||
 | 
			
		||||
If you need to proceed with finer tuning, you can set a per-queue class in
 | 
			
		||||
such way:
 | 
			
		||||
 | 
			
		||||
    $conf['queue_class_NAME'] = 'Redis_Queue';
 | 
			
		||||
 | 
			
		||||
Where NAME is the arbitrary module given queue name, used as first parameter
 | 
			
		||||
for the method DrupalQueue::get().
 | 
			
		||||
 | 
			
		||||
THIS IS STILL VERY EXPERIMENTAL. The queue should work without any problems
 | 
			
		||||
except it does not implement the item lease time correctly, this means that
 | 
			
		||||
items that are too long to process won't be released back and forth but will
 | 
			
		||||
block the thread processing it instead. This is the only side effect I am
 | 
			
		||||
aware of at the current time.
 | 
			
		||||
 | 
			
		||||
Failover, sharding and partionning
 | 
			
		||||
==================================
 | 
			
		||||
 | 
			
		||||
Important notice
 | 
			
		||||
----------------
 | 
			
		||||
 | 
			
		||||
There are numerous support and feature request issues about client sharding,
 | 
			
		||||
failover ability, multi-server connection, ability to read from slave and
 | 
			
		||||
server clustering opened in the issue queue. Note that there is not one
 | 
			
		||||
universally efficient solution for this: most of the solutions require that
 | 
			
		||||
you cannot use the MULTI/EXEC command using more than one key, and that you
 | 
			
		||||
cannot use complex UNION and intersection features anymore.
 | 
			
		||||
 | 
			
		||||
This module does not implement any kind of client side key hashing or sharding
 | 
			
		||||
and never intended to; We recommend that you read the official Redis
 | 
			
		||||
documentation page about partionning.
 | 
			
		||||
 | 
			
		||||
The best solution for clustering and sharding today seems to be the proxy
 | 
			
		||||
assisted partionning using tools such as Twemproxy.
 | 
			
		||||
 | 
			
		||||
Current components state
 | 
			
		||||
------------------------
 | 
			
		||||
 | 
			
		||||
As of now, provided components are simple enough so they never use WATCH or
 | 
			
		||||
MULTI/EXEC transaction blocks on multiple keys : this means that you can use
 | 
			
		||||
them in an environment doing data sharding/partionning. This remains true
 | 
			
		||||
except when you use a proxy that blocks those commands such as Twemproxy.
 | 
			
		||||
 | 
			
		||||
Lock
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
Lock backend works on a single key per lock, it theorically guarantees the
 | 
			
		||||
atomicity of operations therefore is usable in a sharded environement. Sadly
 | 
			
		||||
if you use proxy assisted sharding such as Twemproxy, WATCH, MULTI and EXEC
 | 
			
		||||
commands won't pass making it non shardable.
 | 
			
		||||
 | 
			
		||||
Path
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
Path backend does not use on transactions, it is safe to use in a sharded
 | 
			
		||||
environment. Note that this backend uses a single HASH key per language
 | 
			
		||||
and per way (alias to source or source to alias) and therefore won't benefit
 | 
			
		||||
greatly if not at all from being sharded.
 | 
			
		||||
 | 
			
		||||
Cache
 | 
			
		||||
-----
 | 
			
		||||
 | 
			
		||||
Cache uses pipelined transactions but does not uses it to guarantee any kind
 | 
			
		||||
of data consistency. If you use a smart sharding proxy it is supposed to work
 | 
			
		||||
transparently without any hickups.
 | 
			
		||||
 | 
			
		||||
Queue
 | 
			
		||||
-----
 | 
			
		||||
 | 
			
		||||
Queue is still in development. There might be problems in the long term for
 | 
			
		||||
this component in sharded environments.
 | 
			
		||||
 | 
			
		||||
Testing
 | 
			
		||||
=======
 | 
			
		||||
 | 
			
		||||
Due to Drupal unit testing API being incredibly stupid, the unit tests can only
 | 
			
		||||
work with PHP >=5.3 while the module will work gracefully with PHP 5.2 (at least
 | 
			
		||||
using the PhpRedis client).
 | 
			
		||||
 | 
			
		||||
I did not find any hint about making tests being configurable, so per default
 | 
			
		||||
the tested Redis server must always be on localhost with default configuration.
 | 
			
		||||
@@ -0,0 +1,108 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
abstract class Redis_AbstractBackend implements Redis_BackendInterface
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * Key components name separator
 | 
			
		||||
     */
 | 
			
		||||
    const KEY_SEPARATOR = ':';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var string
 | 
			
		||||
     */
 | 
			
		||||
    private $prefix;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var string
 | 
			
		||||
     */
 | 
			
		||||
    private $namespace;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var mixed
 | 
			
		||||
     */
 | 
			
		||||
    private $client;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Default constructor
 | 
			
		||||
     *
 | 
			
		||||
     * @param mixed $client
 | 
			
		||||
     *   Redis client
 | 
			
		||||
     * @param string $namespace
 | 
			
		||||
     *   Component namespace
 | 
			
		||||
     * @param string $prefix
 | 
			
		||||
     *   Component prefix
 | 
			
		||||
     */
 | 
			
		||||
    public function __construct($client, $namespace = null, $prefix = null)
 | 
			
		||||
    {
 | 
			
		||||
        $this->client = $client;
 | 
			
		||||
        $this->prefix = $prefix;
 | 
			
		||||
 | 
			
		||||
        if (null !== $namespace) {
 | 
			
		||||
            $this->namespace = $namespace;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    final public function setClient($client)
 | 
			
		||||
    {
 | 
			
		||||
        $this->client = $client;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    final public function getClient()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->client;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    final public function setPrefix($prefix)
 | 
			
		||||
    {
 | 
			
		||||
        $this->prefix = $prefix;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    final public function getPrefix()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->prefix;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    final public function setNamespace($namespace)
 | 
			
		||||
    {
 | 
			
		||||
        $this->namespace = $namespace;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    final public function getNamespace()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->namespace;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get prefixed key
 | 
			
		||||
     *
 | 
			
		||||
     * @param string|string[] $parts
 | 
			
		||||
     *   Arbitrary number of strings to compose the key
 | 
			
		||||
     *
 | 
			
		||||
     * @return string
 | 
			
		||||
     */
 | 
			
		||||
    public function getKey($parts = array())
 | 
			
		||||
    {
 | 
			
		||||
        $key = array();
 | 
			
		||||
 | 
			
		||||
        if (null !== $this->prefix) {
 | 
			
		||||
            $key[] = $this->prefix;
 | 
			
		||||
        }
 | 
			
		||||
        if (null !== $this->namespace) {
 | 
			
		||||
            $key[] = $this->namespace;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($parts) {
 | 
			
		||||
            if (is_array($parts)) {
 | 
			
		||||
                foreach ($parts as $part) {
 | 
			
		||||
                    if ($part) {
 | 
			
		||||
                        $key[] = $part;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                $key[] = $parts;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return implode(self::KEY_SEPARATOR, array_filter($key));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,59 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Client based Redis component
 | 
			
		||||
 */
 | 
			
		||||
interface Redis_BackendInterface
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * Set client
 | 
			
		||||
     *
 | 
			
		||||
     * @param mixed $client
 | 
			
		||||
     */
 | 
			
		||||
    public function setClient($client);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get client
 | 
			
		||||
     *
 | 
			
		||||
     * @return mixed
 | 
			
		||||
     */
 | 
			
		||||
    public function getClient();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Set prefix
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $prefix
 | 
			
		||||
     */
 | 
			
		||||
    public function setPrefix($prefix);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get prefix
 | 
			
		||||
     *
 | 
			
		||||
     * @return string
 | 
			
		||||
     */
 | 
			
		||||
    public function getPrefix();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Set namespace
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $namespace
 | 
			
		||||
     */
 | 
			
		||||
    public function setNamespace($namespace);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get namespace
 | 
			
		||||
     *
 | 
			
		||||
     * @return string
 | 
			
		||||
     */
 | 
			
		||||
    public function getNamespace();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get full key name using the set prefix
 | 
			
		||||
     *
 | 
			
		||||
     * @param string ...
 | 
			
		||||
     *   Any numer of strings to append to path using the separator
 | 
			
		||||
     *
 | 
			
		||||
     * @return string
 | 
			
		||||
     */
 | 
			
		||||
    public function getKey();
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										655
									
								
								sites/all/modules/contrib/dev/redis/lib/Redis/Cache.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										655
									
								
								sites/all/modules/contrib/dev/redis/lib/Redis/Cache.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,655 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Because those objects will be spawned during boostrap all its configuration
 | 
			
		||||
 * must be set in the settings.php file.
 | 
			
		||||
 *
 | 
			
		||||
 * You will find the driver specific implementation in the Redis_Cache_*
 | 
			
		||||
 * classes as they may differ in how the API handles transaction, pipelining
 | 
			
		||||
 * and return values.
 | 
			
		||||
 */
 | 
			
		||||
class Redis_Cache
 | 
			
		||||
    implements DrupalCacheInterface
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * Default lifetime for permanent items.
 | 
			
		||||
     * Approximatively 1 year.
 | 
			
		||||
     */
 | 
			
		||||
    const LIFETIME_PERM_DEFAULT = 31536000;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Uses EVAL scripts to flush data when called
 | 
			
		||||
     *
 | 
			
		||||
     * This remains the default behavior and is safe until you use a single
 | 
			
		||||
     * Redis server instance and its version is >= 2.6 (older version don't
 | 
			
		||||
     * support EVAL).
 | 
			
		||||
     */
 | 
			
		||||
    const FLUSH_NORMAL = 0;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * This mode is tailored for sharded Redis servers instances usage: it
 | 
			
		||||
     * will never delete entries but only mark the latest flush timestamp
 | 
			
		||||
     * into one of the servers in the shard. It will proceed to delete on
 | 
			
		||||
     * read single entries when invalid entries are being loaded.
 | 
			
		||||
     */
 | 
			
		||||
    const FLUSH_SHARD = 3;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Same as the one above, plus attempt to do pipelining when possible.
 | 
			
		||||
     *
 | 
			
		||||
     * This is supposed to work with sharding proxies that supports
 | 
			
		||||
     * pipelining themselves, such as Twemproxy.
 | 
			
		||||
     */
 | 
			
		||||
    const FLUSH_SHARD_WITH_PIPELINING = 4;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Computed keys are let's say arround 60 characters length due to
 | 
			
		||||
     * key prefixing, which makes 1,000 keys DEL command to be something
 | 
			
		||||
     * arround 50,000 bytes length: this is huge and may not pass into
 | 
			
		||||
     * Redis, let's split this off.
 | 
			
		||||
     * Some recommend to never get higher than 1,500 bytes within the same
 | 
			
		||||
     * command which makes us forced to split this at a very low threshold:
 | 
			
		||||
     * 20 seems a safe value here (1,280 average length).
 | 
			
		||||
     */
 | 
			
		||||
    const KEY_THRESHOLD = 20;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var Redis_Cache_BackendInterface
 | 
			
		||||
     */
 | 
			
		||||
    protected $backend;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var string
 | 
			
		||||
     */
 | 
			
		||||
    protected $bin;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * When the global 'cache_lifetime' Drupal variable is set to a value, the
 | 
			
		||||
     * cache backends should not expire temporary entries by themselves per
 | 
			
		||||
     * Drupal signature. Volatile items will be dropped accordingly to their
 | 
			
		||||
     * set lifetime.
 | 
			
		||||
     *
 | 
			
		||||
     * @var boolean
 | 
			
		||||
     */
 | 
			
		||||
    protected $allowTemporaryFlush = true;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * When in shard mode, the backend cannot proceed to multiple keys
 | 
			
		||||
     * operations, and won't delete keys on flush calls.
 | 
			
		||||
     *
 | 
			
		||||
     * @var boolean
 | 
			
		||||
     */
 | 
			
		||||
    protected $isSharded = false;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * When in shard mode, the proxy may or may not support pipelining,
 | 
			
		||||
     * Twemproxy is known to support it.
 | 
			
		||||
     *
 | 
			
		||||
     * @var boolean
 | 
			
		||||
     */
 | 
			
		||||
    protected $allowPipeline = false;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Default TTL for CACHE_PERMANENT items.
 | 
			
		||||
     *
 | 
			
		||||
     * See "Default lifetime for permanent items" section of README.txt
 | 
			
		||||
     * file for a comprehensive explaination of why this exists.
 | 
			
		||||
     *
 | 
			
		||||
     * @var int
 | 
			
		||||
     */
 | 
			
		||||
    protected $permTtl = self::LIFETIME_PERM_DEFAULT;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Maximum TTL for this bin from Drupal configuration.
 | 
			
		||||
     *
 | 
			
		||||
     * @var int
 | 
			
		||||
     */
 | 
			
		||||
    protected $maxTtl = 0;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Flush permanent and volatile cached values
 | 
			
		||||
     *
 | 
			
		||||
     * @var string[]
 | 
			
		||||
     *   First value is permanent latest flush time and second value
 | 
			
		||||
     *   is volatile latest flush time
 | 
			
		||||
     */
 | 
			
		||||
    protected $flushCache = null;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Is this bin in shard mode
 | 
			
		||||
     *
 | 
			
		||||
     * @return boolean
 | 
			
		||||
     */
 | 
			
		||||
    public function isSharded()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->isSharded;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Does this bin allow pipelining through sharded environment
 | 
			
		||||
     *
 | 
			
		||||
     * @return boolean
 | 
			
		||||
     */
 | 
			
		||||
    public function allowPipeline()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->allowPipeline;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Does this bin allow temporary item flush
 | 
			
		||||
     *
 | 
			
		||||
     * @return boolean
 | 
			
		||||
     */
 | 
			
		||||
    public function allowTemporaryFlush()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->allowTemporaryFlush;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get TTL for CACHE_PERMANENT items.
 | 
			
		||||
     *
 | 
			
		||||
     * @return int
 | 
			
		||||
     *   Lifetime in seconds.
 | 
			
		||||
     */
 | 
			
		||||
    public function getPermTtl()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->permTtl;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get maximum TTL for all items.
 | 
			
		||||
     *
 | 
			
		||||
     * @return int
 | 
			
		||||
     *   Lifetime in seconds.
 | 
			
		||||
     */
 | 
			
		||||
    public function getMaxTtl()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->maxTtl;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * {@inheritdoc}
 | 
			
		||||
     */
 | 
			
		||||
    public function __construct($bin)
 | 
			
		||||
    {
 | 
			
		||||
        $this->bin = $bin;
 | 
			
		||||
 | 
			
		||||
        $className = Redis_Client::getClass(Redis_Client::REDIS_IMPL_CACHE);
 | 
			
		||||
        $this->backend = new $className(Redis_Client::getClient(), $bin, Redis_Client::getDefaultPrefix($bin));
 | 
			
		||||
 | 
			
		||||
        $this->refreshCapabilities();
 | 
			
		||||
        $this->refreshPermTtl();
 | 
			
		||||
        $this->refreshMaxTtl();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Find from Drupal variables the clear mode.
 | 
			
		||||
     */
 | 
			
		||||
    public function refreshCapabilities()
 | 
			
		||||
    {
 | 
			
		||||
        if (0 < variable_get('cache_lifetime', 0)) {
 | 
			
		||||
            // Per Drupal default behavior, when the 'cache_lifetime' variable
 | 
			
		||||
            // is set we must not flush any temporary items since they have a
 | 
			
		||||
            // life time.
 | 
			
		||||
            $this->allowTemporaryFlush = false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (null !== ($mode = variable_get('redis_flush_mode', null))) {
 | 
			
		||||
            $mode = (int)$mode;
 | 
			
		||||
        } else {
 | 
			
		||||
            $mode = self::FLUSH_NORMAL;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $this->isSharded = self::FLUSH_SHARD === $mode || self::FLUSH_SHARD_WITH_PIPELINING === $mode;
 | 
			
		||||
        $this->allowPipeline = self::FLUSH_SHARD !== $mode;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Find from Drupal variables the right permanent items TTL.
 | 
			
		||||
     */
 | 
			
		||||
    protected function refreshPermTtl()
 | 
			
		||||
    {
 | 
			
		||||
        $ttl = null;
 | 
			
		||||
        if (null === ($ttl = variable_get('redis_perm_ttl_' . $this->bin, null))) {
 | 
			
		||||
            if (null === ($ttl = variable_get('redis_perm_ttl', null))) {
 | 
			
		||||
                $ttl = self::LIFETIME_PERM_DEFAULT;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if ($ttl === (int)$ttl) {
 | 
			
		||||
            $this->permTtl = $ttl;
 | 
			
		||||
        } else {
 | 
			
		||||
            if ($iv = DateInterval::createFromDateString($ttl)) {
 | 
			
		||||
                // http://stackoverflow.com/questions/14277611/convert-dateinterval-object-to-seconds-in-php
 | 
			
		||||
                $this->permTtl = ($iv->y * 31536000 + $iv->m * 2592000 + $iv->d * 86400 + $iv->h * 3600 + $iv->i * 60 + $iv->s);
 | 
			
		||||
            } else {
 | 
			
		||||
                // Sorry but we have to log this somehow.
 | 
			
		||||
                trigger_error(sprintf("Parsed TTL '%s' has an invalid value: switching to default", $ttl));
 | 
			
		||||
                $this->permTtl = self::LIFETIME_PERM_DEFAULT;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Find from Drupal variables the maximum cache lifetime.
 | 
			
		||||
     */
 | 
			
		||||
    public function refreshMaxTtl()
 | 
			
		||||
    {
 | 
			
		||||
        // And now cache lifetime. Be aware we exclude negative values
 | 
			
		||||
        // considering those are Drupal misconfiguration.
 | 
			
		||||
        $maxTtl = variable_get('cache_lifetime', 0);
 | 
			
		||||
        if (0 < $maxTtl) {
 | 
			
		||||
            if ($maxTtl < $this->permTtl) {
 | 
			
		||||
                $this->maxTtl = $maxTtl;
 | 
			
		||||
            } else {
 | 
			
		||||
                $this->maxTtl = $this->permTtl;
 | 
			
		||||
            }
 | 
			
		||||
        } else if ($this->permTtl) {
 | 
			
		||||
            $this->maxTtl = $this->permTtl;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Set last flush time
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $permanent
 | 
			
		||||
     * @param string $volatile
 | 
			
		||||
     */
 | 
			
		||||
    public function setLastFlushTime($permanent = false, $volatile = false)
 | 
			
		||||
    {
 | 
			
		||||
        // Here we need to fetch absolute values from backend, to avoid
 | 
			
		||||
        // concurrency problems and ensure data validity.
 | 
			
		||||
        list($flushPerm, $flushVolatile) = $this->backend->getLastFlushTime();
 | 
			
		||||
 | 
			
		||||
        $checksum = $this->getValidChecksum(
 | 
			
		||||
            max(array(
 | 
			
		||||
                $flushPerm,
 | 
			
		||||
                $flushVolatile,
 | 
			
		||||
                $permanent,
 | 
			
		||||
                time(),
 | 
			
		||||
            ))
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        if ($permanent) {
 | 
			
		||||
            $this->backend->setLastFlushTimeFor($checksum, false);
 | 
			
		||||
            $this->backend->setLastFlushTimeFor($checksum, true);
 | 
			
		||||
            $this->flushCache = array($checksum, $checksum);
 | 
			
		||||
        } else if ($volatile) {
 | 
			
		||||
            $this->backend->setLastFlushTimeFor($checksum, true);
 | 
			
		||||
            $this->flushCache = array($flushPerm, $checksum);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get latest flush time
 | 
			
		||||
     *
 | 
			
		||||
     * @return string[]
 | 
			
		||||
     *   First value is the latest flush time for permanent entries checksum,
 | 
			
		||||
     *   second value is the latest flush time for volatile entries checksum.
 | 
			
		||||
     */
 | 
			
		||||
    public function getLastFlushTime()
 | 
			
		||||
    {
 | 
			
		||||
        if (!$this->flushCache) {
 | 
			
		||||
            $this->flushCache = $this->backend->getLastFlushTime();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
         // At the very first hit, we might not have the timestamps set, thus
 | 
			
		||||
         // we need to create them to avoid our entry being considered as
 | 
			
		||||
         // invalid
 | 
			
		||||
        if (!$this->flushCache[0]) {
 | 
			
		||||
            $this->setLastFlushTime(true, true);
 | 
			
		||||
        } else if (!$this->flushCache[1]) {
 | 
			
		||||
            $this->setLastFlushTime(false, true);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $this->flushCache;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Create cache entry
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $cid
 | 
			
		||||
     * @param mixed $data
 | 
			
		||||
     *
 | 
			
		||||
     * @return array
 | 
			
		||||
     */
 | 
			
		||||
    protected function createEntryHash($cid, $data, $expire = CACHE_PERMANENT)
 | 
			
		||||
    {
 | 
			
		||||
        list($flushPerm, $flushVolatile) = $this->getLastFlushTime();
 | 
			
		||||
 | 
			
		||||
        if (CACHE_TEMPORARY === $expire) {
 | 
			
		||||
            $validityThreshold = max(array($flushVolatile, $flushPerm));
 | 
			
		||||
        } else {
 | 
			
		||||
            $validityThreshold = $flushPerm;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $time = $this->getValidChecksum($validityThreshold);
 | 
			
		||||
 | 
			
		||||
        $hash = array(
 | 
			
		||||
            'cid'     => $cid,
 | 
			
		||||
            'created' => $time,
 | 
			
		||||
            'expire'  => $expire,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // Let Redis handle the data types itself.
 | 
			
		||||
        if (!is_string($data)) {
 | 
			
		||||
            $hash['data'] = serialize($data);
 | 
			
		||||
            $hash['serialized'] = 1;
 | 
			
		||||
        } else {
 | 
			
		||||
            $hash['data'] = $data;
 | 
			
		||||
            $hash['serialized'] = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $hash;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Expand cache entry from fetched data
 | 
			
		||||
     *
 | 
			
		||||
     * @param array $values
 | 
			
		||||
     *   Raw values fetched from Redis server data
 | 
			
		||||
     *
 | 
			
		||||
     * @return array
 | 
			
		||||
     *   Or FALSE if entry is invalid
 | 
			
		||||
     */
 | 
			
		||||
    protected function expandEntry(array $values, $flushPerm, $flushVolatile)
 | 
			
		||||
    {
 | 
			
		||||
        // Check for entry being valid.
 | 
			
		||||
        if (empty($values['cid'])) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // This ensures backward compatibility with older version of
 | 
			
		||||
        // this module's data still stored in Redis.
 | 
			
		||||
        if (isset($values['expire'])) {
 | 
			
		||||
            $expire = (int)$values['expire'];
 | 
			
		||||
            // Ensure the entry is valid and have not expired.
 | 
			
		||||
            if ($expire !== CACHE_PERMANENT && $expire !== CACHE_TEMPORARY && $expire <= time()) {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Ensure the entry does not predate the last flush time.
 | 
			
		||||
        if ($this->allowTemporaryFlush && !empty($values['volatile'])) {
 | 
			
		||||
            $validityThreshold = max(array($flushPerm, $flushVolatile));
 | 
			
		||||
        } else {
 | 
			
		||||
            $validityThreshold = $flushPerm;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($values['created'] <= $validityThreshold) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $entry = (object)$values;
 | 
			
		||||
 | 
			
		||||
        // Reduce the checksum to the real timestamp part
 | 
			
		||||
        $entry->created = (int)$entry->created;
 | 
			
		||||
 | 
			
		||||
        if ($entry->serialized) {
 | 
			
		||||
            $entry->data = unserialize($entry->data);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $entry;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * {@inheritdoc}
 | 
			
		||||
     */
 | 
			
		||||
    public function get($cid)
 | 
			
		||||
    {
 | 
			
		||||
        $values = $this->backend->get($cid);
 | 
			
		||||
 | 
			
		||||
        if (empty($values)) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        list($flushPerm, $flushVolatile) = $this->getLastFlushTime();
 | 
			
		||||
 | 
			
		||||
        $entry = $this->expandEntry($values, $flushPerm, $flushVolatile);
 | 
			
		||||
 | 
			
		||||
        if (!$entry) { // This entry exists but is invalid.
 | 
			
		||||
            $this->backend->delete($cid);
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $entry;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * {@inheritdoc}
 | 
			
		||||
     */
 | 
			
		||||
    public function getMultiple(&$cids)
 | 
			
		||||
    {
 | 
			
		||||
        $ret    = array();
 | 
			
		||||
        $delete = array();
 | 
			
		||||
 | 
			
		||||
        if (!$this->allowPipeline) {
 | 
			
		||||
            $entries = array();
 | 
			
		||||
            foreach ($cids as $cid) {
 | 
			
		||||
                if ($entry = $this->backend->get($cid)) {
 | 
			
		||||
                    $entries[$cid] = $entry;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            $entries = $this->backend->getMultiple($cids);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        list($flushPerm, $flushVolatile) = $this->getLastFlushTime();
 | 
			
		||||
 | 
			
		||||
        foreach ($cids as $key => $cid) {
 | 
			
		||||
            if (!empty($entries[$cid])) {
 | 
			
		||||
                $entry = $this->expandEntry($entries[$cid], $flushPerm, $flushVolatile);
 | 
			
		||||
            } else {
 | 
			
		||||
                $entry = null;
 | 
			
		||||
            }
 | 
			
		||||
            if (empty($entry)) {
 | 
			
		||||
                $delete[] = $cid;
 | 
			
		||||
            } else {
 | 
			
		||||
                $ret[$cid] = $entry;
 | 
			
		||||
                unset($cids[$key]);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!empty($delete)) {
 | 
			
		||||
            if ($this->allowPipeline) {
 | 
			
		||||
                foreach ($delete as $id) {
 | 
			
		||||
                    $this->backend->delete($id);
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                $this->backend->deleteMultiple($delete);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * {@inheritdoc}
 | 
			
		||||
     */
 | 
			
		||||
    public function set($cid, $data, $expire = CACHE_PERMANENT)
 | 
			
		||||
    {
 | 
			
		||||
        $hash   = $this->createEntryHash($cid, $data, $expire);
 | 
			
		||||
        $maxTtl = $this->getMaxTtl();
 | 
			
		||||
 | 
			
		||||
        switch ($expire) {
 | 
			
		||||
 | 
			
		||||
            case CACHE_PERMANENT:
 | 
			
		||||
                $this->backend->set($cid, $hash, $maxTtl, false);
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            case CACHE_TEMPORARY:
 | 
			
		||||
                $this->backend->set($cid, $hash, $maxTtl, true);
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            default:
 | 
			
		||||
                $ttl = $expire - time();
 | 
			
		||||
                // Ensure $expire consistency
 | 
			
		||||
                if ($ttl <= 0) {
 | 
			
		||||
                    // Entry has already expired, but we may have a stalled
 | 
			
		||||
                    // older cache entry remaining there, ensure it wont
 | 
			
		||||
                    // happen by doing a preventive delete
 | 
			
		||||
                    $this->backend->delete($cid);
 | 
			
		||||
                } else {
 | 
			
		||||
                    if ($maxTtl && $maxTtl < $ttl) {
 | 
			
		||||
                        $ttl = $maxTtl;
 | 
			
		||||
                    }
 | 
			
		||||
                    $this->backend->set($cid, $hash, $ttl, false);
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * {@inheritdoc}
 | 
			
		||||
     */
 | 
			
		||||
    public function clear($cid = null, $wildcard = false)
 | 
			
		||||
    {
 | 
			
		||||
        if (null === $cid && !$wildcard) {
 | 
			
		||||
            // Drupal asked for volatile entries flush, this will happen
 | 
			
		||||
            // during cron run, mostly
 | 
			
		||||
            $this->setLastFlushTime(false, true);
 | 
			
		||||
 | 
			
		||||
            if (!$this->isSharded && $this->allowTemporaryFlush) {
 | 
			
		||||
                $this->backend->flushVolatile();
 | 
			
		||||
            }
 | 
			
		||||
        } else if ($wildcard) {
 | 
			
		||||
            if (empty($cid)) {
 | 
			
		||||
                // This seems to be an error, just do nothing.
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if ('*' === $cid) {
 | 
			
		||||
                // Use max() to ensure we invalidate both correctly
 | 
			
		||||
                $this->setLastFlushTime(true);
 | 
			
		||||
 | 
			
		||||
                if (!$this->isSharded) {
 | 
			
		||||
                      $this->backend->flush();
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                if (!$this->isSharded) {
 | 
			
		||||
                    $this->backend->deleteByPrefix($cid);
 | 
			
		||||
                } else {
 | 
			
		||||
                    // @todo This needs a map algorithm the same way memcache
 | 
			
		||||
                    // module implemented it for invalidity by prefixes. This
 | 
			
		||||
                    // is a very stupid fallback
 | 
			
		||||
                    $this->setLastFlushTime(true);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        } else if (is_array($cid)) {
 | 
			
		||||
            $this->backend->deleteMultiple($cid);
 | 
			
		||||
        } else {
 | 
			
		||||
            $this->backend->delete($cid);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function isEmpty()
 | 
			
		||||
    {
 | 
			
		||||
       return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * From the given timestamp build an incremental safe time-based identifier.
 | 
			
		||||
     *
 | 
			
		||||
     * Due to potential accidental cache wipes, when a server goes down in the
 | 
			
		||||
     * cluster or when a server triggers its LRU algorithm wipe-out, keys that
 | 
			
		||||
     * matches flush or tags checksum might be dropped.
 | 
			
		||||
     *
 | 
			
		||||
     * Per default, each new inserted tag will trigger a checksum computation to
 | 
			
		||||
     * be stored in the Redis server as a timestamp. In order to ensure a checksum
 | 
			
		||||
     * validity a simple comparison between the tag checksum and the cache entry
 | 
			
		||||
     * checksum will tell us if the entry pre-dates the current checksum or not,
 | 
			
		||||
     * thus telling us its state. The main problem we experience is that Redis
 | 
			
		||||
     * is being so fast it is able to create and drop entries at same second,
 | 
			
		||||
     * sometime even the same micro second. The only safe way to avoid conflicts
 | 
			
		||||
     * is to checksum using an arbitrary computed number (a sequence).
 | 
			
		||||
     *
 | 
			
		||||
     * Drupal core does exactly this thus tags checksums are additions of each tag
 | 
			
		||||
     * individual checksum; each tag checksum is a independent arbitrary serial
 | 
			
		||||
     * that gets incremented starting with 0 (no invalidation done yet) to n (n
 | 
			
		||||
     * invalidations) which grows over time. This way the checksum computation
 | 
			
		||||
     * always rises and we have a sensible default that works in all cases.
 | 
			
		||||
     *
 | 
			
		||||
     * This model works as long as you can ensure consistency for the serial
 | 
			
		||||
     * storage over time. Nevertheless, as explained upper, in our case this
 | 
			
		||||
     * serial might be dropped at some point for various valid technical reasons:
 | 
			
		||||
     * if we start over to 0, we may accidentally compute a checksum which already
 | 
			
		||||
     * existed in the past and make invalid entries turn back to valid again.
 | 
			
		||||
     *
 | 
			
		||||
     * In order to prevent this behavior, using a timestamp as part of the serial
 | 
			
		||||
     * ensures that we won't experience this problem in a time range wider than a
 | 
			
		||||
     * single second, which is safe enough for us. But using timestamp creates a
 | 
			
		||||
     * new problem: Redis is so fast that we can set or delete hundreds of entries
 | 
			
		||||
     * easily during the same second: an entry created then invalidated the same
 | 
			
		||||
     * second will create false positives (entry is being considered as valid) -
 | 
			
		||||
     * note that depending on the check algorithm, false negative may also happen
 | 
			
		||||
     * the same way. Therefore we need to have an abitrary serial value to be
 | 
			
		||||
     * incremented in order to enforce our checks to be more strict.
 | 
			
		||||
     *
 | 
			
		||||
     * The solution to both the first (the need for a time based checksum in case
 | 
			
		||||
     * of checksum data being dropped) and the second (the need to have an
 | 
			
		||||
     * arbitrary predictible serial value to avoid false positives or negatives)
 | 
			
		||||
     * we are combining the two: every checksum will be built this way:
 | 
			
		||||
     *
 | 
			
		||||
     *   UNIXTIMESTAMP.SERIAL
 | 
			
		||||
     *
 | 
			
		||||
     * For example:
 | 
			
		||||
     *
 | 
			
		||||
     *   1429789217.017
 | 
			
		||||
     *
 | 
			
		||||
     * will reprensent the 17th invalidation of the 1429789217 exact second which
 | 
			
		||||
     * happened while writing this documentation. The next tag being invalidated
 | 
			
		||||
     * the same second will then have this checksum:
 | 
			
		||||
     *
 | 
			
		||||
     *   1429789217.018
 | 
			
		||||
     *
 | 
			
		||||
     * And so on...
 | 
			
		||||
     *
 | 
			
		||||
     * In order to make it consitent with PHP string and float comparison we need
 | 
			
		||||
     * to set fixed precision over the decimal, and store as a string to avoid
 | 
			
		||||
     * possible float precision problems when comparing.
 | 
			
		||||
     *
 | 
			
		||||
     * This algorithm is not fully failsafe, but allows us to proceed to 1000
 | 
			
		||||
     * operations on the same checksum during the same second, which is a
 | 
			
		||||
     * sufficiently great value to reduce the conflict probability to almost
 | 
			
		||||
     * zero for most uses cases.
 | 
			
		||||
     *
 | 
			
		||||
     * @param int|string $timestamp
 | 
			
		||||
     *   "TIMESTAMP[.INCREMENT]" string
 | 
			
		||||
     *
 | 
			
		||||
     * @return string
 | 
			
		||||
     *   The next "TIMESTAMP.INCREMENT" string.
 | 
			
		||||
     */
 | 
			
		||||
    public function getNextIncrement($timestamp = null)
 | 
			
		||||
    {
 | 
			
		||||
        if (!$timestamp) {
 | 
			
		||||
            return time() . '.000';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (false !== ($pos = strpos($timestamp, '.'))) {
 | 
			
		||||
            $inc = substr($timestamp, $pos + 1, 3);
 | 
			
		||||
 | 
			
		||||
            return ((int)$timestamp) . '.' . str_pad($inc + 1, 3, '0', STR_PAD_LEFT);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $timestamp . '.000';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get valid checksum
 | 
			
		||||
     *
 | 
			
		||||
     * @param int|string $previous
 | 
			
		||||
     *   "TIMESTAMP[.INCREMENT]" string
 | 
			
		||||
     *
 | 
			
		||||
     * @return string
 | 
			
		||||
     *   The next "TIMESTAMP.INCREMENT" string.
 | 
			
		||||
     *
 | 
			
		||||
     * @see Redis_Cache::getNextIncrement()
 | 
			
		||||
     */
 | 
			
		||||
    public function getValidChecksum($previous = null)
 | 
			
		||||
    {
 | 
			
		||||
        if (time() === (int)$previous) {
 | 
			
		||||
            return $this->getNextIncrement($previous);
 | 
			
		||||
        } else {
 | 
			
		||||
            return $this->getNextIncrement();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,102 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Real cache backend primitives. This functions will be used by the
 | 
			
		||||
 * Redis_Cache wrapper class that implements the high-level logic that
 | 
			
		||||
 * allows us to be Drupal compatible.
 | 
			
		||||
 */
 | 
			
		||||
interface Redis_Cache_BackendInterface extends Redis_BackendInterface
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * Defaut constructor
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $namespace
 | 
			
		||||
     */
 | 
			
		||||
    public function __construct($client, $namespace);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get namespace
 | 
			
		||||
     *
 | 
			
		||||
     * @return string
 | 
			
		||||
     */
 | 
			
		||||
    public function getNamespace();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Set last flush time
 | 
			
		||||
     *
 | 
			
		||||
     * @param int $time
 | 
			
		||||
     * @param boolean $volatile
 | 
			
		||||
     */
 | 
			
		||||
    public function setLastFlushTimeFor($time, $volatile = false);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get last flush time
 | 
			
		||||
     *
 | 
			
		||||
     * @return int[]
 | 
			
		||||
     *   First value is for non-volatile items, second value is for volatile items.
 | 
			
		||||
     */
 | 
			
		||||
    public function getLastFlushTime();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get a single entry
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $id
 | 
			
		||||
     *
 | 
			
		||||
     * @return stdClass
 | 
			
		||||
     *   Cache entry or false if the entry does not exists.
 | 
			
		||||
     */
 | 
			
		||||
    public function get($id);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get multiple entries
 | 
			
		||||
     *
 | 
			
		||||
     * @param string[] $idList
 | 
			
		||||
     *
 | 
			
		||||
     * @return stdClass[]
 | 
			
		||||
     *   Existing cache entries keyed by id,
 | 
			
		||||
     */
 | 
			
		||||
    public function getMultiple(array $idList);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Set a single entry
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $id
 | 
			
		||||
     * @param mixed $data
 | 
			
		||||
     * @param int $ttl
 | 
			
		||||
     * @param boolean $volatile
 | 
			
		||||
     */
 | 
			
		||||
    public function set($id, $data, $ttl = null, $volatile = false);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Delete a single entry
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $cid
 | 
			
		||||
     */
 | 
			
		||||
    public function delete($id);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Delete multiple entries
 | 
			
		||||
     *
 | 
			
		||||
     * This method should not use a single DEL command but use a pipeline instead
 | 
			
		||||
     *
 | 
			
		||||
     * @param array $idList
 | 
			
		||||
     */
 | 
			
		||||
    public function deleteMultiple(array $idList);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Delete entries by prefix
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $prefix
 | 
			
		||||
     */
 | 
			
		||||
    public function deleteByPrefix($prefix);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Flush all entries
 | 
			
		||||
     */
 | 
			
		||||
    public function flush();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Flush all entries marked as temporary
 | 
			
		||||
     */
 | 
			
		||||
    public function flushVolatile();
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										39
									
								
								sites/all/modules/contrib/dev/redis/lib/Redis/Cache/Base.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								sites/all/modules/contrib/dev/redis/lib/Redis/Cache/Base.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,39 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @todo
 | 
			
		||||
 *   - Improve lua scripts by using SCAN family commands
 | 
			
		||||
 *   - Deambiguate why we need the namespace only for flush*() operations
 | 
			
		||||
 *   - Implement the isEmpty() method by using SCAN or KEYS
 | 
			
		||||
 */
 | 
			
		||||
abstract class Redis_Cache_Base extends Redis_AbstractBackend
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * Lastest cache flush KEY name
 | 
			
		||||
     */
 | 
			
		||||
    const LAST_FLUSH_KEY = '_last_flush';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Delete by prefix lua script
 | 
			
		||||
     */
 | 
			
		||||
    const EVAL_DELETE_PREFIX = <<<EOT
 | 
			
		||||
local keys = redis.call("KEYS", ARGV[1])
 | 
			
		||||
for i, k in ipairs(keys) do
 | 
			
		||||
    redis.call("DEL", k)
 | 
			
		||||
end
 | 
			
		||||
return 1
 | 
			
		||||
EOT;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Delete volatile by prefix lua script
 | 
			
		||||
     */
 | 
			
		||||
    const EVAL_DELETE_VOLATILE = <<<EOT
 | 
			
		||||
local keys = redis.call('KEYS', ARGV[1])
 | 
			
		||||
for i, k in ipairs(keys) do
 | 
			
		||||
    if "1" == redis.call("HGET", k, "volatile") then
 | 
			
		||||
        redis.call("DEL", k)
 | 
			
		||||
    end
 | 
			
		||||
end
 | 
			
		||||
return 1
 | 
			
		||||
EOT;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										149
									
								
								sites/all/modules/contrib/dev/redis/lib/Redis/Cache/PhpRedis.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										149
									
								
								sites/all/modules/contrib/dev/redis/lib/Redis/Cache/PhpRedis.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,149 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Predis cache backend.
 | 
			
		||||
 */
 | 
			
		||||
class Redis_Cache_PhpRedis extends Redis_Cache_Base
 | 
			
		||||
{
 | 
			
		||||
    public function setLastFlushTimeFor($time, $volatile = false)
 | 
			
		||||
    {
 | 
			
		||||
        $client = $this->getClient();
 | 
			
		||||
        $key    = $this->getKey(self::LAST_FLUSH_KEY);
 | 
			
		||||
 | 
			
		||||
        if ($volatile) {
 | 
			
		||||
            $client->hset($key, 'volatile', $time);
 | 
			
		||||
        } else {
 | 
			
		||||
            $client->hmset($key, array(
 | 
			
		||||
                'permanent' => $time,
 | 
			
		||||
                'volatile' => $time,
 | 
			
		||||
            ));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getLastFlushTime()
 | 
			
		||||
    {
 | 
			
		||||
        $client = $this->getClient();
 | 
			
		||||
        $key    = $this->getKey(self::LAST_FLUSH_KEY);
 | 
			
		||||
        $values = $client->hmget($key, array("permanent", "volatile"));
 | 
			
		||||
 | 
			
		||||
        if (empty($values) || !is_array($values)) {
 | 
			
		||||
            $ret = array(0, 0);
 | 
			
		||||
        } else {
 | 
			
		||||
            if (empty($values['permanent'])) {
 | 
			
		||||
                $values['permanent'] = 0;
 | 
			
		||||
            }
 | 
			
		||||
            if (empty($values['volatile'])) {
 | 
			
		||||
                $values['volatile'] = 0;
 | 
			
		||||
            }
 | 
			
		||||
            $ret = array($values['permanent'], $values['volatile']);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function get($id)
 | 
			
		||||
    {
 | 
			
		||||
        $client = $this->getClient();
 | 
			
		||||
        $key    = $this->getKey($id);
 | 
			
		||||
        $values = $client->hgetall($key);
 | 
			
		||||
 | 
			
		||||
        // Recent versions of PhpRedis will return the Redis instance
 | 
			
		||||
        // instead of an empty array when the HGETALL target key does
 | 
			
		||||
        // not exists. I see what you did there.
 | 
			
		||||
        if (empty($values) || !is_array($values)) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $values;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getMultiple(array $idList)
 | 
			
		||||
    {
 | 
			
		||||
        $client = $this->getClient();
 | 
			
		||||
 | 
			
		||||
        $ret = array();
 | 
			
		||||
 | 
			
		||||
        $pipe = $client->multi(Redis::PIPELINE);
 | 
			
		||||
        foreach ($idList as $id) {
 | 
			
		||||
            $pipe->hgetall($this->getKey($id));
 | 
			
		||||
        }
 | 
			
		||||
        $replies = $pipe->exec();
 | 
			
		||||
 | 
			
		||||
        foreach (array_values($idList) as $line => $id) {
 | 
			
		||||
            if (!empty($replies[$line]) && is_array($replies[$line])) {
 | 
			
		||||
                $ret[$id] = $replies[$line];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function set($id, $data, $ttl = null, $volatile = false)
 | 
			
		||||
    {
 | 
			
		||||
        // Ensure TTL consistency: if the caller gives us an expiry timestamp
 | 
			
		||||
        // in the past the key will expire now and will never be read.
 | 
			
		||||
        // Behavior between Predis and PhpRedis seems to change here: when
 | 
			
		||||
        // setting a negative expire time, PhpRedis seems to ignore the
 | 
			
		||||
        // command and leave the key permanent.
 | 
			
		||||
        if (null !== $ttl && $ttl <= 0) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $data['volatile'] = (int)$volatile;
 | 
			
		||||
 | 
			
		||||
        $client = $this->getClient();
 | 
			
		||||
        $key    = $this->getKey($id);
 | 
			
		||||
 | 
			
		||||
        $pipe = $client->multi(Redis::PIPELINE);
 | 
			
		||||
        $pipe->hmset($key, $data);
 | 
			
		||||
 | 
			
		||||
        if (null !== $ttl) {
 | 
			
		||||
            $pipe->expire($key, $ttl);
 | 
			
		||||
        }
 | 
			
		||||
        $pipe->exec();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function delete($id)
 | 
			
		||||
    {
 | 
			
		||||
        $this->getClient()->del($this->getKey($id));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function deleteMultiple(array $idList)
 | 
			
		||||
    {
 | 
			
		||||
        $client = $this->getClient();
 | 
			
		||||
 | 
			
		||||
        $pipe = $client->multi(Redis::PIPELINE);
 | 
			
		||||
        foreach ($idList as $id) {
 | 
			
		||||
            $pipe->del($this->getKey($id));
 | 
			
		||||
        }
 | 
			
		||||
        // Don't care if something failed.
 | 
			
		||||
        $pipe->exec();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function deleteByPrefix($prefix)
 | 
			
		||||
    {
 | 
			
		||||
        $client = $this->getClient();
 | 
			
		||||
        $ret = $client->eval(self::EVAL_DELETE_PREFIX, array($this->getKey($prefix . '*')));
 | 
			
		||||
        if (1 != $ret) {
 | 
			
		||||
            trigger_error(sprintf("EVAL failed: %s", $client->getLastError()), E_USER_ERROR);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function flush()
 | 
			
		||||
    {
 | 
			
		||||
        $client = $this->getClient();
 | 
			
		||||
        $ret = $client->eval(self::EVAL_DELETE_PREFIX, array($this->getKey('*')));
 | 
			
		||||
        if (1 != $ret) {
 | 
			
		||||
            trigger_error(sprintf("EVAL failed: %s", $client->getLastError()), E_USER_ERROR);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function flushVolatile()
 | 
			
		||||
    {
 | 
			
		||||
        $client = $this->getClient();
 | 
			
		||||
        $ret = $client->eval(self::EVAL_DELETE_VOLATILE, array($this->getKey('*')));
 | 
			
		||||
        if (1 != $ret) {
 | 
			
		||||
            trigger_error(sprintf("EVAL failed: %s", $client->getLastError()), E_USER_ERROR);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										145
									
								
								sites/all/modules/contrib/dev/redis/lib/Redis/Cache/Predis.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										145
									
								
								sites/all/modules/contrib/dev/redis/lib/Redis/Cache/Predis.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,145 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Predis cache backend.
 | 
			
		||||
 */
 | 
			
		||||
class Redis_Cache_Predis extends Redis_Cache_Base
 | 
			
		||||
{
 | 
			
		||||
    public function setLastFlushTimeFor($time, $volatile = false)
 | 
			
		||||
    {
 | 
			
		||||
        $client = $this->getClient();
 | 
			
		||||
        $key    = $this->getKey(self::LAST_FLUSH_KEY);
 | 
			
		||||
 | 
			
		||||
        if ($volatile) {
 | 
			
		||||
            $client->hset($key, 'volatile', $time);
 | 
			
		||||
        } else {
 | 
			
		||||
            $client->hmset($key, array(
 | 
			
		||||
                'permanent' => $time,
 | 
			
		||||
                'volatile' => $time,
 | 
			
		||||
            ));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getLastFlushTime()
 | 
			
		||||
    {
 | 
			
		||||
        $client = $this->getClient();
 | 
			
		||||
        $key    = $this->getKey(self::LAST_FLUSH_KEY);
 | 
			
		||||
        $values = $client->hmget($key, array("permanent", "volatile"));
 | 
			
		||||
 | 
			
		||||
        if (empty($values) || !is_array($values)) {
 | 
			
		||||
            $values = array(0, 0);
 | 
			
		||||
        } else {
 | 
			
		||||
            if (empty($values[0])) {
 | 
			
		||||
                $values[0] = 0;
 | 
			
		||||
            }
 | 
			
		||||
            if (empty($values[1])) {
 | 
			
		||||
                $values[1] = 0;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $values;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function get($id)
 | 
			
		||||
    {
 | 
			
		||||
        $client = $this->getClient();
 | 
			
		||||
        $key    = $this->getKey($id);
 | 
			
		||||
        $values = $client->hgetall($key);
 | 
			
		||||
 | 
			
		||||
        // Recent versions of PhpRedis will return the Redis instance
 | 
			
		||||
        // instead of an empty array when the HGETALL target key does
 | 
			
		||||
        // not exists. I see what you did there.
 | 
			
		||||
        if (empty($values) || !is_array($values)) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $values;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getMultiple(array $idList)
 | 
			
		||||
    {
 | 
			
		||||
        $ret = array();
 | 
			
		||||
 | 
			
		||||
        $pipe = $this->getClient()->pipeline();
 | 
			
		||||
        foreach ($idList as $id) {
 | 
			
		||||
            $pipe->hgetall($this->getKey($id));
 | 
			
		||||
        }
 | 
			
		||||
        $replies = $pipe->execute();
 | 
			
		||||
 | 
			
		||||
        foreach (array_values($idList) as $line => $id) {
 | 
			
		||||
            // HGETALL signature seems to differ depending on Predis versions.
 | 
			
		||||
            // This was found just after Predis update. Even though I'm not sure
 | 
			
		||||
            // this comes from Predis or just because we're misusing it.
 | 
			
		||||
            if (!empty($replies[$line]) && is_array($replies[$line])) {
 | 
			
		||||
                $ret[$id] = $replies[$line];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function set($id, $data, $ttl = null, $volatile = false)
 | 
			
		||||
    {
 | 
			
		||||
        // Ensure TTL consistency: if the caller gives us an expiry timestamp
 | 
			
		||||
        // in the past the key will expire now and will never be read.
 | 
			
		||||
        // Behavior between Predis and PhpRedis seems to change here: when
 | 
			
		||||
        // setting a negative expire time, PhpRedis seems to ignore the
 | 
			
		||||
        // command and leave the key permanent.
 | 
			
		||||
        if (null !== $ttl && $ttl <= 0) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $key = $this->getKey($id);
 | 
			
		||||
 | 
			
		||||
        $data['volatile'] = (int)$volatile;
 | 
			
		||||
 | 
			
		||||
        $pipe = $this->getClient()->pipeline();
 | 
			
		||||
        $pipe->hmset($key, $data);
 | 
			
		||||
        if (null !== $ttl) {
 | 
			
		||||
            $pipe->expire($key, $ttl);
 | 
			
		||||
        }
 | 
			
		||||
        $pipe->execute();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function delete($id)
 | 
			
		||||
    {
 | 
			
		||||
        $client = $this->getClient();
 | 
			
		||||
        $client->del($this->getKey($id));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function deleteMultiple(array $idList)
 | 
			
		||||
    {
 | 
			
		||||
        $pipe = $this->getClient()->pipeline();
 | 
			
		||||
        foreach ($idList as $id) {
 | 
			
		||||
            $pipe->del($this->getKey($id));
 | 
			
		||||
        }
 | 
			
		||||
        $pipe->execute();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function deleteByPrefix($prefix)
 | 
			
		||||
    {
 | 
			
		||||
        $client = $this->getClient();
 | 
			
		||||
        $ret = $client->eval(self::EVAL_DELETE_PREFIX, 0, $this->getKey($prefix . '*'));
 | 
			
		||||
        if (1 != $ret) {
 | 
			
		||||
            trigger_error(sprintf("EVAL failed: %s", $client->getLastError()), E_USER_ERROR);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function flush()
 | 
			
		||||
    {
 | 
			
		||||
        $client = $this->getClient();
 | 
			
		||||
        $ret = $client->eval(self::EVAL_DELETE_PREFIX, 0, $this->getKey('*'));
 | 
			
		||||
        if (1 != $ret) {
 | 
			
		||||
            trigger_error(sprintf("EVAL failed: %s", $client->getLastError()), E_USER_ERROR);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function flushVolatile()
 | 
			
		||||
    {
 | 
			
		||||
        $client = $this->getClient();
 | 
			
		||||
        $ret = $client->eval(self::EVAL_DELETE_VOLATILE, 0, $this->getKey('*'));
 | 
			
		||||
        if (1 != $ret) {
 | 
			
		||||
            trigger_error(sprintf("EVAL failed: %s", $client->getLastError()), E_USER_ERROR);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,66 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This typically brings 80..85% compression in ~20ms/mb write, 5ms/mb read.
 | 
			
		||||
 */
 | 
			
		||||
class Redis_CacheCompressed extends Redis_Cache implements DrupalCacheInterface
 | 
			
		||||
{
 | 
			
		||||
    private $compressionSizeThreshold = 100;
 | 
			
		||||
    private $compressionRatio = 1;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * {@inheritdoc}
 | 
			
		||||
     */
 | 
			
		||||
    public function __construct($bin)
 | 
			
		||||
    {
 | 
			
		||||
        parent::__construct($bin);
 | 
			
		||||
 | 
			
		||||
        $this->compressionSizeThreshold = (int)variable_get('cache_compression_size_threshold', 100);
 | 
			
		||||
        if ($this->compressionSizeThreshold < 0) {
 | 
			
		||||
            trigger_error('cache_compression_size_threshold must be 0 or a positive integer, negative value found, switching back to default 100', E_USER_WARNING);
 | 
			
		||||
            $this->compressionSizeThreshold = 100;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Minimum compression level (1) has good ratio in low time.
 | 
			
		||||
        $this->compressionRatio = (int)variable_get('cache_compression_ratio', 1);
 | 
			
		||||
        if ($this->compressionRatio < 1 || 9 < $this->compressionRatio) {
 | 
			
		||||
            trigger_error('cache_compression_ratio must be between 1 and 9, out of bounds value found, switching back to default 1', E_USER_WARNING);
 | 
			
		||||
            $this->compressionRatio = 1;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * {@inheritdoc}
 | 
			
		||||
     */
 | 
			
		||||
    protected function createEntryHash($cid, $data, $expire = CACHE_PERMANENT)
 | 
			
		||||
    {
 | 
			
		||||
        $hash = parent::createEntryHash($cid, $data, $expire);
 | 
			
		||||
 | 
			
		||||
        // Empiric level when compression makes sense.
 | 
			
		||||
        if (!$this->compressionSizeThreshold || strlen($hash['data']) > $this->compressionSizeThreshold) {
 | 
			
		||||
 | 
			
		||||
            $hash['data'] = gzcompress($hash['data'], $this->compressionRatio);
 | 
			
		||||
            $hash['compressed'] = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $hash;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * {@inheritdoc}
 | 
			
		||||
     */
 | 
			
		||||
    protected function expandEntry(array $values, $flushPerm, $flushVolatile)
 | 
			
		||||
    {
 | 
			
		||||
        if (!empty($values['data']) && !empty($values['compressed'])) {
 | 
			
		||||
            // Uncompress, suppress warnings e.g. for broken CRC32.
 | 
			
		||||
            $values['data'] = @gzuncompress($values['data']);
 | 
			
		||||
 | 
			
		||||
            // In such cases, void the cache entry.
 | 
			
		||||
            if ($values['data'] === false) {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return parent::expandEntry($values, $flushPerm, $flushVolatile);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										241
									
								
								sites/all/modules/contrib/dev/redis/lib/Redis/Client.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										241
									
								
								sites/all/modules/contrib/dev/redis/lib/Redis/Client.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,241 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
// It may happen we get here with no autoloader set during the Drupal core
 | 
			
		||||
// early bootstrap phase, at cache backend init time.
 | 
			
		||||
if (!interface_exists('Redis_Client_FactoryInterface')) {
 | 
			
		||||
    require_once dirname(__FILE__) . '/Client/FactoryInterface.php';
 | 
			
		||||
    require_once dirname(__FILE__) . '/Client/Manager.php';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This static class only reason to exist is to tie Drupal global
 | 
			
		||||
 * configuration to OOP driven code of this module: it will handle
 | 
			
		||||
 * everything that must be read from global configuration and let
 | 
			
		||||
 * other components live without any existence of it
 | 
			
		||||
 */
 | 
			
		||||
class Redis_Client
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * Cache implementation namespace.
 | 
			
		||||
     */
 | 
			
		||||
    const REDIS_IMPL_CACHE = 'Redis_Cache_';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Lock implementation namespace.
 | 
			
		||||
     */
 | 
			
		||||
    const REDIS_IMPL_LOCK = 'Redis_Lock_';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Cache implementation namespace.
 | 
			
		||||
     */
 | 
			
		||||
    const REDIS_IMPL_QUEUE = 'Redis_Queue_';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Path implementation namespace.
 | 
			
		||||
     */
 | 
			
		||||
    const REDIS_IMPL_PATH = 'Redis_Path_';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Client factory implementation namespace.
 | 
			
		||||
     */
 | 
			
		||||
    const REDIS_IMPL_CLIENT = 'Redis_Client_';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var Redis_Client_Manager
 | 
			
		||||
     */
 | 
			
		||||
    private static $manager;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var string
 | 
			
		||||
     */
 | 
			
		||||
    static protected $globalPrefix;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get site default global prefix
 | 
			
		||||
     *
 | 
			
		||||
     * @return string
 | 
			
		||||
     */
 | 
			
		||||
    static public function getGlobalPrefix()
 | 
			
		||||
    {
 | 
			
		||||
        // Provide a fallback for multisite. This is on purpose not inside the
 | 
			
		||||
        // getPrefixForBin() function in order to decouple the unified prefix
 | 
			
		||||
        // variable logic and custom module related security logic, that is not
 | 
			
		||||
        // necessary for all backends. We can't just use HTTP_HOST, as multiple
 | 
			
		||||
        // hosts might be using the same database. Or, more commonly, a site
 | 
			
		||||
        // might not be a multisite at all, but might be using Drush leading to
 | 
			
		||||
        // a separate HTTP_HOST of 'default'. Likewise, we can't rely on
 | 
			
		||||
        // conf_path(), as settings.php might be modifying what database to
 | 
			
		||||
        // connect to. To mirror what core does with database caching we use
 | 
			
		||||
        // the DB credentials to inform our cache key.
 | 
			
		||||
      if (null === self::$globalPrefix) {
 | 
			
		||||
            if (isset($GLOBALS['db_url']) && is_string($GLOBALS['db_url'])) {
 | 
			
		||||
                // Drupal 6 specifics when using the cache_backport module, we
 | 
			
		||||
                // therefore cannot use \Database class to determine database
 | 
			
		||||
                // settings.
 | 
			
		||||
              self::$globalPrefix = md5($GLOBALS['db_url']);
 | 
			
		||||
            } else {
 | 
			
		||||
                require_once DRUPAL_ROOT . '/includes/database/database.inc';
 | 
			
		||||
                $dbInfo = Database::getConnectionInfo();
 | 
			
		||||
                $active = $dbInfo['default'];
 | 
			
		||||
                self::$globalPrefix = md5($active['host'] . $active['database'] . $active['prefix']['default']);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return self::$globalPrefix;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get global default prefix
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $namespace
 | 
			
		||||
     *
 | 
			
		||||
     * @return string
 | 
			
		||||
     */
 | 
			
		||||
    static public function getDefaultPrefix($namespace = null)
 | 
			
		||||
    {
 | 
			
		||||
        $ret = null;
 | 
			
		||||
 | 
			
		||||
        if (!empty($GLOBALS['drupal_test_info']['test_run_id'])) {
 | 
			
		||||
            $ret = $GLOBALS['drupal_test_info']['test_run_id'];
 | 
			
		||||
        } else {
 | 
			
		||||
            $prefixes = variable_get('cache_prefix', null);
 | 
			
		||||
 | 
			
		||||
            if (is_string($prefixes)) {
 | 
			
		||||
                // Variable can be a string which then considered as a default
 | 
			
		||||
                // behavior.
 | 
			
		||||
                $ret = $prefixes;
 | 
			
		||||
            } else if (null !== $namespace && isset($prefixes[$namespace])) {
 | 
			
		||||
                if (false !== $prefixes[$namespace]) {
 | 
			
		||||
                    // If entry is set and not false an explicit prefix is set
 | 
			
		||||
                    // for the bin.
 | 
			
		||||
                    $ret = $prefixes[$namespace];
 | 
			
		||||
                } else {
 | 
			
		||||
                    // If we have an explicit false it means no prefix whatever
 | 
			
		||||
                    // is the default configuration.
 | 
			
		||||
                    $ret = '';
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                // Key is not set, we can safely rely on default behavior.
 | 
			
		||||
                if (isset($prefixes['default']) && false !== $prefixes['default']) {
 | 
			
		||||
                    $ret = $prefixes['default'];
 | 
			
		||||
                } else {
 | 
			
		||||
                    // When default is not set or an explicit false this means
 | 
			
		||||
                    // no prefix.
 | 
			
		||||
                    $ret = '';
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (empty($ret)) {
 | 
			
		||||
            $ret = Redis_Client::getGlobalPrefix();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get client manager
 | 
			
		||||
     *
 | 
			
		||||
     * @return Redis_Client_Manager
 | 
			
		||||
     */
 | 
			
		||||
    static public function getManager()
 | 
			
		||||
    {
 | 
			
		||||
        global $conf;
 | 
			
		||||
 | 
			
		||||
        if (null === self::$manager) {
 | 
			
		||||
 | 
			
		||||
            $className = self::getClass(self::REDIS_IMPL_CLIENT);
 | 
			
		||||
            $factory = new $className();
 | 
			
		||||
 | 
			
		||||
            // Build server list from conf
 | 
			
		||||
            $serverList = array();
 | 
			
		||||
            if (isset($conf['redis_servers'])) {
 | 
			
		||||
                $serverList = $conf['redis_servers'];
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (empty($serverList) || !isset($serverList['default'])) {
 | 
			
		||||
 | 
			
		||||
                // Backward configuration compatibility with older versions
 | 
			
		||||
                $serverList[Redis_Client_Manager::REALM_DEFAULT] = array();
 | 
			
		||||
 | 
			
		||||
                foreach (array('host', 'port', 'base', 'password', 'socket') as $key) {
 | 
			
		||||
                    if (isset($conf['redis_client_' . $key])) {
 | 
			
		||||
                        $serverList[Redis_Client_Manager::REALM_DEFAULT][$key] = $conf['redis_client_' . $key];
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            self::$manager = new Redis_Client_Manager($factory, $serverList);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return self::$manager;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Find client class name
 | 
			
		||||
     *
 | 
			
		||||
     * @return string
 | 
			
		||||
     */
 | 
			
		||||
    static public function getClientInterfaceName()
 | 
			
		||||
    {
 | 
			
		||||
        global $conf;
 | 
			
		||||
 | 
			
		||||
        if (!empty($conf['redis_client_interface'])) {
 | 
			
		||||
            return $conf['redis_client_interface'];
 | 
			
		||||
        } else if (class_exists('Predis\Client')) {
 | 
			
		||||
            // Transparent and abitrary preference for Predis library.
 | 
			
		||||
            return  $conf['redis_client_interface'] = 'Predis';
 | 
			
		||||
        } else if (class_exists('Redis')) {
 | 
			
		||||
            // Fallback on PhpRedis if available.
 | 
			
		||||
            return $conf['redis_client_interface'] = 'PhpRedis';
 | 
			
		||||
        } else {
 | 
			
		||||
            throw new Exception("No client interface set.");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * For unit test use only
 | 
			
		||||
     */
 | 
			
		||||
    static public function reset(Redis_Client_Manager $manager = null)
 | 
			
		||||
    {
 | 
			
		||||
        self::$manager = $manager;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the client for the 'default' realm
 | 
			
		||||
     *
 | 
			
		||||
     * @return mixed
 | 
			
		||||
     *
 | 
			
		||||
     * @deprecated
 | 
			
		||||
     */
 | 
			
		||||
    public static function getClient()
 | 
			
		||||
    {
 | 
			
		||||
        return self::getManager()->getClient();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get specific class implementing the current client usage for the specific
 | 
			
		||||
     * asked core subsystem.
 | 
			
		||||
     * 
 | 
			
		||||
     * @param string $system
 | 
			
		||||
     *   One of the Redis_Client::IMPL_* constant.
 | 
			
		||||
     * @param string $clientName
 | 
			
		||||
     *   Client name, if fixed.
 | 
			
		||||
     * 
 | 
			
		||||
     * @return string
 | 
			
		||||
     *   Class name, if found.
 | 
			
		||||
     *
 | 
			
		||||
     * @deprecated
 | 
			
		||||
     */
 | 
			
		||||
    static public function getClass($system)
 | 
			
		||||
    {
 | 
			
		||||
        $class = $system . self::getClientInterfaceName();
 | 
			
		||||
 | 
			
		||||
        if (!class_exists($class)) {
 | 
			
		||||
            throw new Exception(sprintf("Class '%s' does not exist", $class));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $class;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -0,0 +1,32 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Client proxy, client handling class tied to the bare mininum.
 | 
			
		||||
 */
 | 
			
		||||
interface Redis_Client_FactoryInterface {
 | 
			
		||||
  /**
 | 
			
		||||
   * Get the connected client instance.
 | 
			
		||||
   *
 | 
			
		||||
   * @param array $options
 | 
			
		||||
   *   Options from the server pool configuration that may contain:
 | 
			
		||||
   *     - host
 | 
			
		||||
   *     - port
 | 
			
		||||
   *     - database
 | 
			
		||||
   *     - password
 | 
			
		||||
   *     - socket
 | 
			
		||||
   *
 | 
			
		||||
   * @return mixed
 | 
			
		||||
   *   Real client depends from the library behind.
 | 
			
		||||
   */
 | 
			
		||||
  public function getClient($options = array());
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Get underlaying library name used.
 | 
			
		||||
   * 
 | 
			
		||||
   * This can be useful for contribution code that may work with only some of
 | 
			
		||||
   * the provided clients.
 | 
			
		||||
   * 
 | 
			
		||||
   * @return string
 | 
			
		||||
   */
 | 
			
		||||
  public function getName();
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										144
									
								
								sites/all/modules/contrib/dev/redis/lib/Redis/Client/Manager.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								sites/all/modules/contrib/dev/redis/lib/Redis/Client/Manager.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,144 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Client pool manager for multi-server configurations
 | 
			
		||||
 */
 | 
			
		||||
class Redis_Client_Manager
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * Redis default host
 | 
			
		||||
     */
 | 
			
		||||
    const REDIS_DEFAULT_HOST = '127.0.0.1';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Redis default port
 | 
			
		||||
     */
 | 
			
		||||
    const REDIS_DEFAULT_PORT = 6379;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Redis default socket (will override host and port)
 | 
			
		||||
     */
 | 
			
		||||
    const REDIS_DEFAULT_SOCKET = null;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Redis default database: will select none (Database 0)
 | 
			
		||||
     */
 | 
			
		||||
    const REDIS_DEFAULT_BASE = null;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Redis default password: will not authenticate
 | 
			
		||||
     */
 | 
			
		||||
    const REDIS_DEFAULT_PASSWORD = null;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Default realm
 | 
			
		||||
     */
 | 
			
		||||
    const REALM_DEFAULT = 'default';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Client interface name (PhpRedis or Predis)
 | 
			
		||||
     *
 | 
			
		||||
     * @var string
 | 
			
		||||
     */
 | 
			
		||||
    private $interfaceName;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var array[]
 | 
			
		||||
     */
 | 
			
		||||
    private $serverList = array();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var mixed[]
 | 
			
		||||
     */
 | 
			
		||||
    private $clients = array();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var Redis_Client_FactoryInterface
 | 
			
		||||
     */
 | 
			
		||||
    private $factory;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Default constructor
 | 
			
		||||
     *
 | 
			
		||||
     * @param Redis_Client_FactoryInterface $factory
 | 
			
		||||
     *   Client factory
 | 
			
		||||
     * @param array $serverList
 | 
			
		||||
     *   Server connection info list
 | 
			
		||||
     */
 | 
			
		||||
    public function __construct(Redis_Client_FactoryInterface $factory, $serverList = array())
 | 
			
		||||
    {
 | 
			
		||||
        $this->factory = $factory;
 | 
			
		||||
        $this->serverList = $serverList;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get client for the given realm
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $realm
 | 
			
		||||
     * @param boolean $allowDefault
 | 
			
		||||
     *
 | 
			
		||||
     * @return mixed
 | 
			
		||||
     */
 | 
			
		||||
    public function getClient($realm = self::REALM_DEFAULT, $allowDefault = true)
 | 
			
		||||
    {
 | 
			
		||||
        if (!isset($this->clients[$realm])) {
 | 
			
		||||
            $client = $this->createClient($realm);
 | 
			
		||||
 | 
			
		||||
            if (false === $client) {
 | 
			
		||||
                if (self::REALM_DEFAULT !== $realm && $allowDefault) {
 | 
			
		||||
                    $this->clients[$realm] = $this->getClient(self::REALM_DEFAULT);
 | 
			
		||||
                } else {
 | 
			
		||||
                    throw new InvalidArgumentException(sprintf("Could not find client for realm '%s'", $realm));
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                $this->clients[$realm] = $client;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $this->clients[$realm];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Build connection parameters array from current Drupal settings
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $realm
 | 
			
		||||
     *
 | 
			
		||||
     * @return boolean|string[]
 | 
			
		||||
     *   A key-value pairs of configuration values or false if realm is
 | 
			
		||||
     *   not defined per-configuration
 | 
			
		||||
     */
 | 
			
		||||
    private function buildOptions($realm)
 | 
			
		||||
    {
 | 
			
		||||
        $info = null;
 | 
			
		||||
 | 
			
		||||
        if (isset($this->serverList[$realm])) {
 | 
			
		||||
            $info = $this->serverList[$realm];
 | 
			
		||||
        } else {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $info += array(
 | 
			
		||||
            'host'     => self::REDIS_DEFAULT_HOST,
 | 
			
		||||
            'port'     => self::REDIS_DEFAULT_PORT,
 | 
			
		||||
            'base'     => self::REDIS_DEFAULT_BASE,
 | 
			
		||||
            'password' => self::REDIS_DEFAULT_PASSWORD,
 | 
			
		||||
            'socket'   => self::REDIS_DEFAULT_SOCKET
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        return array_filter($info);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get client singleton
 | 
			
		||||
     */
 | 
			
		||||
    private function createClient($realm)
 | 
			
		||||
    {
 | 
			
		||||
        $info = $this->buildOptions($realm);
 | 
			
		||||
 | 
			
		||||
        if (false === $info) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $this->factory->getClient($info);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,36 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * PhpRedis client specific implementation.
 | 
			
		||||
 */
 | 
			
		||||
class Redis_Client_PhpRedis implements Redis_Client_FactoryInterface {
 | 
			
		||||
 | 
			
		||||
  public function getClient($options = array()) {
 | 
			
		||||
    $client = new Redis;
 | 
			
		||||
 | 
			
		||||
    if (!empty($options['socket'])) {
 | 
			
		||||
      $client->connect($options['socket']);
 | 
			
		||||
    } else {
 | 
			
		||||
      $client->connect($options['host'], $options['port']);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (isset($options['password'])) {
 | 
			
		||||
      $client->auth($options['password']);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (isset($options['base'])) {
 | 
			
		||||
      $client->select($options['base']);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Do not allow PhpRedis serialize itself data, we are going to do it
 | 
			
		||||
    // ourself. This will ensure less memory footprint on Redis size when
 | 
			
		||||
    // we will attempt to store small values.
 | 
			
		||||
    $client->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE);
 | 
			
		||||
 | 
			
		||||
    return $client;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public function getName() {
 | 
			
		||||
    return 'PhpRedis';
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										145
									
								
								sites/all/modules/contrib/dev/redis/lib/Redis/Client/Predis.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										145
									
								
								sites/all/modules/contrib/dev/redis/lib/Redis/Client/Predis.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,145 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Predis client specific implementation.
 | 
			
		||||
 */
 | 
			
		||||
class Redis_Client_Predis implements Redis_Client_FactoryInterface {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Circular depedency breaker.
 | 
			
		||||
   */
 | 
			
		||||
  static protected $autoloaderRegistered = false;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * If the first cache get operation happens after the core autoloader has
 | 
			
		||||
   * been registered to PHP, during our autoloader registration we will
 | 
			
		||||
   * trigger it when calling class_exists(): core autoloader will then run
 | 
			
		||||
   * cache_get() during autoloading but sadly this will run our autoloader
 | 
			
		||||
   * registration once again. The second time we are called the circular
 | 
			
		||||
   * dependency breaker will act and we will do nothing, ending up in a
 | 
			
		||||
   * class instanciation attempt while the autoloader is still not loaded.
 | 
			
		||||
   */
 | 
			
		||||
  static protected $stupidCoreWorkaround = 0;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Define Predis base path if not already set, and if we need to set the
 | 
			
		||||
   * autoloader by ourself. This will ensure no crash. Best way would have
 | 
			
		||||
   * been that Drupal ships a PSR-0 autoloader, in which we could manually
 | 
			
		||||
   * add our library path.
 | 
			
		||||
   * 
 | 
			
		||||
   * We cannot do that in the file header, PHP class_exists() function wont
 | 
			
		||||
   * see classes being loaded during the autoloading because this file is
 | 
			
		||||
   * loaded by another autoloader: attempting the class_exists() during a
 | 
			
		||||
   * pending autoloading would cause PHP to crash and ignore the rest of the
 | 
			
		||||
   * file silentely (WTF!?). By delaying this at the getClient() call we
 | 
			
		||||
   * ensure we are not in the class loading process anymore.
 | 
			
		||||
   */
 | 
			
		||||
  public static function setPredisAutoload() {
 | 
			
		||||
 | 
			
		||||
    if (self::$autoloaderRegistered) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    self::$stupidCoreWorkaround++;
 | 
			
		||||
 | 
			
		||||
    // If you attempt to set Drupal's bin cache_bootstrap using Redis, you
 | 
			
		||||
    // will experience an infinite loop (breaking by itself the second time
 | 
			
		||||
    // it passes by): the following call will wake up autoloaders (and we
 | 
			
		||||
    // want that to work since user may have set its own autoloader) but
 | 
			
		||||
    // will wake up Drupal's one too, and because Drupal core caches its
 | 
			
		||||
    // file map, this will trigger this method to be called a second time
 | 
			
		||||
    // and boom! Adios bye bye. That's why this will be called early in the
 | 
			
		||||
    // 'redis.autoload.inc' file instead.
 | 
			
		||||
    if (1 < self::$stupidCoreWorkaround || !class_exists('Predis\Client')) {
 | 
			
		||||
 | 
			
		||||
      if (!defined('PREDIS_BASE_PATH')) {
 | 
			
		||||
        $search = DRUPAL_ROOT . '/sites/all/libraries/predis';
 | 
			
		||||
        define('PREDIS_BASE_PATH', $search);
 | 
			
		||||
      } else {
 | 
			
		||||
        $search = PREDIS_BASE_PATH;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (is_dir($search . '/src')) { // Predis v1.x
 | 
			
		||||
        define('PREDIS_VERSION_MAJOR', 1);
 | 
			
		||||
      } else if (is_dir($search . '/lib')) { // Predis v0.x
 | 
			
		||||
        define('PREDIS_VERSION_MAJOR', 0);
 | 
			
		||||
      } else {
 | 
			
		||||
        throw new Exception("PREDIS_BASE_PATH constant must be set, Predis library must live in sites/all/libraries/predis.");
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // Register a simple autoloader for Predis library. Since the Predis
 | 
			
		||||
      // library is PHP 5.3 only, we can afford doing closures safely.
 | 
			
		||||
      switch (PREDIS_VERSION_MAJOR) {
 | 
			
		||||
 | 
			
		||||
        case 0:
 | 
			
		||||
          $autoload = function($classname) { // PSR-0 autoloader.
 | 
			
		||||
            if (0 === strpos($classname, 'Predis\\')) {
 | 
			
		||||
              $filename = PREDIS_BASE_PATH . '/lib/' . str_replace('\\', '/', $classname) . '.php';
 | 
			
		||||
              return (bool)require_once $filename;
 | 
			
		||||
            }
 | 
			
		||||
            return false;
 | 
			
		||||
          };
 | 
			
		||||
          break;
 | 
			
		||||
 | 
			
		||||
        case 1:
 | 
			
		||||
          // Register a simple autoloader for Predis library. Since the Predis
 | 
			
		||||
          // library is PHP 5.3 only, we can afford doing closures safely.
 | 
			
		||||
          $autoload = function($classname) { // PSR-4 autoloader
 | 
			
		||||
            if (0 === strpos($classname, 'Predis\\')) {
 | 
			
		||||
              $filename = PREDIS_BASE_PATH . '/src/' . str_replace('\\', '/', substr($classname, 7)) . '.php';
 | 
			
		||||
              return (bool)require_once $filename;
 | 
			
		||||
            }
 | 
			
		||||
            return false;
 | 
			
		||||
          };
 | 
			
		||||
          break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if ($autoload) {
 | 
			
		||||
        spl_autoload_register($autoload);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // Same reason why we have the stupid core workaround, if this happens
 | 
			
		||||
      // during a second autoload call, PHP won't call the newly registered
 | 
			
		||||
      // autoloader function, so just load the file.
 | 
			
		||||
      if (1 < self::$stupidCoreWorkaround) {
 | 
			
		||||
        call_user_func($autoload, 'Predis\Client');
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    self::$autoloaderRegistered = true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public function getClient($options = array()) {
 | 
			
		||||
 | 
			
		||||
    self::setPredisAutoload();
 | 
			
		||||
 | 
			
		||||
    if (!empty($options['socket'])) {
 | 
			
		||||
      $options['scheme'] = 'unix';
 | 
			
		||||
      $options['path'] = $options['socket'];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    foreach ($options as $key => $value) {
 | 
			
		||||
      if (!isset($value)) {
 | 
			
		||||
        unset($options[$key]);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // I'm not sure why but the error handler is driven crazy if timezone
 | 
			
		||||
    // is not set at this point.
 | 
			
		||||
    // Hopefully Drupal will restore the right one this once the current
 | 
			
		||||
    // account has logged in.
 | 
			
		||||
    date_default_timezone_set(@date_default_timezone_get());
 | 
			
		||||
 | 
			
		||||
    $client = new \Predis\Client($options);
 | 
			
		||||
 | 
			
		||||
    if (isset($options['base']) && 0 !== $options['base']) {
 | 
			
		||||
        $client->select((int)$options['base']);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return $client;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public function getName() {
 | 
			
		||||
    return 'Predis';
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										31
									
								
								sites/all/modules/contrib/dev/redis/lib/Redis/Lock.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								sites/all/modules/contrib/dev/redis/lib/Redis/Lock.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,31 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Lock backend singleton handling.
 | 
			
		||||
 */
 | 
			
		||||
class Redis_Lock {
 | 
			
		||||
    /**
 | 
			
		||||
     * @var Redis_Lock_BackendInterface
 | 
			
		||||
     */
 | 
			
		||||
    private static $instance;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get actual lock backend.
 | 
			
		||||
     * 
 | 
			
		||||
     * @return Redis_Lock_BackendInterface
 | 
			
		||||
     */
 | 
			
		||||
    public static function getBackend()
 | 
			
		||||
    {
 | 
			
		||||
        if (!isset(self::$instance)) {
 | 
			
		||||
 | 
			
		||||
            $className = Redis_Client::getClass(Redis_Client::REDIS_IMPL_LOCK);
 | 
			
		||||
 | 
			
		||||
            self::$instance = new $className(
 | 
			
		||||
                Redis_Client::getClient(),
 | 
			
		||||
                Redis_Client::getDefaultPrefix('lock')
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return self::$instance;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,61 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Lock backend interface.
 | 
			
		||||
 */
 | 
			
		||||
interface Redis_Lock_BackendInterface {
 | 
			
		||||
  /**
 | 
			
		||||
   * Acquire lock.
 | 
			
		||||
   * 
 | 
			
		||||
   * @param string $name
 | 
			
		||||
   *   Lock name.
 | 
			
		||||
   * @param float $timeout = 30.0
 | 
			
		||||
   *   (optional) Lock lifetime in seconds.
 | 
			
		||||
   * 
 | 
			
		||||
   * @return bool
 | 
			
		||||
   */
 | 
			
		||||
  public function lockAcquire($name, $timeout = 30.0);
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Check if lock is available for acquire.
 | 
			
		||||
   * 
 | 
			
		||||
   * @param string $name
 | 
			
		||||
   *   Lock to acquire.
 | 
			
		||||
   * 
 | 
			
		||||
   * @return bool
 | 
			
		||||
   */
 | 
			
		||||
  public function lockMayBeAvailable($name);
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Wait a short amount of time before a second lock acquire attempt.
 | 
			
		||||
   * 
 | 
			
		||||
   * @param string $name
 | 
			
		||||
   *   Lock name currently being locked.
 | 
			
		||||
   * @param int $delay = 30
 | 
			
		||||
   *   Miliseconds to wait for.
 | 
			
		||||
   */
 | 
			
		||||
  public function lockWait($name, $delay = 30);
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Release given lock.
 | 
			
		||||
   * 
 | 
			
		||||
   * @param string $name
 | 
			
		||||
   */
 | 
			
		||||
  public function lockRelease($name);
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Release all locks for the given lock token identifier.
 | 
			
		||||
   * 
 | 
			
		||||
   * @param string $lockId = NULL
 | 
			
		||||
   *   (optional) If none given, remove all lock from the current page.
 | 
			
		||||
   */
 | 
			
		||||
  public function lockReleaseAll($lock_id = NULL);
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Get the unique page token for locks. Locks will be wipeout at each end of
 | 
			
		||||
   * page request on a token basis.
 | 
			
		||||
   * 
 | 
			
		||||
   * @return string
 | 
			
		||||
   */
 | 
			
		||||
  public function getLockId();
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,89 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Lock backend shared methods.
 | 
			
		||||
 */
 | 
			
		||||
abstract class Redis_Lock_DefaultBackend
 | 
			
		||||
    extends Redis_AbstractBackend
 | 
			
		||||
    implements Redis_Lock_BackendInterface
 | 
			
		||||
{
 | 
			
		||||
  /**
 | 
			
		||||
   * Current page lock token identifier.
 | 
			
		||||
   *
 | 
			
		||||
   * @var string
 | 
			
		||||
   */
 | 
			
		||||
  protected $_lockId;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Existing locks for this page.
 | 
			
		||||
   *
 | 
			
		||||
   * @var array
 | 
			
		||||
   */
 | 
			
		||||
  protected $_locks = array();
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Default implementation from actual Drupal core.
 | 
			
		||||
   *
 | 
			
		||||
   * @see Redis_Lock_BackendInterface::lockWait()
 | 
			
		||||
   */
 | 
			
		||||
  public function lockWait($name, $delay = 30) {
 | 
			
		||||
    // Pause the process for short periods between calling
 | 
			
		||||
    // lock_may_be_available(). This prevents hitting the database with constant
 | 
			
		||||
    // database queries while waiting, which could lead to performance issues.
 | 
			
		||||
    // However, if the wait period is too long, there is the potential for a
 | 
			
		||||
    // large number of processes to be blocked waiting for a lock, especially
 | 
			
		||||
    // if the item being rebuilt is commonly requested. To address both of these
 | 
			
		||||
    // concerns, begin waiting for 25ms, then add 25ms to the wait period each
 | 
			
		||||
    // time until it reaches 500ms. After this point polling will continue every
 | 
			
		||||
    // 500ms until $delay is reached.
 | 
			
		||||
 | 
			
		||||
    // $delay is passed in seconds, but we will be using usleep(), which takes
 | 
			
		||||
    // microseconds as a parameter. Multiply it by 1 million so that all
 | 
			
		||||
    // further numbers are equivalent.
 | 
			
		||||
    $delay = (int) $delay * 1000000;
 | 
			
		||||
 | 
			
		||||
    // Begin sleeping at 25ms.
 | 
			
		||||
    $sleep = 25000;
 | 
			
		||||
    while ($delay > 0) {
 | 
			
		||||
      // This function should only be called by a request that failed to get a
 | 
			
		||||
      // lock, so we sleep first to give the parallel request a chance to finish
 | 
			
		||||
      // and release the lock.
 | 
			
		||||
      usleep($sleep);
 | 
			
		||||
      // After each sleep, increase the value of $sleep until it reaches
 | 
			
		||||
      // 500ms, to reduce the potential for a lock stampede.
 | 
			
		||||
      $delay = $delay - $sleep;
 | 
			
		||||
      $sleep = min(500000, $sleep + 25000, $delay);
 | 
			
		||||
      if ($this->lockMayBeAvailable($name)) {
 | 
			
		||||
        // No longer need to wait.
 | 
			
		||||
        return FALSE;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    // The caller must still wait longer to get the lock.
 | 
			
		||||
    return TRUE;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Default implementation from actual Drupal core.
 | 
			
		||||
   *
 | 
			
		||||
   * @see Redis_Lock_BackendInterface::getLockId()
 | 
			
		||||
   */
 | 
			
		||||
  public function getLockId() {
 | 
			
		||||
    if (!isset($this->_lockId)) {
 | 
			
		||||
      $this->_lockId = uniqid(mt_rand(), TRUE);
 | 
			
		||||
      // We only register a shutdown function if a lock is used.
 | 
			
		||||
      drupal_register_shutdown_function('lock_release_all', $this->_lockId);
 | 
			
		||||
    }
 | 
			
		||||
    return $this->_lockId;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Generate a redis key name for the current lock name
 | 
			
		||||
   */
 | 
			
		||||
  public function getKey($name = null) {
 | 
			
		||||
    if (null === $name) {
 | 
			
		||||
      return parent::getKey('lock');
 | 
			
		||||
    } else {
 | 
			
		||||
      return parent::getKey(array('lock', $name));
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										138
									
								
								sites/all/modules/contrib/dev/redis/lib/Redis/Lock/PhpRedis.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								sites/all/modules/contrib/dev/redis/lib/Redis/Lock/PhpRedis.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,138 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Predis lock backend implementation.
 | 
			
		||||
 *
 | 
			
		||||
 * This implementation works with a single key per lock so is viable when
 | 
			
		||||
 * doing client side sharding and/or using consistent hashing algorithm.
 | 
			
		||||
 */
 | 
			
		||||
class Redis_Lock_PhpRedis extends Redis_Lock_DefaultBackend {
 | 
			
		||||
 | 
			
		||||
  public function lockAcquire($name, $timeout = 30.0) {
 | 
			
		||||
    $client = $this->getClient();
 | 
			
		||||
    $key    = $this->getKey($name);
 | 
			
		||||
    $id     = $this->getLockId();
 | 
			
		||||
 | 
			
		||||
    // Insure that the timeout is at least 1 second, we cannot do otherwise with
 | 
			
		||||
    // Redis, this is a minor change to the function signature, but in real life
 | 
			
		||||
    // nobody will notice with so short duration.
 | 
			
		||||
    $timeout = ceil(max($timeout, 1));
 | 
			
		||||
 | 
			
		||||
    // If we already have the lock, check for his owner and attempt a new EXPIRE
 | 
			
		||||
    // command on it.
 | 
			
		||||
    if (isset($this->_locks[$name])) {
 | 
			
		||||
 | 
			
		||||
      // Create a new transaction, for atomicity.
 | 
			
		||||
      $client->watch($key);
 | 
			
		||||
 | 
			
		||||
      // Global tells us we are the owner, but in real life it could have expired
 | 
			
		||||
      // and another process could have taken it, check that.
 | 
			
		||||
      if ($client->get($key) != $id) {
 | 
			
		||||
        // Explicit UNWATCH we are not going to run the MULTI/EXEC block.
 | 
			
		||||
        $client->unwatch();
 | 
			
		||||
        unset($this->_locks[$name]);
 | 
			
		||||
        return FALSE;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // See https://github.com/phpredis/phpredis#watch-unwatch
 | 
			
		||||
      // MULTI and other commands can fail, so we can't chain calls.
 | 
			
		||||
      if (FALSE !== ($result = $client->multi())) {
 | 
			
		||||
        $client->setex($key, $timeout, $id);
 | 
			
		||||
        $result = $client->exec();
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // Did it broke?
 | 
			
		||||
      if (FALSE === $result) {
 | 
			
		||||
        unset($this->_locks[$name]);
 | 
			
		||||
        // Explicit transaction release which also frees the WATCH'ed key.
 | 
			
		||||
        $client->discard();
 | 
			
		||||
        return FALSE;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return ($this->_locks[$name] = TRUE);
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      $client->watch($key);
 | 
			
		||||
      $owner = $client->get($key);
 | 
			
		||||
 | 
			
		||||
      // If the $key is set they lock is not available
 | 
			
		||||
      if (!empty($owner) && $id != $owner) {
 | 
			
		||||
        $client->unwatch();
 | 
			
		||||
        return FALSE;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // See https://github.com/phpredis/phpredis#watch-unwatch
 | 
			
		||||
      // MULTI and other commands can fail, so we can't chain calls.
 | 
			
		||||
      if (FALSE !== ($result = $client->multi())) {
 | 
			
		||||
        $client->setex($key, $timeout, $id);
 | 
			
		||||
        $result->exec();
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // If another client modified the $key value, transaction will be discarded
 | 
			
		||||
      // $result will be set to FALSE. This means atomicity have been broken and
 | 
			
		||||
      // the other client took the lock instead of us.
 | 
			
		||||
      if (FALSE === $result) {
 | 
			
		||||
        // Explicit transaction release which also frees the WATCH'ed key.
 | 
			
		||||
        $client->discard();
 | 
			
		||||
        return FALSE;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // Register the lock.
 | 
			
		||||
      return ($this->_locks[$name] = TRUE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return FALSE;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public function lockMayBeAvailable($name) {
 | 
			
		||||
    $client = $this->getClient();
 | 
			
		||||
    $key    = $this->getKey($name);
 | 
			
		||||
    $id     = $this->getLockId();
 | 
			
		||||
 | 
			
		||||
    $value = $client->get($key);
 | 
			
		||||
 | 
			
		||||
    return FALSE === $value || $id == $value;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public function lockRelease($name) {
 | 
			
		||||
    $client = $this->getClient();
 | 
			
		||||
    $key    = $this->getKey($name);
 | 
			
		||||
    $id     = $this->getLockId();
 | 
			
		||||
 | 
			
		||||
    unset($this->_locks[$name]);
 | 
			
		||||
 | 
			
		||||
    // Ensure the lock deletion is an atomic transaction. If another thread
 | 
			
		||||
    // manages to removes all lock, we can not alter it anymore else we will
 | 
			
		||||
    // release the lock for the other thread and cause race conditions.
 | 
			
		||||
    $client->watch($key);
 | 
			
		||||
 | 
			
		||||
    if ($client->get($key) == $id) {
 | 
			
		||||
      $client->multi();
 | 
			
		||||
      $client->delete($key);
 | 
			
		||||
      $client->exec();
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      $client->unwatch();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public function lockReleaseAll($lock_id = NULL) {
 | 
			
		||||
    if (!isset($lock_id) && empty($this->_locks)) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $client = $this->getClient();
 | 
			
		||||
    $id     = isset($lock_id) ? $lock_id : $this->getLockId();
 | 
			
		||||
 | 
			
		||||
    // We can afford to deal with a slow algorithm here, this should not happen
 | 
			
		||||
    // on normal run because we should have removed manually all our locks.
 | 
			
		||||
    foreach (array_keys($this->_locks) as $name) {
 | 
			
		||||
      $key   = $this->getKey($name);
 | 
			
		||||
      $owner = $client->get($key);
 | 
			
		||||
 | 
			
		||||
      if (empty($owner) || $owner == $id) {
 | 
			
		||||
        $client->delete($key);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										137
									
								
								sites/all/modules/contrib/dev/redis/lib/Redis/Lock/Predis.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								sites/all/modules/contrib/dev/redis/lib/Redis/Lock/Predis.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,137 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Predis lock backend implementation.
 | 
			
		||||
 *
 | 
			
		||||
 * This implementation works with a single key per lock so is viable when
 | 
			
		||||
 * doing client side sharding and/or using consistent hashing algorithm.
 | 
			
		||||
 */
 | 
			
		||||
class Redis_Lock_Predis extends Redis_Lock_DefaultBackend {
 | 
			
		||||
 | 
			
		||||
  public function lockAcquire($name, $timeout = 30.0) {
 | 
			
		||||
    $client = $this->getClient();
 | 
			
		||||
    $key    = $this->getKey($name);
 | 
			
		||||
    $id     = $this->getLockId();
 | 
			
		||||
 | 
			
		||||
    // Insure that the timeout is at least 1 second, we cannot do otherwise with
 | 
			
		||||
    // Redis, this is a minor change to the function signature, but in real life
 | 
			
		||||
    // nobody will notice with so short duration.
 | 
			
		||||
    $timeout = ceil(max($timeout, 1));
 | 
			
		||||
 | 
			
		||||
    // If we already have the lock, check for his owner and attempt a new EXPIRE
 | 
			
		||||
    // command on it.
 | 
			
		||||
    if (isset($this->_locks[$name])) {
 | 
			
		||||
 | 
			
		||||
      // Create a new transaction, for atomicity.
 | 
			
		||||
      $client->watch($key);
 | 
			
		||||
 | 
			
		||||
      // Global tells us we are the owner, but in real life it could have expired
 | 
			
		||||
      // and another process could have taken it, check that.
 | 
			
		||||
      if ($client->get($key) != $id) {
 | 
			
		||||
        $client->unwatch();
 | 
			
		||||
        unset($this->_locks[$name]);
 | 
			
		||||
        return FALSE;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      $replies = $client->pipeline(function($pipe) use ($key, $timeout, $id) {
 | 
			
		||||
        $pipe->multi();
 | 
			
		||||
        $pipe->setex($key, $timeout, $id);
 | 
			
		||||
        $pipe->exec();
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      $execReply = array_pop($replies);
 | 
			
		||||
 | 
			
		||||
      if (FALSE === $execReply[0]) {
 | 
			
		||||
        unset($this->_locks[$name]);
 | 
			
		||||
        return FALSE;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return TRUE;
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      $client->watch($key);
 | 
			
		||||
      $owner = $client->get($key);
 | 
			
		||||
 | 
			
		||||
      if (!empty($owner) && $owner != $id) {
 | 
			
		||||
        $client->unwatch();
 | 
			
		||||
        unset($this->_locks[$name]);
 | 
			
		||||
        return FALSE;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      $replies = $client->pipeline(function($pipe) use ($key, $timeout, $id) {
 | 
			
		||||
        $pipe->multi();
 | 
			
		||||
        $pipe->setex($key, $timeout, $id);
 | 
			
		||||
        $pipe->exec();
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      $execReply = array_pop($replies);
 | 
			
		||||
 | 
			
		||||
      // If another client modified the $key value, transaction will be discarded
 | 
			
		||||
      // $result will be set to FALSE. This means atomicity have been broken and
 | 
			
		||||
      // the other client took the lock instead of us.
 | 
			
		||||
      // EXPIRE and SETEX won't return something here, EXEC return is index 0
 | 
			
		||||
      // This was determined debugging, seems to be Predis specific.
 | 
			
		||||
      if (FALSE === $execReply[0]) {
 | 
			
		||||
        return FALSE;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // Register the lock and return.
 | 
			
		||||
      return ($this->_locks[$name] = TRUE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return FALSE;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public function lockMayBeAvailable($name) {
 | 
			
		||||
    $client = $this->getClient();
 | 
			
		||||
    $key    = $this->getKey($name);
 | 
			
		||||
    $id     = $this->getLockId();
 | 
			
		||||
 | 
			
		||||
    $value = $client->get($key);
 | 
			
		||||
 | 
			
		||||
    return empty($value) || $id == $value;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public function lockRelease($name) {
 | 
			
		||||
    $client = $this->getClient();
 | 
			
		||||
    $key    = $this->getKey($name);
 | 
			
		||||
    $id     = $this->getLockId();
 | 
			
		||||
 | 
			
		||||
    unset($this->_locks[$name]);
 | 
			
		||||
 | 
			
		||||
    // Ensure the lock deletion is an atomic transaction. If another thread
 | 
			
		||||
    // manages to removes all lock, we can not alter it anymore else we will
 | 
			
		||||
    // release the lock for the other thread and cause race conditions.
 | 
			
		||||
    $client->watch($key);
 | 
			
		||||
 | 
			
		||||
    if ($client->get($key) == $id) {
 | 
			
		||||
      $client->multi();
 | 
			
		||||
      $client->del(array($key));
 | 
			
		||||
      $client->exec();
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      $client->unwatch();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public function lockReleaseAll($lock_id = NULL) {
 | 
			
		||||
    if (!isset($lock_id) && empty($this->_locks)) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $client = $this->getClient();
 | 
			
		||||
    $id     = isset($lock_id) ? $lock_id : $this->getLockId();
 | 
			
		||||
 | 
			
		||||
    // We can afford to deal with a slow algorithm here, this should not happen
 | 
			
		||||
    // on normal run because we should have removed manually all our locks.
 | 
			
		||||
    foreach (array_keys($this->_locks) as $name) {
 | 
			
		||||
      $key   = $this->getKey($name);
 | 
			
		||||
      $owner = $client->get($key);
 | 
			
		||||
 | 
			
		||||
      if (empty($owner) || $owner == $id) {
 | 
			
		||||
        $client->del(array($key));
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -0,0 +1,105 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Common implementation for Redis-based implementations
 | 
			
		||||
 */
 | 
			
		||||
abstract class Redis_Path_AbstractHashLookup extends Redis_AbstractBackend implements
 | 
			
		||||
    Redis_Path_HashLookupInterface
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * @todo document me
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $key
 | 
			
		||||
     * @param string $hkey
 | 
			
		||||
     * @param string $hvalue
 | 
			
		||||
     */
 | 
			
		||||
    abstract protected function saveInHash($key, $hkey, $hvalue);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @todo document me
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $key
 | 
			
		||||
     * @param string $hkey
 | 
			
		||||
     * @param string $hvalue
 | 
			
		||||
     */
 | 
			
		||||
    abstract protected function deleteInHash($key, $hkey, $hvalue);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @todo document me
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $keyPrefix
 | 
			
		||||
     * @param string $hkey
 | 
			
		||||
     * @param string $language
 | 
			
		||||
     */
 | 
			
		||||
    abstract protected function lookupInHash($keyPrefix, $hkey, $language = null);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Normalize value to avoid duplicate or false negatives
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $value
 | 
			
		||||
     *
 | 
			
		||||
     * @return string
 | 
			
		||||
     */
 | 
			
		||||
    private function normalize($value)
 | 
			
		||||
    {
 | 
			
		||||
        if (null !== $value) {
 | 
			
		||||
            return strtolower(trim($value));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * {@inheritdoc}
 | 
			
		||||
     */
 | 
			
		||||
    public function saveAlias($source, $alias, $language = null)
 | 
			
		||||
    {
 | 
			
		||||
        $alias  = $this->normalize($alias);
 | 
			
		||||
        $source = $this->normalize($source);
 | 
			
		||||
 | 
			
		||||
        if (null === $language) {
 | 
			
		||||
            $language = LANGUAGE_NONE;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!empty($source)) {
 | 
			
		||||
            $this->saveInHash($this->getKey(array(self::KEY_ALIAS, $language)), $source, $alias);
 | 
			
		||||
        }
 | 
			
		||||
        if (!empty($alias)) {
 | 
			
		||||
            $this->saveInHash($this->getKey(array(self::KEY_SOURCE, $language)), $alias, $source);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * {@inheritdoc}
 | 
			
		||||
     */
 | 
			
		||||
    public function deleteAlias($source, $alias, $language = null)
 | 
			
		||||
    {
 | 
			
		||||
        $alias  = $this->normalize($alias);
 | 
			
		||||
        $source = $this->normalize($source);
 | 
			
		||||
 | 
			
		||||
        if (null === $language) {
 | 
			
		||||
            $language = LANGUAGE_NONE;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $this->deleteInHash($this->getKey(array(self::KEY_ALIAS, $language)), $source, $alias);
 | 
			
		||||
        $this->deleteInHash($this->getKey(array(self::KEY_SOURCE, $language)), $alias, $source);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * {@inheritdoc}
 | 
			
		||||
     */
 | 
			
		||||
    public function lookupAlias($source, $language = null)
 | 
			
		||||
    {
 | 
			
		||||
        $source = $this->normalize($source);
 | 
			
		||||
 | 
			
		||||
        return $this->lookupInHash(self::KEY_ALIAS, $source, $language);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * {@inheritdoc}
 | 
			
		||||
     */
 | 
			
		||||
    public function lookupSource($alias, $language = null)
 | 
			
		||||
    {
 | 
			
		||||
        $alias = $this->normalize($alias);
 | 
			
		||||
 | 
			
		||||
        return $this->lookupInHash(self::KEY_SOURCE, $alias, $language);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,109 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Very fast hash based lookup interface.
 | 
			
		||||
 *
 | 
			
		||||
 * This will work for any key-value store whether it's APC, Redis, memcache...
 | 
			
		||||
 * Rationale behind this is that Drupal calls hundreds of time per request the
 | 
			
		||||
 * drupal_lookup_path() function and we need it to be very fast. The key of
 | 
			
		||||
 * success to keep it stupid simple and coherent as the same time is that we
 | 
			
		||||
 * consider this backend as a cache (more or less permanent) that might be
 | 
			
		||||
 * cleared at any time, and synchronized as when necessary or incrementally.
 | 
			
		||||
 * This should be very fast.
 | 
			
		||||
 *
 | 
			
		||||
 * Redis implementation will be the following:
 | 
			
		||||
 *
 | 
			
		||||
 * Aliases are stored into a Redis HASH and are stored per language basis.
 | 
			
		||||
 * Key is:
 | 
			
		||||
 *   [SITEPREFIX:]path:dst:LANGUAGE
 | 
			
		||||
 * Keys inside the hash are a MD5() of the source and values are the alias
 | 
			
		||||
 *
 | 
			
		||||
 * Sources are also stored the same way except the HASH key is the following:
 | 
			
		||||
 *   [SITEPREFIX:]path:src:LANGUAGE
 | 
			
		||||
 * Keys inside the hash are a MD5() of the alias and values are the sources.
 | 
			
		||||
 *
 | 
			
		||||
 * In both case values are a comma separated list of string values.
 | 
			
		||||
 *
 | 
			
		||||
 * The MD5() should give us low collision algorithm and we'll keep it until
 | 
			
		||||
 * no one experiences any problem.
 | 
			
		||||
 *
 | 
			
		||||
 * Alias and sources are always looked up using the language, hence the
 | 
			
		||||
 * different keys for different languages.
 | 
			
		||||
 */
 | 
			
		||||
interface Redis_Path_HashLookupInterface
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * Alias HASH key prefix
 | 
			
		||||
     */
 | 
			
		||||
    const KEY_ALIAS = 'path:a';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Source HASH key prefix
 | 
			
		||||
     */
 | 
			
		||||
    const KEY_SOURCE = 'path:s';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Null value (not existing yet cached value)
 | 
			
		||||
     */
 | 
			
		||||
    const VALUE_NULL = '!';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Values separator for hash values
 | 
			
		||||
     */
 | 
			
		||||
    const VALUE_SEPARATOR = '#';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Alias is being inserted with the given source
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $source
 | 
			
		||||
     * @param string $alias
 | 
			
		||||
     * @param string $language
 | 
			
		||||
     */
 | 
			
		||||
    public function saveAlias($source, $alias, $language = null);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Alias is being deleted for the given source
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $source
 | 
			
		||||
     * @param string $alias
 | 
			
		||||
     * @param string $language
 | 
			
		||||
     */
 | 
			
		||||
    public function deleteAlias($source, $alias, $language = null);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * A language is being deleted
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $language
 | 
			
		||||
     */
 | 
			
		||||
    public function deleteLanguage($language);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Lookup any alias for the given source
 | 
			
		||||
     *
 | 
			
		||||
     * First that has been inserted wins over the others
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $source
 | 
			
		||||
     * @param string $language
 | 
			
		||||
     *
 | 
			
		||||
     * @return string|null|false
 | 
			
		||||
     *   - The string value if found
 | 
			
		||||
     *   - null if not found
 | 
			
		||||
     *   - false if set as non existing
 | 
			
		||||
     */
 | 
			
		||||
    public function lookupAlias($source, $language = null);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Lookup any source for the given alias
 | 
			
		||||
     *
 | 
			
		||||
     * First that has been inserted wins over the others
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $alias
 | 
			
		||||
     * @param string $language
 | 
			
		||||
     *
 | 
			
		||||
     * @return string|null|false
 | 
			
		||||
     *   - The string value if found
 | 
			
		||||
     *   - null if not found
 | 
			
		||||
     *   - false if set as non existing
 | 
			
		||||
     */
 | 
			
		||||
    public function lookupSource($alias, $language = null);
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,27 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Null implementation.
 | 
			
		||||
 */
 | 
			
		||||
class Redis_Path_NullHashLookup implements Redis_Path_HashLookupInterface
 | 
			
		||||
{
 | 
			
		||||
    public function saveAlias($source, $alias, $language = null)
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function deleteAlias($source, $alias, $language = null)
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function deleteLanguage($language)
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function lookupAlias($source, $language = null)
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function lookupSource($alias, $language = null)
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										108
									
								
								sites/all/modules/contrib/dev/redis/lib/Redis/Path/PhpRedis.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								sites/all/modules/contrib/dev/redis/lib/Redis/Path/PhpRedis.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,108 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * PhpRedis implementation.
 | 
			
		||||
 *
 | 
			
		||||
 * @todo
 | 
			
		||||
 *   Set high expire value to the hash for rotation when memory is empty
 | 
			
		||||
 *   React upon cache clear all and rebuild path list?
 | 
			
		||||
 */
 | 
			
		||||
class Redis_Path_PhpRedis extends Redis_Path_AbstractHashLookup
 | 
			
		||||
{
 | 
			
		||||
    protected function saveInHash($key, $hkey, $hvalue)
 | 
			
		||||
    {
 | 
			
		||||
        $client = $this->getClient();
 | 
			
		||||
 | 
			
		||||
        $value = $client->hget($key, $hkey);
 | 
			
		||||
 | 
			
		||||
        if ($value === self::VALUE_NULL) { // Remove any null values
 | 
			
		||||
            $value = null;
 | 
			
		||||
        }
 | 
			
		||||
        if ($value) {
 | 
			
		||||
            $existing = explode(self::VALUE_SEPARATOR, $value);
 | 
			
		||||
            if (!in_array($hvalue, $existing)) {
 | 
			
		||||
                // Prepend the most recent path to ensure it always be
 | 
			
		||||
                // first fetched one
 | 
			
		||||
                // @todo Ensure in case of update that its position does
 | 
			
		||||
                // not changes (pid ordering in Drupal core)
 | 
			
		||||
                $value = $hvalue . self::VALUE_SEPARATOR . $value;
 | 
			
		||||
            } else { // Do nothing on empty value
 | 
			
		||||
              $value = null;
 | 
			
		||||
            }
 | 
			
		||||
        } else if (empty($hvalue)) {
 | 
			
		||||
            $value = self::VALUE_NULL;
 | 
			
		||||
        } else {
 | 
			
		||||
            $value = $hvalue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!empty($value)) {
 | 
			
		||||
            $client->hset($key, $hkey, $value);
 | 
			
		||||
        }
 | 
			
		||||
        // Empty value here means that we already got it
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function deleteInHash($key, $hkey, $hvalue)
 | 
			
		||||
    {
 | 
			
		||||
        $client = $this->getClient();
 | 
			
		||||
 | 
			
		||||
        $value = $client->hget($key, $hkey);
 | 
			
		||||
 | 
			
		||||
        if ($value) {
 | 
			
		||||
            $existing = explode(self::VALUE_SEPARATOR, $value);
 | 
			
		||||
            if (false !== ($index = array_search($hvalue, $existing))) {
 | 
			
		||||
                if (1 === count($existing)) {
 | 
			
		||||
                    $client->hdel($key, $hkey);
 | 
			
		||||
                } else {
 | 
			
		||||
                    unset($existing[$index]);
 | 
			
		||||
                    $client->hset($key, $hkey, implode(self::VALUE_SEPARATOR, $existing));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function lookupInHash($keyPrefix, $hkey, $language = null)
 | 
			
		||||
    {
 | 
			
		||||
        $client = $this->getClient();
 | 
			
		||||
 | 
			
		||||
        if (null === $language) {
 | 
			
		||||
            $language = LANGUAGE_NONE;
 | 
			
		||||
            $doNoneLookup = false;
 | 
			
		||||
        } else if (LANGUAGE_NONE === $language) {
 | 
			
		||||
            $doNoneLookup = false;
 | 
			
		||||
        } else {
 | 
			
		||||
            $doNoneLookup = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $ret = $client->hget($this->getKey(array($keyPrefix, $language)), $hkey);
 | 
			
		||||
        if ($doNoneLookup && (!$ret || self::VALUE_NULL === $ret)) {
 | 
			
		||||
            $previous = $ret;
 | 
			
		||||
            $ret = $client->hget($this->getKey(array($keyPrefix, LANGUAGE_NONE)), $hkey);
 | 
			
		||||
            if (!$ret || self::VALUE_NULL === $ret) {
 | 
			
		||||
                // Restore null placeholder else we loose conversion to false
 | 
			
		||||
                // and drupal_lookup_path() would attempt saving it once again
 | 
			
		||||
                $ret = $previous;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (self::VALUE_NULL === $ret) {
 | 
			
		||||
            return false; // Needs conversion
 | 
			
		||||
        }
 | 
			
		||||
        if (empty($ret)) {
 | 
			
		||||
            return null; // Value not found
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $existing = explode(self::VALUE_SEPARATOR, $ret);
 | 
			
		||||
 | 
			
		||||
        return reset($existing);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * {@inheritdoc}
 | 
			
		||||
     */
 | 
			
		||||
    public function deleteLanguage($language)
 | 
			
		||||
    {
 | 
			
		||||
        $client = $this->getClient();
 | 
			
		||||
        $client->del($this->getKey(array(self::KEY_ALIAS, $language)));
 | 
			
		||||
        $client->del($this->getKey(array(self::KEY_SOURCE, $language)));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										108
									
								
								sites/all/modules/contrib/dev/redis/lib/Redis/Path/Predis.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								sites/all/modules/contrib/dev/redis/lib/Redis/Path/Predis.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,108 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * PhpRedis implementation.
 | 
			
		||||
 *
 | 
			
		||||
 * @todo
 | 
			
		||||
 *   Set high expire value to the hash for rotation when memory is empty
 | 
			
		||||
 *   React upon cache clear all and rebuild path list?
 | 
			
		||||
 */
 | 
			
		||||
class Redis_Path_Predis extends Redis_Path_AbstractHashLookup
 | 
			
		||||
{
 | 
			
		||||
    protected function saveInHash($key, $hkey, $hvalue)
 | 
			
		||||
    {
 | 
			
		||||
        $client = $this->getClient();
 | 
			
		||||
 | 
			
		||||
        $value = $client->hget($key, $hkey);
 | 
			
		||||
 | 
			
		||||
        if ($value === self::VALUE_NULL) { // Remove any null values
 | 
			
		||||
            $value = null;
 | 
			
		||||
        }
 | 
			
		||||
        if ($value) {
 | 
			
		||||
            $existing = explode(self::VALUE_SEPARATOR, $value);
 | 
			
		||||
            if (!in_array($hvalue, $existing)) {
 | 
			
		||||
                // Prepend the most recent path to ensure it always be
 | 
			
		||||
                // first fetched one
 | 
			
		||||
                // @todo Ensure in case of update that its position does
 | 
			
		||||
                // not changes (pid ordering in Drupal core)
 | 
			
		||||
                $value = $hvalue . self::VALUE_SEPARATOR . $value;
 | 
			
		||||
            } else { // Do nothing on empty value
 | 
			
		||||
              $value = null;
 | 
			
		||||
            }
 | 
			
		||||
        } else if (empty($hvalue)) {
 | 
			
		||||
            $value = self::VALUE_NULL;
 | 
			
		||||
        } else {
 | 
			
		||||
            $value = $hvalue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!empty($value)) {
 | 
			
		||||
            $client->hset($key, $hkey, $value);
 | 
			
		||||
        }
 | 
			
		||||
        // Empty value here means that we already got it
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function deleteInHash($key, $hkey, $hvalue)
 | 
			
		||||
    {
 | 
			
		||||
        $client = $this->getClient();
 | 
			
		||||
 | 
			
		||||
        $value = $client->hget($key, $hkey);
 | 
			
		||||
 | 
			
		||||
        if ($value) {
 | 
			
		||||
            $existing = explode(self::VALUE_SEPARATOR, $value);
 | 
			
		||||
            if (false !== ($index = array_search($hvalue, $existing))) {
 | 
			
		||||
                if (1 === count($existing)) {
 | 
			
		||||
                    $client->hdel($key, $hkey);
 | 
			
		||||
                } else {
 | 
			
		||||
                    unset($existing[$index]);
 | 
			
		||||
                    $client->hset($key, $hkey, implode(self::VALUE_SEPARATOR, $existing));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function lookupInHash($keyPrefix, $hkey, $language = null)
 | 
			
		||||
    {
 | 
			
		||||
        $client = $this->getClient();
 | 
			
		||||
 | 
			
		||||
        if (null === $language) {
 | 
			
		||||
            $language = LANGUAGE_NONE;
 | 
			
		||||
            $doNoneLookup = false;
 | 
			
		||||
        } else if (LANGUAGE_NONE === $language) {
 | 
			
		||||
            $doNoneLookup = false;
 | 
			
		||||
        } else {
 | 
			
		||||
            $doNoneLookup = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $ret = $client->hget($this->getKey(array($keyPrefix, $language)), $hkey);
 | 
			
		||||
        if ($doNoneLookup && (!$ret || self::VALUE_NULL === $ret)) {
 | 
			
		||||
            $previous = $ret;
 | 
			
		||||
            $ret = $client->hget($this->getKey(array($keyPrefix, LANGUAGE_NONE)), $hkey);
 | 
			
		||||
            if (!$ret || self::VALUE_NULL === $ret) {
 | 
			
		||||
                // Restore null placeholder else we loose conversion to false
 | 
			
		||||
                // and drupal_lookup_path() would attempt saving it once again
 | 
			
		||||
                $ret = $previous;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (self::VALUE_NULL === $ret) {
 | 
			
		||||
            return false; // Needs conversion
 | 
			
		||||
        }
 | 
			
		||||
        if (empty($ret)) {
 | 
			
		||||
            return null; // Value not found
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $existing = explode(self::VALUE_SEPARATOR, $ret);
 | 
			
		||||
 | 
			
		||||
        return reset($existing);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * {@inheritdoc}
 | 
			
		||||
     */
 | 
			
		||||
    public function deleteLanguage($language)
 | 
			
		||||
    {
 | 
			
		||||
        $client = $this->getClient();
 | 
			
		||||
        $client->del($this->getKey(array(self::KEY_ALIAS, $language)));
 | 
			
		||||
        $client->del($this->getKey(array(self::KEY_SOURCE, $language)));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										58
									
								
								sites/all/modules/contrib/dev/redis/lib/Redis/Queue.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								sites/all/modules/contrib/dev/redis/lib/Redis/Queue.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,58 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
class Redis_Queue implements DrupalReliableQueueInterface
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * @var DrupalQueueInterface
 | 
			
		||||
     */
 | 
			
		||||
    protected $backend;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Default contructor
 | 
			
		||||
     *
 | 
			
		||||
     * Beware that DrupalQueueInterface does not defines the __construct
 | 
			
		||||
     * method in the interface yet is being used from DrupalQueue::get() 
 | 
			
		||||
     *
 | 
			
		||||
     * @param unknown $name
 | 
			
		||||
     */
 | 
			
		||||
    public function __construct($name)
 | 
			
		||||
    {
 | 
			
		||||
        $className = Redis_Client::getClass(Redis_Client::REDIS_IMPL_QUEUE);
 | 
			
		||||
        $this->backend = new $className(Redis_Client::getClient(), $name);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function createItem($data)
 | 
			
		||||
    {
 | 
			
		||||
        return $this->backend->createItem($data);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function numberOfItems()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->backend->numberOfItems();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function claimItem($lease_time = 3600)
 | 
			
		||||
    {
 | 
			
		||||
        return $this->backend->claimItem($lease_time);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function deleteItem($item)
 | 
			
		||||
    {
 | 
			
		||||
        return $this->backend->deleteItem($item);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function releaseItem($item)
 | 
			
		||||
    {
 | 
			
		||||
        return $this->backend->releaseItem($item);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function createQueue()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->backend->createQueue();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function deleteQueue()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->backend->deleteQueue();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										99
									
								
								sites/all/modules/contrib/dev/redis/lib/Redis/Queue/Base.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								sites/all/modules/contrib/dev/redis/lib/Redis/Queue/Base.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,99 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Redis allows implementing reliable queues, here is the spec:
 | 
			
		||||
 *
 | 
			
		||||
 *  - For each queue, you have 4 different HASH:
 | 
			
		||||
 *
 | 
			
		||||
 *     - One for queued items queue:NAME:queued
 | 
			
		||||
 *
 | 
			
		||||
 *     - One for claimed items being processed: queue:NAME:claimed
 | 
			
		||||
 *
 | 
			
		||||
 *     - One for claimed items leave time: queue:NAME:leave
 | 
			
		||||
 *       Items from this one will be arbitrarily fetched at cron
 | 
			
		||||
 *       time and released when leave is outdated.
 | 
			
		||||
 *
 | 
			
		||||
 *     - One containing the item values and other valuable stateful
 | 
			
		||||
 *       information: queue:NAME:data ;
 | 
			
		||||
 *
 | 
			
		||||
 *        - For example, current job maximum identifier (auto increment
 | 
			
		||||
 *          emulation) will be stored in the "sequence" HASH key
 | 
			
		||||
 *
 | 
			
		||||
 *        - All other keys within the HASH will be the items themselves,
 | 
			
		||||
 *          keys for those will always be numeric
 | 
			
		||||
 *
 | 
			
		||||
 *     - Each time a queue will be emptied, even during a pragmatic process,
 | 
			
		||||
 *       it will be automatically deleted, reseting the sequence counter to
 | 
			
		||||
 *       the 0 value each time
 | 
			
		||||
 *
 | 
			
		||||
 *  - Algorithm is a variation of the one described in "Reliable queue"
 | 
			
		||||
 *    section of http://redis.io/commands/rpoplpush and partial port of what
 | 
			
		||||
 *    you can find in the http://drupal.org/project/redis_queue module.
 | 
			
		||||
 *
 | 
			
		||||
 * You will find the driver specific implementation in the Redis_Queue_*
 | 
			
		||||
 * classes as they may differ in how the API handles transaction, pipelining
 | 
			
		||||
 * and return values.
 | 
			
		||||
 */
 | 
			
		||||
abstract class Redis_Queue_Base extends Redis_AbstractBackend implements
 | 
			
		||||
    DrupalReliableQueueInterface
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * Key prefix for queue data.
 | 
			
		||||
     */
 | 
			
		||||
    const QUEUE_KEY_PREFIX = 'queue';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Data HASH sequence key name.
 | 
			
		||||
     */
 | 
			
		||||
    const QUEUE_HKEY_SEQ = 'seq';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get data HASH key
 | 
			
		||||
     *
 | 
			
		||||
     * Key will already be prefixed
 | 
			
		||||
     *
 | 
			
		||||
     * @return string
 | 
			
		||||
     */
 | 
			
		||||
    public function getKeyForData()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->getKey('data');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get queued items LIST key
 | 
			
		||||
     *
 | 
			
		||||
     * Key will already be prefixed
 | 
			
		||||
     *
 | 
			
		||||
     * @return string
 | 
			
		||||
     */
 | 
			
		||||
    public function getKeyForQueue()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->getKey('queued');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get claimed LIST key
 | 
			
		||||
     *
 | 
			
		||||
     * Key will already be prefixed
 | 
			
		||||
     *
 | 
			
		||||
     * @return string
 | 
			
		||||
     */
 | 
			
		||||
    public function getKeyForClaimed()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->getKey('claimed');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Default contructor
 | 
			
		||||
     *
 | 
			
		||||
     * Beware that DrupalQueueInterface does not defines the __construct
 | 
			
		||||
     * method in the interface yet is being used from DrupalQueue::get()
 | 
			
		||||
     *
 | 
			
		||||
     * @param mixed $client
 | 
			
		||||
     * @param string $name
 | 
			
		||||
     */
 | 
			
		||||
    public function __construct($client, $name)
 | 
			
		||||
    {
 | 
			
		||||
        parent::__construct($client, self::QUEUE_KEY_PREFIX . $name);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										106
									
								
								sites/all/modules/contrib/dev/redis/lib/Redis/Queue/PhpRedis.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								sites/all/modules/contrib/dev/redis/lib/Redis/Queue/PhpRedis.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,106 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @todo
 | 
			
		||||
 *   Set high expire value to the hash for rotation when memory is empty
 | 
			
		||||
 *   React upon cache clear all and rebuild path list?
 | 
			
		||||
 */
 | 
			
		||||
class Redis_Queue_PhpRedis extends Redis_Queue_Base
 | 
			
		||||
{
 | 
			
		||||
    public function createItem($data)
 | 
			
		||||
    {
 | 
			
		||||
        $client = $this->getClient();
 | 
			
		||||
 | 
			
		||||
        $dKey = $this->getKeyForData();
 | 
			
		||||
        $qKey = $this->getKeyForQueue();
 | 
			
		||||
 | 
			
		||||
        // Identifier does not not need to be in the transaction,
 | 
			
		||||
        // in case of any error we'll just skip a value in the sequence.
 | 
			
		||||
        $id = $client->hincrby($dKey, self::QUEUE_HKEY_SEQ, 1);
 | 
			
		||||
 | 
			
		||||
        $record = new stdClass();
 | 
			
		||||
        $record->qid = $id;
 | 
			
		||||
        $record->data = $data;
 | 
			
		||||
        $record->timestamp = time();
 | 
			
		||||
 | 
			
		||||
        $pipe = $client->multi(Redis::PIPELINE);
 | 
			
		||||
        // Thanks to the redis_queue standalone module maintainers for
 | 
			
		||||
        // this piece of code, very effective. Note that we added the
 | 
			
		||||
        // pipeline thought.
 | 
			
		||||
        $pipe->hsetnx($dKey, $id, serialize($record));
 | 
			
		||||
        $pipe->llen($qKey);
 | 
			
		||||
        $pipe->lpush($qKey, $id);
 | 
			
		||||
        $ret = $pipe->exec();
 | 
			
		||||
 | 
			
		||||
        if (!$success = ($ret[0] && $ret[1] < $ret[2])) {
 | 
			
		||||
            if ($ret[0]) {
 | 
			
		||||
                // HSETNEX worked but not the PUSH command we therefore
 | 
			
		||||
                // need to drop the inserted data. I would have prefered
 | 
			
		||||
                // a DISCARD instead but we are in pipelined transaction
 | 
			
		||||
                // we cannot actually do a DISCARD here.
 | 
			
		||||
                $client->hdel($dKey, $id);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $success;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function numberOfItems()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->getClient()->llen($this->getKeyForQueue());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function claimItem($lease_time = 30)
 | 
			
		||||
    {
 | 
			
		||||
        // @todo Deal with lease
 | 
			
		||||
        $client = $this->getClient();
 | 
			
		||||
 | 
			
		||||
        $id = $client->rpoplpush(
 | 
			
		||||
            $this->getKeyForQueue(),
 | 
			
		||||
            $this->getKeyForClaimed()
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        if ($id) {
 | 
			
		||||
            if ($item = $client->hget($this->getKeyForData(), $id)) {
 | 
			
		||||
                if ($item = unserialize($item)) {
 | 
			
		||||
                    return $item;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function deleteItem($item)
 | 
			
		||||
    {
 | 
			
		||||
        $pipe = $this->getClient()->multi(Redis::PIPELINE);
 | 
			
		||||
        $pipe->lrem($this->getKeyForQueue(), $item->qid);
 | 
			
		||||
        $pipe->lrem($this->getKeyForClaimed(), $item->qid);
 | 
			
		||||
        $pipe->hdel($this->getKeyForData(), $item->qid);
 | 
			
		||||
        $pipe->exec();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function releaseItem($item)
 | 
			
		||||
    {
 | 
			
		||||
        $pipe = $this->getClient()->multi(Redis::PIPELINE);
 | 
			
		||||
        $pipe->lrem($this->getKeyForClaimed(), $item->qid, -1);
 | 
			
		||||
        $pipe->lpush($this->getKeyForQueue(), $item->qid);
 | 
			
		||||
        $ret = $pipe->exec();
 | 
			
		||||
 | 
			
		||||
        return $ret[0] && $ret[1];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function createQueue()
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function deleteQueue()
 | 
			
		||||
    {
 | 
			
		||||
        $this->getClient()->del(
 | 
			
		||||
            $this->getKeyForQueue(),
 | 
			
		||||
            $this->getKeyForClaimed(),
 | 
			
		||||
            $this->getKeyForData()
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -0,0 +1,141 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
abstract class Redis_Tests_AbstractUnitTestCase extends DrupalUnitTestCase
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * @var boolean
 | 
			
		||||
     */
 | 
			
		||||
    static protected $loaderEnabled = false;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Enable the autoloader
 | 
			
		||||
     *
 | 
			
		||||
     * This exists in this class in case the autoloader is not set into the
 | 
			
		||||
     * settings.php file or another way
 | 
			
		||||
     *
 | 
			
		||||
     * @return void|boolean
 | 
			
		||||
     */
 | 
			
		||||
    static protected function enableAutoload()
 | 
			
		||||
    {
 | 
			
		||||
        if (self::$loaderEnabled) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        if (class_exists('Redis_Client')) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        spl_autoload_register(function ($className) {
 | 
			
		||||
            $parts = explode('_', $className);
 | 
			
		||||
            if ('Redis' === $parts[0]) {
 | 
			
		||||
                $filename = __DIR__ . '/../lib/' . implode('/', $parts) . '.php';
 | 
			
		||||
                return (bool) include_once $filename;
 | 
			
		||||
            }
 | 
			
		||||
            return false;
 | 
			
		||||
        }, null, true);
 | 
			
		||||
 | 
			
		||||
        self::$loaderEnabled = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Drupal $conf array backup
 | 
			
		||||
     *
 | 
			
		||||
     * @var array
 | 
			
		||||
     */
 | 
			
		||||
    private $originalConf = array(
 | 
			
		||||
        'cache_lifetime'          => null,
 | 
			
		||||
        'cache_prefix'            => null,
 | 
			
		||||
        'redis_client_interface'  => null,
 | 
			
		||||
        'redis_eval_enabled'      => null,
 | 
			
		||||
        'redis_flush_mode'        => null,
 | 
			
		||||
        'redis_perm_ttl'          => null,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Prepare Drupal environmment for testing
 | 
			
		||||
     */
 | 
			
		||||
    final private function prepareDrupalEnvironment()
 | 
			
		||||
    {
 | 
			
		||||
        // Site on which the tests are running may define this variable
 | 
			
		||||
        // in their own settings.php file case in which it will be merged
 | 
			
		||||
        // with testing site
 | 
			
		||||
        global $conf;
 | 
			
		||||
        foreach (array_keys($this->originalConf) as $key) {
 | 
			
		||||
            if (isset($conf[$key])) {
 | 
			
		||||
                $this->originalConf[$key] = $conf[$key];
 | 
			
		||||
                unset($conf[$key]);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        $conf['cache_prefix'] = $this->testId;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Restore Drupal environment after testing.
 | 
			
		||||
     */
 | 
			
		||||
    final private function restoreDrupalEnvironment()
 | 
			
		||||
    {
 | 
			
		||||
        $GLOBALS['conf'] = $this->originalConf + $GLOBALS['conf'];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Prepare client manager
 | 
			
		||||
     */
 | 
			
		||||
    final private function prepareClientManager()
 | 
			
		||||
    {
 | 
			
		||||
        $interface = $this->getClientInterface();
 | 
			
		||||
 | 
			
		||||
        if (null === $interface) {
 | 
			
		||||
            throw new \Exception("Test skipped due to missing driver");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $GLOBALS['conf']['redis_client_interface'] = $interface;
 | 
			
		||||
        Redis_Client::reset();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Restore client manager
 | 
			
		||||
     */
 | 
			
		||||
    final private function restoreClientManager()
 | 
			
		||||
    {
 | 
			
		||||
        Redis_Client::reset();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Set up the Redis configuration.
 | 
			
		||||
     *
 | 
			
		||||
     * Set up the needed variables using variable_set() if necessary.
 | 
			
		||||
     *
 | 
			
		||||
     * @return string
 | 
			
		||||
     *   Client interface or null if not exists
 | 
			
		||||
     */
 | 
			
		||||
    abstract protected function getClientInterface();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * {@inheritdoc}
 | 
			
		||||
     */
 | 
			
		||||
    public function setUp()
 | 
			
		||||
    {
 | 
			
		||||
        self::enableAutoload();
 | 
			
		||||
 | 
			
		||||
        $this->prepareDrupalEnvironment();
 | 
			
		||||
        $this->prepareClientManager();
 | 
			
		||||
 | 
			
		||||
        parent::setUp();
 | 
			
		||||
 | 
			
		||||
        drupal_install_schema('system');
 | 
			
		||||
        drupal_install_schema('locale');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * {@inheritdoc}
 | 
			
		||||
     */
 | 
			
		||||
    public function tearDown()
 | 
			
		||||
    {
 | 
			
		||||
        drupal_uninstall_schema('locale');
 | 
			
		||||
        drupal_uninstall_schema('system');
 | 
			
		||||
 | 
			
		||||
        $this->restoreDrupalEnvironment();
 | 
			
		||||
        $this->restoreClientManager();
 | 
			
		||||
 | 
			
		||||
        parent::tearDown();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,60 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
class Redis_Tests_Admin_VariableTestCase extends DrupalWebTestCase
 | 
			
		||||
{
 | 
			
		||||
    public static function getInfo()
 | 
			
		||||
    {
 | 
			
		||||
        return array(
 | 
			
		||||
            'name'  => 'Redis variables',
 | 
			
		||||
            'description'  => 'Checks that Redis module variables are correctly type hinted when saved.',
 | 
			
		||||
            'group' => 'Redis',
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected $adminUser;
 | 
			
		||||
 | 
			
		||||
    public function setUp()
 | 
			
		||||
    {
 | 
			
		||||
        parent::setUp('redis');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testSave()
 | 
			
		||||
    {
 | 
			
		||||
        $this->adminUser = $this->drupalCreateUser(array('administer site configuration'));
 | 
			
		||||
        $this->drupalLogin($this->adminUser);
 | 
			
		||||
 | 
			
		||||
        // Tests port is an int.
 | 
			
		||||
        $this->drupalGet('admin/config/development/performance/redis');
 | 
			
		||||
        $edit = array(
 | 
			
		||||
            'redis_client_base'      => '',
 | 
			
		||||
            'redis_client_port'      => '1234',
 | 
			
		||||
            'redis_client_host'      => 'localhost',
 | 
			
		||||
            'redis_client_interface' => '',
 | 
			
		||||
        );
 | 
			
		||||
        $this->drupalPost('admin/config/development/performance/redis', $edit, t('Save configuration'));
 | 
			
		||||
 | 
			
		||||
        // Force variable cache to refresh.
 | 
			
		||||
        $test = variable_initialize();
 | 
			
		||||
        $conf = &$GLOBALS['conf'];
 | 
			
		||||
 | 
			
		||||
        $this->assertFalse(array_key_exists('redis_client_base', $conf), "Empty int value has been removed");
 | 
			
		||||
        $this->assertFalse(array_key_exists('redis_client_interface', $conf), "Empty string value has been removed");
 | 
			
		||||
        $this->assertIdentical($conf['redis_client_port'], 1234, "Saved int is an int");
 | 
			
		||||
        $this->assertIdentical($conf['redis_client_host'], 'localhost', "Saved string is a string");
 | 
			
		||||
 | 
			
		||||
        $this->drupalGet('admin/config/development/performance/redis');
 | 
			
		||||
        $edit = array(
 | 
			
		||||
            'redis_client_base'      => '0',
 | 
			
		||||
            'redis_client_port'      => '1234',
 | 
			
		||||
            'redis_client_host'      => 'localhost',
 | 
			
		||||
            'redis_client_interface' => '',
 | 
			
		||||
        );
 | 
			
		||||
        $this->drupalPost('admin/config/development/performance/redis', $edit, t('Save configuration'));
 | 
			
		||||
 | 
			
		||||
        // Force variable cache to refresh.
 | 
			
		||||
        $test = variable_initialize();
 | 
			
		||||
        $conf = &$GLOBALS['conf'];
 | 
			
		||||
 | 
			
		||||
        $this->assertIdentical($conf['redis_client_base'], 0, "Saved 0 valueed int is an int");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,27 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
if (!class_exists('Redis_Tests_Cache_FixesUnitTestCase')) {
 | 
			
		||||
  require_once(__DIR__ . '/FixesUnitTestCase.php');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class Redis_Tests_Cache_CompressedPhpRedisFixesUnitTestCase extends Redis_Tests_Cache_FixesUnitTestCase
 | 
			
		||||
{
 | 
			
		||||
    public static function getInfo()
 | 
			
		||||
    {
 | 
			
		||||
        return array(
 | 
			
		||||
            'name'        => 'Compressed PhpRedis cache fixes',
 | 
			
		||||
            'description' => 'Tests Redis module cache fixes feature.',
 | 
			
		||||
            'group'       => 'Redis',
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function createCacheInstance($name = null)
 | 
			
		||||
    {
 | 
			
		||||
        return new Redis_CacheCompressed($name);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function getClientInterface()
 | 
			
		||||
    {
 | 
			
		||||
        return 'PhpRedis';
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,27 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
if (!class_exists('Redis_Tests_Cache_FlushUnitTestCase')) {
 | 
			
		||||
  require_once(__DIR__ . '/FlushUnitTestCase.php');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class Redis_Tests_Cache_CompressedPhpRedisFlushUnitTestCase extends Redis_Tests_Cache_FlushUnitTestCase
 | 
			
		||||
{
 | 
			
		||||
    public static function getInfo()
 | 
			
		||||
    {
 | 
			
		||||
        return array(
 | 
			
		||||
            'name'        => 'Compressed PhpRedis cache flush',
 | 
			
		||||
            'description' => 'Tests Redis module cache flush modes feature.',
 | 
			
		||||
            'group'       => 'Redis',
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function createCacheInstance($name = null)
 | 
			
		||||
    {
 | 
			
		||||
        return new Redis_CacheCompressed($name);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function getClientInterface()
 | 
			
		||||
    {
 | 
			
		||||
        return 'PhpRedis';
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,25 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
class Redis_Tests_Cache_CompressedPhpRedisShardedFixesUnitTestCase extends Redis_Tests_Cache_FixesUnitTestCase
 | 
			
		||||
{
 | 
			
		||||
    public static function getInfo()
 | 
			
		||||
    {
 | 
			
		||||
        return array(
 | 
			
		||||
            'name'        => 'Compressed PhpRedis cache fixes (S)',
 | 
			
		||||
            'description' => 'Tests Redis module cache fixes feature.',
 | 
			
		||||
            'group'       => 'Redis',
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function createCacheInstance($name = null)
 | 
			
		||||
    {
 | 
			
		||||
        return new Redis_CacheCompressed($name);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function getClientInterface()
 | 
			
		||||
    {
 | 
			
		||||
        $GLOBALS['conf']['redis_flush_mode'] = Redis_Cache::FLUSH_SHARD;
 | 
			
		||||
 | 
			
		||||
        return 'PhpRedis';
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,25 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
class Redis_Tests_Cache_CompressedPhpRedisShardedFlushUnitTestCase extends Redis_Tests_Cache_FlushUnitTestCase
 | 
			
		||||
{
 | 
			
		||||
    public static function getInfo()
 | 
			
		||||
    {
 | 
			
		||||
        return array(
 | 
			
		||||
            'name'        => 'Compressed PhpRedis cache flush (S)',
 | 
			
		||||
            'description' => 'Tests Redis module cache flush modes feature.',
 | 
			
		||||
            'group'       => 'Redis',
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function createCacheInstance($name = null)
 | 
			
		||||
    {
 | 
			
		||||
        return new Redis_CacheCompressed($name);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function getClientInterface()
 | 
			
		||||
    {
 | 
			
		||||
        $GLOBALS['conf']['redis_flush_mode'] = Redis_Cache::FLUSH_SHARD;
 | 
			
		||||
 | 
			
		||||
        return 'PhpRedis';
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,25 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
class Redis_Tests_Cache_CompressedPhpRedisShardedWithPipelineFixesUnitTestCase extends Redis_Tests_Cache_FixesUnitTestCase
 | 
			
		||||
{
 | 
			
		||||
    public static function getInfo()
 | 
			
		||||
    {
 | 
			
		||||
        return array(
 | 
			
		||||
            'name'        => 'Compressed PhpRedis cache fixes (SP)',
 | 
			
		||||
            'description' => 'Tests Redis module cache fixes feature.',
 | 
			
		||||
            'group'       => 'Redis',
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function createCacheInstance($name = null)
 | 
			
		||||
    {
 | 
			
		||||
        return new Redis_CacheCompressed($name);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function getClientInterface()
 | 
			
		||||
    {
 | 
			
		||||
        $GLOBALS['conf']['redis_flush_mode'] = Redis_Cache::FLUSH_SHARD_WITH_PIPELINING;
 | 
			
		||||
 | 
			
		||||
        return 'PhpRedis';
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,209 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
if (!class_exists('Redis_Tests_AbstractUnitTestCase')) {
 | 
			
		||||
  require_once(__DIR__ . '/../AbstractUnitTestCase.php');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Bugfixes made over time test class.
 | 
			
		||||
 */
 | 
			
		||||
abstract class Redis_Tests_Cache_FixesUnitTestCase extends Redis_Tests_AbstractUnitTestCase
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * @var Cache bin identifier
 | 
			
		||||
     */
 | 
			
		||||
    static private $id = 1;
 | 
			
		||||
 | 
			
		||||
    protected function createCacheInstance($name = null)
 | 
			
		||||
    {
 | 
			
		||||
        return new Redis_Cache($name);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get cache backend
 | 
			
		||||
     *
 | 
			
		||||
     * @return Redis_Cache
 | 
			
		||||
     */
 | 
			
		||||
    final protected function getBackend($name = null)
 | 
			
		||||
    {
 | 
			
		||||
        if (null === $name) {
 | 
			
		||||
            // This is needed to avoid conflict between tests, each test
 | 
			
		||||
            // seems to use the same Redis namespace and conflicts are
 | 
			
		||||
            // possible.
 | 
			
		||||
            $name = 'cache' . (self::$id++);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $backend = $this->createCacheInstance($name);
 | 
			
		||||
 | 
			
		||||
        $this->assert(true, "Redis client is " . ($backend->isSharded() ? '' : "NOT ") . " sharded");
 | 
			
		||||
        $this->assert(true, "Redis client is " . ($backend->allowTemporaryFlush() ? '' : "NOT ") . " allowed to flush temporary entries");
 | 
			
		||||
        $this->assert(true, "Redis client is " . ($backend->allowPipeline() ? '' : "NOT ") . " allowed to use pipeline");
 | 
			
		||||
 | 
			
		||||
        return $backend;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testTemporaryCacheExpire()
 | 
			
		||||
    {
 | 
			
		||||
        global $conf; // We are in unit tests so variable table does not exist.
 | 
			
		||||
 | 
			
		||||
        $backend = $this->getBackend();
 | 
			
		||||
 | 
			
		||||
        // Permanent entry.
 | 
			
		||||
        $backend->set('test1', 'foo', CACHE_PERMANENT);
 | 
			
		||||
        $data = $backend->get('test1');
 | 
			
		||||
        $this->assertNotEqual(false, $data);
 | 
			
		||||
        $this->assertIdentical('foo', $data->data);
 | 
			
		||||
 | 
			
		||||
        // Permanent entries should not be dropped on clear() call.
 | 
			
		||||
        $backend->clear();
 | 
			
		||||
        $data = $backend->get('test1');
 | 
			
		||||
        $this->assertNotEqual(false, $data);
 | 
			
		||||
        $this->assertIdentical('foo', $data->data);
 | 
			
		||||
 | 
			
		||||
        // Expiring entry with permanent default lifetime.
 | 
			
		||||
        $conf['cache_lifetime'] = 0;
 | 
			
		||||
        $backend->set('test2', 'bar', CACHE_TEMPORARY);
 | 
			
		||||
        sleep(2);
 | 
			
		||||
        $data = $backend->get('test2');
 | 
			
		||||
        $this->assertNotEqual(false, $data);
 | 
			
		||||
        $this->assertIdentical('bar', $data->data);
 | 
			
		||||
        sleep(2);
 | 
			
		||||
        $data = $backend->get('test2');
 | 
			
		||||
        $this->assertNotEqual(false, $data);
 | 
			
		||||
        $this->assertIdentical('bar', $data->data);
 | 
			
		||||
 | 
			
		||||
        // Expiring entry with negative lifetime.
 | 
			
		||||
        $backend->set('test3', 'baz', time() - 100);
 | 
			
		||||
        $data = $backend->get('test3');
 | 
			
		||||
        $this->assertEqual(false, $data);
 | 
			
		||||
 | 
			
		||||
        // Expiring entry with short lifetime.
 | 
			
		||||
        $backend->set('test4', 'foobar', time() + 2);
 | 
			
		||||
        $data = $backend->get('test4');
 | 
			
		||||
        $this->assertNotEqual(false, $data);
 | 
			
		||||
        $this->assertIdentical('foobar', $data->data);
 | 
			
		||||
        sleep(4);
 | 
			
		||||
        $data = $backend->get('test4');
 | 
			
		||||
        $this->assertEqual(false, $data);
 | 
			
		||||
 | 
			
		||||
        // Expiring entry with short default lifetime.
 | 
			
		||||
        $conf['cache_lifetime'] = 1;
 | 
			
		||||
        $backend->refreshMaxTtl();
 | 
			
		||||
        $backend->set('test5', 'foobaz', CACHE_TEMPORARY);
 | 
			
		||||
        $data = $backend->get('test5');
 | 
			
		||||
        $this->assertNotEqual(false, $data);
 | 
			
		||||
        $this->assertIdentical('foobaz', $data->data);
 | 
			
		||||
        sleep(3);
 | 
			
		||||
        $data = $backend->get('test5');
 | 
			
		||||
        $this->assertEqual(false, $data);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testDefaultPermTtl()
 | 
			
		||||
    {
 | 
			
		||||
        global $conf;
 | 
			
		||||
        unset($conf['redis_perm_ttl']);
 | 
			
		||||
        $backend = $this->getBackend();
 | 
			
		||||
        $this->assertIdentical(Redis_Cache::LIFETIME_PERM_DEFAULT, $backend->getPermTtl());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testUserSetDefaultPermTtl()
 | 
			
		||||
    {
 | 
			
		||||
        global $conf;
 | 
			
		||||
        // This also testes string parsing. Not fully, but at least one case.
 | 
			
		||||
        $conf['redis_perm_ttl'] = "3 months";
 | 
			
		||||
        $backend = $this->getBackend();
 | 
			
		||||
        $this->assertIdentical(7776000, $backend->getPermTtl());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testUserSetPermTtl()
 | 
			
		||||
    {
 | 
			
		||||
        global $conf;
 | 
			
		||||
        // This also testes string parsing. Not fully, but at least one case.
 | 
			
		||||
        $conf['redis_perm_ttl'] = "1 months";
 | 
			
		||||
        $backend = $this->getBackend();
 | 
			
		||||
        $this->assertIdentical(2592000, $backend->getPermTtl());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testGetMultiple()
 | 
			
		||||
    {
 | 
			
		||||
        $backend = $this->getBackend();
 | 
			
		||||
 | 
			
		||||
        $backend->set('multiple1', 1);
 | 
			
		||||
        $backend->set('multiple2', 2);
 | 
			
		||||
        $backend->set('multiple3', 3);
 | 
			
		||||
        $backend->set('multiple4', 4);
 | 
			
		||||
 | 
			
		||||
        $cidList = array('multiple1', 'multiple2', 'multiple3', 'multiple4', 'multiple5');
 | 
			
		||||
        $ret = $backend->getMultiple($cidList);
 | 
			
		||||
 | 
			
		||||
        $this->assertEqual(1, count($cidList));
 | 
			
		||||
        $this->assertFalse(isset($cidList[0]));
 | 
			
		||||
        $this->assertFalse(isset($cidList[1]));
 | 
			
		||||
        $this->assertFalse(isset($cidList[2]));
 | 
			
		||||
        $this->assertFalse(isset($cidList[3]));
 | 
			
		||||
        $this->assertTrue(isset($cidList[4]));
 | 
			
		||||
 | 
			
		||||
        $this->assertEqual(4, count($ret));
 | 
			
		||||
        $this->assertTrue(isset($ret['multiple1']));
 | 
			
		||||
        $this->assertTrue(isset($ret['multiple2']));
 | 
			
		||||
        $this->assertTrue(isset($ret['multiple3']));
 | 
			
		||||
        $this->assertTrue(isset($ret['multiple4']));
 | 
			
		||||
        $this->assertFalse(isset($ret['multiple5']));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testPermTtl()
 | 
			
		||||
    {
 | 
			
		||||
        global $conf;
 | 
			
		||||
        // This also testes string parsing. Not fully, but at least one case.
 | 
			
		||||
        $conf['redis_perm_ttl'] = "2 seconds";
 | 
			
		||||
        $backend = $this->getBackend();
 | 
			
		||||
        $this->assertIdentical(2, $backend->getPermTtl());
 | 
			
		||||
 | 
			
		||||
        $backend->set('test6', 'cats are mean');
 | 
			
		||||
        $this->assertIdentical('cats are mean', $backend->get('test6')->data);
 | 
			
		||||
 | 
			
		||||
        sleep(3);
 | 
			
		||||
        $item = $backend->get('test6');
 | 
			
		||||
        $this->assertTrue(empty($item));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testClearAsArray()
 | 
			
		||||
    {
 | 
			
		||||
        $backend = $this->getBackend();
 | 
			
		||||
 | 
			
		||||
        $backend->set('test7', 1);
 | 
			
		||||
        $backend->set('test8', 2);
 | 
			
		||||
        $backend->set('test9', 3);
 | 
			
		||||
 | 
			
		||||
        $backend->clear(array('test7', 'test9'));
 | 
			
		||||
 | 
			
		||||
        $item = $backend->get('test7');
 | 
			
		||||
        $this->assertTrue(empty($item));
 | 
			
		||||
        $item = $backend->get('test8');
 | 
			
		||||
        $this->assertEqual(2, $item->data);
 | 
			
		||||
        $item = $backend->get('test9');
 | 
			
		||||
        $this->assertTrue(empty($item));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testGetMultipleAlterCidsWhenCacheHitsOnly()
 | 
			
		||||
    {
 | 
			
		||||
        $backend = $this->getBackend();
 | 
			
		||||
        $backend->clear('*', true); // It seems that there are leftovers.
 | 
			
		||||
 | 
			
		||||
        $backend->set('mtest1', 'pouf');
 | 
			
		||||
 | 
			
		||||
        $cids_partial_hit = array('foo' => 'mtest1', 'bar' => 'mtest2');
 | 
			
		||||
        $entries = $backend->getMultiple($cids_partial_hit);
 | 
			
		||||
        $this->assertIdentical(1, count($entries));
 | 
			
		||||
        // Note that the key is important because the method should
 | 
			
		||||
        // keep the keys synchronized.
 | 
			
		||||
        $this->assertEqual(array('bar' => 'mtest2'), $cids_partial_hit);
 | 
			
		||||
 | 
			
		||||
        $backend->clear('mtest1');
 | 
			
		||||
 | 
			
		||||
        $cids_no_hit = array('cat' => 'mtest1', 'dog' => 'mtest2');
 | 
			
		||||
        $entries = $backend->getMultiple($cids_no_hit);
 | 
			
		||||
        $this->assertIdentical(0, count($entries));
 | 
			
		||||
        $this->assertEqual(array('cat' => 'mtest1', 'dog' => 'mtest2'), $cids_no_hit);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,185 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
abstract class Redis_Tests_Cache_FlushUnitTestCase extends Redis_Tests_AbstractUnitTestCase
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * @var Cache bin identifier
 | 
			
		||||
     */
 | 
			
		||||
    static private $id = 1;
 | 
			
		||||
 | 
			
		||||
    protected function createCacheInstance($name = null)
 | 
			
		||||
    {
 | 
			
		||||
        return new Redis_Cache($name);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get cache backend
 | 
			
		||||
     *
 | 
			
		||||
     * @return Redis_Cache
 | 
			
		||||
     */
 | 
			
		||||
    final protected function getBackend($name = null)
 | 
			
		||||
    {
 | 
			
		||||
        if (null === $name) {
 | 
			
		||||
            // This is needed to avoid conflict between tests, each test
 | 
			
		||||
            // seems to use the same Redis namespace and conflicts are
 | 
			
		||||
            // possible.
 | 
			
		||||
            $name = 'cache' . (self::$id++);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $backend = $this->createCacheInstance($name);
 | 
			
		||||
 | 
			
		||||
        $this->assert(true, "Redis client is " . ($backend->isSharded() ? '' : "NOT ") . " sharded");
 | 
			
		||||
        $this->assert(true, "Redis client is " . ($backend->allowTemporaryFlush() ? '' : "NOT ") . " allowed to flush temporary entries");
 | 
			
		||||
        $this->assert(true, "Redis client is " . ($backend->allowPipeline() ? '' : "NOT ") . " allowed to use pipeline");
 | 
			
		||||
 | 
			
		||||
        return $backend;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Tests that with a default cache lifetime temporary non expired
 | 
			
		||||
     * items are kept even when in temporary flush mode.
 | 
			
		||||
     */
 | 
			
		||||
    public function testFlushIsTemporaryWithLifetime()
 | 
			
		||||
    {
 | 
			
		||||
        $GLOBALS['conf']['cache_lifetime'] = 112;
 | 
			
		||||
 | 
			
		||||
        $backend = $this->getBackend();
 | 
			
		||||
 | 
			
		||||
        // Even though we set a flush mode into this bin, Drupal default
 | 
			
		||||
        // behavior when a cache_lifetime is set is to override the backend
 | 
			
		||||
        // one in order to keep the core behavior and avoid potential
 | 
			
		||||
        // nasty bugs.
 | 
			
		||||
        $this->assertFalse($backend->allowTemporaryFlush());
 | 
			
		||||
 | 
			
		||||
        $backend->set('test7', 42, CACHE_PERMANENT);
 | 
			
		||||
        $backend->set('test8', 'foo', CACHE_TEMPORARY);
 | 
			
		||||
        $backend->set('test9', 'bar', time() + 1000);
 | 
			
		||||
 | 
			
		||||
        $backend->clear();
 | 
			
		||||
 | 
			
		||||
        $cache = $backend->get('test7');
 | 
			
		||||
        $this->assertNotEqual(false, $cache);
 | 
			
		||||
        $this->assertEqual($cache->data, 42);
 | 
			
		||||
        $cache = $backend->get('test8');
 | 
			
		||||
        $this->assertNotEqual(false, $cache);
 | 
			
		||||
        $this->assertEqual($cache->data, 'foo');
 | 
			
		||||
        $cache = $backend->get('test9');
 | 
			
		||||
        $this->assertNotEqual(false, $cache);
 | 
			
		||||
        $this->assertEqual($cache->data, 'bar');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Tests that with no default cache lifetime all temporary items are
 | 
			
		||||
     * droppped when in temporary flush mode.
 | 
			
		||||
     */
 | 
			
		||||
    public function testFlushIsTemporaryWithoutLifetime()
 | 
			
		||||
    {
 | 
			
		||||
        $backend = $this->getBackend();
 | 
			
		||||
 | 
			
		||||
        $this->assertTrue($backend->allowTemporaryFlush());
 | 
			
		||||
 | 
			
		||||
        $backend->set('test10', 42, CACHE_PERMANENT);
 | 
			
		||||
        // Ugly concatenation with the mode, but it will be visible in tests
 | 
			
		||||
        // reports if the entry shows up, thus allowing us to know which real
 | 
			
		||||
        // test case is run at this time
 | 
			
		||||
        $backend->set('test11', 'foo' . $backend->isSharded(), CACHE_TEMPORARY);
 | 
			
		||||
        $backend->set('test12', 'bar' . $backend->isSharded(), time() + 10);
 | 
			
		||||
 | 
			
		||||
        $backend->clear();
 | 
			
		||||
 | 
			
		||||
        $cache = $backend->get('test10');
 | 
			
		||||
        $this->assertNotEqual(false, $cache);
 | 
			
		||||
        $this->assertEqual($cache->data, 42);
 | 
			
		||||
        $this->assertFalse($backend->get('test11'));
 | 
			
		||||
 | 
			
		||||
        $cache = $backend->get('test12');
 | 
			
		||||
        $this->assertNotEqual(false, $cache);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testNormalFlushing()
 | 
			
		||||
    {
 | 
			
		||||
        $backend = $this->getBackend();
 | 
			
		||||
        $backendUntouched = $this->getBackend();
 | 
			
		||||
 | 
			
		||||
        // Set a few entries.
 | 
			
		||||
        $backend->set('test13', 'foo');
 | 
			
		||||
        $backend->set('test14', 'bar', CACHE_TEMPORARY);
 | 
			
		||||
        $backend->set('test15', 'baz', time() + 3);
 | 
			
		||||
 | 
			
		||||
        $backendUntouched->set('test16', 'dog');
 | 
			
		||||
        $backendUntouched->set('test17', 'cat', CACHE_TEMPORARY);
 | 
			
		||||
        $backendUntouched->set('test18', 'xor', time() + 5);
 | 
			
		||||
 | 
			
		||||
        // This should not do anything (bugguy command)
 | 
			
		||||
        $backend->clear('', true);
 | 
			
		||||
        $backend->clear('', false);
 | 
			
		||||
        $this->assertNotIdentical(false, $backend->get('test13'));
 | 
			
		||||
        $this->assertNotIdentical(false, $backend->get('test14'));
 | 
			
		||||
        $this->assertNotIdentical(false, $backend->get('test15'));
 | 
			
		||||
        $this->assertNotIdentical(false, $backendUntouched->get('test16'));
 | 
			
		||||
        $this->assertNotIdentical(false, $backendUntouched->get('test17'));
 | 
			
		||||
        $this->assertNotIdentical(false, $backendUntouched->get('test18'));
 | 
			
		||||
 | 
			
		||||
        // This should clear every one, permanent and volatile
 | 
			
		||||
        $backend->clear('*', true);
 | 
			
		||||
        $this->assertFalse($backend->get('test13'));
 | 
			
		||||
        $this->assertFalse($backend->get('test14'));
 | 
			
		||||
        $this->assertFalse($backend->get('test15'));
 | 
			
		||||
        $this->assertNotIdentical(false, $backendUntouched->get('test16'));
 | 
			
		||||
        $this->assertNotIdentical(false, $backendUntouched->get('test17'));
 | 
			
		||||
        $this->assertNotIdentical(false, $backendUntouched->get('test18'));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testPrefixDeletionWithSeparatorChar()
 | 
			
		||||
    {
 | 
			
		||||
        $backend = $this->getBackend();
 | 
			
		||||
 | 
			
		||||
        $backend->set('testprefix10', 'foo');
 | 
			
		||||
        $backend->set('testprefix11', 'foo');
 | 
			
		||||
        $backend->set('testprefix:12', 'bar');
 | 
			
		||||
        $backend->set('testprefix:13', 'baz');
 | 
			
		||||
        $backend->set('testnoprefix14', 'giraffe');
 | 
			
		||||
        $backend->set('testnoprefix:15', 'elephant');
 | 
			
		||||
 | 
			
		||||
        $backend->clear('testprefix:', true);
 | 
			
		||||
        $this->assertFalse($backend->get('testprefix:12'));
 | 
			
		||||
        $this->assertFalse($backend->get('testprefix:13'));
 | 
			
		||||
        // @todo Temporary fix
 | 
			
		||||
        // At the moment shard enabled backends will erase all data instead
 | 
			
		||||
        // of just removing by prefix, so those tests won't pass
 | 
			
		||||
        if (!$backend->isSharded()) {
 | 
			
		||||
            $this->assertNotIdentical(false, $backend->get('testprefix10'));
 | 
			
		||||
            $this->assertNotIdentical(false, $backend->get('testprefix11'));
 | 
			
		||||
            $this->assertNotIdentical(false, $backend->get('testnoprefix14'));
 | 
			
		||||
            $this->assertNotIdentical(false, $backend->get('testnoprefix:15'));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $backend->clear('testprefix', true);
 | 
			
		||||
        $this->assertFalse($backend->get('testprefix10'));
 | 
			
		||||
        $this->assertFalse($backend->get('testprefix11'));
 | 
			
		||||
        // @todo Temporary fix
 | 
			
		||||
        // At the moment shard enabled backends will erase all data instead
 | 
			
		||||
        // of just removing by prefix, so those tests won't pass
 | 
			
		||||
        if (!$backend->isSharded()) {
 | 
			
		||||
            $this->assertNotIdentical(false, $backend->get('testnoprefix14'));
 | 
			
		||||
            $this->assertNotIdentical(false, $backend->get('testnoprefix:15'));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testOrder()
 | 
			
		||||
    {
 | 
			
		||||
        $backend = $this->getBackend();
 | 
			
		||||
 | 
			
		||||
        for ($i = 0; $i < 10; ++$i) {
 | 
			
		||||
            $id = 'speedtest' . $i;
 | 
			
		||||
            $backend->set($id, 'somevalue');
 | 
			
		||||
            $this->assertNotIdentical(false, $backend->get($id));
 | 
			
		||||
            $backend->clear('*', true);
 | 
			
		||||
            // Value created the same second before is dropped
 | 
			
		||||
            $this->assertFalse($backend->get($id));
 | 
			
		||||
            $backend->set($id, 'somevalue');
 | 
			
		||||
            // Value created the same second after is kept
 | 
			
		||||
            $this->assertNotIdentical(false, $backend->get($id));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,22 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
if (!class_exists('Redis_Tests_Cache_FixesUnitTestCase')) {
 | 
			
		||||
  require_once(__DIR__ . '/FixesUnitTestCase.php');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class Redis_Tests_Cache_PhpRedisFixesUnitTestCase extends Redis_Tests_Cache_FixesUnitTestCase
 | 
			
		||||
{
 | 
			
		||||
    public static function getInfo()
 | 
			
		||||
    {
 | 
			
		||||
        return array(
 | 
			
		||||
            'name'        => 'PhpRedis cache fixes',
 | 
			
		||||
            'description' => 'Tests Redis module cache fixes feature.',
 | 
			
		||||
            'group'       => 'Redis',
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function getClientInterface()
 | 
			
		||||
    {
 | 
			
		||||
        return 'PhpRedis';
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,22 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
if (!class_exists('Redis_Tests_Cache_FlushUnitTestCase')) {
 | 
			
		||||
  require_once(__DIR__ . '/FlushUnitTestCase.php');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class Redis_Tests_Cache_PhpRedisFlushUnitTestCase extends Redis_Tests_Cache_FlushUnitTestCase
 | 
			
		||||
{
 | 
			
		||||
    public static function getInfo()
 | 
			
		||||
    {
 | 
			
		||||
        return array(
 | 
			
		||||
            'name'        => 'PhpRedis cache flush',
 | 
			
		||||
            'description' => 'Tests Redis module cache flush modes feature.',
 | 
			
		||||
            'group'       => 'Redis',
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function getClientInterface()
 | 
			
		||||
    {
 | 
			
		||||
        return 'PhpRedis';
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,20 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
class Redis_Tests_Cache_PhpRedisShardedFixesUnitTestCase extends Redis_Tests_Cache_FixesUnitTestCase
 | 
			
		||||
{
 | 
			
		||||
    public static function getInfo()
 | 
			
		||||
    {
 | 
			
		||||
        return array(
 | 
			
		||||
            'name'        => 'PhpRedis cache fixes (S)',
 | 
			
		||||
            'description' => 'Tests Redis module cache fixes feature.',
 | 
			
		||||
            'group'       => 'Redis',
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function getClientInterface()
 | 
			
		||||
    {
 | 
			
		||||
        $GLOBALS['conf']['redis_flush_mode'] = Redis_Cache::FLUSH_SHARD;
 | 
			
		||||
 | 
			
		||||
        return 'PhpRedis';
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,20 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
class Redis_Tests_Cache_PhpRedisShardedFlushUnitTestCase extends Redis_Tests_Cache_FlushUnitTestCase
 | 
			
		||||
{
 | 
			
		||||
    public static function getInfo()
 | 
			
		||||
    {
 | 
			
		||||
        return array(
 | 
			
		||||
            'name'        => 'PhpRedis cache flush (S)',
 | 
			
		||||
            'description' => 'Tests Redis module cache flush modes feature.',
 | 
			
		||||
            'group'       => 'Redis',
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function getClientInterface()
 | 
			
		||||
    {
 | 
			
		||||
        $GLOBALS['conf']['redis_flush_mode'] = Redis_Cache::FLUSH_SHARD;
 | 
			
		||||
 | 
			
		||||
        return 'PhpRedis';
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,20 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
class Redis_Tests_Cache_PhpRedisShardedWithPipelineFixesUnitTestCase extends Redis_Tests_Cache_FixesUnitTestCase
 | 
			
		||||
{
 | 
			
		||||
    public static function getInfo()
 | 
			
		||||
    {
 | 
			
		||||
        return array(
 | 
			
		||||
            'name'        => 'PhpRedis cache fixes (SP)',
 | 
			
		||||
            'description' => 'Tests Redis module cache fixes feature.',
 | 
			
		||||
            'group'       => 'Redis',
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function getClientInterface()
 | 
			
		||||
    {
 | 
			
		||||
        $GLOBALS['conf']['redis_flush_mode'] = Redis_Cache::FLUSH_SHARD_WITH_PIPELINING;
 | 
			
		||||
 | 
			
		||||
        return 'PhpRedis';
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,18 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
class Redis_Tests_Cache_PredisFixesUnitTestCase extends Redis_Tests_Cache_FixesUnitTestCase
 | 
			
		||||
{
 | 
			
		||||
    public static function getInfo()
 | 
			
		||||
    {
 | 
			
		||||
        return array(
 | 
			
		||||
            'name'         => 'Predis cache fixes',
 | 
			
		||||
            'description'  => 'Tests Redis module cache fixes feature.',
 | 
			
		||||
            'group'        => 'Redis',
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function getClientInterface()
 | 
			
		||||
    {
 | 
			
		||||
        return 'Predis';
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,18 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
class Redis_Tests_Cache_PredisFlushUnitTestCase extends Redis_Tests_Cache_FlushUnitTestCase
 | 
			
		||||
{
 | 
			
		||||
    public static function getInfo()
 | 
			
		||||
    {
 | 
			
		||||
        return array(
 | 
			
		||||
            'name'         => 'Predis cache flush',
 | 
			
		||||
            'description'  => 'Tests Redis module cache flush modes feature.',
 | 
			
		||||
            'group'        => 'Redis',
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function getClientInterface()
 | 
			
		||||
    {
 | 
			
		||||
        return 'Predis';
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,20 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
class Redis_Tests_Cache_PredisShardedFixesUnitTestCase extends Redis_Tests_Cache_FixesUnitTestCase
 | 
			
		||||
{
 | 
			
		||||
    public static function getInfo()
 | 
			
		||||
    {
 | 
			
		||||
        return array(
 | 
			
		||||
            'name'         => 'Predis cache fixes (S)',
 | 
			
		||||
            'description'  => 'Tests Redis module cache fixes feature.',
 | 
			
		||||
            'group'        => 'Redis',
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function getClientInterface()
 | 
			
		||||
    {
 | 
			
		||||
        $GLOBALS['conf']['redis_flush_mode'] = Redis_Cache::FLUSH_SHARD;
 | 
			
		||||
 | 
			
		||||
        return 'Predis';
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,20 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
class Redis_Tests_Cache_PredisShardedFlushUnitTestCase extends Redis_Tests_Cache_FlushUnitTestCase
 | 
			
		||||
{
 | 
			
		||||
    public static function getInfo()
 | 
			
		||||
    {
 | 
			
		||||
        return array(
 | 
			
		||||
            'name'         => 'Predis cache flush (S)',
 | 
			
		||||
            'description'  => 'Tests Redis module cache flush modes feature.',
 | 
			
		||||
            'group'        => 'Redis',
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function getClientInterface()
 | 
			
		||||
    {
 | 
			
		||||
        $GLOBALS['conf']['redis_flush_mode'] = Redis_Cache::FLUSH_SHARD;
 | 
			
		||||
 | 
			
		||||
        return 'Predis';
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,20 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
class Redis_Tests_Cache_PredisShardedWithPipelineFixesUnitTestCase extends Redis_Tests_Cache_FixesUnitTestCase
 | 
			
		||||
{
 | 
			
		||||
    public static function getInfo()
 | 
			
		||||
    {
 | 
			
		||||
        return array(
 | 
			
		||||
            'name'         => 'Predis cache fixes (SP)',
 | 
			
		||||
            'description'  => 'Tests Redis module cache fixes feature.',
 | 
			
		||||
            'group'        => 'Redis',
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function getClientInterface()
 | 
			
		||||
    {
 | 
			
		||||
        $GLOBALS['conf']['redis_flush_mode'] = Redis_Cache::FLUSH_SHARD_WITH_PIPELINING;
 | 
			
		||||
 | 
			
		||||
        return 'Predis';
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,66 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
class Redis_Tests_Client_UnitTestCase extends Redis_Tests_AbstractUnitTestCase
 | 
			
		||||
{
 | 
			
		||||
    public static function getInfo()
 | 
			
		||||
    {
 | 
			
		||||
        return array(
 | 
			
		||||
            'name'        => 'Redis client manager',
 | 
			
		||||
            'description' => 'Tests Redis module client manager feature.',
 | 
			
		||||
            'group'       => 'Redis',
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function getClientInterface()
 | 
			
		||||
    {
 | 
			
		||||
        return 'PhpRedis';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getManager()
 | 
			
		||||
    {
 | 
			
		||||
        return new Redis_Client_Manager(
 | 
			
		||||
            new Redis_Tests_Client_MockFactory(),
 | 
			
		||||
            array(
 | 
			
		||||
                'default' => array(),
 | 
			
		||||
                'foo' => array(
 | 
			
		||||
                    'host' => 'foo.com',
 | 
			
		||||
                    'port' => 666,
 | 
			
		||||
                ),
 | 
			
		||||
                'bar' => array(
 | 
			
		||||
                    'host' => 'bar.com',
 | 
			
		||||
                ),
 | 
			
		||||
            )
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testManagerServerList()
 | 
			
		||||
    {
 | 
			
		||||
        $manager = $this->getManager();
 | 
			
		||||
 | 
			
		||||
        $defaultClient = $manager->getClient();
 | 
			
		||||
        $this->assertTrue(is_object($defaultClient));
 | 
			
		||||
 | 
			
		||||
        // Ensure defaults are OK
 | 
			
		||||
        $this->assertIdentical(Redis_Client_Manager::REDIS_DEFAULT_HOST, $defaultClient->host);
 | 
			
		||||
        $this->assertIdentical(Redis_Client_Manager::REDIS_DEFAULT_PORT, $defaultClient->port);
 | 
			
		||||
        $this->assertFalse(property_exists($defaultClient, 'base'));
 | 
			
		||||
        $this->assertFalse(property_exists($defaultClient, 'password'));
 | 
			
		||||
 | 
			
		||||
        $client = $manager->getClient('foo');
 | 
			
		||||
        $this->assertIdentical('foo.com', $client->host);
 | 
			
		||||
        $this->assertIdentical(666, $client->port);
 | 
			
		||||
 | 
			
		||||
        $client = $manager->getClient('bar');
 | 
			
		||||
        $this->assertIdentical('bar.com', $client->host);
 | 
			
		||||
        $this->assertIdentical(Redis_Client_Manager::REDIS_DEFAULT_PORT, $client->port);
 | 
			
		||||
 | 
			
		||||
        $this->assertIdentical($defaultClient, $manager->getClient('non_existing'));
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            $manager->getClient('other_non_existing', false);
 | 
			
		||||
            $this->assert(false);
 | 
			
		||||
        } catch (\InvalidArgumentException $e) {
 | 
			
		||||
            $this->assert(true);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,14 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
class Redis_Tests_Client_MockFactory implements Redis_Client_FactoryInterface
 | 
			
		||||
{
 | 
			
		||||
    public function getClient($options = array())
 | 
			
		||||
    {
 | 
			
		||||
        return (object)$options;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getName()
 | 
			
		||||
    {
 | 
			
		||||
        return 'Mock';
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,119 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
abstract class Redis_Tests_Lock_LockingUnitTestCase extends Redis_Tests_AbstractUnitTestCase
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * Ensure lock flush at tear down
 | 
			
		||||
     *
 | 
			
		||||
     * @var array
 | 
			
		||||
     */
 | 
			
		||||
    protected $backends = array();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the lock client class name
 | 
			
		||||
     *
 | 
			
		||||
     * @return string
 | 
			
		||||
     *   Lock backend class name or null if cannot spawn it
 | 
			
		||||
     */
 | 
			
		||||
    abstract protected function getLockBackendClass();
 | 
			
		||||
 | 
			
		||||
    public function tearDown()
 | 
			
		||||
    {
 | 
			
		||||
        if (!empty($this->backends)) {
 | 
			
		||||
            foreach ($this->backends as $backend) {
 | 
			
		||||
                $backend->lockReleaseAll();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $this->backends = array();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        parent::tearDown();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Create a new lock backend with a generated lock id
 | 
			
		||||
     *
 | 
			
		||||
     * @return Redis_Lock_BackendInterface
 | 
			
		||||
     */
 | 
			
		||||
    public function createLockBackend()
 | 
			
		||||
    {
 | 
			
		||||
        if (!$this->getLockBackendClass()) {
 | 
			
		||||
            throw new \Exception("Lock backend class does not exist");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $className = Redis_Client::getClass(Redis_Client::REDIS_IMPL_LOCK);
 | 
			
		||||
 | 
			
		||||
        return $this->backends[] = new $className(
 | 
			
		||||
            Redis_Client::getClient(),
 | 
			
		||||
            Redis_Client::getDefaultPrefix('lock')
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testLock()
 | 
			
		||||
    {
 | 
			
		||||
        $b1 = $this->createLockBackend();
 | 
			
		||||
        $b2 = $this->createLockBackend();
 | 
			
		||||
 | 
			
		||||
        $s = $b1->lockAcquire('test1', 20000);
 | 
			
		||||
        $this->assertTrue($s, "Lock test1 acquired");
 | 
			
		||||
 | 
			
		||||
        $s = $b1->lockAcquire('test1', 20000);
 | 
			
		||||
        $this->assertTrue($s, "Lock test1 acquired a second time by the same thread");
 | 
			
		||||
 | 
			
		||||
        $s = $b2->lockAcquire('test1', 20000);
 | 
			
		||||
        $this->assertFalse($s, "Lock test1 could not be acquired by another thread");
 | 
			
		||||
 | 
			
		||||
        $b2->lockRelease('test1');
 | 
			
		||||
        $s = $b2->lockAcquire('test1');
 | 
			
		||||
        $this->assertFalse($s, "Lock test1 could not be released by another thread");
 | 
			
		||||
 | 
			
		||||
        $b1->lockRelease('test1');
 | 
			
		||||
        $s = $b2->lockAcquire('test1');
 | 
			
		||||
        $this->assertTrue($s, "Lock test1 has been released by the first thread");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testReleaseAll()
 | 
			
		||||
    {
 | 
			
		||||
        $b1 = $this->createLockBackend();
 | 
			
		||||
        $b2 = $this->createLockBackend();
 | 
			
		||||
 | 
			
		||||
        $b1->lockAcquire('test1', 200);
 | 
			
		||||
        $b1->lockAcquire('test2', 2000);
 | 
			
		||||
        $b1->lockAcquire('test3', 20000);
 | 
			
		||||
 | 
			
		||||
        $s = $b2->lockAcquire('test2');
 | 
			
		||||
        $this->assertFalse($s, "Lock test2 could not be released by another thread");
 | 
			
		||||
        $s = $b2->lockAcquire('test3');
 | 
			
		||||
        $this->assertFalse($s, "Lock test4 could not be released by another thread");
 | 
			
		||||
 | 
			
		||||
        $b1->lockReleaseAll();
 | 
			
		||||
 | 
			
		||||
        $s = $b2->lockAcquire('test1');
 | 
			
		||||
        $this->assertTrue($s, "Lock test1 has been released");
 | 
			
		||||
        $s = $b2->lockAcquire('test2');
 | 
			
		||||
        $this->assertTrue($s, "Lock test2 has been released");
 | 
			
		||||
        $s = $b2->lockAcquire('test3');
 | 
			
		||||
        $this->assertTrue($s, "Lock test3 has been released");
 | 
			
		||||
 | 
			
		||||
        $b2->lockReleaseAll();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testConcurentLock()
 | 
			
		||||
    {
 | 
			
		||||
        /*
 | 
			
		||||
         * Code for web test case
 | 
			
		||||
         *
 | 
			
		||||
        $this->drupalGet('redis/acquire/test1/1000');
 | 
			
		||||
        $this->assertText("REDIS_ACQUIRED", "Lock test1 acquired");
 | 
			
		||||
 | 
			
		||||
        $this->drupalGet('redis/acquire/test1/1');
 | 
			
		||||
        $this->assertText("REDIS_FAILED", "Lock test1 could not be acquired by a second thread");
 | 
			
		||||
 | 
			
		||||
        $this->drupalGet('redis/acquire/test2/1000');
 | 
			
		||||
        $this->assertText("REDIS_ACQUIRED", "Lock test2 acquired");
 | 
			
		||||
 | 
			
		||||
        $this->drupalGet('redis/acquire/test2/1');
 | 
			
		||||
        $this->assertText("REDIS_FAILED", "Lock test2 could not be acquired by a second thread");
 | 
			
		||||
         */
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,27 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
if (!class_exists('Redis_Tests_Lock_LockingUnitTestCase')) {
 | 
			
		||||
  require_once(__DIR__ . '/LockingUnitTestCase.php');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class Redis_Tests_Lock_PhpRedisLockingUnitTestCase extends Redis_Tests_Lock_LockingUnitTestCase
 | 
			
		||||
{
 | 
			
		||||
    public static function getInfo()
 | 
			
		||||
    {
 | 
			
		||||
        return array(
 | 
			
		||||
            'name'        => 'PhpRedis Redis locking',
 | 
			
		||||
            'description' => 'Ensure that Redis locking feature is working OK.',
 | 
			
		||||
            'group'       => 'Redis',
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function getLockBackendClass()
 | 
			
		||||
    {
 | 
			
		||||
        return 'Redis_Lock_PhpRedis';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function getClientInterface()
 | 
			
		||||
    {
 | 
			
		||||
        return 'PhpRedis';
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,23 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
class Redis_Tests_Lock_PredisLockingUnitTestCase extends Redis_Tests_Lock_LockingUnitTestCase
 | 
			
		||||
{
 | 
			
		||||
    public static function getInfo()
 | 
			
		||||
    {
 | 
			
		||||
        return array(
 | 
			
		||||
            'name'        => 'Predis Redis locking',
 | 
			
		||||
            'description' => 'Ensure that Redis locking feature is working OK.',
 | 
			
		||||
            'group'       => 'Redis',
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function getLockBackendClass()
 | 
			
		||||
    {
 | 
			
		||||
        return 'Redis_Lock_Predis';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function getClientInterface()
 | 
			
		||||
    {
 | 
			
		||||
        return 'Predis';
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,148 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Bugfixes made over time test class.
 | 
			
		||||
 */
 | 
			
		||||
abstract class Redis_Tests_Path_PathUnitTestCase extends Redis_Tests_AbstractUnitTestCase
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * @var Cache bin identifier
 | 
			
		||||
     */
 | 
			
		||||
    static private $id = 1;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get cache backend
 | 
			
		||||
     *
 | 
			
		||||
     * @return Redis_Path_HashLookupInterface
 | 
			
		||||
     */
 | 
			
		||||
    final protected function getBackend($name = null)
 | 
			
		||||
    {
 | 
			
		||||
        if (null === $name) {
 | 
			
		||||
            // This is needed to avoid conflict between tests, each test
 | 
			
		||||
            // seems to use the same Redis namespace and conflicts are
 | 
			
		||||
            // possible.
 | 
			
		||||
            $name = 'cache' . (self::$id++);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $className = Redis_Client::getClass(Redis_Client::REDIS_IMPL_PATH);
 | 
			
		||||
        $hashLookup = new $className(Redis_Client::getClient(), 'path', Redis_Client::getDefaultPrefix('path'));
 | 
			
		||||
 | 
			
		||||
        return $hashLookup;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Tests basic functionnality
 | 
			
		||||
     */
 | 
			
		||||
    public function testPathLookup()
 | 
			
		||||
    {
 | 
			
		||||
        $backend = $this->getBackend();
 | 
			
		||||
 | 
			
		||||
        $source = $backend->lookupSource('node-1-fr', 'fr');
 | 
			
		||||
        $this->assertIdentical(null, $source);
 | 
			
		||||
        $alias = $backend->lookupAlias('node/1', 'fr');
 | 
			
		||||
        $this->assertIdentical(null, $source);
 | 
			
		||||
 | 
			
		||||
        $backend->saveAlias('node/1', 'node-1-fr', 'fr');
 | 
			
		||||
        $source = $backend->lookupSource('node-1-fr', 'fr');
 | 
			
		||||
        $source = $backend->lookupSource('node-1-fr', 'fr');
 | 
			
		||||
        $this->assertIdentical('node/1', $source);
 | 
			
		||||
        $alias = $backend->lookupAlias('node/1', 'fr');
 | 
			
		||||
        $this->assertIdentical('node-1-fr', $alias);
 | 
			
		||||
 | 
			
		||||
        // Delete and ensure it does not exist anymore.
 | 
			
		||||
        $backend->deleteAlias('node/1', 'node-1-fr', 'fr');
 | 
			
		||||
        $source = $backend->lookupSource('node-1-fr', 'fr');
 | 
			
		||||
        $this->assertIdentical(null, $source);
 | 
			
		||||
        $alias = $backend->lookupAlias('node/1', 'fr');
 | 
			
		||||
        $this->assertIdentical(null, $source);
 | 
			
		||||
 | 
			
		||||
        // Set more than one aliases and ensure order at loading.
 | 
			
		||||
        $backend->saveAlias('node/1', 'node-1-fr-1', 'fr');
 | 
			
		||||
        $backend->saveAlias('node/1', 'node-1-fr-2', 'fr');
 | 
			
		||||
        $backend->saveAlias('node/1', 'node-1-fr-3', 'fr');
 | 
			
		||||
        $alias = $backend->lookupAlias('node/1', 'fr');
 | 
			
		||||
        $this->assertIdentical('node-1-fr-3', $alias);
 | 
			
		||||
 | 
			
		||||
        // Add another alias to test the delete language feature.
 | 
			
		||||
        // Also add some other languages aliases.
 | 
			
		||||
        $backend->saveAlias('node/1', 'node-1');
 | 
			
		||||
        $backend->saveAlias('node/2', 'node-2-en', 'en');
 | 
			
		||||
        $backend->saveAlias('node/3', 'node-3-ca', 'ca');
 | 
			
		||||
 | 
			
		||||
        // Ok, delete fr and tests every other are still there.
 | 
			
		||||
        $backend->deleteLanguage('fr');
 | 
			
		||||
        $alias = $backend->lookupAlias('node/1');
 | 
			
		||||
        $this->assertIdentical('node-1', $alias);
 | 
			
		||||
        $alias = $backend->lookupAlias('node/2', 'en');
 | 
			
		||||
        $this->assertIdentical('node-2-en', $alias);
 | 
			
		||||
        $alias = $backend->lookupAlias('node/3', 'ca');
 | 
			
		||||
        $this->assertIdentical('node-3-ca', $alias);
 | 
			
		||||
 | 
			
		||||
        // Now create back a few entries in some langage and
 | 
			
		||||
        // ensure fallback to no language also works.
 | 
			
		||||
        $backend->saveAlias('node/4', 'node-4');
 | 
			
		||||
        $backend->saveAlias('node/4', 'node-4-es', 'es');
 | 
			
		||||
        $alias = $backend->lookupAlias('node/4');
 | 
			
		||||
        $this->assertIdentical('node-4', $alias);
 | 
			
		||||
        $alias = $backend->lookupAlias('node/4', 'es');
 | 
			
		||||
        $this->assertIdentical('node-4-es', $alias);
 | 
			
		||||
        $alias = $backend->lookupAlias('node/4', 'fr');
 | 
			
		||||
        $this->assertIdentical('node-4', $alias);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Tests https://www.drupal.org/node/2728831
 | 
			
		||||
     */
 | 
			
		||||
    public function testSomeEdgeCaseFalseNegative()
 | 
			
		||||
    {
 | 
			
		||||
        $backend = $this->getBackend();
 | 
			
		||||
 | 
			
		||||
        $backend->deleteLanguage('fr');
 | 
			
		||||
        $backend->deleteLanguage('und');
 | 
			
		||||
        $backend->saveAlias('node/123', 'node-123');
 | 
			
		||||
 | 
			
		||||
        // Language lookup should return the language neutral value if no value
 | 
			
		||||
        $source = $backend->lookupSource('node-123', 'fr');
 | 
			
		||||
        $this->assertIdentical($source, 'node/123');
 | 
			
		||||
        $source = $backend->lookupAlias('node/123', 'fr');
 | 
			
		||||
        $this->assertIdentical($source, 'node-123');
 | 
			
		||||
 | 
			
		||||
        // Now, let's consider we have an item we don't know if it exists or
 | 
			
		||||
        // not, per definition we should not return a strict FALSE but a NULL
 | 
			
		||||
        // value instead to tell "we don't know anything about this". In a
 | 
			
		||||
        // very specific use-case, if the language neutral value is a strict
 | 
			
		||||
        // "not exists" value, it should still return NULL instead of FALSE
 | 
			
		||||
        // if another language was asked for.
 | 
			
		||||
 | 
			
		||||
        // Store "value null" for the language neutral entry
 | 
			
		||||
        $backend->saveAlias('node/456', Redis_Path_HashLookupInterface::VALUE_NULL);
 | 
			
		||||
        $source = $backend->lookupAlias('node/456');
 | 
			
		||||
        $this->assertIdentical(false, $source);
 | 
			
		||||
 | 
			
		||||
        $source = $backend->lookupAlias('node/456', 'fr');
 | 
			
		||||
        $this->assertIdentical(null, $source);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Tests that lookup is case insensitive
 | 
			
		||||
     */
 | 
			
		||||
    public function testCaseInsensitivePathLookup()
 | 
			
		||||
    {
 | 
			
		||||
        $backend = $this->getBackend();
 | 
			
		||||
 | 
			
		||||
        $backend->saveAlias('node/1', 'Node-1-FR', 'fr');
 | 
			
		||||
        $source = $backend->lookupSource('NODE-1-fr', 'fr');
 | 
			
		||||
        $this->assertIdentical('node/1', $source);
 | 
			
		||||
        $source = $backend->lookupSource('node-1-FR', 'fr');
 | 
			
		||||
        $this->assertIdentical('node/1', $source);
 | 
			
		||||
        $alias = $backend->lookupAlias('node/1', 'fr');
 | 
			
		||||
        $this->assertIdentical('node-1-fr', strtolower($alias));
 | 
			
		||||
 | 
			
		||||
        // Delete and ensure it does not exist anymore.
 | 
			
		||||
        $backend->deleteAlias('node/1', 'node-1-FR', 'fr');
 | 
			
		||||
        $source = $backend->lookupSource('Node-1-FR', 'fr');
 | 
			
		||||
        $this->assertIdentical(null, $source);
 | 
			
		||||
        $alias = $backend->lookupAlias('node/1', 'fr');
 | 
			
		||||
        $this->assertIdentical(null, $source);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,22 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
if (!class_exists('Redis_Tests_Path_PathUnitTestCase')) {
 | 
			
		||||
  require_once(__DIR__ . '/PathUnitTestCase.php');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class Redis_Tests_Path_PhpRedisPathUnitTestCase extends Redis_Tests_Path_PathUnitTestCase
 | 
			
		||||
{
 | 
			
		||||
    public static function getInfo()
 | 
			
		||||
    {
 | 
			
		||||
        return array(
 | 
			
		||||
            'name'        => 'PhpRedis path inc replacement',
 | 
			
		||||
            'description' => 'Tests PhpRedis path inc replacement.',
 | 
			
		||||
            'group'       => 'Redis',
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function getClientInterface()
 | 
			
		||||
    {
 | 
			
		||||
        return 'PhpRedis';
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,18 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
class Redis_Tests_Path_PredisPathUnitTestCase extends Redis_Tests_Path_PathUnitTestCase
 | 
			
		||||
{
 | 
			
		||||
    public static function getInfo()
 | 
			
		||||
    {
 | 
			
		||||
        return array(
 | 
			
		||||
            'name'        => 'Predis path inc replacement',
 | 
			
		||||
            'description' => 'Tests Predis path inc replacement.',
 | 
			
		||||
            'group'       => 'Redis',
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function getClientInterface()
 | 
			
		||||
    {
 | 
			
		||||
        return 'Predis';
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,22 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
if (!class_exists('Redis_Tests_Queue_QueueUnitTestCase')) {
 | 
			
		||||
  require_once(__DIR__ . '/QueueUnitTestCase.php');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class Redis_Tests_Queue_PhpRedisQueueUnitTestCase extends Redis_Tests_Queue_QueueUnitTestCase
 | 
			
		||||
{
 | 
			
		||||
    public static function getInfo()
 | 
			
		||||
    {
 | 
			
		||||
        return array(
 | 
			
		||||
            'name'        => 'PhpRedis Redis queue',
 | 
			
		||||
            'description' => 'Ensure that Redis queue feature is working OK.',
 | 
			
		||||
            'group'       => 'Redis',
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function getClientInterface()
 | 
			
		||||
    {
 | 
			
		||||
        return 'PhpRedis';
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,20 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
class Redis_Tests_Queue_PredisQueueUnitTestCase extends Redis_Tests_Queue_QueueUnitTestCase
 | 
			
		||||
{
 | 
			
		||||
    public static function getInfo()
 | 
			
		||||
    {
 | 
			
		||||
        return array(
 | 
			
		||||
            'name'        => 'Predis Redis queue',
 | 
			
		||||
            'description' => 'Ensure that Redis queue feature is working OK.',
 | 
			
		||||
            'group'       => 'Redis',
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function getClientInterface()
 | 
			
		||||
    {
 | 
			
		||||
        return 'Predis';
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 */
 | 
			
		||||
@@ -0,0 +1,133 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Some tests in there credits goes to the redis_queue module.
 | 
			
		||||
 * Thanks to their author.
 | 
			
		||||
 */
 | 
			
		||||
abstract class Redis_Tests_Queue_QueueUnitTestCase extends Redis_Tests_AbstractUnitTestCase
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * @var Redis_Queue
 | 
			
		||||
     */
 | 
			
		||||
    public $queue;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var string
 | 
			
		||||
     */
 | 
			
		||||
    public $name;
 | 
			
		||||
 | 
			
		||||
    public function setUp()
 | 
			
		||||
    {
 | 
			
		||||
        parent::setUp();
 | 
			
		||||
 | 
			
		||||
        module_load_include('inc', 'system', 'system.queue');
 | 
			
		||||
 | 
			
		||||
        $this->name = 'redis-queue-test-' . time();
 | 
			
		||||
        $this->queue = new Redis_Queue($this->name);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function tearDown()
 | 
			
		||||
    {
 | 
			
		||||
        $this->queue->deleteQueue();
 | 
			
		||||
        $this->name = null;
 | 
			
		||||
 | 
			
		||||
        parent::tearDown();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testCreate()
 | 
			
		||||
    {
 | 
			
		||||
        $res = $this->queue->createItem('test-queue-item-create');
 | 
			
		||||
        $num = $this->queue->numberOfItems();
 | 
			
		||||
        $this->assertEqual(1, $num);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testClaim()
 | 
			
		||||
    {
 | 
			
		||||
        $data = 'test-queue-item-claimed';
 | 
			
		||||
        $res = $this->queue->createItem($data);
 | 
			
		||||
        $item = $this->queue->claimItem();
 | 
			
		||||
        $this->assertEqual($data, $item->data);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
    public function testClaimBlocking()
 | 
			
		||||
    {
 | 
			
		||||
        $data = 'test-queue-item-claimed';
 | 
			
		||||
        $res = $this->queue->createItem($data);
 | 
			
		||||
        $this->assertTrue($res);
 | 
			
		||||
        $item = $this->queue->claimItemBlocking(10);
 | 
			
		||||
        $this->assertEqual($data, $item->data);
 | 
			
		||||
    }
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    public function testRelease()
 | 
			
		||||
    {
 | 
			
		||||
        $data = 'test-queue-item';
 | 
			
		||||
 | 
			
		||||
        $res = $this->queue->createItem($data);
 | 
			
		||||
        $item = $this->queue->claimItem();
 | 
			
		||||
 | 
			
		||||
        $num = $this->queue->numberOfItems();
 | 
			
		||||
        $this->assertEqual(0, $num);
 | 
			
		||||
 | 
			
		||||
        $res = $this->queue->releaseItem($item);
 | 
			
		||||
 | 
			
		||||
        $num = $this->queue->numberOfItems();
 | 
			
		||||
        $this->assertEqual(1, $num);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testOrder()
 | 
			
		||||
    {
 | 
			
		||||
        $keys = array('test1', 'test2', 'test3');
 | 
			
		||||
        foreach ($keys as $k) {
 | 
			
		||||
            $this->queue->createItem($k);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $first = $this->queue->claimItem();
 | 
			
		||||
        $this->assertEqual($first->data, $keys[0]);
 | 
			
		||||
 | 
			
		||||
        $second = $this->queue->claimItem();
 | 
			
		||||
        $this->assertEqual($second->data, $keys[1]);
 | 
			
		||||
 | 
			
		||||
        $this->queue->releaseItem($first);
 | 
			
		||||
 | 
			
		||||
        $third = $this->queue->claimItem();
 | 
			
		||||
        $this->assertEqual($third->data, $keys[2]);
 | 
			
		||||
 | 
			
		||||
        $first_again = $this->queue->claimItem();
 | 
			
		||||
        $this->assertEqual($first_again->data, $keys[0]);
 | 
			
		||||
 | 
			
		||||
        $num = $this->queue->numberOfItems();
 | 
			
		||||
        $this->assertEqual(0, $num);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
    public function lease()
 | 
			
		||||
    {
 | 
			
		||||
        $data = 'test-queue-item';
 | 
			
		||||
        $res = $this->queue->createItem($data);
 | 
			
		||||
        $num = $this->queue->numberOfItems();
 | 
			
		||||
        $this->assertEquals(1, $num);
 | 
			
		||||
        $item = $this->queue->claimItem(1);
 | 
			
		||||
        // In Redis 2.4 the expire could be between zero to one seconds off. 
 | 
			
		||||
        sleep(2);
 | 
			
		||||
        $expired = $this->queue->expire();
 | 
			
		||||
        $this->assertEquals(1, $expired);
 | 
			
		||||
        $this->assertEquals(1, $this->queue->numberOfItems());
 | 
			
		||||
        // Create a second queue to test expireAll()
 | 
			
		||||
        $q2 = new RedisQueue($this->name . '_2');
 | 
			
		||||
        $q2->createItem($data);
 | 
			
		||||
        $q2->createItem($data);
 | 
			
		||||
        $this->assertEquals(2, $q2->numberOfItems());
 | 
			
		||||
        $item = $this->queue->claimItem(1);
 | 
			
		||||
        $item2 = $q2->claimItem(1);
 | 
			
		||||
        $this->assertEquals(1, $q2->numberOfItems());
 | 
			
		||||
        sleep(2);
 | 
			
		||||
        $expired = $this->queue->expireAll();
 | 
			
		||||
        $this->assertEquals(2, $expired);
 | 
			
		||||
        $this->assertEquals(1, $this->queue->numberOfItems());
 | 
			
		||||
        $this->assertEquals(2, $q2->numberOfItems());
 | 
			
		||||
        $q2->deleteQueue();
 | 
			
		||||
    }
 | 
			
		||||
     */
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										104
									
								
								sites/all/modules/contrib/dev/redis/redis.admin.inc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								sites/all/modules/contrib/dev/redis/redis.admin.inc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,104 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @file
 | 
			
		||||
 * Redis module administration pages.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Main settings and review administration screen.
 | 
			
		||||
 */
 | 
			
		||||
function redis_settings_form($form, &$form_state) {
 | 
			
		||||
 | 
			
		||||
  $form['connection'] = array(
 | 
			
		||||
    '#type' => 'fieldset',
 | 
			
		||||
    '#title' => t("Connection information"),
 | 
			
		||||
    '#collapsible' => TRUE,
 | 
			
		||||
    '#collapsed' => TRUE,
 | 
			
		||||
  );
 | 
			
		||||
  $form['connection']['scheme'] = array(
 | 
			
		||||
    '#type' => 'textfield',
 | 
			
		||||
    '#title' => t("Scheme"),
 | 
			
		||||
    '#default_value' => 'tcp',
 | 
			
		||||
    '#disabled' => TRUE,
 | 
			
		||||
    '#description' => t("Connection scheme.") . " " . t("Only <em>tcp</em> is currently supported. This is ignored when using a UNIX socket."),
 | 
			
		||||
  );
 | 
			
		||||
  $form['connection']['redis_client_host'] = array(
 | 
			
		||||
    '#type' => 'textfield',
 | 
			
		||||
    '#title' => t("Host"),
 | 
			
		||||
    '#default_value' => variable_get('redis_client_host', NULL),
 | 
			
		||||
    '#description' => t("Redis server host. Default is <em>@default</em>.", array('@default' => Redis_Client_Manager::REDIS_DEFAULT_HOST)),
 | 
			
		||||
  );
 | 
			
		||||
  $form['connection']['redis_client_port'] = array(
 | 
			
		||||
    '#type' => 'textfield',
 | 
			
		||||
    '#title' => t("Port"),
 | 
			
		||||
    '#default_value' => variable_get('redis_client_port', NULL),
 | 
			
		||||
    '#description' => t("Redis server port. Default is <em>@default</em>.", array('@default' => Redis_Client_Manager::REDIS_DEFAULT_PORT)),
 | 
			
		||||
  );
 | 
			
		||||
  $form['connection']['redis_client_socket'] = array(
 | 
			
		||||
    '#type' => 'textfield',
 | 
			
		||||
    '#title' => t("UNIX socket"),
 | 
			
		||||
    '#default_value' => variable_get('redis_client_socket', NULL),
 | 
			
		||||
    '#description' => t("Redis UNIX socket for connection. If set remote server host and port will be ignored."),
 | 
			
		||||
  );
 | 
			
		||||
  $form['connection']['redis_client_base'] = array(
 | 
			
		||||
    '#type' => 'textfield',
 | 
			
		||||
    '#title' => t("Database"),
 | 
			
		||||
    '#default_value' => variable_get('redis_client_base', NULL),
 | 
			
		||||
    '#description' => t("Redis server database. Default is none, Redis server will autoselect the database 0."),
 | 
			
		||||
  );
 | 
			
		||||
  $form['connection']['redis_client_interface'] = array(
 | 
			
		||||
    '#type' => 'radios',
 | 
			
		||||
    '#title' => t("Client"),
 | 
			
		||||
    '#options' => array(
 | 
			
		||||
      NULL => t("None or automatic"),
 | 
			
		||||
      'PhpRedis' => t("PhpRedis PHP extension"),
 | 
			
		||||
      'Predis' => t("Predis PHP library"),
 | 
			
		||||
    ),
 | 
			
		||||
    '#default_value' => variable_get('redis_client_interface', NULL),
 | 
			
		||||
    '#description' => t("Redis low level backend."),
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  $form = system_settings_form($form);
 | 
			
		||||
 | 
			
		||||
  // Enforce empty values drop from the $form_state in order to avoid empty
 | 
			
		||||
  // values saving. Empty values would cause the isset() checks in client
 | 
			
		||||
  // options to see false positives and fail upon connection.
 | 
			
		||||
  array_unshift($form['#submit'], 'redis_settings_form_submit_clean_values');
 | 
			
		||||
 | 
			
		||||
  return $form;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Deep clean of $form_state values.
 | 
			
		||||
 */
 | 
			
		||||
function redis_settings_form_submit_clean_values($form, &$form_state) {
 | 
			
		||||
 | 
			
		||||
  $string_values = array('redis_client_host', 'redis_client_interface');
 | 
			
		||||
 | 
			
		||||
  foreach ($string_values as $name) {
 | 
			
		||||
    // Empty check is sufficient to verify that the field is indeed empty.
 | 
			
		||||
    if (empty($form_state['values'][$name])) {
 | 
			
		||||
      // Using unset() will keep the key in the array, with an associated NULL
 | 
			
		||||
      // value. While this wouldn't really matter, it's safer to remove it so
 | 
			
		||||
      // that system_settings_form_submit() won't find it and attempt to save
 | 
			
		||||
      // it.
 | 
			
		||||
      $form_state['values'] = array_diff_key($form_state['values'], array($name => NULL));
 | 
			
		||||
      variable_del($name);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  $numeric_values = array('redis_client_base', 'redis_client_port');
 | 
			
		||||
 | 
			
		||||
  foreach ($numeric_values as $name) {
 | 
			
		||||
    // Numeric values can be both of NULL or 0 (NULL meaning the value is not
 | 
			
		||||
    // not set and the client will use the default, while 0 has a business
 | 
			
		||||
    // meaning and should be kept as is).
 | 
			
		||||
    if ('0' !== $form_state['values'][$name] && empty($form_state['values'][$name])) {
 | 
			
		||||
      $form_state['values'] = array_diff_key($form_state['values'], array($name => NULL));
 | 
			
		||||
      variable_del($name);
 | 
			
		||||
    } else {
 | 
			
		||||
      $form_state['values'][$name] = (int)$form_state['values'][$name];
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										25
									
								
								sites/all/modules/contrib/dev/redis/redis.autoload.inc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								sites/all/modules/contrib/dev/redis/redis.autoload.inc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @file
 | 
			
		||||
 * Redis module autoloader.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Autoloader micro optimization, work with constant as much as we can.
 | 
			
		||||
 */
 | 
			
		||||
define('REDIS_ROOT', dirname(__FILE__) . '/lib');
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Redis module specific autoloader, compatible with spl_register_autoload().
 | 
			
		||||
 */
 | 
			
		||||
function redis_autoload($class_name) {
 | 
			
		||||
  if ('Redis' === substr($class_name, 0, 5)) {
 | 
			
		||||
    $filename = REDIS_ROOT . '/' . str_replace('_', '/', $class_name) . '.php';
 | 
			
		||||
    return @include_once $filename;
 | 
			
		||||
  }
 | 
			
		||||
  return FALSE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Register our custom autoloader.
 | 
			
		||||
spl_autoload_register('redis_autoload');
 | 
			
		||||
							
								
								
									
										44
									
								
								sites/all/modules/contrib/dev/redis/redis.info
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								sites/all/modules/contrib/dev/redis/redis.info
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,44 @@
 | 
			
		||||
name = "Redis"
 | 
			
		||||
description = "Provide a module placeholder, for using as dependency for module that needs Redis."
 | 
			
		||||
package = Performance
 | 
			
		||||
version = VERSION
 | 
			
		||||
core = 7.x
 | 
			
		||||
configure = admin/config/development/performance/redis
 | 
			
		||||
; Drupal Simpletest cannot rely on a real autoloader
 | 
			
		||||
files[] = lib/Redis/Tests/AbstractUnitTestCase.php
 | 
			
		||||
files[] = lib/Redis/Tests/Admin/VariableTestCase.test
 | 
			
		||||
files[] = lib/Redis/Tests/Cache/FixesUnitTestCase.php
 | 
			
		||||
files[] = lib/Redis/Tests/Cache/FlushUnitTestCase.php
 | 
			
		||||
files[] = lib/Redis/Tests/Cache/CompressedPhpRedisFixesUnitTestCase.test
 | 
			
		||||
files[] = lib/Redis/Tests/Cache/CompressedPhpRedisFlushUnitTestCase.test
 | 
			
		||||
files[] = lib/Redis/Tests/Cache/CompressedPhpRedisShardedFixesUnitTestCase.test
 | 
			
		||||
files[] = lib/Redis/Tests/Cache/CompressedPhpRedisShardedFlushUnitTestCase.test
 | 
			
		||||
files[] = lib/Redis/Tests/Cache/CompressedPhpRedisShardedWithPipelineFixesUnitTestCase.test
 | 
			
		||||
files[] = lib/Redis/Tests/Cache/PhpRedisFixesUnitTestCase.test
 | 
			
		||||
files[] = lib/Redis/Tests/Cache/PhpRedisFlushUnitTestCase.test
 | 
			
		||||
files[] = lib/Redis/Tests/Cache/PhpRedisShardedFixesUnitTestCase.test
 | 
			
		||||
files[] = lib/Redis/Tests/Cache/PhpRedisShardedFlushUnitTestCase.test
 | 
			
		||||
files[] = lib/Redis/Tests/Cache/PhpRedisShardedWithPipelineFixesUnitTestCase.test
 | 
			
		||||
files[] = lib/Redis/Tests/Cache/PredisFixesUnitTestCase.test
 | 
			
		||||
files[] = lib/Redis/Tests/Cache/PredisFlushUnitTestCase.test
 | 
			
		||||
files[] = lib/Redis/Tests/Cache/PredisShardedFixesUnitTestCase.test
 | 
			
		||||
files[] = lib/Redis/Tests/Cache/PredisShardedFlushUnitTestCase.test
 | 
			
		||||
files[] = lib/Redis/Tests/Cache/PredisShardedWithPipelineFixesUnitTestCase.test
 | 
			
		||||
files[] = lib/Redis/Tests/Client/ClientUnitTestCase.test
 | 
			
		||||
files[] = lib/Redis/Tests/Client/MockFactory.php
 | 
			
		||||
files[] = lib/Redis/Tests/Lock/LockingUnitTestCase.php
 | 
			
		||||
files[] = lib/Redis/Tests/Lock/PhpRedisLockingUnitTestCase.test
 | 
			
		||||
files[] = lib/Redis/Tests/Lock/PredisLockingUnitTestCase.test
 | 
			
		||||
files[] = lib/Redis/Tests/Path/PathUnitTestCase.php
 | 
			
		||||
files[] = lib/Redis/Tests/Path/PhpRedisPathUnitTestCase.test
 | 
			
		||||
files[] = lib/Redis/Tests/Path/PredisPathUnitTestCase.test
 | 
			
		||||
files[] = lib/Redis/Tests/Queue/QueueUnitTestCase.php
 | 
			
		||||
files[] = lib/Redis/Tests/Queue/PhpRedisQueueUnitTestCase.test
 | 
			
		||||
files[] = lib/Redis/Tests/Queue/PredisQueueUnitTestCase.test
 | 
			
		||||
 | 
			
		||||
; Information added by Drupal.org packaging script on 2017-12-22
 | 
			
		||||
version = "7.x-3.17"
 | 
			
		||||
core = "7.x"
 | 
			
		||||
project = "redis"
 | 
			
		||||
datestamp = "1513939095"
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										38
									
								
								sites/all/modules/contrib/dev/redis/redis.install
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								sites/all/modules/contrib/dev/redis/redis.install
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,38 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @file
 | 
			
		||||
 * Redis install related functions.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Implements hook_requirements().
 | 
			
		||||
 */
 | 
			
		||||
function redis_requirements($phase) {
 | 
			
		||||
 | 
			
		||||
  // This module is configured via settings.php file. Using any other phase
 | 
			
		||||
  // than runtime to proceed to some consistency checks is useless.
 | 
			
		||||
  if ('runtime' !== $phase) {
 | 
			
		||||
    return array();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  $requirements = array();
 | 
			
		||||
 | 
			
		||||
  try {
 | 
			
		||||
    Redis_Client::getClient();
 | 
			
		||||
    $requirements['redis'] = array(
 | 
			
		||||
      'title'       => "Redis",
 | 
			
		||||
      'value'       => t("Connected, using the <em>@name</em> client.", array('@name' => Redis_Client::getClientInterfaceName())),
 | 
			
		||||
      'severity'    => REQUIREMENT_OK,
 | 
			
		||||
    );
 | 
			
		||||
  } catch (Exception $e) {
 | 
			
		||||
    $requirements['redis'] = array(
 | 
			
		||||
      'title'       => "Redis",
 | 
			
		||||
      'value'       => t("Not connected."),
 | 
			
		||||
      'severity'    => REQUIREMENT_WARNING,
 | 
			
		||||
      'description' => t("No Redis client connected. Please ensure that your Redis connection is working properly. If you are not using a Redis server connection you should disable this module."),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return $requirements;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										63
									
								
								sites/all/modules/contrib/dev/redis/redis.lock.inc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								sites/all/modules/contrib/dev/redis/redis.lock.inc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,63 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @file
 | 
			
		||||
 * Drupal core lock.inc replacement.
 | 
			
		||||
 * 
 | 
			
		||||
 * Do not use this file directly, it will be included by the backend specific
 | 
			
		||||
 * implementation when added to settings.php file.
 | 
			
		||||
 * 
 | 
			
		||||
 * See README.txt file for details.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
// Include our own autoloader to ensure classes to be there.
 | 
			
		||||
// We cannot rely on core in case of early bootstrap phases.
 | 
			
		||||
require_once dirname(__FILE__) . '/redis.autoload.inc';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Foo function, keeping it for API consistency (Drupal 7).
 | 
			
		||||
 */
 | 
			
		||||
function lock_initialize() {}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Foo function, keeping it for API consistency (Drupal 6).
 | 
			
		||||
 */
 | 
			
		||||
function lock_init() {}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Foo function, keeping it for API consistency.
 | 
			
		||||
 * Some insane people may actually use it.
 | 
			
		||||
 */
 | 
			
		||||
function _lock_id() {
 | 
			
		||||
  return Redis_Lock::getBackend()->getLockId();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function lock_acquire($name, $timeout = 30.0) {
 | 
			
		||||
  return Redis_Lock::getBackend()->lockAcquire($name, $timeout);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function lock_may_be_available($name) {
 | 
			
		||||
  return Redis_Lock::getBackend()->lockMayBeAvailable($name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function lock_wait($name, $delay = 30) {
 | 
			
		||||
  return Redis_Lock::getBackend()->lockWait($name, $delay);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function lock_release($name) {
 | 
			
		||||
  return Redis_Lock::getBackend()->lockRelease($name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function lock_release_all($lock_id = NULL) {
 | 
			
		||||
  return Redis_Lock::getBackend()->lockReleaseAll($lock_id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Since D6 doesn't have the drupal_register_shutdown_function
 | 
			
		||||
// that is called in lib/Redis/Lock/Backend/Default.php define
 | 
			
		||||
// the wrapper here.
 | 
			
		||||
if (!function_exists('drupal_register_shutdown_function')) {
 | 
			
		||||
  function drupal_register_shutdown_function(){
 | 
			
		||||
    $args = func_get_args();
 | 
			
		||||
    call_user_func_array('register_shutdown_function', $args);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										73
									
								
								sites/all/modules/contrib/dev/redis/redis.module
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								sites/all/modules/contrib/dev/redis/redis.module
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,73 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @file
 | 
			
		||||
 * Redis module.
 | 
			
		||||
 *
 | 
			
		||||
 * This file is a placeholder for other modules that need the Redis client for
 | 
			
		||||
 * something else than lock and cache.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
// Include our own autoloader to ensure classes to be there.
 | 
			
		||||
// We cannot rely on core in case of early bootstrap phases.
 | 
			
		||||
require_once dirname(__FILE__) . '/redis.autoload.inc';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Implements hook_menu().
 | 
			
		||||
 */
 | 
			
		||||
function redis_menu() {
 | 
			
		||||
  $items = array();
 | 
			
		||||
  $items['admin/config/development/performance/cache'] = array(
 | 
			
		||||
    'title' => "Cache",
 | 
			
		||||
    'type' => MENU_DEFAULT_LOCAL_TASK,
 | 
			
		||||
  );
 | 
			
		||||
  $items['admin/config/development/performance/redis'] = array(
 | 
			
		||||
    'title' => "Redis",
 | 
			
		||||
    'page callback' => 'drupal_get_form',
 | 
			
		||||
    'page arguments' => array('redis_settings_form'),
 | 
			
		||||
    'access arguments' => array('administer site configuration'),
 | 
			
		||||
    'type' => MENU_LOCAL_TASK,
 | 
			
		||||
    'file' => 'redis.admin.inc',
 | 
			
		||||
  );
 | 
			
		||||
  return $items;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Implements hook_help().
 | 
			
		||||
 */
 | 
			
		||||
function redis_help($path, $arg) {
 | 
			
		||||
  switch ($path) {
 | 
			
		||||
    case 'admin/config/development/performance/redis':
 | 
			
		||||
      $messages =
 | 
			
		||||
        '<p>' . t("Redis module is optional if you are using only a cache or lock backend. The full API will be automatically loaded and its configuration will live into the <em>settings.php</em> file. If you access to this screen, it's probably because another contrib module needs it as a dependency for using the Redis client. If you didn't enabled such module, you are strongly advised to disable the Redis module on the module page.") . '</p>' .
 | 
			
		||||
        '<p>' . t("While Redis client configuration can be changed through the web, if you are using a cache or lock backend they must be set in the <em>settings.php</em> file. Once this done, any modification done using this form will be ignored, and real settings in use will be get at early bootstrap phase, before the configuration system is bootstrapped.") . '</p>';
 | 
			
		||||
      if (Redis_Client::getClient()) {
 | 
			
		||||
        $messages .= '<p><strong>' . t("Current connected client uses the <em>@name</em> library.", array('@name' => Redis_Client::getClientInterfaceName())) . '</strong></p>';
 | 
			
		||||
      }
 | 
			
		||||
      return $messages;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Get Redis client for php-redis extension.
 | 
			
		||||
 *
 | 
			
		||||
 * @return \Redis
 | 
			
		||||
 */
 | 
			
		||||
function phpredis_client_get() {
 | 
			
		||||
  if ('PhpRedis' !== variable_get('redis_client_interface')) {
 | 
			
		||||
    throw new \LogicException("Redis is not configured to use the php-redis client");
 | 
			
		||||
  }
 | 
			
		||||
  return Redis_Client::getClient();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Get Redis client for Predis library.
 | 
			
		||||
 *
 | 
			
		||||
 * @return \Predis\Client
 | 
			
		||||
 */
 | 
			
		||||
function predis_client_get() {
 | 
			
		||||
  if ('Predis' !== variable_get('redis_client_interface')) {
 | 
			
		||||
    throw new \LogicException("Redis is not configured to use the Predis client");
 | 
			
		||||
  }
 | 
			
		||||
  return Redis_Client::getClient();
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										594
									
								
								sites/all/modules/contrib/dev/redis/redis.path.inc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										594
									
								
								sites/all/modules/contrib/dev/redis/redis.path.inc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,594 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @file
 | 
			
		||||
 * Drupal default includes/path.inc file copy which only differs in:
 | 
			
		||||
 *  - drupal_lookup_path() which is the only performance critical.
 | 
			
		||||
 *  - path_*() functions for synchronization.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Get Redis path lookup backend.
 | 
			
		||||
 *
 | 
			
		||||
 * @return Redis_Path_HashLookupInterface
 | 
			
		||||
 */
 | 
			
		||||
function redis_path_backend_get() {
 | 
			
		||||
  $hashLookup = &drupal_static(__FUNCTION__, null);
 | 
			
		||||
 | 
			
		||||
  if (null === $hashLookup) {
 | 
			
		||||
    try {
 | 
			
		||||
      $className = Redis_Client::getClass(Redis_Client::REDIS_IMPL_PATH);
 | 
			
		||||
      $hashLookup = new $className(Redis_Client::getClient(), 'path', Redis_Client::getDefaultPrefix('path'));
 | 
			
		||||
    } catch (Exception $e) {
 | 
			
		||||
      $hashLookup = new Redis_Path_NullHashLookup();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return $hashLookup;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Initialize the $_GET['q'] variable to the proper normal path.
 | 
			
		||||
 */
 | 
			
		||||
function drupal_path_initialize() {
 | 
			
		||||
  // Ensure $_GET['q'] is set before calling drupal_normal_path(), to support
 | 
			
		||||
  // path caching with hook_url_inbound_alter().
 | 
			
		||||
  if (empty($_GET['q'])) {
 | 
			
		||||
    $_GET['q'] = variable_get('site_frontpage', 'node');
 | 
			
		||||
  }
 | 
			
		||||
  $_GET['q'] = drupal_get_normal_path($_GET['q']);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Given an alias, return its Drupal system URL if one exists. Given a Drupal
 | 
			
		||||
 * system URL return one of its aliases if such a one exists. Otherwise,
 | 
			
		||||
 * return FALSE.
 | 
			
		||||
 *
 | 
			
		||||
 * @param $action
 | 
			
		||||
 *   One of the following values:
 | 
			
		||||
 *   - wipe: delete the alias cache.
 | 
			
		||||
 *   - alias: return an alias for a given Drupal system path (if one exists).
 | 
			
		||||
 *   - source: return the Drupal system URL for a path alias (if one exists).
 | 
			
		||||
 * @param $path
 | 
			
		||||
 *   The path to investigate for corresponding aliases or system URLs.
 | 
			
		||||
 * @param $path_language
 | 
			
		||||
 *   Optional language code to search the path with. Defaults to the page language.
 | 
			
		||||
 *   If there's no path defined for that language it will search paths without
 | 
			
		||||
 *   language.
 | 
			
		||||
 *
 | 
			
		||||
 * @return
 | 
			
		||||
 *   Either a Drupal system path, an aliased path, or FALSE if no path was
 | 
			
		||||
 *   found.
 | 
			
		||||
 */
 | 
			
		||||
function drupal_lookup_path($action, $path = '', $path_language = NULL) {
 | 
			
		||||
  global $language_url;
 | 
			
		||||
 | 
			
		||||
  static $cache, $denyAdmin;
 | 
			
		||||
 | 
			
		||||
  if (null === $cache) {
 | 
			
		||||
    $cache = array('whitelist' => variable_get('path_alias_whitelist'));
 | 
			
		||||
    if (null === $cache['whitelist']) {
 | 
			
		||||
      $cache['whitelist'] = drupal_path_alias_whitelist_rebuild();
 | 
			
		||||
    }
 | 
			
		||||
    $denyAdmin = (bool)variable_get('path_alias_admin_blacklist', true);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // If no language is explicitly specified we default to the current URL
 | 
			
		||||
  // language. If we used a language different from the one conveyed by the
 | 
			
		||||
  // requested URL, we might end up being unable to check if there is a path
 | 
			
		||||
  // alias matching the URL path.
 | 
			
		||||
  if (!$path_language = $path_language ? $path_language : $language_url->language) {
 | 
			
		||||
    $path_language = LANGUAGE_NONE;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!empty($path) && isset($cache[$path_language][$action][$path])) {
 | 
			
		||||
    return $cache[$path_language][$action][$path];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!empty($path)) {
 | 
			
		||||
    $path = strtolower(trim($path));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  $ret = null;
 | 
			
		||||
  $hashLookup = redis_path_backend_get();
 | 
			
		||||
 | 
			
		||||
  switch ($action) {
 | 
			
		||||
 | 
			
		||||
    case 'wipe':
 | 
			
		||||
      $cache = array();
 | 
			
		||||
      $cache['whitelist'] = drupal_path_alias_whitelist_rebuild();
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case 'alias':
 | 
			
		||||
      if (empty($path)) {
 | 
			
		||||
        return false;
 | 
			
		||||
      }
 | 
			
		||||
      // Check the path whitelist, if the top_level part before the first /
 | 
			
		||||
      // is not in the list, then there is no need to do anything further,
 | 
			
		||||
      // it is not in the database.
 | 
			
		||||
      if (!isset($cache['whitelist'][strtok($path, '/')])) {
 | 
			
		||||
        return false;
 | 
			
		||||
      }
 | 
			
		||||
      // Deny admin paths.
 | 
			
		||||
      if ($denyAdmin && path_is_admin($path)) {
 | 
			
		||||
        return false;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      $ret = $hashLookup->lookupAlias($path, $path_language);
 | 
			
		||||
      if (null === $ret) {
 | 
			
		||||
        // Original Drupal algorithm.
 | 
			
		||||
        // This will also update the $path_language variable so Redis will store
 | 
			
		||||
        // the right language (keeps track of LANGUAGE_NONE or specific language
 | 
			
		||||
        // so that default fallback behavior is the same that core).
 | 
			
		||||
        if ($path_language == LANGUAGE_NONE) {
 | 
			
		||||
          list ($ret, $path_language) = db_query_range("SELECT alias, language FROM {url_alias} WHERE source = :source AND language = :language ORDER BY pid DESC", 0, 1, array(
 | 
			
		||||
            ':source' => $path,
 | 
			
		||||
            ':language' => $path_language,
 | 
			
		||||
          ))->fetch(PDO::FETCH_NUM);
 | 
			
		||||
        } else if ($path_language > LANGUAGE_NONE) {
 | 
			
		||||
          list ($ret, $path_language) = db_query_range("SELECT alias, language FROM {url_alias} WHERE source = :source AND language IN (:language) ORDER BY language DESC, pid DESC", 0, 1, array(
 | 
			
		||||
            ':source' => $path,
 | 
			
		||||
            ':language' => array($path_language, LANGUAGE_NONE),
 | 
			
		||||
          ))->fetch(PDO::FETCH_NUM);
 | 
			
		||||
        } else {
 | 
			
		||||
          list ($ret, $path_language) = db_query_range("SELECT alias, language FROM {url_alias} WHERE source = :source AND language IN (:language) ORDER BY language ASC, pid DESC", 0, 1, array(
 | 
			
		||||
            ':source' => $path,
 | 
			
		||||
            ':language' => array($path_language, LANGUAGE_NONE),
 | 
			
		||||
          ))->fetch(PDO::FETCH_NUM);
 | 
			
		||||
        }
 | 
			
		||||
        // Getting here with a value means we need to cache it
 | 
			
		||||
        if (empty($ret)) {
 | 
			
		||||
          $ret = false;
 | 
			
		||||
        }
 | 
			
		||||
        $hashLookup->saveAlias($path, $ret, $path_language);
 | 
			
		||||
      }
 | 
			
		||||
      $cache[$path_language]['alias'][$path] = $ret;
 | 
			
		||||
      $cache[$path_language]['source'][$ret] = $path;
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case 'source':
 | 
			
		||||
      if (empty($path)) {
 | 
			
		||||
        return false;
 | 
			
		||||
      }
 | 
			
		||||
      // Even thought given entry is an alias, if it conflicts with an
 | 
			
		||||
      // existing admin path just deny any lookup.
 | 
			
		||||
      if ($denyAdmin && path_is_admin($path)) {
 | 
			
		||||
        return false;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      $ret = $hashLookup->lookupSource($path, $path_language);
 | 
			
		||||
      if (null === $ret) {
 | 
			
		||||
        // Original Drupal algorithm.
 | 
			
		||||
        // This will also update the $path_language variable so Redis will store
 | 
			
		||||
        // the right language (keeps track of LANGUAGE_NONE or specific language
 | 
			
		||||
        // so that default fallback behavior is the same that core).
 | 
			
		||||
        if ($path_language == LANGUAGE_NONE) {
 | 
			
		||||
          list ($ret, $path_language) = db_query_range("SELECT source, language FROM {url_alias} WHERE alias = :alias AND language = :language ORDER BY pid DESC", 0, 1, array(
 | 
			
		||||
            ':alias' => $path,
 | 
			
		||||
            ':language' => LANGUAGE_NONE,
 | 
			
		||||
          ))->fetch(PDO::FETCH_NUM);
 | 
			
		||||
        } else if ($path_language > LANGUAGE_NONE) {
 | 
			
		||||
          list ($ret, $path_language) = db_query_range("SELECT source, language FROM {url_alias} WHERE alias = :alias AND language IN (:language) ORDER BY language DESC, pid DESC", 0, 1, array(
 | 
			
		||||
            ':alias' => $path,
 | 
			
		||||
            ':language' => array($path_language, LANGUAGE_NONE),
 | 
			
		||||
          ))->fetch(PDO::FETCH_NUM);
 | 
			
		||||
        } else {
 | 
			
		||||
          list ($ret, $path_language) = db_query_range("SELECT source, language FROM {url_alias} WHERE alias = :alias AND language IN (:language) ORDER BY language ASC, pid DESC", 0, 1, array(
 | 
			
		||||
            ':alias' => $path,
 | 
			
		||||
            ':language' => array($path_language, LANGUAGE_NONE),
 | 
			
		||||
          ))->fetch(PDO::FETCH_NUM);
 | 
			
		||||
        }
 | 
			
		||||
        // Getting here with a value means we need to cache it
 | 
			
		||||
        if (empty($ret)) {
 | 
			
		||||
          $ret = false;
 | 
			
		||||
        } else {
 | 
			
		||||
          $ret = strtolower(trim($ret));
 | 
			
		||||
        }
 | 
			
		||||
        $hashLookup->saveAlias($ret, $path, $path_language);
 | 
			
		||||
      }
 | 
			
		||||
      $cache[$path_language]['alias'][$ret] = $path;
 | 
			
		||||
      $cache[$path_language]['source'][$path] = $ret;
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return $ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Cache system paths for a page.
 | 
			
		||||
 *
 | 
			
		||||
 * Cache an array of the system paths available on each page. We assume
 | 
			
		||||
 * that aliases will be needed for the majority of these paths during
 | 
			
		||||
 * subsequent requests, and load them in a single query during
 | 
			
		||||
 * drupal_lookup_path().
 | 
			
		||||
 */
 | 
			
		||||
function drupal_cache_system_paths() {
 | 
			
		||||
  // Check if the system paths for this page were loaded from cache in this
 | 
			
		||||
  // request to avoid writing to cache on every request.
 | 
			
		||||
  $cache = &drupal_static('drupal_lookup_path', array());
 | 
			
		||||
  if (empty($cache['system_paths']) && !empty($cache['map'])) {
 | 
			
		||||
    // Generate a cache ID (cid) specifically for this page.
 | 
			
		||||
    $cid = current_path();
 | 
			
		||||
    // The static $map array used by drupal_lookup_path() includes all
 | 
			
		||||
    // system paths for the page request.
 | 
			
		||||
    if ($paths = current($cache['map'])) {
 | 
			
		||||
      $data = array_keys($paths);
 | 
			
		||||
      $expire = REQUEST_TIME + (60 * 60 * 24);
 | 
			
		||||
      cache_set($cid, $data, 'cache_path', $expire);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Given an internal Drupal path, return the alias set by the administrator.
 | 
			
		||||
 *
 | 
			
		||||
 * If no path is provided, the function will return the alias of the current
 | 
			
		||||
 * page.
 | 
			
		||||
 *
 | 
			
		||||
 * @param $path
 | 
			
		||||
 *   An internal Drupal path.
 | 
			
		||||
 * @param $path_language
 | 
			
		||||
 *   An optional language code to look up the path in.
 | 
			
		||||
 *
 | 
			
		||||
 * @return
 | 
			
		||||
 *   An aliased path if one was found, or the original path if no alias was
 | 
			
		||||
 *   found.
 | 
			
		||||
 */
 | 
			
		||||
function drupal_get_path_alias($path = NULL, $path_language = NULL) {
 | 
			
		||||
  // If no path is specified, use the current page's path.
 | 
			
		||||
  if ($path == NULL) {
 | 
			
		||||
    $path = $_GET['q'];
 | 
			
		||||
  }
 | 
			
		||||
  $result = $path;
 | 
			
		||||
  if ($alias = drupal_lookup_path('alias', $path, $path_language)) {
 | 
			
		||||
    $result = $alias;
 | 
			
		||||
  }
 | 
			
		||||
  return $result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Given a path alias, return the internal path it represents.
 | 
			
		||||
 *
 | 
			
		||||
 * @param $path
 | 
			
		||||
 *   A Drupal path alias.
 | 
			
		||||
 * @param $path_language
 | 
			
		||||
 *   An optional language code to look up the path in.
 | 
			
		||||
 *
 | 
			
		||||
 * @return
 | 
			
		||||
 *   The internal path represented by the alias, or the original alias if no
 | 
			
		||||
 *   internal path was found.
 | 
			
		||||
 */
 | 
			
		||||
function drupal_get_normal_path($path, $path_language = NULL) {
 | 
			
		||||
  $original_path = $path;
 | 
			
		||||
 | 
			
		||||
  // Lookup the path alias first.
 | 
			
		||||
  if ($source = drupal_lookup_path('source', $path, $path_language)) {
 | 
			
		||||
    $path = $source;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Allow other modules to alter the inbound URL. We cannot use drupal_alter()
 | 
			
		||||
  // here because we need to run hook_url_inbound_alter() in the reverse order
 | 
			
		||||
  // of hook_url_outbound_alter().
 | 
			
		||||
  foreach (array_reverse(module_implements('url_inbound_alter')) as $module) {
 | 
			
		||||
    $function = $module . '_url_inbound_alter';
 | 
			
		||||
    $function($path, $original_path, $path_language);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return $path;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Check if the current page is the front page.
 | 
			
		||||
 *
 | 
			
		||||
 * @return
 | 
			
		||||
 *   Boolean value: TRUE if the current page is the front page; FALSE if otherwise.
 | 
			
		||||
 */
 | 
			
		||||
function drupal_is_front_page() {
 | 
			
		||||
  // Use the advanced drupal_static() pattern, since this is called very often.
 | 
			
		||||
  static $drupal_static_fast;
 | 
			
		||||
  if (!isset($drupal_static_fast)) {
 | 
			
		||||
    $drupal_static_fast['is_front_page'] = &drupal_static(__FUNCTION__);
 | 
			
		||||
  }
 | 
			
		||||
  $is_front_page = &$drupal_static_fast['is_front_page'];
 | 
			
		||||
 | 
			
		||||
  if (!isset($is_front_page)) {
 | 
			
		||||
    // As drupal_path_initialize updates $_GET['q'] with the 'site_frontpage' path,
 | 
			
		||||
    // we can check it against the 'site_frontpage' variable.
 | 
			
		||||
    $is_front_page = ($_GET['q'] == variable_get('site_frontpage', 'node'));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return $is_front_page;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Check if a path matches any pattern in a set of patterns.
 | 
			
		||||
 *
 | 
			
		||||
 * @param $path
 | 
			
		||||
 *   The path to match.
 | 
			
		||||
 * @param $patterns
 | 
			
		||||
 *   String containing a set of patterns separated by \n, \r or \r\n.
 | 
			
		||||
 *
 | 
			
		||||
 * @return
 | 
			
		||||
 *   Boolean value: TRUE if the path matches a pattern, FALSE otherwise.
 | 
			
		||||
 */
 | 
			
		||||
function drupal_match_path($path, $patterns) {
 | 
			
		||||
  $regexps = &drupal_static(__FUNCTION__);
 | 
			
		||||
 | 
			
		||||
  if (!isset($regexps[$patterns])) {
 | 
			
		||||
    // Convert path settings to a regular expression.
 | 
			
		||||
    // Therefore replace newlines with a logical or, /* with asterisks and the <front> with the frontpage.
 | 
			
		||||
    $to_replace = array(
 | 
			
		||||
      '/(\r\n?|\n)/', // newlines
 | 
			
		||||
      '/\\\\\*/',     // asterisks
 | 
			
		||||
      '/(^|\|)\\\\<front\\\\>($|\|)/' // <front>
 | 
			
		||||
    );
 | 
			
		||||
    $replacements = array(
 | 
			
		||||
      '|',
 | 
			
		||||
      '.*',
 | 
			
		||||
      '\1' . preg_quote(variable_get('site_frontpage', 'node'), '/') . '\2'
 | 
			
		||||
    );
 | 
			
		||||
    $patterns_quoted = preg_quote($patterns, '/');
 | 
			
		||||
    $regexps[$patterns] = '/^(' . preg_replace($to_replace, $replacements, $patterns_quoted) . ')$/';
 | 
			
		||||
  }
 | 
			
		||||
  return (bool)preg_match($regexps[$patterns], $path);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Return the current URL path of the page being viewed.
 | 
			
		||||
 *
 | 
			
		||||
 * Examples:
 | 
			
		||||
 * - http://example.com/node/306 returns "node/306".
 | 
			
		||||
 * - http://example.com/drupalfolder/node/306 returns "node/306" while
 | 
			
		||||
 *   base_path() returns "/drupalfolder/".
 | 
			
		||||
 * - http://example.com/path/alias (which is a path alias for node/306) returns
 | 
			
		||||
 *   "node/306" as opposed to the path alias.
 | 
			
		||||
 *
 | 
			
		||||
 * This function is not available in hook_boot() so use $_GET['q'] instead.
 | 
			
		||||
 * However, be careful when doing that because in the case of Example #3
 | 
			
		||||
 * $_GET['q'] will contain "path/alias". If "node/306" is needed, calling
 | 
			
		||||
 * drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL) makes this function available.
 | 
			
		||||
 *
 | 
			
		||||
 * @return
 | 
			
		||||
 *   The current Drupal URL path.
 | 
			
		||||
 *
 | 
			
		||||
 * @see request_path()
 | 
			
		||||
 */
 | 
			
		||||
function current_path() {
 | 
			
		||||
  return $_GET['q'];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Rebuild the path alias white list.
 | 
			
		||||
 *
 | 
			
		||||
 * @param $source
 | 
			
		||||
 *   An optional system path for which an alias is being inserted.
 | 
			
		||||
 *
 | 
			
		||||
 * @return
 | 
			
		||||
 *   An array containing a white list of path aliases.
 | 
			
		||||
 */
 | 
			
		||||
function drupal_path_alias_whitelist_rebuild($source = NULL) {
 | 
			
		||||
  // When paths are inserted, only rebuild the whitelist if the system path
 | 
			
		||||
  // has a top level component which is not already in the whitelist.
 | 
			
		||||
  if (!empty($source)) {
 | 
			
		||||
    $whitelist = variable_get('path_alias_whitelist', NULL);
 | 
			
		||||
    if (isset($whitelist[strtok($source, '/')])) {
 | 
			
		||||
      return $whitelist;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  // For each alias in the database, get the top level component of the system
 | 
			
		||||
  // path it corresponds to. This is the portion of the path before the first
 | 
			
		||||
  // '/', if present, otherwise the whole path itself.
 | 
			
		||||
  $whitelist = array();
 | 
			
		||||
  $result = db_query("SELECT DISTINCT SUBSTRING_INDEX(source, '/', 1) AS path FROM {url_alias}");
 | 
			
		||||
  foreach ($result as $row) {
 | 
			
		||||
    $whitelist[$row->path] = TRUE;
 | 
			
		||||
  }
 | 
			
		||||
  variable_set('path_alias_whitelist', $whitelist);
 | 
			
		||||
  return $whitelist;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Fetches a specific URL alias from the database.
 | 
			
		||||
 *
 | 
			
		||||
 * @param $conditions
 | 
			
		||||
 *   A string representing the source, a number representing the pid, or an
 | 
			
		||||
 *   array of query conditions.
 | 
			
		||||
 *
 | 
			
		||||
 * @return
 | 
			
		||||
 *   FALSE if no alias was found or an associative array containing the
 | 
			
		||||
 *   following keys:
 | 
			
		||||
 *   - source: The internal system path.
 | 
			
		||||
 *   - alias: The URL alias.
 | 
			
		||||
 *   - pid: Unique path alias identifier.
 | 
			
		||||
 *   - language: The language of the alias.
 | 
			
		||||
 */
 | 
			
		||||
function path_load($conditions) {
 | 
			
		||||
  if (is_numeric($conditions)) {
 | 
			
		||||
    $conditions = array('pid' => $conditions);
 | 
			
		||||
  }
 | 
			
		||||
  elseif (is_string($conditions)) {
 | 
			
		||||
    $conditions = array('source' => $conditions);
 | 
			
		||||
  }
 | 
			
		||||
  elseif (!is_array($conditions)) {
 | 
			
		||||
    return FALSE;
 | 
			
		||||
  }
 | 
			
		||||
  $select = db_select('url_alias');
 | 
			
		||||
  foreach ($conditions as $field => $value) {
 | 
			
		||||
    $select->condition($field, $value);
 | 
			
		||||
  }
 | 
			
		||||
  return $select
 | 
			
		||||
    ->fields('url_alias')
 | 
			
		||||
    ->execute()
 | 
			
		||||
    ->fetchAssoc();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Save a path alias to the database.
 | 
			
		||||
 *
 | 
			
		||||
 * @param $path
 | 
			
		||||
 *   An associative array containing the following keys:
 | 
			
		||||
 *   - source: The internal system path.
 | 
			
		||||
 *   - alias: The URL alias.
 | 
			
		||||
 *   - pid: (optional) Unique path alias identifier.
 | 
			
		||||
 *   - language: (optional) The language of the alias.
 | 
			
		||||
 */
 | 
			
		||||
function path_save(&$path) {
 | 
			
		||||
  $path += array('language' => LANGUAGE_NONE);
 | 
			
		||||
 | 
			
		||||
  // Load the stored alias, if any.
 | 
			
		||||
  if (!empty($path['pid']) && !isset($path['original'])) {
 | 
			
		||||
    $path['original'] = path_load($path['pid']);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (empty($path['pid'])) {
 | 
			
		||||
    drupal_write_record('url_alias', $path);
 | 
			
		||||
    module_invoke_all('path_insert', $path);
 | 
			
		||||
  }
 | 
			
		||||
  else {
 | 
			
		||||
    drupal_write_record('url_alias', $path, array('pid'));
 | 
			
		||||
    module_invoke_all('path_update', $path);
 | 
			
		||||
  }
 | 
			
		||||
  if (!empty($path['original'])) {
 | 
			
		||||
    redis_path_backend_get()->deleteAlias($path['original']['source'], $path['original']['alias'], $path['original']['language']);
 | 
			
		||||
  }
 | 
			
		||||
  redis_path_backend_get()->saveAlias($path['source'], $path['alias'], $path['language']);
 | 
			
		||||
 | 
			
		||||
  // Clear internal properties.
 | 
			
		||||
  unset($path['original']);
 | 
			
		||||
 | 
			
		||||
  // Clear the static alias cache.
 | 
			
		||||
  drupal_clear_path_cache($path['source']);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Delete a URL alias.
 | 
			
		||||
 *
 | 
			
		||||
 * @param $criteria
 | 
			
		||||
 *   A number representing the pid or an array of criteria.
 | 
			
		||||
 */
 | 
			
		||||
function path_delete($criteria) {
 | 
			
		||||
  if (!is_array($criteria)) {
 | 
			
		||||
    $criteria = array('pid' => $criteria);
 | 
			
		||||
  }
 | 
			
		||||
  $path = path_load($criteria);
 | 
			
		||||
  $query = db_delete('url_alias');
 | 
			
		||||
  foreach ($criteria as $field => $value) {
 | 
			
		||||
    $query->condition($field, $value);
 | 
			
		||||
  }
 | 
			
		||||
  $query->execute();
 | 
			
		||||
  module_invoke_all('path_delete', $path);
 | 
			
		||||
  redis_path_backend_get()->deleteAlias($path['source'], $path['alias'], $path['language']);
 | 
			
		||||
  drupal_clear_path_cache($path['source']);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Determines whether a path is in the administrative section of the site.
 | 
			
		||||
 *
 | 
			
		||||
 * By default, paths are considered to be non-administrative. If a path does
 | 
			
		||||
 * not match any of the patterns in path_get_admin_paths(), or if it matches
 | 
			
		||||
 * both administrative and non-administrative patterns, it is considered
 | 
			
		||||
 * non-administrative.
 | 
			
		||||
 *
 | 
			
		||||
 * @param $path
 | 
			
		||||
 *   A Drupal path.
 | 
			
		||||
 *
 | 
			
		||||
 * @return
 | 
			
		||||
 *   TRUE if the path is administrative, FALSE otherwise.
 | 
			
		||||
 *
 | 
			
		||||
 * @see path_get_admin_paths()
 | 
			
		||||
 * @see hook_admin_paths()
 | 
			
		||||
 * @see hook_admin_paths_alter()
 | 
			
		||||
 */
 | 
			
		||||
function path_is_admin($path) {
 | 
			
		||||
  $path_map = &drupal_static(__FUNCTION__);
 | 
			
		||||
  if (!isset($path_map['admin'][$path])) {
 | 
			
		||||
    $patterns = path_get_admin_paths();
 | 
			
		||||
    $path_map['admin'][$path] = drupal_match_path($path, $patterns['admin']);
 | 
			
		||||
    $path_map['non_admin'][$path] = drupal_match_path($path, $patterns['non_admin']);
 | 
			
		||||
  }
 | 
			
		||||
  return $path_map['admin'][$path] && !$path_map['non_admin'][$path];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Gets a list of administrative and non-administrative paths.
 | 
			
		||||
 *
 | 
			
		||||
 * @return array
 | 
			
		||||
 *   An associative array containing the following keys:
 | 
			
		||||
 *   'admin': An array of administrative paths and regular expressions
 | 
			
		||||
 *            in a format suitable for drupal_match_path().
 | 
			
		||||
 *   'non_admin': An array of non-administrative paths and regular expressions.
 | 
			
		||||
 *
 | 
			
		||||
 * @see hook_admin_paths()
 | 
			
		||||
 * @see hook_admin_paths_alter()
 | 
			
		||||
 */
 | 
			
		||||
function path_get_admin_paths() {
 | 
			
		||||
  $patterns = &drupal_static(__FUNCTION__);
 | 
			
		||||
  if (!isset($patterns)) {
 | 
			
		||||
    $paths = module_invoke_all('admin_paths');
 | 
			
		||||
    drupal_alter('admin_paths', $paths);
 | 
			
		||||
    // Combine all admin paths into one array, and likewise for non-admin paths,
 | 
			
		||||
    // for easier handling.
 | 
			
		||||
    $patterns = array();
 | 
			
		||||
    $patterns['admin'] = array();
 | 
			
		||||
    $patterns['non_admin'] = array();
 | 
			
		||||
    foreach ($paths as $path => $enabled) {
 | 
			
		||||
      if ($enabled) {
 | 
			
		||||
        $patterns['admin'][] = $path;
 | 
			
		||||
      }
 | 
			
		||||
      else {
 | 
			
		||||
        $patterns['non_admin'][] = $path;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    $patterns['admin'] = implode("\n", $patterns['admin']);
 | 
			
		||||
    $patterns['non_admin'] = implode("\n", $patterns['non_admin']);
 | 
			
		||||
  }
 | 
			
		||||
  return $patterns;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Checks a path exists and the current user has access to it.
 | 
			
		||||
 *
 | 
			
		||||
 * @param $path
 | 
			
		||||
 *   The path to check.
 | 
			
		||||
 * @param $dynamic_allowed
 | 
			
		||||
 *   Whether paths with menu wildcards (like user/%) should be allowed.
 | 
			
		||||
 *
 | 
			
		||||
 * @return
 | 
			
		||||
 *   TRUE if it is a valid path AND the current user has access permission,
 | 
			
		||||
 *   FALSE otherwise.
 | 
			
		||||
 */
 | 
			
		||||
function drupal_valid_path($path, $dynamic_allowed = FALSE) {
 | 
			
		||||
  global $menu_admin;
 | 
			
		||||
  // We indicate that a menu administrator is running the menu access check.
 | 
			
		||||
  $menu_admin = TRUE;
 | 
			
		||||
  if ($path == '<front>' || url_is_external($path)) {
 | 
			
		||||
    $item = array('access' => TRUE);
 | 
			
		||||
  }
 | 
			
		||||
  elseif ($dynamic_allowed && preg_match('/\/\%/', $path)) {
 | 
			
		||||
    // Path is dynamic (ie 'user/%'), so check directly against menu_router table.
 | 
			
		||||
    if ($item = db_query("SELECT * FROM {menu_router} where path = :path", array(':path' => $path))->fetchAssoc()) {
 | 
			
		||||
      $item['link_path']  = $item['path'];
 | 
			
		||||
      $item['link_title'] = $item['title'];
 | 
			
		||||
      $item['external']   = FALSE;
 | 
			
		||||
      $item['options'] = '';
 | 
			
		||||
      _menu_link_translate($item);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  else {
 | 
			
		||||
    $item = menu_get_item($path);
 | 
			
		||||
  }
 | 
			
		||||
  $menu_admin = FALSE;
 | 
			
		||||
  return $item && $item['access'];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Clear the path cache.
 | 
			
		||||
 *
 | 
			
		||||
 * @param $source
 | 
			
		||||
 *   An optional system path for which an alias is being changed.
 | 
			
		||||
 */
 | 
			
		||||
function drupal_clear_path_cache($source = NULL) {
 | 
			
		||||
  // Clear the drupal_lookup_path() static cache.
 | 
			
		||||
  drupal_static_reset('drupal_lookup_path');
 | 
			
		||||
  drupal_path_alias_whitelist_rebuild($source);
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user