Drupal: add missing modules
This commit is contained in:
parent
3dcf246bda
commit
ad550e0afe
5
frontend/drupal/modules/xautoload/.travis.yml
Normal file
5
frontend/drupal/modules/xautoload/.travis.yml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
language: php
|
||||||
|
php:
|
||||||
|
- 5.5
|
||||||
|
- 5.4
|
||||||
|
- 5.3
|
339
frontend/drupal/modules/xautoload/LICENSE.txt
Normal file
339
frontend/drupal/modules/xautoload/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.
|
3
frontend/drupal/modules/xautoload/README.md
Normal file
3
frontend/drupal/modules/xautoload/README.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
|
||||||
|
|
||||||
|
[](https://travis-ci.org/donquixote/drupal-xautoload)
|
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is not used anywhere in xautoload, but could be used by other
|
||||||
|
* modules.
|
||||||
|
*/
|
||||||
|
class xautoload_FinderPlugin_CheckIncludePath implements xautoload_FinderPlugin_Interface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function findFile($api, $logical_base_path, $relative_path) {
|
||||||
|
$path = $logical_base_path . $relative_path;
|
||||||
|
return $api->suggestFile_checkIncludePath($path);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Drupal\xautoload\ClassFinder\InjectedApi\InjectedApiInterface;
|
||||||
|
use Drupal\xautoload\DirectoryBehavior\DirectoryBehaviorInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* X Autoload plugins are for:
|
||||||
|
* - More exotic autoload patterns that are incompatible with PSR-0 or PEAR
|
||||||
|
* - Situations where we don't want to register a ton of namespaces, and using
|
||||||
|
* a plugin instead gives us performance benefits.
|
||||||
|
*/
|
||||||
|
interface xautoload_FinderPlugin_Interface extends DirectoryBehaviorInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the file for a class that in PSR-0 or PEAR would be in
|
||||||
|
* $psr_0_root . '/' . $path_fragment . $path_suffix
|
||||||
|
*
|
||||||
|
* E.g.:
|
||||||
|
* - The class we look for is Some\Namespace\Some\Class
|
||||||
|
* - The file is actually in "exotic/location.php". This is not following
|
||||||
|
* PSR-0 or PEAR standard, so we need a plugin.
|
||||||
|
* -> The class finder will transform the class name to
|
||||||
|
* "Some/Namespace/Some/Class.php"
|
||||||
|
* - The plugin was registered for the namespace "Some\Namespace". This is
|
||||||
|
* because all those exotic classes all begin with Some\Namespace\
|
||||||
|
* -> The arguments will be:
|
||||||
|
* ($api = the API object, see below)
|
||||||
|
* $logical_base_path = "Some/Namespace/"
|
||||||
|
* $relative_path = "Some/Class.php"
|
||||||
|
* $api->getClass() gives the original class name, if we still need it.
|
||||||
|
* -> We are supposed to:
|
||||||
|
* if ($api->suggestFile('exotic/location.php')) {
|
||||||
|
* return TRUE;
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @param InjectedApiInterface $api
|
||||||
|
* An object with a suggestFile() method.
|
||||||
|
* We are supposed to suggest files until suggestFile() returns TRUE, or we
|
||||||
|
* have no more suggestions.
|
||||||
|
* @param string $logical_base_path
|
||||||
|
* The key that this plugin was registered with.
|
||||||
|
* With trailing '/'.
|
||||||
|
* @param string $relative_path
|
||||||
|
* Second part of the canonical path, ending with '.php'.
|
||||||
|
*
|
||||||
|
* @return bool|null
|
||||||
|
* TRUE, if the file was found.
|
||||||
|
* FALSE or NULL, otherwise.
|
||||||
|
*/
|
||||||
|
function findFile($api, $logical_base_path, $relative_path);
|
||||||
|
}
|
@ -0,0 +1,248 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Drupal\xautoload\Adapter\LocalDirectoryAdapter;
|
||||||
|
use Drupal\xautoload\Util;
|
||||||
|
use Drupal\xautoload\ClassFinder\ExtendedClassFinderInterface;
|
||||||
|
use Drupal\xautoload\Adapter\ClassFinderAdapter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An instance of this class is passed around to implementations of
|
||||||
|
* hook_xautoload(). It acts as a wrapper around the ClassFinder, to register
|
||||||
|
* stuff.
|
||||||
|
*
|
||||||
|
* Most of the methods here are deprecated. You should use the methods inherited
|
||||||
|
* from the base class, LocalDirectoryAdapter, instead.
|
||||||
|
*/
|
||||||
|
class xautoload_InjectedAPI_hookXautoload extends LocalDirectoryAdapter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ExtendedClassFinderInterface
|
||||||
|
*/
|
||||||
|
protected $finder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ExtendedClassFinderInterface $finder
|
||||||
|
* The class finder object.
|
||||||
|
* @param string $localDirectory
|
||||||
|
*
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
static function create($finder, $localDirectory) {
|
||||||
|
$adapter = ClassFinderAdapter::create($finder);
|
||||||
|
return new self($adapter, $localDirectory);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ClassFinderAdapter $adapter
|
||||||
|
* The class finder object.
|
||||||
|
* @param string $localDirectory
|
||||||
|
*/
|
||||||
|
function __construct($adapter, $localDirectory) {
|
||||||
|
parent::__construct($adapter, $localDirectory);
|
||||||
|
$this->finder = $adapter->getFinder();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prefix stuff
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register an additional prefix for this module.
|
||||||
|
* Note: Drupal\<module name>\ is already registered for <module dir>/lib.
|
||||||
|
*
|
||||||
|
* @deprecated
|
||||||
|
*
|
||||||
|
* @param string $prefix
|
||||||
|
* The prefix.
|
||||||
|
* @param string $prefix_root_dir
|
||||||
|
* Prefix root dir.
|
||||||
|
* If $relative is TRUE, this is relative to the extension module dir.
|
||||||
|
* If $relative is FALSE, this is an absolute path.
|
||||||
|
* @param boolean $relative
|
||||||
|
* Whether or not the path is relative to the current extension dir.
|
||||||
|
*/
|
||||||
|
function prefixRoot($prefix, $prefix_root_dir = NULL, $relative = TRUE) {
|
||||||
|
$prefix_root_dir = $this->processDir($prefix_root_dir, $relative);
|
||||||
|
$this->finder->registerPrefixRoot($prefix, $prefix_root_dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register an additional namespace for this module.
|
||||||
|
* Note: Drupal\<module name>\ is already registered for <module dir>/lib.
|
||||||
|
*
|
||||||
|
* @deprecated
|
||||||
|
*
|
||||||
|
* @param string $prefix
|
||||||
|
* The namespace
|
||||||
|
* @param string $prefix_deep_dir
|
||||||
|
* PSR-0 root dir.
|
||||||
|
* If $relative is TRUE, this is relative to the current extension dir.
|
||||||
|
* If $relative is FALSE, this is an absolute path.
|
||||||
|
* @param boolean $relative
|
||||||
|
* Whether or not the path is relative to the current extension dir.
|
||||||
|
*/
|
||||||
|
function prefixDeep($prefix, $prefix_deep_dir = NULL, $relative = TRUE) {
|
||||||
|
$prefix_deep_dir = $this->processDir($prefix_deep_dir, $relative);
|
||||||
|
$this->finder->registerPrefixDeep($prefix, $prefix_deep_dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Legacy: Plugins were called "Handler" before.
|
||||||
|
*
|
||||||
|
* @deprecated
|
||||||
|
*
|
||||||
|
* @param string $prefix
|
||||||
|
* @param xautoload_FinderPlugin_Interface $plugin
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* The key under which the plugin was registered. This can later be used to
|
||||||
|
* unregister the plugin again.
|
||||||
|
*/
|
||||||
|
function prefixHandler($prefix, $plugin) {
|
||||||
|
$key = Util::randomString();
|
||||||
|
$this->finder->registerPrefixDeep($prefix, $key, $plugin);
|
||||||
|
|
||||||
|
return $key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a prefix plugin object
|
||||||
|
*
|
||||||
|
* @deprecated
|
||||||
|
*
|
||||||
|
* @param string $prefix
|
||||||
|
* @param xautoload_FinderPlugin_Interface $plugin
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* The key under which the plugin was registered. This can later be used to
|
||||||
|
* unregister the plugin again.
|
||||||
|
*/
|
||||||
|
function prefixPlugin($prefix, $plugin) {
|
||||||
|
$key = Util::randomString();
|
||||||
|
$this->finder->registerPrefixDeep($prefix, $key, $plugin);
|
||||||
|
|
||||||
|
return $key;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Namespace stuff
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register an additional namespace for this module.
|
||||||
|
* Note: Drupal\<module name>\ is already registered for <module dir>/lib.
|
||||||
|
*
|
||||||
|
* @deprecated
|
||||||
|
*
|
||||||
|
* @param string $namespace
|
||||||
|
* The namespace
|
||||||
|
* @param string $psr_0_root_dir
|
||||||
|
* PSR-0 root dir.
|
||||||
|
* If $relative is TRUE, this is relative to the current module dir.
|
||||||
|
* If $relative is FALSE, this is an absolute path.
|
||||||
|
* @param boolean $relative
|
||||||
|
* Whether or not the path is relative to the current extension dir.
|
||||||
|
*/
|
||||||
|
function namespaceRoot($namespace, $psr_0_root_dir = NULL, $relative = TRUE) {
|
||||||
|
$psr_0_root_dir = $this->processDir($psr_0_root_dir, $relative);
|
||||||
|
$this->finder->registerNamespaceRoot($namespace, $psr_0_root_dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register an additional namespace for this module.
|
||||||
|
* Note: Drupal\<module name>\ is already registered for <module dir>/lib.
|
||||||
|
*
|
||||||
|
* @deprecated
|
||||||
|
*
|
||||||
|
* @param string $namespace
|
||||||
|
* The namespace
|
||||||
|
* @param string $namespace_deep_dir
|
||||||
|
* PSR-0 root dir.
|
||||||
|
* If $relative is TRUE, this is relative to the current extension dir.
|
||||||
|
* If $relative is FALSE, this is an absolute path.
|
||||||
|
* @param boolean $relative
|
||||||
|
* Whether or not the path is relative to the current extension dir.
|
||||||
|
*/
|
||||||
|
function namespaceDeep($namespace, $namespace_deep_dir = NULL, $relative = TRUE) {
|
||||||
|
$namespace_deep_dir = $this->processDir($namespace_deep_dir, $relative);
|
||||||
|
$this->finder->registerNamespaceDeep($namespace, $namespace_deep_dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a namespace plugin object
|
||||||
|
*
|
||||||
|
* @deprecated
|
||||||
|
*
|
||||||
|
* @param string $namespace
|
||||||
|
* @param xautoload_FinderPlugin_Interface $plugin
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* The key under which the plugin was registered. This can later be used to
|
||||||
|
* unregister the plugin again.
|
||||||
|
*/
|
||||||
|
function namespacePlugin($namespace, $plugin) {
|
||||||
|
$key = Util::randomString();
|
||||||
|
$this->finder->registerNamespaceDeep($namespace, $key, $plugin);
|
||||||
|
|
||||||
|
return $key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Legacy: Plugins were called "Handler" before.
|
||||||
|
*
|
||||||
|
* @deprecated
|
||||||
|
*
|
||||||
|
* @param string $namespace
|
||||||
|
* @param xautoload_FinderPlugin_Interface $plugin
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* The key under which the plugin was registered. This can later be used to
|
||||||
|
* unregister the plugin again.
|
||||||
|
*/
|
||||||
|
function namespaceHandler($namespace, $plugin) {
|
||||||
|
$key = Util::randomString();
|
||||||
|
$this->finder->registerNamespaceDeep($namespace, $key, $plugin);
|
||||||
|
|
||||||
|
return $key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process a given directory to make it relative to Drupal root,
|
||||||
|
* instead of relative to the current extension dir.
|
||||||
|
*
|
||||||
|
* @deprecated
|
||||||
|
*
|
||||||
|
* @param string $dir
|
||||||
|
* The directory path that we want to make absolute.
|
||||||
|
* @param boolean $relative
|
||||||
|
* If TRUE, the $dir will be transformed from relative to absolute.
|
||||||
|
* If FALSE, the $dir is assumed to already be absolute, and remain unchanged.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* The modified (absolute) directory path.
|
||||||
|
*/
|
||||||
|
protected function processDir($dir, $relative) {
|
||||||
|
if (!isset($dir)) {
|
||||||
|
return $this->localDirectory . 'lib/';
|
||||||
|
}
|
||||||
|
$dir = strlen($dir)
|
||||||
|
? rtrim($dir, '/') . '/'
|
||||||
|
: '';
|
||||||
|
|
||||||
|
return $relative
|
||||||
|
? $this->localDirectory . $dir
|
||||||
|
: $dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Explicitly set the base for relative paths.
|
||||||
|
*
|
||||||
|
* Alias for LocalDirectoryAdapter::setLocalDirectory()
|
||||||
|
*
|
||||||
|
* @param string $dir
|
||||||
|
* New relative base path.
|
||||||
|
*/
|
||||||
|
function setExtensionDir($dir) {
|
||||||
|
$this->localDirectory = strlen($dir)
|
||||||
|
? rtrim($dir, '/') . '/'
|
||||||
|
: '';
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\Tests;
|
||||||
|
|
||||||
|
use Drupal\xautoload\Util;
|
||||||
|
|
||||||
|
class EnvironmentSnapshotMaker {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected static $snapshots = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $module
|
||||||
|
* @param string $phase
|
||||||
|
* @param string[] $classes
|
||||||
|
*/
|
||||||
|
static function takeSnapshot($module, $phase, $classes) {
|
||||||
|
self::$snapshots[$module][$phase] = self::buildSnapshot($classes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $module
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
static function getSnapshots($module) {
|
||||||
|
return isset(self::$snapshots[$module])
|
||||||
|
? self::$snapshots[$module]
|
||||||
|
: array();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string[] $classes
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected static function buildSnapshot($classes) {
|
||||||
|
|
||||||
|
$observations = array();
|
||||||
|
|
||||||
|
// Test that all classes are available immediately at boot time.
|
||||||
|
foreach ($classes as $class) {
|
||||||
|
$observations['class_exists'][$class] = class_exists($class);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check variable_get().
|
||||||
|
$observations[XAUTOLOAD_VARNAME_CACHE_TYPES] = variable_get(XAUTOLOAD_VARNAME_CACHE_TYPES);
|
||||||
|
$observations[XAUTOLOAD_VARNAME_CACHE_LAZY] = variable_get(XAUTOLOAD_VARNAME_CACHE_LAZY);
|
||||||
|
|
||||||
|
$observations['db_connection_info'] = \Database::getConnectionInfo();
|
||||||
|
|
||||||
|
$spl_autoload_stack = array();
|
||||||
|
foreach (spl_autoload_functions() as $callback) {
|
||||||
|
$spl_autoload_stack[] = Util::callbackToString($callback);
|
||||||
|
}
|
||||||
|
$observations['spl_autoload_functions'] = $spl_autoload_stack;
|
||||||
|
|
||||||
|
return $observations;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,130 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\Tests;
|
||||||
|
|
||||||
|
use Drupal\xautoload\ClassFinder\ClassFinder;
|
||||||
|
use Drupal\xautoload\ClassFinder\InjectedApi\CollectFilesInjectedApi;
|
||||||
|
use Drupal\xautoload\ClassLoader\ClassLoaderInterface;
|
||||||
|
use Drupal\xautoload\Util;
|
||||||
|
|
||||||
|
class XAutoloadUnitTestCase extends \DrupalUnitTestCase {
|
||||||
|
|
||||||
|
static function getInfo() {
|
||||||
|
return array(
|
||||||
|
'name' => 'X Autoload unit test',
|
||||||
|
'description' => 'Test the xautoload class finder.',
|
||||||
|
'group' => 'X Autoload',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setUp() {
|
||||||
|
|
||||||
|
// drupal_load('module', 'xautoload') would register namespaces for all
|
||||||
|
// enabled modules, which is not intended for this unit test.
|
||||||
|
// Instead, we just include xautoload.early.inc.
|
||||||
|
require_once __DIR__ . '/../../../../xautoload.early.inc';
|
||||||
|
|
||||||
|
// Make sure we use the regular loader, not the APC one.
|
||||||
|
// Also make sure to prepend this one. Otherwise, the core class loader will
|
||||||
|
// try to load xautoload-related stuff, e.g. xautoload_Mock_* stuff, and
|
||||||
|
// will fail due to the database.
|
||||||
|
foreach (spl_autoload_functions() as $callback) {
|
||||||
|
if (is_array($callback)
|
||||||
|
&& ($loader = $callback[0])
|
||||||
|
&& $loader instanceof ClassLoaderInterface
|
||||||
|
) {
|
||||||
|
$loader->unregister();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xautoload()->finder->register(TRUE);
|
||||||
|
|
||||||
|
// Do the regular setUp().
|
||||||
|
parent::setUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
function testAutoloadStackOrder() {
|
||||||
|
$expected = array(
|
||||||
|
'Drupal\\xautoload\\ClassFinder\\ClassFinder->loadClass()',
|
||||||
|
/* @see _drupal_bootstrap_database() */
|
||||||
|
'drupal_autoload_class',
|
||||||
|
'drupal_autoload_interface',
|
||||||
|
/* @see simpletest_classloader_register() */
|
||||||
|
'_simpletest_autoload_psr4_psr0',
|
||||||
|
);
|
||||||
|
|
||||||
|
$actual = array();
|
||||||
|
foreach (spl_autoload_functions() as $callback) {
|
||||||
|
$actual[] = Util::callbackToString($callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->assertEqualBlock($expected, $actual, "SPL autoload stack:");
|
||||||
|
}
|
||||||
|
|
||||||
|
function testNamespaces() {
|
||||||
|
|
||||||
|
// Prepare the class finder.
|
||||||
|
$finder = new ClassFinder();
|
||||||
|
$finder->add('Drupal\\ex_ample', 'sites/all/modules/contrib/ex_ample/lib-psr0');
|
||||||
|
$finder->addPsr4('Drupal\\ex_ample', 'sites/all/modules/contrib/ex_ample/lib-psr4');
|
||||||
|
|
||||||
|
// Test class finding for 'Drupal\\ex_ample\\Abc_Def'.
|
||||||
|
$this->assertFinderSuggestions($finder, 'Drupal\\ex_ample\\Abc_Def', array(
|
||||||
|
// Class finder is expected to suggest these files, in the exact order,
|
||||||
|
// until one of them is accepted.
|
||||||
|
array('suggestFile', 'sites/all/modules/contrib/ex_ample/lib-psr0/Drupal/ex_ample/Abc/Def.php'),
|
||||||
|
array('suggestFile', 'sites/all/modules/contrib/ex_ample/lib-psr4/Abc_Def.php'),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
function testPrefixes() {
|
||||||
|
|
||||||
|
// Prepare the class finder.
|
||||||
|
$finder = new ClassFinder();
|
||||||
|
$finder->registerPrefixDeep('ex_ample', 'sites/all/modules/contrib/ex_ample/lib');
|
||||||
|
$finder->registerPrefixRoot('ex_ample', 'sites/all/modules/contrib/ex_ample/vendor');
|
||||||
|
|
||||||
|
// Test class finding for 'ex_ample_Abc_Def'.
|
||||||
|
$this->assertFinderSuggestions($finder, 'ex_ample_Abc_Def', array(
|
||||||
|
// Class finder is expected to suggest these files, in the exact order,
|
||||||
|
// until one of them is accepted.
|
||||||
|
array('suggestFile', 'sites/all/modules/contrib/ex_ample/lib/Abc/Def.php'),
|
||||||
|
array('suggestFile', 'sites/all/modules/contrib/ex_ample/vendor/ex/ample/Abc/Def.php'),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ClassFinder $finder
|
||||||
|
* @param string $class
|
||||||
|
* @param array $expectedSuggestions
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* Result of the assertion
|
||||||
|
*/
|
||||||
|
protected function assertFinderSuggestions($finder, $class, array $expectedSuggestions) {
|
||||||
|
$success = TRUE;
|
||||||
|
for ($iAccept = 0; $iAccept < count($expectedSuggestions); ++$iAccept) {
|
||||||
|
list($method_name, $file) = $expectedSuggestions[$iAccept];
|
||||||
|
$api = new CollectFilesInjectedApi($class, $method_name, $file);
|
||||||
|
$finder->apiFindFile($api, $class);
|
||||||
|
$suggestions = $api->getSuggestions();
|
||||||
|
$expected = array_slice($expectedSuggestions, 0, $iAccept + 1);
|
||||||
|
$success = $success && $this->assertEqualBlock($expected, $suggestions, "Finder suggestions for class <code>$class</code>:");
|
||||||
|
}
|
||||||
|
return $success;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $expected
|
||||||
|
* @param mixed $actual
|
||||||
|
* @param string $label
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* Result of the assertion
|
||||||
|
*/
|
||||||
|
protected function assertEqualBlock($expected, $actual, $label) {
|
||||||
|
$label .= '<br/>' .
|
||||||
|
'Expected: <pre>' . var_export($expected, TRUE) . '</pre>' .
|
||||||
|
'Actual: <pre>' . var_export($actual, TRUE) . '</pre>';
|
||||||
|
return $this->assertEqual($expected, $actual, $label);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,264 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\Tests;
|
||||||
|
|
||||||
|
class XAutoloadWebTestCase extends \DrupalWebTestCase {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
static function getInfo() {
|
||||||
|
return array(
|
||||||
|
'name' => 'X Autoload web test',
|
||||||
|
'description' => 'Test xautoload class loading for an example module.',
|
||||||
|
'group' => 'X Autoload',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function setUp() {
|
||||||
|
parent::setUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function testNoCache() {
|
||||||
|
$this->xautoloadTestWithCacheTypes(array(), TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function testApcCache() {
|
||||||
|
$cache_types = array(
|
||||||
|
'apc' => 'apc',
|
||||||
|
'xcache' => 'xcache',
|
||||||
|
'wincache' => 'wincache',
|
||||||
|
);
|
||||||
|
$this->xautoloadTestWithCacheTypes($cache_types, TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $cache_types
|
||||||
|
* The autoloader modes that are enabled, e.g.
|
||||||
|
* array('apc' => 'apc', 'xcache' => 'xcache')
|
||||||
|
* @param bool $cache_lazy
|
||||||
|
* Whether the "lazy" mode is enabled.
|
||||||
|
*/
|
||||||
|
protected function xautoloadTestWithCacheTypes($cache_types, $cache_lazy) {
|
||||||
|
|
||||||
|
variable_set(XAUTOLOAD_VARNAME_CACHE_TYPES, $cache_types);
|
||||||
|
$this->pass("Set cache types: " . var_export($cache_types, TRUE));
|
||||||
|
|
||||||
|
variable_set(XAUTOLOAD_VARNAME_CACHE_LAZY, $cache_lazy);
|
||||||
|
$this->pass("Set cache lazy mode: " . var_export($cache_lazy, TRUE));
|
||||||
|
|
||||||
|
// Enable xautoload.
|
||||||
|
module_enable(array('xautoload'), FALSE);
|
||||||
|
|
||||||
|
// At this time the xautoload_cache_mode setting is not in effect yet,
|
||||||
|
// so we have to clear old cached values from APC cache.
|
||||||
|
xautoload()->cacheManager->renewCachePrefix();
|
||||||
|
|
||||||
|
module_enable(array(
|
||||||
|
'xautoload_test_1',
|
||||||
|
'xautoload_test_2',
|
||||||
|
'xautoload_test_3',
|
||||||
|
'xautoload_test_4',
|
||||||
|
'xautoload_test_5',
|
||||||
|
), FALSE);
|
||||||
|
menu_rebuild();
|
||||||
|
|
||||||
|
foreach (array(
|
||||||
|
'xautoload_test_1' => array('Drupal\xautoload_test_1\ExampleClass'),
|
||||||
|
'xautoload_test_2' => array('xautoload_test_2_ExampleClass'),
|
||||||
|
'xautoload_test_3' => array('Drupal\xautoload_test_3\ExampleClass'),
|
||||||
|
) as $module => $classes) {
|
||||||
|
$classes_on_include = in_array($module, array('xautoload_test_2', 'xautoload_test_3'));
|
||||||
|
$this->xautoloadModuleEnabled($module, $classes, $classes_on_include);
|
||||||
|
$this->xautoloadModuleCheckJson($module, $cache_types, $cache_lazy, $classes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $module
|
||||||
|
* @param string[] $classes
|
||||||
|
* @param bool $classes_on_include
|
||||||
|
*/
|
||||||
|
protected function xautoloadModuleEnabled($module, $classes, $classes_on_include) {
|
||||||
|
|
||||||
|
EnvironmentSnapshotMaker::takeSnapshot($module, 'later', $classes);
|
||||||
|
|
||||||
|
$all = EnvironmentSnapshotMaker::getSnapshots($module);
|
||||||
|
|
||||||
|
foreach ($all as $phase => $observations) {
|
||||||
|
$when = ($phase === 'early')
|
||||||
|
? 'on drupal_load() during module_enable()'
|
||||||
|
: (($phase === 'later')
|
||||||
|
? 'after hook_modules_enabled()'
|
||||||
|
: 'at an undefined time'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Test the classes of the example module.
|
||||||
|
foreach ($classes as $class) {
|
||||||
|
// Test that the class was already found in $phase.
|
||||||
|
$this->assertTrue(isset($observations['class_exists'][$class]), "Class $class was checked $when.");
|
||||||
|
if ($classes_on_include || $phase !== 'early') {
|
||||||
|
$this->assertTrue($observations['class_exists'][$class], "Class $class was found $when.");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$this->assertFalse($observations['class_exists'][$class], "Class $class cannot be found $when.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $module
|
||||||
|
* @param array $cache_types
|
||||||
|
* The autoloader modes that are enabled, e.g.
|
||||||
|
* array('apc' => 'apc', 'xcache' => 'xcache')
|
||||||
|
* @param bool $cache_lazy
|
||||||
|
* Whether the "lazy" mode is enabled.
|
||||||
|
* @param string[] $classes
|
||||||
|
*/
|
||||||
|
protected function xautoloadModuleCheckJson($module, $cache_types, $cache_lazy, $classes) {
|
||||||
|
|
||||||
|
$path = "$module.json";
|
||||||
|
$json = $this->drupalGet($path);
|
||||||
|
$all = json_decode($json, TRUE);
|
||||||
|
|
||||||
|
if (!is_array($all) || empty($all)) {
|
||||||
|
$this->fail("$path must return a non-empty json array.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($all as $phase => $observations) {
|
||||||
|
|
||||||
|
$when = ($phase === 'early')
|
||||||
|
? 'on early bootstrap'
|
||||||
|
: (($phase === 'boot')
|
||||||
|
? 'during hook_boot()'
|
||||||
|
: 'at an undefined time'
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->xautoloadCheckTestEnvironment($observations, $cache_types, $cache_lazy, $when);
|
||||||
|
|
||||||
|
// Test the classes of the example module.
|
||||||
|
foreach ($classes as $class) {
|
||||||
|
// Test that the class was already found in $phase.
|
||||||
|
$this->assertTrue($observations['class_exists'][$class], "Class $class was found $when.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $observations
|
||||||
|
* @param array $cache_types
|
||||||
|
* The autoloader modes that are enabled, e.g.
|
||||||
|
* array('apc' => 'apc', 'xcache' => 'xcache')
|
||||||
|
* @param bool $lazy
|
||||||
|
* Whether the "lazy" mode is enabled.
|
||||||
|
* @param $when
|
||||||
|
*/
|
||||||
|
protected function xautoloadCheckTestEnvironment($observations, $cache_types, $lazy, $when) {
|
||||||
|
|
||||||
|
// Check early-bootstrap variables.
|
||||||
|
$label = "$when: xautoload_cache_types:";
|
||||||
|
$this->assertEqualBlock($cache_types, $observations[XAUTOLOAD_VARNAME_CACHE_TYPES], $label);
|
||||||
|
|
||||||
|
$label = "$when: xautoload_cache_lazy:";
|
||||||
|
$this->assertEqualInline($lazy, $observations[XAUTOLOAD_VARNAME_CACHE_LAZY], $label);
|
||||||
|
|
||||||
|
// Check registered class loaders.
|
||||||
|
$expected = $this->expectedAutoloadStackOrder($cache_types);
|
||||||
|
$actual = $observations['spl_autoload_functions'];
|
||||||
|
$label = "$when: spl autoload stack:";
|
||||||
|
$this->assertEqualBlock($expected, $actual, $label);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $cache_types
|
||||||
|
* The autoloader modes that are enabled, e.g.
|
||||||
|
* array('apc' => 'apc', 'xcache' => 'xcache')
|
||||||
|
*
|
||||||
|
* @return string[]
|
||||||
|
* Expected order of class loaders on the spl autoload stack for the given
|
||||||
|
* autoloader mode. Each represented by a string.
|
||||||
|
*/
|
||||||
|
protected function expectedAutoloadStackOrder($cache_types) {
|
||||||
|
|
||||||
|
if (!empty($cache_types['apc']) && extension_loaded('apc') && function_exists('apc_store')) {
|
||||||
|
$loader = 'Drupal\xautoload\ClassLoader\ApcClassLoader->loadClass()';
|
||||||
|
}
|
||||||
|
elseif (!empty($cache_types['wincache']) && extension_loaded('wincache') && function_exists('wincache_ucache_get')) {
|
||||||
|
$loader = 'Drupal\xautoload\ClassLoader\WinCacheClassLoader->loadClass()';
|
||||||
|
}
|
||||||
|
elseif (!empty($cache_types['xcache']) && extension_loaded('Xcache') && function_exists('xcache_get')) {
|
||||||
|
$loader = 'Drupal\xautoload\ClassLoader\XCacheClassLoader->loadClass()';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$loader = 'Drupal\xautoload\ClassFinder\ClassFinder->loadClass()';
|
||||||
|
}
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'drupal_autoload_class',
|
||||||
|
'drupal_autoload_interface',
|
||||||
|
$loader,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assert that a module is disabled.
|
||||||
|
*
|
||||||
|
* @param string $module
|
||||||
|
*/
|
||||||
|
protected function assertModuleDisabled($module) {
|
||||||
|
$this->assertFalse(module_exists($module), "Module $module is disabled.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assert that a module is enabled.
|
||||||
|
*
|
||||||
|
* @param string $module
|
||||||
|
*/
|
||||||
|
protected function assertModuleEnabled($module) {
|
||||||
|
$this->assertTrue(module_exists($module), "Module $module is enabled.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assert that a class is defined.
|
||||||
|
*
|
||||||
|
* @param string $class
|
||||||
|
*/
|
||||||
|
protected function assertClassExists($class) {
|
||||||
|
$this->assertTrue(class_exists($class), "Class '$class' must exist.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $expected
|
||||||
|
* @param mixed $actual
|
||||||
|
* @param string $label
|
||||||
|
*/
|
||||||
|
protected function assertEqualBlock($expected, $actual, $label) {
|
||||||
|
$label .=
|
||||||
|
'Expected: <pre>' . var_export($expected, TRUE) . '</pre>' .
|
||||||
|
'Actual: <pre>' . var_export($actual, TRUE) . '</pre>';
|
||||||
|
$this->assertEqual($expected, $actual, $label);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $expected
|
||||||
|
* @param mixed $actual
|
||||||
|
* @param string $label
|
||||||
|
*/
|
||||||
|
protected function assertEqualInline($expected, $actual, $label) {
|
||||||
|
$label .= '<br/>' .
|
||||||
|
'Expected: <code>' . var_export($expected, TRUE) . '</code><br/>' .
|
||||||
|
'Actual: <code>' . var_export($actual, TRUE) . '</code>';
|
||||||
|
$this->assertEqual($expected, $actual, $label);
|
||||||
|
}
|
||||||
|
}
|
19
frontend/drupal/modules/xautoload/phpunit.xml.dist
Normal file
19
frontend/drupal/modules/xautoload/phpunit.xml.dist
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<phpunit backupGlobals="false"
|
||||||
|
backupStaticAttributes="false"
|
||||||
|
colors="true"
|
||||||
|
convertErrorsToExceptions="true"
|
||||||
|
convertNoticesToExceptions="true"
|
||||||
|
convertWarningsToExceptions="true"
|
||||||
|
processIsolation="false"
|
||||||
|
stopOnFailure="false"
|
||||||
|
syntaxCheck="false"
|
||||||
|
bootstrap="tests/bootstrap.php"
|
||||||
|
>
|
||||||
|
<testsuites>
|
||||||
|
<testsuite name="XAutoload Test Suite">
|
||||||
|
<directory>./tests/src</directory>
|
||||||
|
</testsuite>
|
||||||
|
</testsuites>
|
||||||
|
</phpunit>
|
@ -0,0 +1,287 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\Adapter;
|
||||||
|
|
||||||
|
use Drupal\xautoload\Discovery\ClassMapGenerator;
|
||||||
|
use Drupal\xautoload\Util;
|
||||||
|
use Drupal\xautoload\DirectoryBehavior\DefaultDirectoryBehavior;
|
||||||
|
use Drupal\xautoload\Discovery\ComposerDir;
|
||||||
|
use Drupal\xautoload\Discovery\ComposerJson;
|
||||||
|
use Drupal\xautoload\ClassFinder\GenericPrefixMap;
|
||||||
|
use Drupal\xautoload\DirectoryBehavior\Psr0DirectoryBehavior;
|
||||||
|
use Drupal\xautoload\ClassFinder\ExtendedClassFinderInterface;
|
||||||
|
use Drupal\xautoload\Discovery\ClassMapGeneratorInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An instance of this class is passed around to implementations of
|
||||||
|
* hook_xautoload(). It acts as a wrapper around the ClassFinder, to register
|
||||||
|
* stuff.
|
||||||
|
*/
|
||||||
|
class ClassFinderAdapter implements ClassFinderAdapterInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ExtendedClassFinderInterface
|
||||||
|
*/
|
||||||
|
protected $finder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var GenericPrefixMap
|
||||||
|
*/
|
||||||
|
protected $prefixMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var GenericPrefixMap
|
||||||
|
*/
|
||||||
|
protected $namespaceMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ClassMapGeneratorInterface
|
||||||
|
*/
|
||||||
|
protected $classMapGenerator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ExtendedClassFinderInterface $finder
|
||||||
|
*
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
static function create($finder) {
|
||||||
|
return new self($finder, new ClassMapGenerator());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ExtendedClassFinderInterface $finder
|
||||||
|
* The class finder object.
|
||||||
|
* @param ClassMapGeneratorInterface $classmap_generator
|
||||||
|
*/
|
||||||
|
function __construct($finder, $classmap_generator) {
|
||||||
|
$this->finder = $finder;
|
||||||
|
$this->prefixMap = $finder->getPrefixMap();
|
||||||
|
$this->namespaceMap = $finder->getNamespaceMap();
|
||||||
|
$this->defaultBehavior = new DefaultDirectoryBehavior();
|
||||||
|
$this->psr0Behavior = new Psr0DirectoryBehavior();
|
||||||
|
$this->classMapGenerator = $classmap_generator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \Drupal\xautoload\ClassFinder\GenericPrefixMap
|
||||||
|
*/
|
||||||
|
function getNamespaceMap() {
|
||||||
|
return $this->namespaceMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return GenericPrefixMap
|
||||||
|
*/
|
||||||
|
function getPrefixMap() {
|
||||||
|
return $this->prefixMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return ClassMapGeneratorInterface
|
||||||
|
*/
|
||||||
|
function getClassmapGenerator() {
|
||||||
|
return $this->classMapGenerator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return ClassMapGeneratorInterface
|
||||||
|
*/
|
||||||
|
function getFinder() {
|
||||||
|
return $this->finder;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Discovery
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function addClassmapSources($paths) {
|
||||||
|
$map = $this->classMapGenerator->wildcardPathsToClassmap($paths);
|
||||||
|
$this->addClassMap($map);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Composer tools
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function composerJson($file) {
|
||||||
|
$json = ComposerJson::createFromFile($file);
|
||||||
|
$json->writeToAdapter($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function composerDir($dir) {
|
||||||
|
$dir = ComposerDir::create($dir);
|
||||||
|
$dir->writeToAdapter($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// multiple PSR-0 / PSR-4
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function addMultiplePsr0(array $prefixes) {
|
||||||
|
$namespace_map = array();
|
||||||
|
$prefix_map = array();
|
||||||
|
foreach ($prefixes as $prefix => $paths) {
|
||||||
|
if (FALSE === strpos($prefix, '\\')) {
|
||||||
|
$logical_base_path = Util::prefixLogicalPath($prefix);
|
||||||
|
foreach ((array) $paths as $root_path) {
|
||||||
|
$deep_path = strlen($root_path)
|
||||||
|
? rtrim($root_path, '/') . '/' . $logical_base_path
|
||||||
|
: $logical_base_path;
|
||||||
|
$prefix_map[$logical_base_path][$deep_path] = $this->defaultBehavior;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$logical_base_path = Util::namespaceLogicalPath($prefix);
|
||||||
|
foreach ((array) $paths as $root_path) {
|
||||||
|
$deep_path = strlen($root_path)
|
||||||
|
? rtrim($root_path, '/') . '/' . $logical_base_path
|
||||||
|
: $logical_base_path;
|
||||||
|
$namespace_map[$logical_base_path][$deep_path] = $this->psr0Behavior;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!empty($prefix_map)) {
|
||||||
|
$this->prefixMap->registerDeepPaths($prefix_map);
|
||||||
|
}
|
||||||
|
$this->namespaceMap->registerDeepPaths($namespace_map);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function addMultiplePsr4(array $map) {
|
||||||
|
$namespace_map = array();
|
||||||
|
foreach ($map as $namespace => $paths) {
|
||||||
|
$logical_base_path = Util::namespaceLogicalPath($namespace);
|
||||||
|
foreach ($paths as $root_path) {
|
||||||
|
$deep_path = strlen($root_path)
|
||||||
|
? rtrim($root_path, '/') . '/'
|
||||||
|
: '';
|
||||||
|
$namespace_map[$logical_base_path][$deep_path] = $this->defaultBehavior;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->namespaceMap->registerDeepPaths($namespace_map);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Composer ClassLoader
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function addClassMap(array $classMap) {
|
||||||
|
$this->finder->registerClasses($classMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function add($prefix, $paths) {
|
||||||
|
if (FALSE === strpos($prefix, '\\')) {
|
||||||
|
// Due to the ambiguity of PSR-0, this could be either PEAR-like or namespaced.
|
||||||
|
$logical_base_path = Util::prefixLogicalPath($prefix);
|
||||||
|
foreach ((array) $paths as $root_path) {
|
||||||
|
$deep_path = strlen($root_path)
|
||||||
|
? rtrim($root_path, '/') . '/' . $logical_base_path
|
||||||
|
: $logical_base_path;
|
||||||
|
$this->prefixMap->registerDeepPath(
|
||||||
|
$logical_base_path,
|
||||||
|
$deep_path,
|
||||||
|
$this->defaultBehavior);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Namespaced PSR-0
|
||||||
|
$logical_base_path = Util::namespaceLogicalPath($prefix);
|
||||||
|
foreach ((array) $paths as $root_path) {
|
||||||
|
$deep_path = strlen($root_path)
|
||||||
|
? rtrim($root_path, '/') . '/' . $logical_base_path
|
||||||
|
: $logical_base_path;
|
||||||
|
$this->namespaceMap->registerDeepPath(
|
||||||
|
$logical_base_path,
|
||||||
|
$deep_path,
|
||||||
|
$this->psr0Behavior);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function addPsr0($prefix, $paths) {
|
||||||
|
$this->add($prefix, $paths);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function addPsr4($prefix, $paths) {
|
||||||
|
// Namespaced PSR-4
|
||||||
|
$logical_base_path = Util::namespaceLogicalPath($prefix);
|
||||||
|
foreach ((array) $paths as $deep_path) {
|
||||||
|
$deep_path = strlen($deep_path)
|
||||||
|
? rtrim($deep_path, '/') . '/'
|
||||||
|
: '';
|
||||||
|
$this->namespaceMap->registerDeepPath(
|
||||||
|
$logical_base_path,
|
||||||
|
$deep_path,
|
||||||
|
$this->defaultBehavior);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// More convenience stuff
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function addNamespacePsr0($prefix, $paths) {
|
||||||
|
$logical_base_path = Util::namespaceLogicalPath($prefix);
|
||||||
|
foreach ((array) $paths as $root_path) {
|
||||||
|
$deep_path = strlen($root_path)
|
||||||
|
? rtrim($root_path, '/') . '/' . $logical_base_path
|
||||||
|
: $logical_base_path;
|
||||||
|
$this->namespaceMap->registerDeepPath(
|
||||||
|
$logical_base_path,
|
||||||
|
$deep_path,
|
||||||
|
$this->psr0Behavior);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function addPear($prefix, $paths) {
|
||||||
|
$logical_base_path = Util::prefixLogicalPath($prefix);
|
||||||
|
foreach ((array) $paths as $root_path) {
|
||||||
|
$deep_path = strlen($root_path)
|
||||||
|
? rtrim($root_path, '/') . '/' . $logical_base_path
|
||||||
|
: $logical_base_path;
|
||||||
|
$this->prefixMap->registerDeepPath(
|
||||||
|
$logical_base_path,
|
||||||
|
$deep_path,
|
||||||
|
$this->defaultBehavior);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function addPearFlat($prefix, $paths) {
|
||||||
|
$logical_base_path = Util::prefixLogicalPath($prefix);
|
||||||
|
foreach ((array) $paths as $deep_path) {
|
||||||
|
$deep_path = strlen($deep_path) ? (rtrim($deep_path, '/') . '/') : '';
|
||||||
|
$this->prefixMap->registerDeepPath(
|
||||||
|
$logical_base_path,
|
||||||
|
$deep_path,
|
||||||
|
$this->defaultBehavior
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\Adapter;
|
||||||
|
|
||||||
|
use Drupal\xautoload\ClassFinder\CommonRegistrationInterface;
|
||||||
|
|
||||||
|
interface ClassFinderAdapterInterface extends CommonRegistrationInterface {
|
||||||
|
|
||||||
|
// Discovery
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string[] $paths
|
||||||
|
* File paths or wildcard paths for class discovery.
|
||||||
|
*/
|
||||||
|
function addClassmapSources($paths);
|
||||||
|
|
||||||
|
// Composer tools
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scan a composer.json file provided by a Composer package.
|
||||||
|
*
|
||||||
|
* @param string $file
|
||||||
|
*
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
function composerJson($file);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scan a directory containing Composer-generated autoload files.
|
||||||
|
*
|
||||||
|
* @param string $dir
|
||||||
|
* Directory to look for Composer-generated files. Typically this is the
|
||||||
|
* ../vendor/composer dir.
|
||||||
|
*/
|
||||||
|
function composerDir($dir);
|
||||||
|
|
||||||
|
// multiple PSR-0 / PSR-4
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add multiple PSR-0 namespaces
|
||||||
|
*
|
||||||
|
* @param array $prefixes
|
||||||
|
*/
|
||||||
|
function addMultiplePsr0(array $prefixes);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add multiple PSR-4 namespaces
|
||||||
|
*
|
||||||
|
* @param array $map
|
||||||
|
*/
|
||||||
|
function addMultiplePsr4(array $map);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,174 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\Adapter;
|
||||||
|
|
||||||
|
use Drupal\xautoload\DirectoryBehavior\DefaultDirectoryBehavior;
|
||||||
|
use Drupal\xautoload\ClassFinder\ExtendedClassFinderInterface;
|
||||||
|
use Drupal\xautoload\ClassFinder\Plugin\DrupalExtensionNamespaceFinderPlugin;
|
||||||
|
use Drupal\xautoload\ClassFinder\Plugin\DrupalExtensionUnderscoreFinderPlugin;
|
||||||
|
use Drupal\xautoload\DrupalSystem\DrupalSystemInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service that knows how to register module namespaces and prefixes into the
|
||||||
|
* class loader, and that remembers which modules have already been registered.
|
||||||
|
*/
|
||||||
|
class DrupalExtensionAdapter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Drupal\xautoload\DrupalSystem\DrupalSystemInterface
|
||||||
|
*/
|
||||||
|
protected $system;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ExtendedClassFinderInterface
|
||||||
|
*/
|
||||||
|
protected $finder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* One finder plugin for each extension type ('module', 'theme').
|
||||||
|
*
|
||||||
|
* @var DrupalExtensionNamespaceFinderPlugin[]
|
||||||
|
*/
|
||||||
|
protected $namespaceBehaviors = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* One finder plugin for each extension type ('module', 'theme').
|
||||||
|
*
|
||||||
|
* @var DrupalExtensionUnderscoreFinderPlugin[]
|
||||||
|
*/
|
||||||
|
protected $prefixBehaviors = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The namespace map used in the class loader.
|
||||||
|
*
|
||||||
|
* @var \Drupal\xautoload\ClassFinder\GenericPrefixMap
|
||||||
|
*/
|
||||||
|
protected $namespaceMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The prefix map used in the class loader.
|
||||||
|
*
|
||||||
|
* @var \Drupal\xautoload\ClassFinder\GenericPrefixMap
|
||||||
|
*/
|
||||||
|
protected $prefixMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Which modules have already been processed.
|
||||||
|
*
|
||||||
|
* @var bool[]
|
||||||
|
*/
|
||||||
|
protected $registered = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Directory behavior for PSR-4
|
||||||
|
*
|
||||||
|
* @var DefaultDirectoryBehavior
|
||||||
|
*/
|
||||||
|
protected $defaultBehavior;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param DrupalSystemInterface $system
|
||||||
|
* @param ExtendedClassFinderInterface $finder
|
||||||
|
*/
|
||||||
|
function __construct(DrupalSystemInterface $system, ExtendedClassFinderInterface $finder) {
|
||||||
|
$this->system = $system;
|
||||||
|
$this->finder = $finder;
|
||||||
|
$this->namespaceMap = $finder->getNamespaceMap();
|
||||||
|
$this->prefixMap = $finder->getPrefixMap();
|
||||||
|
foreach (array('module', 'theme') as $extension_type) {
|
||||||
|
$this->namespaceBehaviors[$extension_type] = new DrupalExtensionNamespaceFinderPlugin(
|
||||||
|
$extension_type,
|
||||||
|
$this->namespaceMap,
|
||||||
|
$this->prefixMap,
|
||||||
|
$this->system);
|
||||||
|
$this->prefixBehaviors[$extension_type] = new DrupalExtensionUnderscoreFinderPlugin(
|
||||||
|
$extension_type,
|
||||||
|
$this->namespaceMap,
|
||||||
|
$this->prefixMap,
|
||||||
|
$this->system);
|
||||||
|
}
|
||||||
|
$this->defaultBehavior = new DefaultDirectoryBehavior();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register lazy plugins for enabled Drupal modules and themes, assuming that
|
||||||
|
* we don't know yet whether they use PSR-0, PEAR-Flat, or none of these.
|
||||||
|
*
|
||||||
|
* @param string[] $extensions
|
||||||
|
* An array where the keys are extension names, and the values are extension
|
||||||
|
* types like 'module' or 'theme'.
|
||||||
|
*/
|
||||||
|
function registerExtensions(array $extensions) {
|
||||||
|
|
||||||
|
$prefix_map = array();
|
||||||
|
$namespace_map = array();
|
||||||
|
foreach ($extensions as $name => $type) {
|
||||||
|
if (empty($this->namespaceBehaviors[$type])) {
|
||||||
|
// Unsupported extension type, e.g. "theme_engine".
|
||||||
|
// This can happen if a site was upgraded from Drupal 6.
|
||||||
|
// See https://drupal.org/comment/8503979#comment-8503979
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!empty($this->registered[$name])) {
|
||||||
|
// The extension has already been processed.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$namespace_map['Drupal/' . $name . '/'][$name] = $this->namespaceBehaviors[$type];
|
||||||
|
$prefix_map[str_replace('_', '/', $name) . '/'][$name] = $this->prefixBehaviors[$type];
|
||||||
|
$this->registered[$name] = TRUE;
|
||||||
|
}
|
||||||
|
$this->namespaceMap->registerDeepPaths($namespace_map);
|
||||||
|
$this->prefixMap->registerDeepPaths($prefix_map);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register lazy plugins for a given extension, assuming that we don't know
|
||||||
|
* yet whether it uses PSR-0, PEAR-Flat, or none of these.
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @param string $type
|
||||||
|
*/
|
||||||
|
function registerExtension($name, $type) {
|
||||||
|
if (!empty($this->registered[$name])) {
|
||||||
|
// The extension has already been processed.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$this->namespaceMap->registerDeepPath('Drupal/' . $name . '/', $name, $this->namespaceBehaviors[$type]);
|
||||||
|
$this->prefixMap->registerDeepPath(str_replace('_', '/', $name) . '/', $name, $this->prefixBehaviors[$type]);
|
||||||
|
$this->registered[$name] = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register PSR-4 directory for an extension.
|
||||||
|
* Override previous settings for this extension.
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* The extension name.
|
||||||
|
* @param string $extension_dir
|
||||||
|
* The directory of the extension.
|
||||||
|
* @param string $subdir
|
||||||
|
* The PSR-4 base directory, relative to the extension directory.
|
||||||
|
* E.g. 'lib' or 'src'.
|
||||||
|
*/
|
||||||
|
function registerExtensionPsr4($name, $extension_dir, $subdir) {
|
||||||
|
if (!empty($this->registered[$name])) {
|
||||||
|
if ('psr-4' === $this->registered[$name]) {
|
||||||
|
// It already happened.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Unregister the lazy plugins.
|
||||||
|
$this->namespaceMap->unregisterDeepPath('Drupal/' . $name . '/', $name);
|
||||||
|
$this->prefixMap->unregisterDeepPath(str_replace('_', '/', $name) . '/', $name);
|
||||||
|
}
|
||||||
|
|
||||||
|
$dir = strlen($subdir)
|
||||||
|
? $extension_dir . '/' . trim($subdir, '/') . '/'
|
||||||
|
: $extension_dir . '/';
|
||||||
|
$this->namespaceMap->registerDeepPath('Drupal/' . $name . '/', $dir, $this->defaultBehavior);
|
||||||
|
|
||||||
|
// Re-add the PSR-0 test directory, for consistency's sake.
|
||||||
|
if (is_dir($psr0_tests_dir = $extension_dir . '/lib/Drupal/' . $name . '/Tests')) {
|
||||||
|
$this->namespaceMap->registerDeepPath('Drupal/' . $name . '/Tests/', $psr0_tests_dir, $this->defaultBehavior);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,278 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\Adapter;
|
||||||
|
|
||||||
|
use Drupal\xautoload\Discovery\ComposerDir;
|
||||||
|
use Drupal\xautoload\Discovery\ComposerJson;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An instance of this class is passed around to implementations of
|
||||||
|
* hook_xautoload(). It acts as a wrapper around the ClassFinder, to register
|
||||||
|
* stuff.
|
||||||
|
*/
|
||||||
|
class LocalDirectoryAdapter implements ClassFinderAdapterInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $localDirectory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ClassFinderAdapter
|
||||||
|
*/
|
||||||
|
protected $master;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ClassFinderAdapter $adapter
|
||||||
|
* The class finder object.
|
||||||
|
* @param string $localDirectory
|
||||||
|
*/
|
||||||
|
function __construct(ClassFinderAdapter $adapter, $localDirectory) {
|
||||||
|
// parent::__construct($adapter->finder, $adapter->getClassmapGenerator());
|
||||||
|
$this->master = $adapter;
|
||||||
|
$this->localDirectory = strlen($localDirectory)
|
||||||
|
? rtrim($localDirectory, '/') . '/'
|
||||||
|
: '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an adapter object that is not relative to a local directory.
|
||||||
|
*
|
||||||
|
* @return ClassFinderAdapter
|
||||||
|
*/
|
||||||
|
function absolute() {
|
||||||
|
return $this->master;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Discovery
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds source paths for classmap discovery.
|
||||||
|
*
|
||||||
|
* The classmap for each source will be cached between requests.
|
||||||
|
* A "clear all caches" will trigger a rescan.
|
||||||
|
*
|
||||||
|
* @param string[] $paths
|
||||||
|
* File paths or wildcard paths for class discovery.
|
||||||
|
* @param bool $relative
|
||||||
|
* If TRUE, the paths will be relative to $this->localDirectory.
|
||||||
|
*/
|
||||||
|
function addClassmapSources($paths, $relative = TRUE) {
|
||||||
|
$relative && $this->prependToPaths($paths);
|
||||||
|
$this->master->addClassmapSources($paths);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Composer tools
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scans a composer.json file provided by a Composer package.
|
||||||
|
*
|
||||||
|
* @param string $file
|
||||||
|
* @param bool $relative
|
||||||
|
* If TRUE, the paths will be relative to $this->localDirectory.
|
||||||
|
*
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
function composerJson($file, $relative = TRUE) {
|
||||||
|
$relative && $file = $this->localDirectory . $file;
|
||||||
|
$json = ComposerJson::createFromFile($file);
|
||||||
|
$json->writeToAdapter($this->master);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scans a directory containing Composer-generated autoload files.
|
||||||
|
*
|
||||||
|
* @param string $dir
|
||||||
|
* Directory to look for Composer-generated files. Typically this is the
|
||||||
|
* ../vendor/composer dir.
|
||||||
|
* @param bool $relative
|
||||||
|
* If TRUE, the paths will be relative to $this->localDirectory.
|
||||||
|
*/
|
||||||
|
function composerDir($dir, $relative = TRUE) {
|
||||||
|
$relative && $dir = $this->localDirectory . $dir;
|
||||||
|
$dir = ComposerDir::create($dir);
|
||||||
|
$dir->writeToAdapter($this->master);
|
||||||
|
}
|
||||||
|
|
||||||
|
// multiple PSR-0 / PSR-4
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds multiple PSR-0 prefixes.
|
||||||
|
*
|
||||||
|
* @param array $prefixes
|
||||||
|
* Each array key is a PSR-0 prefix, e.g. "Acme\\FooPackage\\".
|
||||||
|
* Each array value is either a PSR-0 base directory or an array of PSR-0
|
||||||
|
* base directories.
|
||||||
|
* @param bool $relative
|
||||||
|
* If TRUE, the paths will be relative to $this->localDirectory.
|
||||||
|
*/
|
||||||
|
function addMultiplePsr0(array $prefixes, $relative = TRUE) {
|
||||||
|
$relative && $this->prependMultiple($prefixes);
|
||||||
|
$this->master->addMultiplePsr0($prefixes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds multiple PSR-4 namespaces.
|
||||||
|
*
|
||||||
|
* @param array $map
|
||||||
|
* Each array key is a namespace, e.g. "Acme\\FooPackage\\".
|
||||||
|
* Each array value is either a PSR-4 base directory or an array of PSR-4
|
||||||
|
* base directories.
|
||||||
|
* @param bool $relative
|
||||||
|
* If TRUE, the paths will be relative to $this->localDirectory.
|
||||||
|
*/
|
||||||
|
function addMultiplePsr4(array $map, $relative = TRUE) {
|
||||||
|
$relative && $this->prependMultiple($map);
|
||||||
|
$this->master->addMultiplePsr4($map);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Composer ClassLoader
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers an array ("map") of classes to file paths.
|
||||||
|
*
|
||||||
|
* @param array $classMap
|
||||||
|
* The map of classes to file paths.
|
||||||
|
* @param bool $relative
|
||||||
|
* If TRUE, the paths will be relative to $this->localDirectory.
|
||||||
|
*/
|
||||||
|
function addClassMap(array $classMap, $relative = TRUE) {
|
||||||
|
$relative && $this->prependToPaths($classMap);
|
||||||
|
$this->master->addClassMap($classMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a PSR-0 style prefix. Alias for ->addPsr0().
|
||||||
|
*
|
||||||
|
* @param string $prefix
|
||||||
|
* @param string|\string[] $paths
|
||||||
|
* @param bool $relative
|
||||||
|
* If TRUE, the paths will be relative to $this->localDirectory.
|
||||||
|
*/
|
||||||
|
function add($prefix, $paths, $relative = TRUE) {
|
||||||
|
$relative && $this->prependToPaths($paths);
|
||||||
|
$this->master->add($prefix, $paths);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a PSR-0 style prefix. Alias for ->add().
|
||||||
|
*
|
||||||
|
* @param string $prefix
|
||||||
|
* @param string|\string[] $paths
|
||||||
|
* @param bool $relative
|
||||||
|
* If TRUE, the paths will be relative to $this->localDirectory.
|
||||||
|
*/
|
||||||
|
function addPsr0($prefix, $paths, $relative = TRUE) {
|
||||||
|
$relative && $this->prependToPaths($paths);
|
||||||
|
$this->master->add($prefix, $paths);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a PSR-4 style namespace.
|
||||||
|
*
|
||||||
|
* @param string $prefix
|
||||||
|
* @param string|\string[] $paths
|
||||||
|
* @param bool $relative
|
||||||
|
* If TRUE, the paths will be relative to $this->localDirectory.
|
||||||
|
*/
|
||||||
|
function addPsr4($prefix, $paths, $relative = TRUE) {
|
||||||
|
$relative && $this->prependToPaths($paths);
|
||||||
|
$this->master->addPsr4($prefix, $paths);
|
||||||
|
}
|
||||||
|
|
||||||
|
// More convenience stuff
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a PSR-0 style namespace.
|
||||||
|
*
|
||||||
|
* This will assume that we are really dealing with a namespace, even if it
|
||||||
|
* has no '\\' included.
|
||||||
|
*
|
||||||
|
* @param string $prefix
|
||||||
|
* @param string|\string[] $paths
|
||||||
|
* @param bool $relative
|
||||||
|
* If TRUE, the paths will be relative to $this->localDirectory.
|
||||||
|
*/
|
||||||
|
function addNamespacePsr0($prefix, $paths, $relative = TRUE) {
|
||||||
|
$relative && $this->prependToPaths($paths);
|
||||||
|
$this->master->addNamespacePsr0($prefix, $paths);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a PEAR-like prefix.
|
||||||
|
*
|
||||||
|
* This will assume with no further checks that $prefix contains no namespace
|
||||||
|
* separator.
|
||||||
|
*
|
||||||
|
* @param string $prefix
|
||||||
|
* The prefix, e.g. 'Acme_FooPackage_'
|
||||||
|
* @param string|string[] $paths
|
||||||
|
* An array of paths, or one specific path.
|
||||||
|
* E.g. 'lib' for $relative = TRUE,
|
||||||
|
* or 'sites/all/libraries/AcmeFooPackage/lib' for $relative = FALSE.
|
||||||
|
* @param bool $relative
|
||||||
|
* If TRUE, the paths will be relative to $this->localDirectory.
|
||||||
|
*/
|
||||||
|
function addPear($prefix, $paths, $relative = TRUE) {
|
||||||
|
$relative && $this->prependToPaths($paths);
|
||||||
|
$this->master->addPear($prefix, $paths);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a prefix similar to PEAR, but with flat directories.
|
||||||
|
*
|
||||||
|
* This will assume with no further checks that $prefix contains no namespace
|
||||||
|
* separator.
|
||||||
|
*
|
||||||
|
* @param string $prefix
|
||||||
|
* The prefix, e.g. 'Acme_FooPackage_'
|
||||||
|
* @param string|string[] $paths
|
||||||
|
* An array of paths, or one specific path.
|
||||||
|
* E.g. 'lib' for $relative = TRUE,
|
||||||
|
* or 'sites/all/libraries/AcmeFooPackage/lib' for $relative = FALSE.
|
||||||
|
* @param bool $relative
|
||||||
|
* If TRUE, the paths will be relative to $this->localDirectory.
|
||||||
|
*/
|
||||||
|
function addPearFlat($prefix, $paths, $relative = TRUE) {
|
||||||
|
$relative && $this->prependToPaths($paths);
|
||||||
|
$this->master->addPearFlat($prefix, $paths);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Relative path handling
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepends $this->localDirectory to a number of paths.
|
||||||
|
*
|
||||||
|
* @param array $map
|
||||||
|
*/
|
||||||
|
protected function prependMultiple(array &$map) {
|
||||||
|
foreach ($map as &$paths) {
|
||||||
|
$paths = (array) $paths;
|
||||||
|
foreach ($paths as &$path) {
|
||||||
|
$path = $this->localDirectory . $path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepends $this->localDirectory to a number of paths.
|
||||||
|
*
|
||||||
|
* @param string|string[] &$paths
|
||||||
|
*/
|
||||||
|
protected function prependToPaths(&$paths) {
|
||||||
|
if (!is_array($paths)) {
|
||||||
|
$paths = $this->localDirectory . $paths;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
foreach ($paths as &$path) {
|
||||||
|
$path = $this->localDirectory . $path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,68 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\CacheManager;
|
||||||
|
|
||||||
|
use Drupal\xautoload\DrupalSystem\DrupalSystemInterface;
|
||||||
|
use Drupal\xautoload\Util;
|
||||||
|
|
||||||
|
class CacheManager {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $prefix;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Drupal\xautoload\DrupalSystem\DrupalSystemInterface
|
||||||
|
*/
|
||||||
|
protected $system;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var CacheManagerObserverInterface[]
|
||||||
|
*/
|
||||||
|
protected $observers = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $prefix
|
||||||
|
* @param \Drupal\xautoload\DrupalSystem\DrupalSystemInterface $system
|
||||||
|
*/
|
||||||
|
protected function __construct($prefix, DrupalSystemInterface $system) {
|
||||||
|
$this->prefix = $prefix;
|
||||||
|
$this->system = $system;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method has side effects, so it is not the constructor.
|
||||||
|
*
|
||||||
|
* @param \Drupal\xautoload\DrupalSystem\DrupalSystemInterface $system
|
||||||
|
*
|
||||||
|
* @return CacheManager
|
||||||
|
*/
|
||||||
|
static function create(DrupalSystemInterface $system) {
|
||||||
|
$prefix = $system->variableGet(XAUTOLOAD_VARNAME_CACHE_PREFIX, NULL);
|
||||||
|
$manager = new self($prefix, $system);
|
||||||
|
if (empty($prefix)) {
|
||||||
|
$manager->renewCachePrefix();
|
||||||
|
}
|
||||||
|
return $manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param CacheManagerObserverInterface $observer
|
||||||
|
*/
|
||||||
|
function observeCachePrefix($observer) {
|
||||||
|
$observer->setCachePrefix($this->prefix);
|
||||||
|
$this->observers[] = $observer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renew the cache prefix, save it, and notify all observers.
|
||||||
|
*/
|
||||||
|
function renewCachePrefix() {
|
||||||
|
$this->prefix = Util::randomString();
|
||||||
|
$this->system->variableSet(XAUTOLOAD_VARNAME_CACHE_PREFIX, $this->prefix);
|
||||||
|
foreach ($this->observers as $observer) {
|
||||||
|
$observer->setCachePrefix($this->prefix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\CacheManager;
|
||||||
|
|
||||||
|
interface CacheManagerObserverInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the APC prefix after a flush cache.
|
||||||
|
*
|
||||||
|
* @param string $prefix
|
||||||
|
* A prefix for the storage key in APC.
|
||||||
|
*/
|
||||||
|
function setCachePrefix($prefix);
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\CacheMissObserver;
|
||||||
|
|
||||||
|
use Drupal\xautoload\ClassLoader\AbstractClassLoaderDecorator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces the ProxyClassFinder in the ClassLoader with the real ClassLoader.
|
||||||
|
*
|
||||||
|
* xautoload has a number of cached class loaders, working with APC cache or
|
||||||
|
* other key-value stores.
|
||||||
|
*
|
||||||
|
* The cached class loaders use the decorator pattern, and decorate a
|
||||||
|
* ClassFinder object, that will only be consulted on a cache miss.
|
||||||
|
*
|
||||||
|
* xautoload will first give the ClassLoader a ProxyClassFinder that wraps the
|
||||||
|
* real class loader. On the first cache miss, this ProxyClassFinder will
|
||||||
|
* notify all subscribed CacheMissObserverInterface object.
|
||||||
|
*
|
||||||
|
* The job of this particular observer is to replace the ProxyClassFinder, once
|
||||||
|
* it has done its job. Instead, the ClassLoader will get a reference ot the
|
||||||
|
* real ClassLoader, saving the overhead of going through ProxyClassFinder each
|
||||||
|
* time.
|
||||||
|
*
|
||||||
|
* @see ProxyClassFinder
|
||||||
|
*/
|
||||||
|
class CacheMissLoaderSetFinder implements CacheMissObserverInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var AbstractClassLoaderDecorator
|
||||||
|
*/
|
||||||
|
protected $loader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param AbstractClassLoaderDecorator $loader
|
||||||
|
*/
|
||||||
|
function __construct($loader) {
|
||||||
|
$this->loader = $loader;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function cacheMiss($finder) {
|
||||||
|
$this->loader->setFinder($finder);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\CacheMissObserver;
|
||||||
|
|
||||||
|
use Drupal\xautoload\ClassFinder\ExtendedClassFinderInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An operation that can be queued up to be performed on the class finder once
|
||||||
|
* it is initialized.
|
||||||
|
*
|
||||||
|
* In an average request, with the APC cache or similar enabled, class-to-file
|
||||||
|
* mappings will usually be loaded from the cache. The "real" class finder will
|
||||||
|
* only be initialized if one of the classes is not in the cache.
|
||||||
|
*
|
||||||
|
* @see ProxyClassFinder
|
||||||
|
*/
|
||||||
|
interface CacheMissObserverInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes the operation.
|
||||||
|
*
|
||||||
|
* This method will only be called if and when the "real" class finder is
|
||||||
|
* initialized.
|
||||||
|
*
|
||||||
|
* @param ExtendedClassFinderInterface $finder
|
||||||
|
* The class finder.
|
||||||
|
*/
|
||||||
|
function cacheMiss($finder);
|
||||||
|
}
|
@ -0,0 +1,468 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\ClassFinder;
|
||||||
|
|
||||||
|
use Drupal\xautoload\ClassLoader\AbstractClassLoader;
|
||||||
|
use Drupal\xautoload\DirectoryBehavior\DefaultDirectoryBehavior;
|
||||||
|
use Drupal\xautoload\DirectoryBehavior\Psr0DirectoryBehavior;
|
||||||
|
use Drupal\xautoload\Util;
|
||||||
|
|
||||||
|
class ClassFinder extends AbstractClassLoader implements ExtendedClassFinderInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array[]
|
||||||
|
*/
|
||||||
|
protected $classes = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var GenericPrefixMap
|
||||||
|
*/
|
||||||
|
protected $prefixMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var GenericPrefixMap
|
||||||
|
*/
|
||||||
|
protected $namespaceMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var DefaultDirectoryBehavior
|
||||||
|
*/
|
||||||
|
protected $defaultBehavior;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Psr0DirectoryBehavior
|
||||||
|
*/
|
||||||
|
protected $psr0Behavior;
|
||||||
|
|
||||||
|
function __construct() {
|
||||||
|
$this->prefixMap = new GenericPrefixMap('_');
|
||||||
|
$this->namespaceMap = new GenericPrefixMap('\\');
|
||||||
|
$this->defaultBehavior = new DefaultDirectoryBehavior();
|
||||||
|
$this->psr0Behavior = new Psr0DirectoryBehavior();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function getPrefixMap() {
|
||||||
|
return $this->prefixMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function getNamespaceMap() {
|
||||||
|
return $this->namespaceMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Composer compatibility
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function addClassMap(array $classMap) {
|
||||||
|
$this->registerClasses($classMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function add($prefix, $paths) {
|
||||||
|
if (FALSE === strpos($prefix, '\\')) {
|
||||||
|
// Due to the ambiguity of PSR-0, this could be either PEAR-like or namespaced.
|
||||||
|
$logical_base_path = Util::prefixLogicalPath($prefix);
|
||||||
|
foreach ((array) $paths as $root_path) {
|
||||||
|
$deep_path = strlen($root_path)
|
||||||
|
? rtrim($root_path, '/') . '/' . $logical_base_path
|
||||||
|
: $logical_base_path;
|
||||||
|
$this->prefixMap->registerDeepPath(
|
||||||
|
$logical_base_path,
|
||||||
|
$deep_path,
|
||||||
|
$this->defaultBehavior);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Namespaced PSR-0
|
||||||
|
$logical_base_path = Util::namespaceLogicalPath($prefix);
|
||||||
|
foreach ((array) $paths as $root_path) {
|
||||||
|
$deep_path = strlen($root_path)
|
||||||
|
? rtrim($root_path, '/') . '/' . $logical_base_path
|
||||||
|
: $logical_base_path;
|
||||||
|
$this->namespaceMap->registerDeepPath(
|
||||||
|
$logical_base_path,
|
||||||
|
$deep_path,
|
||||||
|
$this->psr0Behavior);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function addPsr0($prefix, $paths) {
|
||||||
|
$this->add($prefix, $paths);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function addPsr4($prefix, $paths) {
|
||||||
|
// Namespaced PSR-4
|
||||||
|
$logical_base_path = Util::namespaceLogicalPath($prefix);
|
||||||
|
foreach ((array) $paths as $deep_path) {
|
||||||
|
$deep_path = strlen($deep_path)
|
||||||
|
? rtrim($deep_path, '/') . '/'
|
||||||
|
: '';
|
||||||
|
$this->namespaceMap->registerDeepPath(
|
||||||
|
$logical_base_path,
|
||||||
|
$deep_path,
|
||||||
|
$this->defaultBehavior);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// More convenience stuff
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function addNamespacePsr0($prefix, $paths) {
|
||||||
|
$logical_base_path = Util::namespaceLogicalPath($prefix);
|
||||||
|
foreach ((array) $paths as $root_path) {
|
||||||
|
$deep_path = strlen($root_path)
|
||||||
|
? rtrim($root_path, '/') . '/' . $logical_base_path
|
||||||
|
: $logical_base_path;
|
||||||
|
$this->namespaceMap->registerDeepPath(
|
||||||
|
$logical_base_path,
|
||||||
|
$deep_path,
|
||||||
|
$this->psr0Behavior);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function addPear($prefix, $paths) {
|
||||||
|
$logical_base_path = Util::prefixLogicalPath($prefix);
|
||||||
|
foreach ((array) $paths as $root_path) {
|
||||||
|
$deep_path = strlen($root_path)
|
||||||
|
? rtrim($root_path, '/') . '/' . $logical_base_path
|
||||||
|
: $logical_base_path;
|
||||||
|
$this->prefixMap->registerDeepPath(
|
||||||
|
$logical_base_path,
|
||||||
|
$deep_path,
|
||||||
|
$this->defaultBehavior);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function addPearFlat($prefix, $paths) {
|
||||||
|
$logical_base_path = Util::prefixLogicalPath($prefix);
|
||||||
|
foreach ((array) $paths as $deep_path) {
|
||||||
|
$deep_path = strlen($deep_path)
|
||||||
|
? (rtrim($deep_path, '/') . '/')
|
||||||
|
: '';
|
||||||
|
$this->prefixMap->registerDeepPath(
|
||||||
|
$logical_base_path,
|
||||||
|
$deep_path,
|
||||||
|
$this->defaultBehavior
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Class map stuff
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function registerClass($class, $file_path) {
|
||||||
|
$this->classes[$class][$file_path] = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function registerClasses($classes) {
|
||||||
|
foreach ($classes as $class => $file_path) {
|
||||||
|
$this->classes[$class][$file_path] = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prefix stuff
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function registerPrefixRoot($prefix, $root_path, $behavior = NULL) {
|
||||||
|
if (!isset($behavior)) {
|
||||||
|
$behavior = $this->defaultBehavior;
|
||||||
|
}
|
||||||
|
$logical_base_path = Util::prefixLogicalPath($prefix);
|
||||||
|
$deep_path = strlen($root_path)
|
||||||
|
? rtrim($root_path, '/') . '/' . $logical_base_path
|
||||||
|
: $logical_base_path;
|
||||||
|
$this->prefixMap->registerDeepPath(
|
||||||
|
$logical_base_path,
|
||||||
|
$deep_path,
|
||||||
|
$behavior);
|
||||||
|
|
||||||
|
if (strlen($prefix)) {
|
||||||
|
// We assume that the class named $prefix is also found at this path.
|
||||||
|
$filepath = substr($deep_path, 0, -1) . '.php';
|
||||||
|
$this->registerClass($prefix, $filepath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function registerPrefixesRoot($map, $behavior = NULL) {
|
||||||
|
if (!isset($behavior)) {
|
||||||
|
$behavior = $this->defaultBehavior;
|
||||||
|
}
|
||||||
|
$deep_map = array();
|
||||||
|
foreach ($map as $prefix => $root_path) {
|
||||||
|
$logical_base_path = Util::prefixLogicalPath($prefix);
|
||||||
|
$deep_path = strlen($root_path)
|
||||||
|
? rtrim($root_path, '/') . '/' . $logical_base_path
|
||||||
|
: $logical_base_path;
|
||||||
|
$deep_map[$logical_base_path][$deep_path] = $behavior;
|
||||||
|
|
||||||
|
// Register the class with name $prefix.
|
||||||
|
if (strlen($prefix)) {
|
||||||
|
$filepath = substr($deep_path, 0, -1) . '.php';
|
||||||
|
$this->classes[$prefix][$filepath] = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->prefixMap->registerDeepPaths($deep_map);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function registerPrefixDeep($prefix, $deep_path, $behavior = NULL) {
|
||||||
|
if (!isset($behavior)) {
|
||||||
|
$behavior = $this->defaultBehavior;
|
||||||
|
}
|
||||||
|
$this->registerPrefixDeepLocation($prefix, $deep_path, $behavior);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function registerPrefixesDeep($map, $behavior = NULL) {
|
||||||
|
if (!isset($behavior)) {
|
||||||
|
$behavior = $this->defaultBehavior;
|
||||||
|
}
|
||||||
|
$deep_map = array();
|
||||||
|
foreach ($map as $prefix => $deep_path) {
|
||||||
|
$logical_base_path = Util::prefixLogicalPath($prefix);
|
||||||
|
$deep_path = strlen($deep_path)
|
||||||
|
? rtrim($deep_path, '/') . '/'
|
||||||
|
: '';
|
||||||
|
$deep_map[$logical_base_path][$deep_path] = $behavior;
|
||||||
|
}
|
||||||
|
$this->prefixMap->registerDeepPaths($deep_map);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function registerPrefixDeepLocation($prefix, $deep_path, $behavior = NULL) {
|
||||||
|
if (!isset($behavior)) {
|
||||||
|
$behavior = $this->defaultBehavior;
|
||||||
|
}
|
||||||
|
$logical_base_path = Util::prefixLogicalPath($prefix);
|
||||||
|
$deep_path = strlen($deep_path)
|
||||||
|
? rtrim($deep_path, '/') . '/'
|
||||||
|
: '';
|
||||||
|
$this->prefixMap->registerDeepPath(
|
||||||
|
$logical_base_path,
|
||||||
|
$deep_path,
|
||||||
|
$behavior);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Namespace stuff
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function registerNamespaceRoot($namespace, $root_path, $behavior = NULL) {
|
||||||
|
if (!isset($behavior)) {
|
||||||
|
$behavior = $this->defaultBehavior;
|
||||||
|
}
|
||||||
|
$logical_base_path = Util::namespaceLogicalPath($namespace);
|
||||||
|
$deep_path = strlen($root_path)
|
||||||
|
? rtrim($root_path, '/') . '/' . $logical_base_path
|
||||||
|
: $logical_base_path;
|
||||||
|
$this->namespaceMap->registerDeepPath(
|
||||||
|
$logical_base_path,
|
||||||
|
$deep_path,
|
||||||
|
$behavior);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function registerNamespacesRoot($map, $behavior = NULL) {
|
||||||
|
if (!isset($behavior)) {
|
||||||
|
$behavior = $this->defaultBehavior;
|
||||||
|
}
|
||||||
|
$deep_map = array();
|
||||||
|
foreach ($map as $namespace => $root_path) {
|
||||||
|
$logical_base_path = Util::namespaceLogicalPath($namespace);
|
||||||
|
$deep_path = strlen($root_path)
|
||||||
|
? rtrim($root_path, '/') . '/' . $logical_base_path
|
||||||
|
: $logical_base_path;
|
||||||
|
$deep_map[$logical_base_path][$deep_path] = $behavior;
|
||||||
|
}
|
||||||
|
$this->namespaceMap->registerDeepPaths($deep_map);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function registerNamespaceDeep($namespace, $path, $behavior = NULL) {
|
||||||
|
if (!isset($behavior)) {
|
||||||
|
$behavior = $this->defaultBehavior;
|
||||||
|
}
|
||||||
|
$logical_base_path = Util::namespaceLogicalPath($namespace);
|
||||||
|
$deep_path = strlen($path)
|
||||||
|
? $path . '/'
|
||||||
|
: '';
|
||||||
|
$this->namespaceMap->registerDeepPath(
|
||||||
|
$logical_base_path,
|
||||||
|
$deep_path,
|
||||||
|
$behavior);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function registerNamespacesDeep($map, $behavior = NULL) {
|
||||||
|
if (!isset($behavior)) {
|
||||||
|
$behavior = $this->defaultBehavior;
|
||||||
|
}
|
||||||
|
$deep_map = array();
|
||||||
|
foreach ($map as $namespace => $deep_path) {
|
||||||
|
$logical_base_path = Util::namespaceLogicalPath($namespace);
|
||||||
|
$deep_path = strlen($deep_path)
|
||||||
|
? rtrim($deep_path, '/') . '/'
|
||||||
|
: '';
|
||||||
|
$deep_map[$logical_base_path][$deep_path] = $behavior;
|
||||||
|
}
|
||||||
|
$this->namespaceMap->registerDeepPaths($deep_map);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function registerNamespaceDeepLocation($namespace, $path, $behavior = NULL) {
|
||||||
|
if (!isset($behavior)) {
|
||||||
|
$behavior = $this->defaultBehavior;
|
||||||
|
}
|
||||||
|
$namespace_path_fragment = Util::namespaceLogicalPath($namespace);
|
||||||
|
$deep_path = strlen($path)
|
||||||
|
? $path . '/'
|
||||||
|
: '';
|
||||||
|
$this->namespaceMap->registerDeepPath(
|
||||||
|
$namespace_path_fragment,
|
||||||
|
$deep_path,
|
||||||
|
$behavior);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function loadClass($class) {
|
||||||
|
|
||||||
|
// Fix the behavior of some PHP versions that prepend '\\' to the class name.
|
||||||
|
if ('\\' === $class[0]) {
|
||||||
|
$class = substr($class, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// First check if the literal class name is registered.
|
||||||
|
if (!empty($this->classes[$class])) {
|
||||||
|
foreach ($this->classes[$class] as $filepath => $true) {
|
||||||
|
if (file_exists($filepath)) {
|
||||||
|
require $filepath;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the class has a namespace.
|
||||||
|
if (FALSE !== $pos = strrpos($class, '\\')) {
|
||||||
|
|
||||||
|
// Build the "logical path" based on PSR-4 replacement rules.
|
||||||
|
$logical_path = str_replace('\\', '/', $class) . '.php';
|
||||||
|
|
||||||
|
return $this->namespaceMap->loadClass($class, $logical_path, $pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the "logical path" based on PEAR replacement rules.
|
||||||
|
$pear_logical_path = str_replace('_', '/', $class) . '.php';
|
||||||
|
|
||||||
|
// Clean up surplus '/' resulting from duplicate underscores, or an
|
||||||
|
// underscore at the beginning of the class.
|
||||||
|
while (FALSE !== $pos = strrpos('/' . $pear_logical_path, '//')) {
|
||||||
|
$pear_logical_path[$pos] = '_';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the class has any underscore.
|
||||||
|
$pos = strrpos($pear_logical_path, '/');
|
||||||
|
|
||||||
|
return $this->prefixMap->loadClass($class, $pear_logical_path, $pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function apiFindFile($api, $class) {
|
||||||
|
|
||||||
|
// Fix the behavior of some PHP versions that prepend '\\' to the class name.
|
||||||
|
if ('\\' === $class[0]) {
|
||||||
|
$class = substr($class, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// First check if the literal class name is registered.
|
||||||
|
if (!empty($this->classes[$class])) {
|
||||||
|
foreach ($this->classes[$class] as $filepath => $true) {
|
||||||
|
if ($api->suggestFile($filepath)) {
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the class has a namespace.
|
||||||
|
if (FALSE !== $pos = strrpos($class, '\\')) {
|
||||||
|
|
||||||
|
// Build the "logical path" based on PSR-4 replacement rules.
|
||||||
|
$logical_path = str_replace('\\', '/', $class) . '.php';
|
||||||
|
|
||||||
|
return $this->namespaceMap->apiFindFile($api, $logical_path, $pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the "logical path" based on PEAR replacement rules.
|
||||||
|
$pear_logical_path = str_replace('_', '/', $class) . '.php';
|
||||||
|
|
||||||
|
// Clean up surplus '/' resulting from duplicate underscores, or an
|
||||||
|
// underscore at the beginning of the class.
|
||||||
|
while (FALSE !== $pos = strrpos('/' . $pear_logical_path, '//')) {
|
||||||
|
$pear_logical_path[$pos] = '_';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the class has any underscore.
|
||||||
|
$pos = strrpos($pear_logical_path, '/');
|
||||||
|
|
||||||
|
return $this->prefixMap->apiFindFile($api, $pear_logical_path, $pos);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\ClassFinder;
|
||||||
|
|
||||||
|
use Drupal\xautoload\ClassLoader\ClassLoaderInterface;
|
||||||
|
|
||||||
|
interface ClassFinderInterface extends ClassLoaderInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the path to the file where the class is defined.
|
||||||
|
*
|
||||||
|
* @param \Drupal\xautoload\ClassFinder\InjectedApi\InjectedApiInterface $api
|
||||||
|
* API object with a suggestFile() method.
|
||||||
|
* We are supposed to call $api->suggestFile($file) with all suggestions we
|
||||||
|
* can find, until it returns TRUE. Once suggestFile() returns TRUE, we stop
|
||||||
|
* and return TRUE as well. The $file will be in the $api object, so we
|
||||||
|
* don't need to return it.
|
||||||
|
* @param string $class
|
||||||
|
* The name of the class, with all namespaces prepended.
|
||||||
|
* E.g. Some\Namespace\Some\Class
|
||||||
|
*
|
||||||
|
* @return TRUE|NULL
|
||||||
|
* TRUE, if we found the file for the class.
|
||||||
|
* That is, if the $api->suggestFile($file) method returned TRUE one time.
|
||||||
|
* NULL, if we have no more suggestions.
|
||||||
|
*/
|
||||||
|
function apiFindFile($api, $class);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,81 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\ClassFinder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class finder interface with additional registration methods.
|
||||||
|
*/
|
||||||
|
interface CommonRegistrationInterface {
|
||||||
|
|
||||||
|
// Composer compatibility
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers an array ("map") of classes to file paths.
|
||||||
|
*
|
||||||
|
* @param array $classMap
|
||||||
|
* Class to filename map. E.g. $classMap['Acme\Foo'] = 'lib/Acme/Foo.php'
|
||||||
|
*/
|
||||||
|
function addClassMap(array $classMap);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a PSR-0 style prefix. Alias for ->addPsr0().
|
||||||
|
*
|
||||||
|
* @param string $prefix
|
||||||
|
* @param string[]|string $paths
|
||||||
|
*/
|
||||||
|
function add($prefix, $paths);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a PSR-0 style prefix. Alias for ->add().
|
||||||
|
*
|
||||||
|
* @param string $prefix
|
||||||
|
* @param string[]|string $paths
|
||||||
|
*/
|
||||||
|
function addPsr0($prefix, $paths);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a PSR-4 style namespace.
|
||||||
|
*
|
||||||
|
* @param string $prefix
|
||||||
|
* @param string[]|string $paths
|
||||||
|
*/
|
||||||
|
function addPsr4($prefix, $paths);
|
||||||
|
|
||||||
|
// More convenience stuff
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a PSR-0 style namespace.
|
||||||
|
*
|
||||||
|
* This will assume that we are really dealing with a namespace, even if it
|
||||||
|
* has no '\\' included.
|
||||||
|
*
|
||||||
|
* @param string $prefix
|
||||||
|
* @param string[]|string $paths
|
||||||
|
*/
|
||||||
|
function addNamespacePsr0($prefix, $paths);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a PEAR-like prefix.
|
||||||
|
*
|
||||||
|
* This will assume with no further checks that $prefix contains no namespace
|
||||||
|
* separator.
|
||||||
|
*
|
||||||
|
* @param string $prefix
|
||||||
|
* @param string[]|string $paths
|
||||||
|
*/
|
||||||
|
function addPear($prefix, $paths);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a prefix similar to PEAR, but with flat directories.
|
||||||
|
*
|
||||||
|
* This will assume with no further checks that $prefix contains no namespace
|
||||||
|
* separator.
|
||||||
|
*
|
||||||
|
* @param string $prefix
|
||||||
|
* @param string[]|string $paths
|
||||||
|
*/
|
||||||
|
function addPearFlat($prefix, $paths);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,215 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\ClassFinder;
|
||||||
|
|
||||||
|
use Drupal\xautoload\DirectoryBehavior\DirectoryBehaviorInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class finder interface with additional registration methods.
|
||||||
|
*/
|
||||||
|
interface ExtendedClassFinderInterface extends ClassFinderInterface, CommonRegistrationInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return GenericPrefixMap
|
||||||
|
*/
|
||||||
|
function getPrefixMap();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return GenericPrefixMap
|
||||||
|
*/
|
||||||
|
function getNamespaceMap();
|
||||||
|
|
||||||
|
// Class map stuff
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a filepath for an individual class.
|
||||||
|
*
|
||||||
|
* @param string $class
|
||||||
|
* The class, e.g. My_Class
|
||||||
|
* @param string $file_path
|
||||||
|
* The path, e.g. "../lib/My/Class.php".
|
||||||
|
*/
|
||||||
|
function registerClass($class, $file_path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register an array ("map") of classes to file paths.
|
||||||
|
*
|
||||||
|
* @param string[] $classes
|
||||||
|
* The map of classes to file paths.
|
||||||
|
*/
|
||||||
|
function registerClasses($classes);
|
||||||
|
|
||||||
|
// Prefix stuff
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a PEAR-style root path for a given class prefix.
|
||||||
|
*
|
||||||
|
* @param string $prefix
|
||||||
|
* Prefix, e.g. "My_Prefix", for classes like "My_Prefix_SomeClass".
|
||||||
|
* This does ALSO cover the class named "My_Prefix" itself.
|
||||||
|
* @param string $root_path
|
||||||
|
* Root path, e.g. "../lib" or "../src", so that classes can be placed e.g.
|
||||||
|
* My_Prefix_SomeClass -> ../lib/My/Prefix/SomeClass.php
|
||||||
|
* My_Prefix -> ../lib/My/Prefix.php
|
||||||
|
* @param DirectoryBehaviorInterface $behavior
|
||||||
|
* If TRUE, then we are not sure if the directory at $path actually exists.
|
||||||
|
* If during the process we find the directory to be nonexistent, we
|
||||||
|
* unregister the path.
|
||||||
|
*/
|
||||||
|
function registerPrefixRoot($prefix, $root_path, $behavior = NULL);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register an array of PEAR-style deep paths for given class prefixes.
|
||||||
|
*
|
||||||
|
* Note:
|
||||||
|
* This actually goes beyond PEAR style, because it also allows "shallow"
|
||||||
|
* PEAR-like structures like
|
||||||
|
* my_library_Some_Class -> (library dir)/src/Some/Class.php
|
||||||
|
* instead of
|
||||||
|
* my_library_Some_Class -> (library dir)/src/my/library/Some/Class.php
|
||||||
|
* via
|
||||||
|
* $finder->registerPrefixDeep('my_library', "$library_dir/src");
|
||||||
|
*
|
||||||
|
* @param string[] $map
|
||||||
|
* Associative array, the keys are the prefixes, the values are the
|
||||||
|
* directories.
|
||||||
|
* This does NOT cover the class named $prefix itself.
|
||||||
|
* @param DirectoryBehaviorInterface $behavior
|
||||||
|
* If TRUE, then we are not sure if the directory at $path actually exists.
|
||||||
|
* If during the process we find the directory to be nonexistent, we
|
||||||
|
* unregister the path.
|
||||||
|
*/
|
||||||
|
function registerPrefixesRoot($map, $behavior = NULL);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a PEAR-style deep path for a given class prefix.
|
||||||
|
*
|
||||||
|
* Note:
|
||||||
|
* This actually goes beyond PEAR style, because it also allows things like
|
||||||
|
* my_library_Some_Class -> (library dir)/src/Some/Class.php
|
||||||
|
* instead of
|
||||||
|
* my_library_Some_Class -> (library dir)/src/my/library/Some/Class.php
|
||||||
|
* via
|
||||||
|
* $finder->registerPrefixDeep('my_library', "$library_dir/src");
|
||||||
|
*
|
||||||
|
* @param string $prefix
|
||||||
|
* Prefix, e.g. "My_Prefix", for classes like "My_Prefix_SomeClass".
|
||||||
|
* This does NOT cover the class named "My_Prefix" itself.
|
||||||
|
* @param string $deep_path
|
||||||
|
* The deep path, e.g. "../lib/My/Prefix", for classes placed in
|
||||||
|
* My_Prefix_SomeClass -> ../lib/My/Prefix/SomeClass.php
|
||||||
|
* @param DirectoryBehaviorInterface $behavior
|
||||||
|
* If TRUE, then we are not sure if the directory at $path actually exists.
|
||||||
|
* If during the process we find the directory to be nonexistent, we
|
||||||
|
* unregister the path.
|
||||||
|
*/
|
||||||
|
function registerPrefixDeep($prefix, $deep_path, $behavior = NULL);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register an array of PEAR-style deep paths for given class prefixes.
|
||||||
|
*
|
||||||
|
* Note:
|
||||||
|
* This actually goes beyond PEAR style, because it also allows "shallow"
|
||||||
|
* PEAR-like structures like
|
||||||
|
* my_library_Some_Class -> (library dir)/src/Some/Class.php
|
||||||
|
* instead of
|
||||||
|
* my_library_Some_Class -> (library dir)/src/my/library/Some/Class.php
|
||||||
|
* via
|
||||||
|
* $finder->registerPrefixDeep('my_library', "$library_dir/src");
|
||||||
|
*
|
||||||
|
* @param string[] $map
|
||||||
|
* Associative array, the keys are the prefixes, the values are the
|
||||||
|
* directories.
|
||||||
|
* This does NOT cover the class named $prefix itself.
|
||||||
|
* @param \Drupal\xautoload\DirectoryBehavior\DirectoryBehaviorInterface $behavior
|
||||||
|
* If TRUE, then we are not sure if the directory at $path actually exists.
|
||||||
|
* If during the process we find the directory to be nonexistent, we
|
||||||
|
* unregister the path.
|
||||||
|
*/
|
||||||
|
function registerPrefixesDeep($map, $behavior = NULL);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a filesystem location for a given class prefix.
|
||||||
|
*
|
||||||
|
* @param string $prefix
|
||||||
|
* The prefix, e.g. "My_Prefix"
|
||||||
|
* @param string $deep_path
|
||||||
|
* The deep filesystem location, e.g. "../lib/My/Prefix".
|
||||||
|
* @param DirectoryBehaviorInterface $behavior
|
||||||
|
* If TRUE, then we are not sure if the directory at $path actually exists.
|
||||||
|
* If during the process we find the directory to be nonexistent, we
|
||||||
|
* unregister the path.
|
||||||
|
*/
|
||||||
|
function registerPrefixDeepLocation($prefix, $deep_path, $behavior = NULL);
|
||||||
|
|
||||||
|
// Namespace stuff
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a PSR-0 root folder for a given namespace.
|
||||||
|
*
|
||||||
|
* @param string $namespace
|
||||||
|
* The namespace, e.g. "My\Namespace", to cover all classes within that,
|
||||||
|
* e.g. My\Namespace\SomeClass, or My\Namespace\Xyz\SomeClass. This does not
|
||||||
|
* cover the root-level class, e.g. My\Namespace
|
||||||
|
* @param string $root_path
|
||||||
|
* The deep path, e.g. "../lib", if classes reside in e.g.
|
||||||
|
* My\Namespace\SomeClass -> ../lib/My/Namespace/SomeClass.php
|
||||||
|
* @param \Drupal\xautoload\DirectoryBehavior\DirectoryBehaviorInterface $behavior
|
||||||
|
* If TRUE, then we are not sure if the directory at $path actually exists.
|
||||||
|
* If during the process we find the directory to be nonexistent, we
|
||||||
|
* unregister the path.
|
||||||
|
*/
|
||||||
|
function registerNamespaceRoot($namespace, $root_path, $behavior = NULL);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register PSR-0 root folders for given namespaces.
|
||||||
|
*
|
||||||
|
* @param string[] $map
|
||||||
|
* Associative array, the keys are the namespaces, the values are the
|
||||||
|
* directories.
|
||||||
|
* @param \Drupal\xautoload\DirectoryBehavior\DirectoryBehaviorInterface $behavior
|
||||||
|
* If TRUE, then we are not sure if the directory at $path actually exists.
|
||||||
|
* If during the process we find the directory to be nonexistent, we
|
||||||
|
* unregister the path.
|
||||||
|
*/
|
||||||
|
function registerNamespacesRoot($map, $behavior = NULL);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alias for registerNamespaceDeepLocation()
|
||||||
|
*
|
||||||
|
* @param string $namespace
|
||||||
|
* The namespace, e.g. "My\Namespace"
|
||||||
|
* @param string $path
|
||||||
|
* The deep path, e.g. "../lib/My/Namespace"
|
||||||
|
* @param \Drupal\xautoload\DirectoryBehavior\DirectoryBehaviorInterface $behavior
|
||||||
|
* If TRUE, then we are not sure if the directory at $path actually exists.
|
||||||
|
* If during the process we find the directory to be nonexistent, we
|
||||||
|
* unregister the path.
|
||||||
|
*/
|
||||||
|
function registerNamespaceDeep($namespace, $path, $behavior = NULL);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a number of "deep" namespace directories at once.
|
||||||
|
*
|
||||||
|
* @param string[] $map
|
||||||
|
* @param DirectoryBehaviorInterface $behavior
|
||||||
|
*/
|
||||||
|
function registerNamespacesDeep($map, $behavior = NULL);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a deep filesystem location for a given namespace.
|
||||||
|
*
|
||||||
|
* @param string $namespace
|
||||||
|
* The namespace, e.g. "My\Namespace"
|
||||||
|
* @param string $path
|
||||||
|
* The deep path, e.g. "../lib/My/Namespace"
|
||||||
|
* @param DirectoryBehaviorInterface $behavior
|
||||||
|
* If TRUE, then we are not sure if the directory at $path actually exists.
|
||||||
|
* If during the process we find the directory to be nonexistent, we
|
||||||
|
* unregister the path.
|
||||||
|
*/
|
||||||
|
function registerNamespaceDeepLocation($namespace, $path, $behavior = NULL);
|
||||||
|
}
|
@ -0,0 +1,242 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\ClassFinder;
|
||||||
|
|
||||||
|
use Drupal\xautoload\DirectoryBehavior\DirectoryBehaviorInterface;
|
||||||
|
use Drupal\xautoload\ClassFinder\InjectedApi\LoadClassInjectedAPI;
|
||||||
|
use Drupal\xautoload\ClassFinder\InjectedApi\InjectedApiInterface;
|
||||||
|
use Drupal\xautoload\DirectoryBehavior\DefaultDirectoryBehavior;
|
||||||
|
use xautoload_FinderPlugin_Interface;
|
||||||
|
use Drupal\xautoload\DirectoryBehavior\Psr0DirectoryBehavior;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class for the class finder.
|
||||||
|
* This is not part of ClassFinder, because we want to use the same logic for
|
||||||
|
* namespaces (PSR-0) and prefixes (PEAR).
|
||||||
|
*
|
||||||
|
* This thing does not actually deal with class names, but with transformed
|
||||||
|
* paths.
|
||||||
|
*
|
||||||
|
* Example A:
|
||||||
|
* When looking for a class \Aaa\Bbb\Ccc_Ddd, the class finder will
|
||||||
|
* 1. Determine that this class is within a namespace.
|
||||||
|
* 2. Transform that into "Aaa/Bbb/Ccc/Ddd.php".
|
||||||
|
* 3. Check if the namespace map evaluator has anything registered for
|
||||||
|
* 3.1. "Aaa/Bbb/"
|
||||||
|
* 3.2. "Aaa/"
|
||||||
|
* 3.3. ""
|
||||||
|
*
|
||||||
|
* Example A:
|
||||||
|
* When looking for a class Aaa_Bbb_Ccc, the class finder will
|
||||||
|
* 1. Determine that this class is NOT within a namespace.
|
||||||
|
* 2. Check if a file is explicitly registered for the class itself.
|
||||||
|
* 3. Transform the class name into "Aaa/Bbb/Ccc.php".
|
||||||
|
* 4. Check if the prefix map evaluator has anything registered for
|
||||||
|
* 4.1. "Aaa/Bbb/"
|
||||||
|
* 4.2. "Aaa/"
|
||||||
|
* 4.3. ""
|
||||||
|
*/
|
||||||
|
class GenericPrefixMap {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var DirectoryBehaviorInterface[][]
|
||||||
|
* Format: $[$logical_base_path][$deep_path] = $behavior
|
||||||
|
*/
|
||||||
|
protected $paths = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
* Either '\\' or '_'.
|
||||||
|
*/
|
||||||
|
protected $separator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $separator
|
||||||
|
*/
|
||||||
|
function __construct($separator) {
|
||||||
|
$this->separator = $separator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If a class file would be in
|
||||||
|
* $psr0_root . '/' . $path_fragment . $path_suffix
|
||||||
|
* then instead, we look in
|
||||||
|
* $deep_path . $path_suffix
|
||||||
|
*
|
||||||
|
* @param string $logical_base_path
|
||||||
|
* The would-be namespace path relative to PSR-0 root.
|
||||||
|
* That is, the namespace with '\\' replaced by '/'.
|
||||||
|
* @param string $deep_path
|
||||||
|
* The filesystem location of the (PSR-0) subfolder for the given namespace.
|
||||||
|
* @param DirectoryBehaviorInterface $behavior
|
||||||
|
* Behavior in this directory.
|
||||||
|
*/
|
||||||
|
function registerDeepPath($logical_base_path, $deep_path, $behavior) {
|
||||||
|
$this->paths[$logical_base_path][$deep_path] = $behavior;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $logical_base_path
|
||||||
|
* The would-be namespace path relative to PSR-0 root.
|
||||||
|
* That is, the namespace with '\\' replaced by '/'.
|
||||||
|
* @param string $deep_path
|
||||||
|
* The filesystem location of the (PSR-0) subfolder for the given namespace.
|
||||||
|
* @param DirectoryBehaviorInterface $behavior
|
||||||
|
* Behavior in this directory.
|
||||||
|
*/
|
||||||
|
function prependDeepPath($logical_base_path, $deep_path, $behavior) {
|
||||||
|
$this->paths[$logical_base_path]
|
||||||
|
= isset($this->paths[$logical_base_path])
|
||||||
|
? array($deep_path => $behavior) + $this->paths[$logical_base_path]
|
||||||
|
: array($deep_path => $behavior);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a bunch of those paths ..
|
||||||
|
*
|
||||||
|
* @param array[] $map
|
||||||
|
*
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
function registerDeepPaths(array $map) {
|
||||||
|
foreach ($map as $key => $paths) {
|
||||||
|
if (isset($this->paths[$key])) {
|
||||||
|
$paths += $this->paths[$key];
|
||||||
|
}
|
||||||
|
$this->paths[$key] = $paths;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a registered path mapping.
|
||||||
|
*
|
||||||
|
* @param string $logical_base_path
|
||||||
|
* @param string $deep_path
|
||||||
|
*/
|
||||||
|
function unregisterDeepPath($logical_base_path, $deep_path) {
|
||||||
|
unset($this->paths[$logical_base_path][$deep_path]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $class
|
||||||
|
* @param string $logical_path
|
||||||
|
* Class name translated into a logical path, either with PSR-4 or with PEAR
|
||||||
|
* translation rules.
|
||||||
|
* @param int|bool $lastpos
|
||||||
|
* Position of the last directory separator in $logical_path.
|
||||||
|
* FALSE, if there is no directory separator in $logical_path.
|
||||||
|
*
|
||||||
|
* @return bool|NULL
|
||||||
|
* TRUE, if the class was found.
|
||||||
|
*/
|
||||||
|
function loadClass($class, $logical_path, $lastpos) {
|
||||||
|
$pos = $lastpos;
|
||||||
|
while (TRUE) {
|
||||||
|
$logical_base_path = (FALSE === $pos)
|
||||||
|
? ''
|
||||||
|
: substr($logical_path, 0, $pos + 1);
|
||||||
|
|
||||||
|
if (isset($this->paths[$logical_base_path])) {
|
||||||
|
foreach ($this->paths[$logical_base_path] as $dir => $behavior) {
|
||||||
|
if ($behavior instanceof DefaultDirectoryBehavior) {
|
||||||
|
// PSR-4 and PEAR
|
||||||
|
if (file_exists($file = $dir . substr($logical_path, $pos + 1))) {
|
||||||
|
require $file;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elseif ($behavior instanceof Psr0DirectoryBehavior) {
|
||||||
|
// PSR-0
|
||||||
|
if (file_exists(
|
||||||
|
$file = $dir
|
||||||
|
. substr($logical_path, $pos + 1, $lastpos - $pos)
|
||||||
|
. str_replace('_', '/', substr($logical_path, $lastpos + 1))
|
||||||
|
)) {
|
||||||
|
require $file;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elseif ($behavior instanceof xautoload_FinderPlugin_Interface) {
|
||||||
|
// Legacy "FinderPlugin".
|
||||||
|
$api = new LoadClassInjectedAPI($class);
|
||||||
|
if ($behavior->findFile($api, $logical_base_path, substr($logical_path, $pos + 1), $dir)) {
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Continue with parent fragment.
|
||||||
|
if (FALSE === $pos) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
$pos = strrpos($logical_base_path, '/', -2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the file for a class that in PSR-0 or PEAR would be in
|
||||||
|
* $psr_0_root . '/' . $path_fragment . $path_suffix
|
||||||
|
*
|
||||||
|
* @param InjectedApiInterface $api
|
||||||
|
* @param string $logical_path
|
||||||
|
* Class name translated into a logical path, either with PSR-4 or with PEAR
|
||||||
|
* translation rules.
|
||||||
|
* @param int|bool $lastpos
|
||||||
|
* Position of the last directory separator in $logical_path.
|
||||||
|
* FALSE, if there is no directory separator in $logical_path.
|
||||||
|
*
|
||||||
|
* @return bool|NULL
|
||||||
|
* TRUE, if the class was found.
|
||||||
|
*/
|
||||||
|
function apiFindFile($api, $logical_path, $lastpos) {
|
||||||
|
$pos = $lastpos;
|
||||||
|
while (TRUE) {
|
||||||
|
$logical_base_path = (FALSE === $pos)
|
||||||
|
? ''
|
||||||
|
: substr($logical_path, 0, $pos + 1);
|
||||||
|
|
||||||
|
if (isset($this->paths[$logical_base_path])) {
|
||||||
|
foreach ($this->paths[$logical_base_path] as $dir => $behavior) {
|
||||||
|
if ($behavior instanceof DefaultDirectoryBehavior) {
|
||||||
|
// PSR-4 and PEAR
|
||||||
|
if ($api->suggestFile($dir . substr($logical_path, $pos + 1))) {
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elseif ($behavior instanceof Psr0DirectoryBehavior) {
|
||||||
|
// PSR-0
|
||||||
|
if ($api->suggestFile(
|
||||||
|
$dir
|
||||||
|
. substr($logical_path, $pos + 1, $lastpos - $pos)
|
||||||
|
. str_replace('_', '/', substr($logical_path, $lastpos + 1))
|
||||||
|
)) {
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elseif ($behavior instanceof xautoload_FinderPlugin_Interface) {
|
||||||
|
// Legacy "FinderPlugin".
|
||||||
|
if ($behavior->findFile($api, $logical_base_path, substr($logical_path, $pos + 1), $dir)) {
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Continue with parent fragment.
|
||||||
|
if (FALSE === $pos) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
$pos = strrpos($logical_base_path, '/', -2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\ClassFinder\InjectedApi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To help testability, we use an injected API instead of just a return value.
|
||||||
|
* The injected API can be mocked to provide a mocked file_exists(), and to
|
||||||
|
* monitor all suggested candidates, not just the correct return value.
|
||||||
|
*/
|
||||||
|
abstract class AbstractInjectedApi implements InjectedApiInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
* The class name to look for. Set in the constructor.
|
||||||
|
*/
|
||||||
|
protected $className;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $class_name
|
||||||
|
* Name of the class or interface we are trying to load.
|
||||||
|
*/
|
||||||
|
function __construct($class_name) {
|
||||||
|
$this->className = $class_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is done in the injected api object, so we can easily provide a mock
|
||||||
|
* implementation.
|
||||||
|
*/
|
||||||
|
function is_dir($dir) {
|
||||||
|
return is_dir($dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the name of the class we are looking for.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* The class we are looking for.
|
||||||
|
*/
|
||||||
|
function getClass() {
|
||||||
|
return $this->className;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dummy method to force autoloading this class (or an ancestor).
|
||||||
|
*/
|
||||||
|
static function forceAutoload() {
|
||||||
|
// Do nothing.
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,147 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\ClassFinder\InjectedApi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To help testability, we use an injected API instead of just a return value.
|
||||||
|
* The injected API can be mocked to provide a mocked file_exists(), and to
|
||||||
|
* monitor all suggested candidates, not just the correct return value.
|
||||||
|
*/
|
||||||
|
class CollectFilesInjectedApi extends AbstractInjectedApi {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
* The method that, if called with $this->file, will return TRUE.
|
||||||
|
*/
|
||||||
|
protected $methodName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
* The file where $this->$method($this->file) will return TRUE.
|
||||||
|
*/
|
||||||
|
protected $file;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array[]
|
||||||
|
* All files that were suggested.
|
||||||
|
*/
|
||||||
|
protected $suggestions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $class_name
|
||||||
|
* @var string $method
|
||||||
|
* The method that, if called with $this->file, will return TRUE.
|
||||||
|
* @param string $file
|
||||||
|
* The file where $this->$method($this->file) will return TRUE.
|
||||||
|
*/
|
||||||
|
function __construct($class_name, $method_name, $file) {
|
||||||
|
$this->methodName = $method_name;
|
||||||
|
$this->file = $file;
|
||||||
|
parent::__construct($class_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When the process has finished, use this to return the result.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* The file that is supposed to declare the class.
|
||||||
|
*/
|
||||||
|
function getSuggestions() {
|
||||||
|
return $this->suggestions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Suggest a file that, if the file exists,
|
||||||
|
* has to declare the class we are looking for.
|
||||||
|
* Only keep the class on success.
|
||||||
|
*
|
||||||
|
* @param string $file
|
||||||
|
* The file that is supposed to declare the class.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* TRUE, if the file exists.
|
||||||
|
* FALSE, otherwise.
|
||||||
|
*/
|
||||||
|
function suggestFile($file) {
|
||||||
|
$this->suggestions[] = array(__FUNCTION__, $file);
|
||||||
|
return __FUNCTION__ === $this->methodName && $file === $this->file;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as suggestFile(), but skip the file_exists(),
|
||||||
|
* assuming that we already know the file exists.
|
||||||
|
*
|
||||||
|
* This could make sense if a plugin already did the file_exists() check.
|
||||||
|
*
|
||||||
|
* @param string $file
|
||||||
|
* The file that is supposed to declare the class.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* TRUE, if the file was found - which is always.
|
||||||
|
*/
|
||||||
|
function suggestFile_skipFileExists($file) {
|
||||||
|
$this->suggestions[] = array(__FUNCTION__, $file);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as suggestFile(), but assume that file_exists() returns TRUE.
|
||||||
|
*
|
||||||
|
* @param string $file
|
||||||
|
* The file that is supposed to declare the class.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* TRUE, if the file was found - which is always.
|
||||||
|
*/
|
||||||
|
function suggestFile_checkNothing($file) {
|
||||||
|
$this->suggestions[] = array(__FUNCTION__, $file);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as suggestFile(), but check the full PHP include path.
|
||||||
|
*
|
||||||
|
* @param string $file
|
||||||
|
* The file that is supposed to declare the class.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* TRUE, if the file exists.
|
||||||
|
* FALSE, otherwise.
|
||||||
|
*/
|
||||||
|
function suggestFile_checkIncludePath($file) {
|
||||||
|
$this->suggestions[] = array(__FUNCTION__, $file);
|
||||||
|
return __FUNCTION__ == $this->methodName && $file === $this->file;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function guessFile($file) {
|
||||||
|
$this->suggestions[] = array(__FUNCTION__, $file);
|
||||||
|
return __FUNCTION__ == $this->methodName && $file === $this->file;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function guessPath($file) {
|
||||||
|
$this->suggestions[] = array(__FUNCTION__, $file);
|
||||||
|
return __FUNCTION__ == $this->methodName && $file === $this->file;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function claimFile($file) {
|
||||||
|
$this->suggestions[] = array(__FUNCTION__, $file);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function claimPath($file) {
|
||||||
|
$this->suggestions[] = array(__FUNCTION__, $file);
|
||||||
|
return __FUNCTION__ == $this->methodName && $file === $this->file;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,145 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\ClassFinder\InjectedApi;
|
||||||
|
|
||||||
|
use Drupal\xautoload\Util;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To help testability, we use an injected API instead of just a return value.
|
||||||
|
* The injected API can be mocked to provide a mocked file_exists(), and to
|
||||||
|
* monitor all suggested candidates, not just the correct return value.
|
||||||
|
*/
|
||||||
|
class FindFileInjectedApi extends AbstractInjectedApi {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
* The file that was found.
|
||||||
|
*/
|
||||||
|
protected $file;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When the process has finished, use this to return the result.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* The file that is supposed to declare the class.
|
||||||
|
*/
|
||||||
|
function getFile() {
|
||||||
|
return $this->file;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Suggest a file that, if the file exists,
|
||||||
|
* has to declare the class we are looking for.
|
||||||
|
* Only keep the class on success.
|
||||||
|
*
|
||||||
|
* @param string $file
|
||||||
|
* The file that is supposed to declare the class.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* TRUE, if the file exists.
|
||||||
|
* FALSE, otherwise.
|
||||||
|
*/
|
||||||
|
function suggestFile($file) {
|
||||||
|
if (file_exists($file)) {
|
||||||
|
$this->file = $file;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as suggestFile(), but skip the file_exists(),
|
||||||
|
* assuming that we already know the file exists.
|
||||||
|
*
|
||||||
|
* This could make sense if a plugin already did the file_exists() check.
|
||||||
|
*
|
||||||
|
* @param string $file
|
||||||
|
* The file that is supposed to declare the class.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* TRUE, if the file was found - which is always.
|
||||||
|
*/
|
||||||
|
function suggestFile_skipFileExists($file) {
|
||||||
|
$this->file = $file;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as suggestFile(), but assume that file_exists() returns TRUE.
|
||||||
|
*
|
||||||
|
* @param string $file
|
||||||
|
* The file that is supposed to declare the class.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* TRUE, if the file was found - which is always.
|
||||||
|
*/
|
||||||
|
function suggestFile_checkNothing($file) {
|
||||||
|
$this->file = $file;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as suggestFile(), but check the full PHP include path.
|
||||||
|
*
|
||||||
|
* @param string $file
|
||||||
|
* The file that is supposed to declare the class.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* TRUE, if the file exists.
|
||||||
|
* FALSE, otherwise.
|
||||||
|
*/
|
||||||
|
function suggestFile_checkIncludePath($file) {
|
||||||
|
if (FALSE !== $file = Util::findFileInIncludePath($file)) {
|
||||||
|
$this->file = $file;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function guessFile($file) {
|
||||||
|
// The file must be included, or else we can't know if it defines the class.
|
||||||
|
require_once $file;
|
||||||
|
if (Util::classIsDefined($this->className)) {
|
||||||
|
$this->file = $file;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function guessPath($file) {
|
||||||
|
if (file_exists($file)) {
|
||||||
|
// The file must be included, or else we can't know if it defines the class.
|
||||||
|
require_once $file;
|
||||||
|
if (Util::classIsDefined($this->className)) {
|
||||||
|
$this->file = $file;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function claimFile($file) {
|
||||||
|
$this->file = $file;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function claimPath($file) {
|
||||||
|
if (file_exists($file)) {
|
||||||
|
$this->file = $file;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,138 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\ClassFinder\InjectedApi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To help testability, we use an injected API instead of just a return value.
|
||||||
|
* The injected API can be mocked to provide a mocked file_exists(), and to
|
||||||
|
* monitor all suggested candidates, not just the correct return value.
|
||||||
|
*/
|
||||||
|
interface InjectedApiInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is done in the injected api object, so we can easily provide a mock
|
||||||
|
* implementation.
|
||||||
|
*/
|
||||||
|
function is_dir($dir);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the name of the class we are looking for.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* The class we are looking for.
|
||||||
|
*/
|
||||||
|
function getClass();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Suggest a file that, if the file exists,
|
||||||
|
* has to declare the class we are looking for.
|
||||||
|
* Only keep the class on success.
|
||||||
|
*
|
||||||
|
* @param string $file
|
||||||
|
* The file that is supposed to declare the class.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* TRUE, if the file exists.
|
||||||
|
* FALSE, otherwise.
|
||||||
|
*/
|
||||||
|
function suggestFile($file);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as suggestFile(), but skip the file_exists(),
|
||||||
|
* assuming that we already know the file exists.
|
||||||
|
*
|
||||||
|
* This could make sense if a plugin already did the file_exists() check.
|
||||||
|
*
|
||||||
|
* @param string $file
|
||||||
|
* The file that is supposed to declare the class.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* TRUE, if the file was found - which is always.
|
||||||
|
*/
|
||||||
|
function suggestFile_skipFileExists($file);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as suggestFile(), but assume that file_exists() returns TRUE.
|
||||||
|
*
|
||||||
|
* @param string $file
|
||||||
|
* The file that is supposed to declare the class.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* TRUE, if the file was found - which is always.
|
||||||
|
*/
|
||||||
|
function suggestFile_checkNothing($file);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as suggestFile(), but check the full PHP include path.
|
||||||
|
*
|
||||||
|
* @param string $file
|
||||||
|
* The file that is supposed to declare the class.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* TRUE, if the file exists.
|
||||||
|
* FALSE, otherwise.
|
||||||
|
*/
|
||||||
|
function suggestFile_checkIncludePath($file);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Suggest a file that MUST exists, and that MAY declare the class we are
|
||||||
|
* looking for.
|
||||||
|
*
|
||||||
|
* This is useful if a plugin already did the is_file() check by itself.
|
||||||
|
*
|
||||||
|
* @param string $file
|
||||||
|
* The file that is supposed to declare the class.
|
||||||
|
*
|
||||||
|
* @return boolean|NULL
|
||||||
|
* TRUE, if we are not interested in further candidates.
|
||||||
|
* FALSE|NULL, if we are interested in further candidates.
|
||||||
|
*/
|
||||||
|
function guessFile($file);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Suggest a file that MAY exist, and that MAY declare the class we are
|
||||||
|
* looking for.
|
||||||
|
*
|
||||||
|
* This is useful if a plugin already did the is_file() check by itself.
|
||||||
|
*
|
||||||
|
* @param string $file
|
||||||
|
* The file that is supposed to declare the class.
|
||||||
|
*
|
||||||
|
* @return boolean|NULL
|
||||||
|
* TRUE, if we are not interested in further candidates.
|
||||||
|
* FALSE|NULL, if we are interested in further candidates.
|
||||||
|
*/
|
||||||
|
function guessPath($file);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Suggest a file that MUST exist, and if so, MUST declare the class we are
|
||||||
|
* looking for.
|
||||||
|
*
|
||||||
|
* This is useful if a plugin already did the is_file() check by itself.
|
||||||
|
*
|
||||||
|
* @param string $file
|
||||||
|
* The file that is supposed to declare the class.
|
||||||
|
*
|
||||||
|
* @return boolean|NULL
|
||||||
|
* TRUE, if we are not interested in further candidates.
|
||||||
|
* FALSE|NULL, if we are interested in further candidates.
|
||||||
|
*/
|
||||||
|
function claimFile($file);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Suggest a file that MAY exist, and if so, MUST declare the class we are
|
||||||
|
* looking for.
|
||||||
|
*
|
||||||
|
* This is useful if a plugin already did the is_file() check by itself.
|
||||||
|
*
|
||||||
|
* @param string $file
|
||||||
|
* The file that is supposed to declare the class.
|
||||||
|
*
|
||||||
|
* @return boolean|NULL
|
||||||
|
* TRUE, if we are not interested in further candidates.
|
||||||
|
* FALSE|NULL, if we are interested in further candidates.
|
||||||
|
*/
|
||||||
|
function claimPath($file);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,135 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\ClassFinder\InjectedApi;
|
||||||
|
|
||||||
|
use Drupal\xautoload\Util;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To help testability, we use an injected API instead of just a return value.
|
||||||
|
* The injected API can be mocked to provide a mocked file_exists(), and to
|
||||||
|
* monitor all suggested candidates, not just the correct return value.
|
||||||
|
*/
|
||||||
|
class LoadClassGetFileInjectedApi extends FindFileInjectedApi {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Suggest a file that, if the file exists,
|
||||||
|
* has to declare the class we are looking for.
|
||||||
|
* Only keep the class on success.
|
||||||
|
*
|
||||||
|
* @param string $file
|
||||||
|
* The file that is supposed to declare the class.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* TRUE, if the file exists.
|
||||||
|
* FALSE, otherwise.
|
||||||
|
*/
|
||||||
|
function suggestFile($file) {
|
||||||
|
if (file_exists($file)) {
|
||||||
|
$this->file = $file;
|
||||||
|
require $file;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as suggestFile(), but skip the file_exists(),
|
||||||
|
* assuming that we already know the file exists.
|
||||||
|
*
|
||||||
|
* This could make sense if a plugin already did the file_exists() check.
|
||||||
|
*
|
||||||
|
* @param string $file
|
||||||
|
* The file that is supposed to declare the class.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* TRUE, if the file was found - which is always.
|
||||||
|
*/
|
||||||
|
function suggestFile_skipFileExists($file) {
|
||||||
|
$this->file = $file;
|
||||||
|
require $file;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as suggestFile(), but assume that file_exists() returns TRUE.
|
||||||
|
*
|
||||||
|
* @param string $file
|
||||||
|
* The file that is supposed to declare the class.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* TRUE, if the file was found - which is always.
|
||||||
|
*/
|
||||||
|
function suggestFile_checkNothing($file) {
|
||||||
|
$this->file = $file;
|
||||||
|
require $file;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as suggestFile(), but check the full PHP include path.
|
||||||
|
*
|
||||||
|
* @param string $file
|
||||||
|
* The file that is supposed to declare the class.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* TRUE, if the file exists.
|
||||||
|
* FALSE, otherwise.
|
||||||
|
*/
|
||||||
|
function suggestFile_checkIncludePath($file) {
|
||||||
|
if (FALSE !== $file = Util::findFileInIncludePath($file)) {
|
||||||
|
$this->file = $file;
|
||||||
|
require $file;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function guessFile($file) {
|
||||||
|
require_once $file;
|
||||||
|
if (Util::classIsDefined($this->className)) {
|
||||||
|
$this->file = $file;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function guessPath($file) {
|
||||||
|
if (file_exists($file)) {
|
||||||
|
require_once $file;
|
||||||
|
if (Util::classIsDefined($this->className)) {
|
||||||
|
$this->file = $file;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function claimFile($file) {
|
||||||
|
require $file;
|
||||||
|
$this->file = $file;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function claimPath($file) {
|
||||||
|
if (file_exists($file)) {
|
||||||
|
require $file;
|
||||||
|
$this->file = $file;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,105 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\ClassFinder\InjectedApi;
|
||||||
|
|
||||||
|
use Drupal\xautoload\Util;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To help testability, we use an injected API instead of just a return value.
|
||||||
|
* The injected API can be mocked to provide a mocked file_exists(), and to
|
||||||
|
* monitor all suggested candidates, not just the correct return value.
|
||||||
|
*/
|
||||||
|
class LoadClassInjectedAPI extends AbstractInjectedApi {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function suggestFile($file) {
|
||||||
|
if (file_exists($file)) {
|
||||||
|
require $file;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function suggestFile_skipFileExists($file) {
|
||||||
|
require $file;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function suggestFile_checkNothing($file) {
|
||||||
|
require $file;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function suggestFile_checkIncludePath($file) {
|
||||||
|
if (FALSE !== $file = Util::findFileInIncludePath($file)) {
|
||||||
|
require $file;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function guessFile($file) {
|
||||||
|
require_once $file;
|
||||||
|
|
||||||
|
return Util::classIsDefined($this->className);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function guessPath($file) {
|
||||||
|
if (file_exists($file)) {
|
||||||
|
require_once $file;
|
||||||
|
|
||||||
|
return Util::classIsDefined($this->className);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function claimFile($file) {
|
||||||
|
require $file;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function claimPath($file) {
|
||||||
|
if (file_exists($file)) {
|
||||||
|
require $file;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,79 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\ClassFinder\Plugin;
|
||||||
|
|
||||||
|
use Drupal\xautoload\ClassFinder\InjectedApi\InjectedApiInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see _registry_check_code()
|
||||||
|
*/
|
||||||
|
class DrupalCoreRegistryPlugin implements FinderPluginInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $baseDir;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $baseDir
|
||||||
|
*/
|
||||||
|
function __construct($baseDir) {
|
||||||
|
$this->baseDir = $baseDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the file for a class that in PSR-0 or PEAR would be in
|
||||||
|
* $psr_0_root . '/' . $path_fragment . $path_suffix
|
||||||
|
*
|
||||||
|
* E.g.:
|
||||||
|
* - The class we look for is Some\Namespace\Some\Class
|
||||||
|
* - The file is actually in "exotic/location.php". This is not following
|
||||||
|
* PSR-0 or PEAR standard, so we need a plugin.
|
||||||
|
* -> The class finder will transform the class name to
|
||||||
|
* "Some/Namespace/Some/Class.php"
|
||||||
|
* - The plugin was registered for the namespace "Some\Namespace". This is
|
||||||
|
* because all those exotic classes all begin with Some\Namespace\
|
||||||
|
* -> The arguments will be:
|
||||||
|
* ($api = the API object, see below)
|
||||||
|
* $logical_base_path = "Some/Namespace/"
|
||||||
|
* $relative_path = "Some/Class.php"
|
||||||
|
* $api->getClass() gives the original class name, if we still need it.
|
||||||
|
* -> We are supposed to:
|
||||||
|
* if ($api->suggestFile('exotic/location.php')) {
|
||||||
|
* return TRUE;
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @param InjectedApiInterface $api
|
||||||
|
* An object with a suggestFile() method.
|
||||||
|
* We are supposed to suggest files until suggestFile() returns TRUE, or we
|
||||||
|
* have no more suggestions.
|
||||||
|
* @param string $logical_base_path_empty
|
||||||
|
* The key that this plugin was registered with.
|
||||||
|
* With trailing '/'.
|
||||||
|
* @param string $relative_path_irrelevant
|
||||||
|
* Second part of the canonical path, ending with '.php'.
|
||||||
|
*
|
||||||
|
* @return bool|null
|
||||||
|
* TRUE, if the file was found.
|
||||||
|
* FALSE or NULL, otherwise.
|
||||||
|
*/
|
||||||
|
function findFile($api, $logical_base_path_empty, $relative_path_irrelevant) {
|
||||||
|
$q = db_select('registry');
|
||||||
|
// Use LIKE here to make the query case-insensitive.
|
||||||
|
$q->condition('name', db_like($api->getClass()), 'LIKE');
|
||||||
|
$q->addField('registry', 'filename');
|
||||||
|
$stmt = $q->execute();
|
||||||
|
while ($relative_path = $stmt->fetchField()) {
|
||||||
|
$file = $this->baseDir . $relative_path;
|
||||||
|
// Attention: The db_select() above can trigger the class loader for
|
||||||
|
// classes and interfaces of the database layer. This can cause some files
|
||||||
|
// to be included twice, if the file defines more than one class.
|
||||||
|
// So we need to use require_once here, instead of require. That is, use
|
||||||
|
// guessFile() instead of claimFile().
|
||||||
|
if ($api->guessFile($file)) {
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,219 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\ClassFinder\Plugin;
|
||||||
|
|
||||||
|
use Drupal\xautoload\ClassFinder\GenericPrefixMap;
|
||||||
|
use Drupal\xautoload\DirectoryBehavior\DefaultDirectoryBehavior;
|
||||||
|
use Drupal\xautoload\DirectoryBehavior\Psr0DirectoryBehavior;
|
||||||
|
use Drupal\xautoload\DrupalSystem\DrupalSystemInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* There are different dimensions of state for each module:
|
||||||
|
*
|
||||||
|
* 1) Classes outside of Drupal\\$modulename\\Tests\\
|
||||||
|
* a) We don't know yet whether these classes are using PSR-0, PSR-4,
|
||||||
|
* PEAR-Flat, or none of these.
|
||||||
|
* b) We know these classes use PSR-0 only.
|
||||||
|
* c) We know these classes use PSR-4 only.
|
||||||
|
* d) We know these classes use PEAR-Flat only.
|
||||||
|
*
|
||||||
|
* 2) Classes inside Drupal\\$modulename\\Tests\\
|
||||||
|
* a) We don't know yet whether these classes are using PSR-0, PSR-4, or none
|
||||||
|
* of these.
|
||||||
|
* b) We know these classes all use PSR-0.
|
||||||
|
* c) We know these classes all use PSR-4.
|
||||||
|
*
|
||||||
|
* Any combination of a state from (1) with a state from (2) is possible.
|
||||||
|
*
|
||||||
|
* The state could even change during the execution of the findClass() method,
|
||||||
|
* due to another autoloader instance being fired during a file inclusion, e.g.
|
||||||
|
* for a base class.
|
||||||
|
*/
|
||||||
|
class DrupalExtensionNamespaceFinderPlugin implements FinderPluginInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
* E.g. 'theme' or 'module'.
|
||||||
|
*/
|
||||||
|
protected $type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Namespace map used in the class finder for PSR-0/4-like mappings.
|
||||||
|
*
|
||||||
|
* @var GenericPrefixMap
|
||||||
|
*/
|
||||||
|
protected $namespaceMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prefix map used in the class finder for PEAR-like mappings.
|
||||||
|
*
|
||||||
|
* @var GenericPrefixMap
|
||||||
|
*/
|
||||||
|
protected $prefixMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Directory behavior for PSR-4.
|
||||||
|
*
|
||||||
|
* @var DefaultDirectoryBehavior
|
||||||
|
*/
|
||||||
|
protected $defaultBehavior;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Directory behavior with the special underscore handling for PSR-0.
|
||||||
|
*
|
||||||
|
* @var Psr0DirectoryBehavior
|
||||||
|
*/
|
||||||
|
protected $psr0Behavior;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var DrupalSystemInterface
|
||||||
|
*/
|
||||||
|
protected $system;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $type
|
||||||
|
* E.g. 'theme' or 'module'.
|
||||||
|
* @param GenericPrefixMap $namespace_map
|
||||||
|
* @param GenericPrefixMap $prefix_map
|
||||||
|
* @param DrupalSystemInterface $system
|
||||||
|
*/
|
||||||
|
function __construct($type, $namespace_map, $prefix_map, $system) {
|
||||||
|
$this->type = $type;
|
||||||
|
$this->prefixMap = $prefix_map;
|
||||||
|
$this->namespaceMap = $namespace_map;
|
||||||
|
$this->defaultBehavior = new DefaultDirectoryBehavior();
|
||||||
|
$this->psr0Behavior = new Psr0DirectoryBehavior();
|
||||||
|
$this->system = $system;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Looks up a class starting with "Drupal\$extension_name\\".
|
||||||
|
*
|
||||||
|
* This plugin method will be called for every class beginning with
|
||||||
|
* "Drupal\\$extension_name\\", as long as the plugin is registered for
|
||||||
|
* $logical_base_path = 'Drupal/$extension_name/'.
|
||||||
|
*
|
||||||
|
* A similar plugin will is registered along with this one for the PEAR-FLAT
|
||||||
|
* pattern, called for every class beginning with $modulename . '_'.
|
||||||
|
*
|
||||||
|
* The plugin will eventually unregister itself and its cousin, once it has
|
||||||
|
* - determined the correct path for the module, and
|
||||||
|
* - determined that the module is using either PSR-0 or PSR-4.
|
||||||
|
* It does that by including the file candidate for PSR-0 and/or PSR-4 and
|
||||||
|
* checking whether the class is now defined.
|
||||||
|
*
|
||||||
|
* The plugin will instead register a direct
|
||||||
|
*
|
||||||
|
* @param \Drupal\xautoload\ClassFinder\InjectedApi\InjectedApiInterface $api
|
||||||
|
* An object with methods like suggestFile() and guessFile().
|
||||||
|
* @param string $logical_base_path
|
||||||
|
* The logical base path determined from the registered namespace.
|
||||||
|
* E.g. 'Drupal/menupoly/'.
|
||||||
|
* @param string $relative_path
|
||||||
|
* Remaining part of the logical path following $logical_base_path.
|
||||||
|
* E.g. 'FooNamespace/BarClass.php'.
|
||||||
|
* @param string|null $extension_name
|
||||||
|
* Second key that the plugin was registered with. Usually this would be the
|
||||||
|
* physical base directory where we prepend the relative path to get the
|
||||||
|
* file path. But in this case it is simply the extensions name.
|
||||||
|
* E.g. 'menupoly'.
|
||||||
|
*
|
||||||
|
* @return bool|null
|
||||||
|
* TRUE, if the file was found.
|
||||||
|
* FALSE or NULL, otherwise.
|
||||||
|
*/
|
||||||
|
function findFile($api, $logical_base_path, $relative_path, $extension_name = NULL) {
|
||||||
|
|
||||||
|
$extension_file = $this->system->drupalGetFilename($this->type, $extension_name);
|
||||||
|
if (empty($extension_file)) {
|
||||||
|
// Extension does not exist, or is not installed.
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
$nspath = 'Drupal/' . $extension_name . '/';
|
||||||
|
$testpath = $nspath . 'Tests/';
|
||||||
|
$uspath = $extension_name . '/';
|
||||||
|
$extension_dir = dirname($extension_file);
|
||||||
|
$src = $extension_dir . '/src/';
|
||||||
|
$lib_psr0 = $extension_dir . '/lib/Drupal/' . $extension_name . '/';
|
||||||
|
$is_test_class = (0 === strpos($relative_path, 'Tests/'));
|
||||||
|
|
||||||
|
// Try PSR-4.
|
||||||
|
if ($api->guessPath($src . $relative_path)) {
|
||||||
|
if ($is_test_class) {
|
||||||
|
// Register PSR-0 directory for "Drupal\\$modulename\\Tests\\"
|
||||||
|
// This generally happens only once per module, because for subsequent
|
||||||
|
// test classes the class will be found before this plugin is triggered.
|
||||||
|
// However, for class_exists() with nonexistent test files, this line
|
||||||
|
// will occur more than once.
|
||||||
|
$this->namespaceMap->registerDeepPath($testpath, $src . 'Tests/', $this->defaultBehavior);
|
||||||
|
// We found the class, but it is a test class, so it does not tell us
|
||||||
|
// anything about whether non-test classes are in PSR-0 or PSR-4.
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register PSR-4 directory for "Drupal\\$modulename\\".
|
||||||
|
$this->namespaceMap->registerDeepPath($nspath, $src, $this->defaultBehavior);
|
||||||
|
|
||||||
|
// Unregister the lazy plugins, including this one, for
|
||||||
|
// "Drupal\\$modulename\\" and for $modulename . '_'.
|
||||||
|
$this->namespaceMap->unregisterDeepPath($nspath, $extension_name);
|
||||||
|
$this->prefixMap->unregisterDeepPath($uspath, $extension_name);
|
||||||
|
|
||||||
|
// Test classes in PSR-4 are already covered by the PSR-4 plugin we just
|
||||||
|
// registered. But test classes in PSR-0 would slip through. So we check
|
||||||
|
// if a separate behavior needs to be registered for those.
|
||||||
|
if (is_dir($lib_psr0 . 'Tests/')) {
|
||||||
|
$this->namespaceMap->registerDeepPath($testpath, $lib_psr0 . 'Tests/', $this->psr0Behavior);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The class was found, so return TRUE.
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build PSR-0 relative path.
|
||||||
|
if (FALSE === $nspos = strrpos($relative_path, '/')) {
|
||||||
|
// No namespace separators in $relative_path, so all underscores must be
|
||||||
|
// replaced.
|
||||||
|
$relative_path = str_replace('_', '/', $relative_path);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Replace only those underscores in $relative_path after the last
|
||||||
|
// namespace separator, from right to left. On average there is no or very
|
||||||
|
// few of them, so this loop rarely iterates even once.
|
||||||
|
while ($nspos < $uspos = strrpos($relative_path, '_')) {
|
||||||
|
$relative_path[$uspos] = '/';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try PSR-0
|
||||||
|
if ($api->guessPath($lib_psr0 . $relative_path)) {
|
||||||
|
if ($is_test_class) {
|
||||||
|
// We know now that there are test classes using PSR-0.
|
||||||
|
$this->namespaceMap->registerDeepPath($testpath, $lib_psr0 . 'Tests/', $this->psr0Behavior);
|
||||||
|
// We found the class, but it is a test class, so it does not tell us
|
||||||
|
// anything about whether non-test classes are in PSR-0 or PSR-4.
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unregister the lazy plugins, including this one.
|
||||||
|
$this->namespaceMap->unregisterDeepPath($nspath, $extension_name);
|
||||||
|
$this->prefixMap->unregisterDeepPath($uspath, $extension_name);
|
||||||
|
|
||||||
|
// Register PSR-0 for regular namespaced classes.
|
||||||
|
$this->namespaceMap->registerDeepPath($nspath, $lib_psr0, $this->psr0Behavior);
|
||||||
|
|
||||||
|
// Test classes in PSR-0 are already covered by the PSR-0 plugin we just
|
||||||
|
// registered. But test classes in PSR-4 would slip through. So we check
|
||||||
|
// if a separate behavior needs to be registered for those.
|
||||||
|
# if (is_dir($src . 'Tests/')) {
|
||||||
|
# $this->namespaceMap->registerDeepPath($testpath, $src . 'Tests/', $this->psr0Behavior);
|
||||||
|
# }
|
||||||
|
|
||||||
|
// The class was found, so return TRUE.
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\ClassFinder\Plugin;
|
||||||
|
|
||||||
|
class DrupalExtensionUnderscoreFinderPlugin extends DrupalExtensionNamespaceFinderPlugin {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function findFile($api, $logical_base_path, $relative_path, $extension_name = NULL) {
|
||||||
|
|
||||||
|
$extension_file = $this->system->drupalGetFilename($this->type, $extension_name);
|
||||||
|
|
||||||
|
if (empty($extension_file)) {
|
||||||
|
// Extension does not exist, or is not installed.
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
$nspath = 'Drupal/' . $extension_name . '/';
|
||||||
|
$testpath = $nspath . 'Tests/';
|
||||||
|
$uspath = $extension_name . '/';
|
||||||
|
$lib = dirname($extension_file) . '/lib/';
|
||||||
|
$lib_psr0 = $lib . 'Drupal/' . $extension_name . '/';
|
||||||
|
|
||||||
|
// Try PEAR-Flat.
|
||||||
|
if ($api->guessPath($lib . $relative_path)) {
|
||||||
|
// Register PEAR-Flat.
|
||||||
|
$this->prefixMap->registerDeepPath($uspath, $lib, $this->defaultBehavior);
|
||||||
|
// Unregister the lazy plugins.
|
||||||
|
$this->namespaceMap->unregisterDeepPath($nspath, $extension_name);
|
||||||
|
$this->prefixMap->unregisterDeepPath($uspath, $extension_name);
|
||||||
|
// See if there are PSR-0 or PSR-4 test classes.
|
||||||
|
if (is_dir($lib_psr0 . 'Tests/')) {
|
||||||
|
$this->namespaceMap->registerDeepPath(
|
||||||
|
$testpath,
|
||||||
|
$lib_psr0 . 'Tests/',
|
||||||
|
$this->psr0Behavior);
|
||||||
|
}
|
||||||
|
if (is_dir($lib . 'Tests/')) {
|
||||||
|
$this->namespaceMap->registerDeepPath(
|
||||||
|
$testpath,
|
||||||
|
$lib . 'Tests/',
|
||||||
|
$this->defaultBehavior);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The class was found, so return TRUE.
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The class was not found, so return FALSE.
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,65 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\ClassFinder\Plugin;
|
||||||
|
|
||||||
|
use Drupal\xautoload\ClassFinder\InjectedApi\InjectedApiInterface;
|
||||||
|
use xautoload_FinderPlugin_Interface;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* X Autoload plugins are for:
|
||||||
|
* - More exotic autoload patterns that are incompatible with PSR-0 or PEAR
|
||||||
|
* - Situations where we don't want to register a ton of namespaces, and using
|
||||||
|
* a plugin instead gives us performance benefits.
|
||||||
|
*/
|
||||||
|
interface FinderPluginInterface extends xautoload_FinderPlugin_Interface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the file for a class that in PSR-0 or PEAR would be in
|
||||||
|
* $psr_0_root . '/' . $path_fragment . $path_suffix
|
||||||
|
*
|
||||||
|
* E.g.:
|
||||||
|
* - The class we look for is Some\Namespace\Some\Class
|
||||||
|
* - The file is actually in "exotic/location.php". This is not following
|
||||||
|
* PSR-0 or PEAR standard, so we need a plugin.
|
||||||
|
* -> The class finder will transform the class name to
|
||||||
|
* "Some/Namespace/Some/Class.php"
|
||||||
|
* - The plugin was registered for the namespace "Some\Namespace". This is
|
||||||
|
* because all those exotic classes all begin with Some\Namespace\
|
||||||
|
* -> The arguments will be:
|
||||||
|
* ($api = the API object, see below)
|
||||||
|
* $path_fragment = "Some/Namespace/"
|
||||||
|
* $path_suffix = "Some/Class.php"
|
||||||
|
* $api->getClass() gives the original class name, if we still need it.
|
||||||
|
* -> We are supposed to:
|
||||||
|
* if ($api->suggestFile('exotic/location.php')) {
|
||||||
|
* return TRUE;
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @param InjectedApiInterface $api
|
||||||
|
* An object with a suggestFile() method.
|
||||||
|
* We are supposed to suggest files until suggestFile() returns TRUE, or we
|
||||||
|
* have no more suggestions.
|
||||||
|
* @param string $path_fragment
|
||||||
|
* The key that this plugin was registered with.
|
||||||
|
* With trailing '/'.
|
||||||
|
* @param string $path_suffix
|
||||||
|
* Second part of the canonical path, ending with '.php'.
|
||||||
|
* @param int|string $id
|
||||||
|
* Id under which the plugin was registered.
|
||||||
|
* This may be a numeric id, or a string key.
|
||||||
|
*
|
||||||
|
* @return bool|null
|
||||||
|
* TRUE, if the file was found.
|
||||||
|
* FALSE, otherwise.
|
||||||
|
*
|
||||||
|
* NOTE:
|
||||||
|
* The signature of this method has changed since the legacy base interface,
|
||||||
|
* with a new optional parameter being added.
|
||||||
|
* Due to a bug in PHP 5.3.0 - 5.3.8, redeclaring the method with the
|
||||||
|
* modified signature would result in a fatal error in these PHP versions.
|
||||||
|
* This is why the method is commented out.
|
||||||
|
* The additional optional parameter can still be added in implementations.
|
||||||
|
*/
|
||||||
|
# function findFile($api, $path_fragment, $path_suffix, $id = NULL);
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\ClassFinder\Plugin;
|
||||||
|
|
||||||
|
use Drupal\xautoload\ClassFinder\InjectedApi\FindFileInjectedApi;
|
||||||
|
|
||||||
|
class Psr4FinderPlugin implements FinderPluginInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param FindFileInjectedApi $api
|
||||||
|
* An object with a suggestFile() method.
|
||||||
|
* We are supposed to suggest files until suggestFile() returns TRUE, or we
|
||||||
|
* have no more suggestions.
|
||||||
|
* @param string $logical_base_path
|
||||||
|
* The key that this plugin was registered with.
|
||||||
|
* With trailing '/'.
|
||||||
|
* @param string $relative_path
|
||||||
|
* Second part of the canonical path, ending with '.php'.
|
||||||
|
* @param int|string $base_dir
|
||||||
|
* Id under which the plugin was registered.
|
||||||
|
* This should be the PSR-4 base directory.
|
||||||
|
*
|
||||||
|
* @return bool|null
|
||||||
|
* TRUE, if the file was found.
|
||||||
|
* FALSE, otherwise.
|
||||||
|
*/
|
||||||
|
function findFile($api, $logical_base_path, $relative_path, $base_dir = NULL) {
|
||||||
|
// The $relative_path has the replacements from PSR-0, which we don't want.
|
||||||
|
// So we need to re-calculate it.
|
||||||
|
$relative_classname = substr($api->getClass(), strlen($logical_base_path));
|
||||||
|
$relative_path = str_replace('\\', '/', $relative_classname) . '.php';
|
||||||
|
return $api->suggestFile($base_dir . '/' . $relative_path);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,90 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\ClassFinder;
|
||||||
|
|
||||||
|
use Drupal\xautoload\ClassLoader\AbstractClassLoader;
|
||||||
|
use Drupal\xautoload\CacheMissObserver\CacheMissObserverInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A placeholder class finder. Used to postpone expensive operations until they
|
||||||
|
* are actually needed.
|
||||||
|
*/
|
||||||
|
class ProxyClassFinder extends AbstractClassLoader implements ClassFinderInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ExtendedClassFinderInterface
|
||||||
|
* The actual class finder.
|
||||||
|
*/
|
||||||
|
protected $finder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var CacheMissObserverInterface[]
|
||||||
|
* Operations to run when the actual finder is initialized.
|
||||||
|
*/
|
||||||
|
protected $cacheMissObservers = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected $initialized = FALSE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ExtendedClassFinderInterface $finder
|
||||||
|
*
|
||||||
|
* @internal param \Drupal\xautoload\Adapter\DrupalExtensionAdapter $helper
|
||||||
|
*/
|
||||||
|
function __construct($finder) {
|
||||||
|
$this->finder = $finder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function loadClass($class) {
|
||||||
|
$this->initFinder();
|
||||||
|
$this->finder->loadClass($class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function apiFindFile($api, $class) {
|
||||||
|
$this->initFinder();
|
||||||
|
|
||||||
|
return $this->finder->apiFindFile($api, $class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param CacheMissObserverInterface $observer
|
||||||
|
*/
|
||||||
|
function observeFirstCacheMiss($observer) {
|
||||||
|
if (!$this->initialized) {
|
||||||
|
$this->cacheMissObservers[] = $observer;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$observer->cacheMiss($this->finder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return ClassFinderInterface
|
||||||
|
*/
|
||||||
|
function getFinder() {
|
||||||
|
$this->initFinder();
|
||||||
|
|
||||||
|
return $this->finder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the finder and notify cache miss observers.
|
||||||
|
*/
|
||||||
|
protected function initFinder() {
|
||||||
|
if (!$this->initialized) {
|
||||||
|
$this->initialized = TRUE;
|
||||||
|
foreach ($this->cacheMissObservers as $operation) {
|
||||||
|
$operation->cacheMiss($this->finder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\ClassLoader;
|
||||||
|
|
||||||
|
use Drupal\xautoload\CacheManager\CacheManagerObserverInterface;
|
||||||
|
use Drupal\xautoload\ClassFinder\ClassFinderInterface;
|
||||||
|
use Drupal\xautoload\CacheManager\CacheManager;
|
||||||
|
|
||||||
|
abstract class AbstractCachedClassLoader
|
||||||
|
extends AbstractClassLoaderDecorator
|
||||||
|
implements CacheManagerObserverInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $prefix;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method has side effects, so it is not the constructor.
|
||||||
|
*
|
||||||
|
* @param ClassFinderInterface $finder
|
||||||
|
* @param CacheManager $cacheManager
|
||||||
|
*
|
||||||
|
* @return self
|
||||||
|
*
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
static function create($finder, $cacheManager) {
|
||||||
|
/** @var self $loader */
|
||||||
|
$loader = new static($finder);
|
||||||
|
if (!$loader->checkRequirements()) {
|
||||||
|
$class = get_class($loader);
|
||||||
|
throw new CacheNotSupportedException("Unable to use $class, because the respetive PHP extension is not enabled.");
|
||||||
|
}
|
||||||
|
$cacheManager->observeCachePrefix($loader);
|
||||||
|
|
||||||
|
return $loader;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected abstract function checkRequirements();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function setCachePrefix($prefix) {
|
||||||
|
$this->prefix = $prefix;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\ClassLoader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Behaves mostly like the Symfony ClassLoader classes.
|
||||||
|
*/
|
||||||
|
abstract class AbstractClassLoader implements ClassLoaderInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers this instance as an autoloader.
|
||||||
|
*
|
||||||
|
* @param boolean $prepend
|
||||||
|
* If TRUE, the loader will be prepended. Otherwise, it will be appended.
|
||||||
|
*/
|
||||||
|
function register($prepend = FALSE) {
|
||||||
|
// http://www.php.net/manual/de/function.spl-autoload-register.php#107362
|
||||||
|
// "when specifying the third parameter (prepend), the function will fail badly in PHP 5.2"
|
||||||
|
if (version_compare(PHP_VERSION, '5.3.0') >= 0) {
|
||||||
|
spl_autoload_register(array($this, 'loadClass'), TRUE, $prepend);
|
||||||
|
}
|
||||||
|
elseif ($prepend) {
|
||||||
|
$loaders = spl_autoload_functions();
|
||||||
|
spl_autoload_register(array($this, 'loadClass'));
|
||||||
|
foreach ($loaders as $loader) {
|
||||||
|
spl_autoload_unregister($loader);
|
||||||
|
spl_autoload_register($loader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
spl_autoload_register(array($this, 'loadClass'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregister from the spl autoload stack.
|
||||||
|
*/
|
||||||
|
function unregister() {
|
||||||
|
spl_autoload_unregister(array($this, 'loadClass'));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\ClassLoader;
|
||||||
|
|
||||||
|
use Drupal\xautoload\ClassFinder\ClassFinderInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Behaves mostly like the Symfony ClassLoader classes.
|
||||||
|
*/
|
||||||
|
abstract class AbstractClassLoaderDecorator extends AbstractClassLoader {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ClassFinderInterface
|
||||||
|
*/
|
||||||
|
protected $finder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ClassFinderInterface $finder
|
||||||
|
* The object that does the actual class finding.
|
||||||
|
*/
|
||||||
|
protected function __construct($finder) {
|
||||||
|
$this->finder = $finder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace the finder with another one.
|
||||||
|
*
|
||||||
|
* @param ClassFinderInterface $finder
|
||||||
|
* The object that does the actual class finding.
|
||||||
|
*/
|
||||||
|
function setFinder($finder) {
|
||||||
|
$this->finder = $finder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function loadClass($class) {
|
||||||
|
$this->finder->loadClass($class);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,137 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\ClassLoader;
|
||||||
|
|
||||||
|
use Drupal\xautoload\CacheManager\CacheManager;
|
||||||
|
use Drupal\xautoload\CacheManager\CacheManagerObserverInterface;
|
||||||
|
use Drupal\xautoload\ClassFinder\ClassFinderInterface;
|
||||||
|
use Drupal\xautoload\ClassFinder\InjectedApi\LoadClassGetFileInjectedApi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bass class for cached class loader decorators where cache entries cannot be
|
||||||
|
* written one by one, but have to be written all at once instead.
|
||||||
|
*
|
||||||
|
* Saving the cache immediately on every cache miss would be too expensive. On
|
||||||
|
* the other hand, saving only at the end of the request might fail if the
|
||||||
|
* request does not end properly, or if some classes are still loaded after the
|
||||||
|
* end-of-process callback.
|
||||||
|
*
|
||||||
|
* The solution is an exponentially growing queue. Cache writing happens not on
|
||||||
|
* every cache miss, but only on the 1st, 3rd, 7th, 15th, 31st, 63rd etc.
|
||||||
|
*
|
||||||
|
* This will result in a "hot" cache after a limited number of requests, and
|
||||||
|
* with a limited number of cache write operations.
|
||||||
|
*/
|
||||||
|
abstract class AbstractQueuedCachedClassLoader
|
||||||
|
extends AbstractClassLoaderDecorator
|
||||||
|
implements CacheManagerObserverInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
private $nMax = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
private $n = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string[]
|
||||||
|
*/
|
||||||
|
private $toBeDeleted = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string[]
|
||||||
|
*/
|
||||||
|
private $toBeAdded = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string[]
|
||||||
|
*/
|
||||||
|
private $classFiles;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method has side effects, so it is not the constructor.
|
||||||
|
*
|
||||||
|
* @param ClassFinderInterface $finder
|
||||||
|
* @param CacheManager $cacheManager
|
||||||
|
*
|
||||||
|
* @return self
|
||||||
|
*
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
static function create($finder, $cacheManager) {
|
||||||
|
/** @var self $loader */
|
||||||
|
$loader = new static($finder);
|
||||||
|
$cacheManager->observeCachePrefix($loader);
|
||||||
|
|
||||||
|
return $loader;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function loadClass($class) {
|
||||||
|
|
||||||
|
// Look if the cache has anything for this class.
|
||||||
|
if (isset($this->classFiles[$class])) {
|
||||||
|
$file = $this->classFiles[$class];
|
||||||
|
// The is_file() check may cost around 0.0045 ms per class file, but this
|
||||||
|
// depends on your system of course.
|
||||||
|
if (is_file($file)) {
|
||||||
|
require $file;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$this->toBeDeleted[$class] = $file;
|
||||||
|
unset($this->classFiles[$class]);
|
||||||
|
++$this->n;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve cache miss.
|
||||||
|
$api = new LoadClassGetFileInjectedApi($class);
|
||||||
|
if ($this->finder->apiFindFile($api, $class)) {
|
||||||
|
// Queue the result for the cache.
|
||||||
|
$this->toBeAdded[$class]
|
||||||
|
= $this->classFiles[$class]
|
||||||
|
= $api->getFile();
|
||||||
|
++$this->n;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save the cache if enough has been queued up.
|
||||||
|
if ($this->n >= $this->nMax) {
|
||||||
|
$this->classFiles = $this->updateClassFiles($this->toBeAdded, $this->toBeDeleted);
|
||||||
|
$this->toBeDeleted = array();
|
||||||
|
$this->toBeAdded = array();
|
||||||
|
$this->nMax *= 2;
|
||||||
|
$this->n = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the new cache prefix after a flush cache.
|
||||||
|
*
|
||||||
|
* @param string $prefix
|
||||||
|
* A prefix for the storage key in APC.
|
||||||
|
*/
|
||||||
|
function setCachePrefix($prefix) {
|
||||||
|
$this->classFiles = $this->loadClassFiles($prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $prefix
|
||||||
|
*
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
abstract protected function loadClassFiles($prefix);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string[] $toBeAdded
|
||||||
|
* @param string[] $toBeRemoved
|
||||||
|
*
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
abstract protected function updateClassFiles($toBeAdded, $toBeRemoved);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\ClassLoader;
|
||||||
|
|
||||||
|
use Drupal\xautoload\CacheManager\CacheManagerObserverInterface;
|
||||||
|
use Drupal\xautoload\ClassFinder\InjectedApi\LoadClassGetFileInjectedApi;
|
||||||
|
|
||||||
|
class ApcClassLoader extends AbstractCachedClassLoader implements CacheManagerObserverInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function checkRequirements() {
|
||||||
|
return extension_loaded('apc') && function_exists('apc_store');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function loadClass($class) {
|
||||||
|
|
||||||
|
// Look if the cache has anything for this class.
|
||||||
|
if ($file = apc_fetch($this->prefix . $class)) {
|
||||||
|
if (is_file($file)) {
|
||||||
|
require $file;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
apc_delete($this->prefix . $class);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve cache miss.
|
||||||
|
$api = new LoadClassGetFileInjectedApi($class);
|
||||||
|
if ($this->finder->apiFindFile($api, $class)) {
|
||||||
|
apc_store($this->prefix . $class, $api->getFile());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\ClassLoader;
|
||||||
|
|
||||||
|
use Drupal\xautoload\CacheManager\CacheManagerObserverInterface;
|
||||||
|
use Drupal\xautoload\ClassFinder\InjectedApi\LoadClassGetFileInjectedApi;
|
||||||
|
|
||||||
|
class ApcuClassLoader extends AbstractCachedClassLoader implements CacheManagerObserverInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function checkRequirements() {
|
||||||
|
return extension_loaded('apcu') && function_exists('apcu_store');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function loadClass($class) {
|
||||||
|
|
||||||
|
// Look if the cache has anything for this class.
|
||||||
|
if ($file = \apcu_fetch($this->prefix . $class)) {
|
||||||
|
if (is_file($file)) {
|
||||||
|
require $file;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
\apcu_delete($this->prefix . $class);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve cache miss.
|
||||||
|
$api = new LoadClassGetFileInjectedApi($class);
|
||||||
|
if ($this->finder->apiFindFile($api, $class)) {
|
||||||
|
\apcu_store($this->prefix . $class, $api->getFile());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\ClassLoader;
|
||||||
|
|
||||||
|
class ApcuQueuedCachedClassLoader extends AbstractQueuedCachedClassLoader {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $prefix;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $prefix
|
||||||
|
*
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
protected function loadClassFiles($prefix) {
|
||||||
|
$this->prefix = $prefix;
|
||||||
|
$cached = \apcu_fetch($this->prefix);
|
||||||
|
|
||||||
|
return !empty($cached)
|
||||||
|
? $cached
|
||||||
|
: array();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string[] $toBeAdded
|
||||||
|
* @param string[] $toBeRemoved
|
||||||
|
*
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
protected function updateClassFiles($toBeAdded, $toBeRemoved) {
|
||||||
|
|
||||||
|
$class_files = $toBeAdded;
|
||||||
|
// Other requests may have already written to the cache, so we get an up to
|
||||||
|
// date version.
|
||||||
|
$cached = \apcu_fetch($this->prefix);
|
||||||
|
if (!empty($cached)) {
|
||||||
|
$class_files += $cached;
|
||||||
|
foreach ($toBeRemoved as $class => $file) {
|
||||||
|
if (isset($class_files[$class]) && $class_files[$class] === $file) {
|
||||||
|
unset($class_files[$class]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
\apcu_store($this->prefix, $class_files);
|
||||||
|
|
||||||
|
return $class_files;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\ClassLoader;
|
||||||
|
|
||||||
|
class CacheNotSupportedException extends \Exception {}
|
@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\ClassLoader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Behaves mostly like the Symfony ClassLoader classes.
|
||||||
|
*/
|
||||||
|
interface ClassLoaderInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers this instance as an autoloader.
|
||||||
|
*
|
||||||
|
* @param boolean $prepend
|
||||||
|
* If TRUE, the loader will be prepended. Otherwise, it will be appended.
|
||||||
|
*/
|
||||||
|
function register($prepend = FALSE);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregister this instance as an autoloader.
|
||||||
|
*/
|
||||||
|
function unregister();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback for class loading. This will include ("require") the file found.
|
||||||
|
*
|
||||||
|
* @param string $class
|
||||||
|
* The class to load.
|
||||||
|
*/
|
||||||
|
function loadClass($class);
|
||||||
|
}
|
@ -0,0 +1,58 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\ClassLoader;
|
||||||
|
|
||||||
|
use Drupal\xautoload\CacheManager\CacheManager;
|
||||||
|
use Drupal\xautoload\ClassFinder\ClassFinderInterface;
|
||||||
|
use Drupal\xautoload\ClassFinder\InjectedApi\LoadClassGetFileInjectedApi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class loader decorator using Drupal's native db cache.
|
||||||
|
*/
|
||||||
|
class DbCacheClassLoader extends AbstractQueuedCachedClassLoader {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $cacheName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $prefix
|
||||||
|
*
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
protected function loadClassFiles($prefix) {
|
||||||
|
$this->cacheName = 'xautoload_db_cache:' . $prefix;
|
||||||
|
$cached = cache_get($this->cacheName);
|
||||||
|
return isset($cached->data)
|
||||||
|
? $cached->data
|
||||||
|
: array();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string[] $toBeAdded
|
||||||
|
* @param string[] $toBeRemoved
|
||||||
|
*
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
protected function updateClassFiles($toBeAdded, $toBeRemoved) {
|
||||||
|
|
||||||
|
$class_files = $toBeAdded;
|
||||||
|
// Other requests may have already written to the cache, so we get an up to
|
||||||
|
// date version.
|
||||||
|
$cached = cache_get($this->cacheName);
|
||||||
|
if (isset($cached->data)) {
|
||||||
|
$class_files += $cached->data;
|
||||||
|
foreach ($toBeRemoved as $class => $file) {
|
||||||
|
if (isset($class_files[$class]) && $class_files[$class] === $file) {
|
||||||
|
unset($class_files[$class]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cache_set($this->cacheName, $class_files);
|
||||||
|
|
||||||
|
return $class_files;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\ClassLoader;
|
||||||
|
|
||||||
|
use Drupal\xautoload\CacheManager\CacheManagerObserverInterface;
|
||||||
|
use Drupal\xautoload\ClassFinder\InjectedApi\LoadClassGetFileInjectedApi;
|
||||||
|
|
||||||
|
class WinCacheClassLoader extends AbstractCachedClassLoader implements CacheManagerObserverInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function checkRequirements() {
|
||||||
|
return extension_loaded('wincache')
|
||||||
|
&& function_exists('wincache_ucache_get');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function loadClass($class) {
|
||||||
|
|
||||||
|
// Look if the cache has anything for this class.
|
||||||
|
if ($file = wincache_ucache_get($this->prefix . $class)) {
|
||||||
|
if (is_file($file)) {
|
||||||
|
require $file;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
wincache_ucache_delete($this->prefix . $class);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve cache miss.
|
||||||
|
$api = new LoadClassGetFileInjectedApi($class);
|
||||||
|
if ($this->finder->apiFindFile($api, $class)) {
|
||||||
|
wincache_ucache_set($this->prefix . $class, $api->getFile());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\ClassLoader;
|
||||||
|
|
||||||
|
use Drupal\xautoload\CacheManager\CacheManagerObserverInterface;
|
||||||
|
use Drupal\xautoload\ClassFinder\InjectedApi\LoadClassGetFileInjectedApi;
|
||||||
|
|
||||||
|
class XCacheClassLoader extends AbstractCachedClassLoader implements CacheManagerObserverInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function checkRequirements() {
|
||||||
|
return extension_loaded('Xcache') && function_exists('xcache_isset');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function loadClass($class) {
|
||||||
|
|
||||||
|
// Look if the cache has anything for this class.
|
||||||
|
if (xcache_isset($this->prefix . $class)
|
||||||
|
&& $file = xcache_get($this->prefix . $class)
|
||||||
|
) {
|
||||||
|
if (is_file($file)) {
|
||||||
|
require $file;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
xcache_unset($this->prefix . $class);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve cache miss.
|
||||||
|
$api = new LoadClassGetFileInjectedApi($class);
|
||||||
|
if ($this->finder->apiFindFile($api, $class)) {
|
||||||
|
xcache_set($this->prefix . $class, $api->getFile());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,76 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\DIC;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see ServiceFactory
|
||||||
|
*/
|
||||||
|
class ServiceContainer implements ServiceContainerInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ServiceFactory
|
||||||
|
*/
|
||||||
|
protected $factory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var object[]
|
||||||
|
*/
|
||||||
|
protected $services = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $key
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
function get($key) {
|
||||||
|
return isset($this->services[$key])
|
||||||
|
? $this->services[$key]
|
||||||
|
: $this->services[$key] = $this->factory->$key($this) ? : FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unset the service for a specific key.
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
*/
|
||||||
|
function reset($key) {
|
||||||
|
$this->services[$key] = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a new service under the given key.
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @param mixed $service
|
||||||
|
*/
|
||||||
|
function set($key, $service) {
|
||||||
|
$this->services[$key] = $service;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Magic getter for a service.
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
function __get($key) {
|
||||||
|
if (isset($this->services[$key])) {
|
||||||
|
return $this->services[$key];
|
||||||
|
}
|
||||||
|
if (!method_exists($this->factory, $key)) {
|
||||||
|
throw new \Exception("Unsupported key '$key' for service factory.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->services[$key] = $this->factory->$key($this) ? : FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ServiceFactory $factory
|
||||||
|
*/
|
||||||
|
function __construct($factory) {
|
||||||
|
$this->factory = $factory;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
namespace Drupal\xautoload\DIC;
|
||||||
|
|
||||||
|
use Drupal\xautoload\Adapter\ClassFinderAdapter;
|
||||||
|
use Drupal\xautoload\Adapter\DrupalExtensionAdapter;
|
||||||
|
use Drupal\xautoload\ClassFinder\ExtendedClassFinderInterface;
|
||||||
|
use Drupal\xautoload\CacheManager\CacheManager;
|
||||||
|
use Drupal\xautoload\ClassFinder\ProxyClassFinder;
|
||||||
|
use Drupal\xautoload\Discovery\ClassMapGenerator;
|
||||||
|
use Drupal\xautoload\Discovery\ClassMapGeneratorInterface;
|
||||||
|
use Drupal\xautoload\DrupalSystem\DrupalSystemInterface;
|
||||||
|
use Drupal\xautoload\Libraries\LibrariesInfoAlter;
|
||||||
|
use Drupal\xautoload\Phases\DrupalPhaseControl;
|
||||||
|
use Drupal\xautoload\Phases\ExtensionNamespaces;
|
||||||
|
use Drupal\xautoload\Main;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property Main $main
|
||||||
|
* @property ClassFinderAdapter $adapter
|
||||||
|
* @property ClassMapGeneratorInterface $classMapGenerator
|
||||||
|
* @property ClassMapGenerator $classMapGeneratorRaw
|
||||||
|
* @property CacheManager $cacheManager
|
||||||
|
* @property ProxyClassFinder $proxyFinder
|
||||||
|
* @property ExtendedClassFinderInterface $classFinder
|
||||||
|
* @property ExtendedClassFinderInterface $finder
|
||||||
|
* Alias for ->classFinder
|
||||||
|
* @property DrupalSystemInterface $system
|
||||||
|
* @property DrupalPhaseControl $phaseControl
|
||||||
|
* @property DrupalExtensionAdapter $extensionRegistrationService
|
||||||
|
* @property ExtensionNamespaces extensionNamespaces
|
||||||
|
* @property LibrariesInfoAlter librariesInfoAlter
|
||||||
|
*
|
||||||
|
* @see \Drupal\xautoload\DIC\ServiceContainer
|
||||||
|
* @see \Drupal\xautoload\DIC\ServiceFactory
|
||||||
|
*/
|
||||||
|
interface ServiceContainerInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a lazy-instantiated service.
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* A key to specify a service.
|
||||||
|
* @return mixed
|
||||||
|
* The service for the given key. Usually an object.
|
||||||
|
*/
|
||||||
|
function __get($key);
|
||||||
|
}
|
174
frontend/drupal/modules/xautoload/src/DIC/ServiceFactory.php
Normal file
174
frontend/drupal/modules/xautoload/src/DIC/ServiceFactory.php
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\DIC;
|
||||||
|
|
||||||
|
use Drupal\xautoload\Adapter\ClassFinderAdapter;
|
||||||
|
use Drupal\xautoload\Adapter\DrupalExtensionAdapter;
|
||||||
|
use Drupal\xautoload\ClassFinder\ClassFinder;
|
||||||
|
use Drupal\xautoload\ClassFinder\ClassFinderInterface;
|
||||||
|
use Drupal\xautoload\CacheManager\CacheManager;
|
||||||
|
use Drupal\xautoload\ClassFinder\ProxyClassFinder;
|
||||||
|
use Drupal\xautoload\Discovery\CachedClassMapGenerator;
|
||||||
|
use Drupal\xautoload\Discovery\ClassMapGenerator;
|
||||||
|
use Drupal\xautoload\DrupalSystem\DrupalSystem;
|
||||||
|
use Drupal\xautoload\DrupalSystem\DrupalSystemInterface;
|
||||||
|
use Drupal\xautoload\Libraries\LibrariesInfoAlter;
|
||||||
|
use Drupal\xautoload\Phases\DrupalCoreRegistryRegistrator;
|
||||||
|
use Drupal\xautoload\Phases\DrupalPhaseControl;
|
||||||
|
use Drupal\xautoload\Phases\ExtensionNamespaces;
|
||||||
|
use Drupal\xautoload\Phases\HookXautoload;
|
||||||
|
use Drupal\xautoload\Libraries\LibrariesOnInit;
|
||||||
|
use Drupal\xautoload\Main;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see ServiceContainerInterface
|
||||||
|
* @see ServiceContainer
|
||||||
|
*/
|
||||||
|
class ServiceFactory {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ServiceContainer $services
|
||||||
|
*
|
||||||
|
* @return Main
|
||||||
|
*/
|
||||||
|
function main($services) {
|
||||||
|
return new Main($services);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ServiceContainer $services
|
||||||
|
*
|
||||||
|
* @return ClassFinderAdapter
|
||||||
|
*/
|
||||||
|
function adapter($services) {
|
||||||
|
return new ClassFinderAdapter($services->finder, $services->classMapGenerator);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ServiceContainer $services
|
||||||
|
*
|
||||||
|
* @return ClassMapGenerator
|
||||||
|
*/
|
||||||
|
function classMapGenerator($services) {
|
||||||
|
return new CachedClassMapGenerator($services->classMapGeneratorRaw, $services->system);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ServiceContainer $services
|
||||||
|
*
|
||||||
|
* @return ClassMapGenerator
|
||||||
|
*/
|
||||||
|
function classMapGeneratorRaw($services) {
|
||||||
|
return new ClassMapGenerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ServiceContainer $services
|
||||||
|
*
|
||||||
|
* @return DrupalExtensionAdapter
|
||||||
|
*/
|
||||||
|
function extensionRegistrationService($services) {
|
||||||
|
return new DrupalExtensionAdapter($services->system, $services->finder);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ServiceContainer $services
|
||||||
|
*
|
||||||
|
* @return CacheManager
|
||||||
|
*/
|
||||||
|
function cacheManager($services) {
|
||||||
|
return CacheManager::create($services->system);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Proxy class finder.
|
||||||
|
*
|
||||||
|
* @param ServiceContainer $services
|
||||||
|
*
|
||||||
|
* @return ClassFinderInterface
|
||||||
|
* Proxy object wrapping the class finder.
|
||||||
|
* This is used to delay namespace registration until the first time the
|
||||||
|
* finder is actually used.
|
||||||
|
* (which might never happen thanks to the APC cache)
|
||||||
|
*/
|
||||||
|
function proxyFinder($services) {
|
||||||
|
// The class finder is cheap to create, so it can use an identity proxy.
|
||||||
|
return new ProxyClassFinder($services->finder);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The class finder (alias for 'finder').
|
||||||
|
*
|
||||||
|
* @param ServiceContainer $services
|
||||||
|
*
|
||||||
|
* @return ClassFinderInterface
|
||||||
|
* Object that can find classes,
|
||||||
|
* and provides methods to register namespaces and prefixes.
|
||||||
|
* Note: The findClass() method expects an InjectedAPI argument.
|
||||||
|
*/
|
||||||
|
function classFinder($services) {
|
||||||
|
return $services->finder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The class finder (alias for 'classFinder').
|
||||||
|
*
|
||||||
|
* @param ServiceContainer $services
|
||||||
|
*
|
||||||
|
* @return ClassFinderInterface
|
||||||
|
* Object that can find classes,
|
||||||
|
* and provides methods to register namespaces and prefixes.
|
||||||
|
* Notes:
|
||||||
|
* - The findClass() method expects an InjectedAPI argument.
|
||||||
|
* - namespaces are only supported since PHP 5.3
|
||||||
|
*/
|
||||||
|
function finder($services) {
|
||||||
|
return new ClassFinder();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ServiceContainer $services
|
||||||
|
*
|
||||||
|
* @return DrupalSystemInterface
|
||||||
|
*/
|
||||||
|
function system($services) {
|
||||||
|
return new DrupalSystem();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ServiceContainer $services
|
||||||
|
*
|
||||||
|
* @return DrupalPhaseControl
|
||||||
|
*/
|
||||||
|
function phaseControl($services) {
|
||||||
|
$observers = array(
|
||||||
|
$services->extensionNamespaces,
|
||||||
|
new HookXautoload($services->system),
|
||||||
|
new LibrariesOnInit($services->system),
|
||||||
|
);
|
||||||
|
if ($services->system->variableGet(XAUTOLOAD_VARNAME_REPLACE_CORE, FALSE)) {
|
||||||
|
$observers[] = new DrupalCoreRegistryRegistrator();
|
||||||
|
}
|
||||||
|
return new DrupalPhaseControl($services->system, $observers);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ServiceContainer $services
|
||||||
|
*
|
||||||
|
* @return ExtensionNamespaces
|
||||||
|
*/
|
||||||
|
function extensionNamespaces($services) {
|
||||||
|
return new ExtensionNamespaces($services->system);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ServiceContainer $services
|
||||||
|
*
|
||||||
|
* @return LibrariesInfoAlter
|
||||||
|
*/
|
||||||
|
function librariesInfoAlter($services) {
|
||||||
|
return new LibrariesInfoAlter();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\DirectoryBehavior;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Directory behavior for PSR-4 and PEAR.
|
||||||
|
*
|
||||||
|
* This class is a marker only, to be checked with instanceof.
|
||||||
|
* @see \Drupal\xautoload\ClassFinder\GenericPrefixMap::loadClass()
|
||||||
|
*/
|
||||||
|
final class DefaultDirectoryBehavior implements DirectoryBehaviorInterface {
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\DirectoryBehavior;
|
||||||
|
|
||||||
|
interface DirectoryBehaviorInterface {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\DirectoryBehavior;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Directory behavior for PSR-0.
|
||||||
|
*
|
||||||
|
* This class is a marker only, to be checked with instanceof.
|
||||||
|
* @see \Drupal\xautoload\ClassFinder\GenericPrefixMap::loadClass()
|
||||||
|
*/
|
||||||
|
final class Psr0DirectoryBehavior implements DirectoryBehaviorInterface {
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\Discovery;
|
||||||
|
|
||||||
|
class CachedClassMapGenerator implements ClassMapGeneratorInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ClassMapGeneratorInterface
|
||||||
|
*/
|
||||||
|
protected $decorated;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Drupal\xautoload\DrupalSystem\DrupalSystemInterface
|
||||||
|
*/
|
||||||
|
protected $system;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ClassMapGeneratorInterface $decorated
|
||||||
|
* @param \Drupal\xautoload\DrupalSystem\DrupalSystemInterface $system
|
||||||
|
*/
|
||||||
|
function __construct($decorated, $system) {
|
||||||
|
$this->decorated = $decorated;
|
||||||
|
$this->system = $system;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string[] $paths
|
||||||
|
*
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
function wildcardPathsToClassmap($paths) {
|
||||||
|
// Attempt to load from cache.
|
||||||
|
$cid = 'xautoload:wildcardPathsToClassmap:' . md5(serialize($paths));
|
||||||
|
$cache = $this->system->cacheGet($cid);
|
||||||
|
if ($cache && isset($cache->data)) {
|
||||||
|
return $cache->data;
|
||||||
|
}
|
||||||
|
// Resolve cache miss and save.
|
||||||
|
$map = $this->decorated->wildcardPathsToClassmap($paths);
|
||||||
|
$this->system->cacheSet($cid, $map);
|
||||||
|
|
||||||
|
return $map;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\Discovery;
|
||||||
|
|
||||||
|
class ClassMapGenerator implements ClassMapGeneratorInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string[] $paths
|
||||||
|
*
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
function wildcardPathsToClassmap($paths) {
|
||||||
|
$files = $this->wildcardPathsToFiles($paths);
|
||||||
|
|
||||||
|
return $this->filesToClassmap($files);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string[] $files
|
||||||
|
*
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
protected function filesToClassmap($files) {
|
||||||
|
$map = array();
|
||||||
|
foreach ($files as $file) {
|
||||||
|
$classes = FileInspector::inspectPhpFile($file);
|
||||||
|
foreach ($classes as $class) {
|
||||||
|
$map[$class] = $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $map;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string[] $paths
|
||||||
|
*
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
protected function wildcardPathsToFiles($paths) {
|
||||||
|
$wildcardFinder = new WildcardFileFinder();
|
||||||
|
$wildcardFinder->addPaths($paths);
|
||||||
|
|
||||||
|
return $wildcardFinder->getFiles();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\Discovery;
|
||||||
|
|
||||||
|
interface ClassMapGeneratorInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string[] $paths
|
||||||
|
*
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
function wildcardPathsToClassmap($paths);
|
||||||
|
}
|
@ -0,0 +1,81 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\Discovery;
|
||||||
|
|
||||||
|
use Drupal\xautoload\Adapter\ClassFinderAdapter;
|
||||||
|
|
||||||
|
class ComposerDir {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $dir;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $dir
|
||||||
|
*
|
||||||
|
* @return self
|
||||||
|
*
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
static function create($dir) {
|
||||||
|
if (!is_dir($dir)) {
|
||||||
|
throw new \Exception("Composer directory '$dir' does not exist.");
|
||||||
|
}
|
||||||
|
return new self($dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $dir
|
||||||
|
*/
|
||||||
|
protected function __construct($dir) {
|
||||||
|
$this->dir = $dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ClassFinderAdapter $adapter
|
||||||
|
*/
|
||||||
|
function writeToAdapter($adapter) {
|
||||||
|
|
||||||
|
// PSR-0 namespaces / prefixes
|
||||||
|
if (is_file($this->dir . '/autoload_namespaces.php')) {
|
||||||
|
$prefixes = require $this->dir . '/autoload_namespaces.php';
|
||||||
|
if (!empty($prefixes)) {
|
||||||
|
$adapter->addMultiplePsr0($prefixes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PSR-4 namespaces
|
||||||
|
if (is_file($this->dir . '/autoload_psr4.php')) {
|
||||||
|
$map = require $this->dir . '/autoload_psr4.php';
|
||||||
|
if (!empty($map)) {
|
||||||
|
$adapter->addMultiplePsr4($map);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Class map
|
||||||
|
if (is_file($this->dir . '/autoload_classmap.php')) {
|
||||||
|
$class_map = require $this->dir . '/autoload_classmap.php';
|
||||||
|
if (!empty($class_map)) {
|
||||||
|
$adapter->addClassMap($class_map);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Include path
|
||||||
|
if (is_file($this->dir . '/include_paths.php')) {
|
||||||
|
$include_paths = require $this->dir . '/include_paths.php';
|
||||||
|
if (!empty($include_paths)) {
|
||||||
|
array_push($include_paths, get_include_path());
|
||||||
|
set_include_path(join(PATH_SEPARATOR, $include_paths));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Include files
|
||||||
|
if (is_file($this->dir . '/autoload_files.php')) {
|
||||||
|
$include_files = require $this->dir . '/autoload_files.php';
|
||||||
|
foreach ($include_files as $file) {
|
||||||
|
require $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
132
frontend/drupal/modules/xautoload/src/Discovery/ComposerJson.php
Normal file
132
frontend/drupal/modules/xautoload/src/Discovery/ComposerJson.php
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\Discovery;
|
||||||
|
|
||||||
|
use Drupal\xautoload\Adapter\ClassFinderAdapter;
|
||||||
|
|
||||||
|
class ComposerJson {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $pathPrefix;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $file
|
||||||
|
*
|
||||||
|
* @return self
|
||||||
|
*
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
static function createFromFile($file) {
|
||||||
|
if (!file_exists($file)) {
|
||||||
|
throw new \Exception("File '$file' does not exist.");
|
||||||
|
}
|
||||||
|
$json = file_get_contents($file);
|
||||||
|
$data = json_decode($json, TRUE);
|
||||||
|
if (NULL === $data && JSON_ERROR_NONE !== json_last_error()) {
|
||||||
|
throw new \Exception("Invalid json in '$file'.");
|
||||||
|
}
|
||||||
|
return self::createFromData($data, dirname($file) . '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $data
|
||||||
|
* @param string $path_prefix
|
||||||
|
*
|
||||||
|
* @return self
|
||||||
|
*
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
static function createFromData($data, $path_prefix) {
|
||||||
|
return empty($data['target-dir'])
|
||||||
|
? new self($data, $path_prefix)
|
||||||
|
: new ComposerJsonTargetDir($data, $path_prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $data
|
||||||
|
* @param string $path_prefix
|
||||||
|
*/
|
||||||
|
protected function __construct(array $data, $path_prefix) {
|
||||||
|
$this->data = $data;
|
||||||
|
$this->pathPrefix = $path_prefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ClassFinderAdapter $adapter
|
||||||
|
*/
|
||||||
|
function writeToAdapter(ClassFinderAdapter $adapter) {
|
||||||
|
|
||||||
|
$data = $this->data;
|
||||||
|
|
||||||
|
if (!empty($data['include-path'])) {
|
||||||
|
$this->addIncludePaths((array)$data['include-path']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($data['autoload']['psr-0'])) {
|
||||||
|
$map = $this->transformMultiple($data['autoload']['psr-0']);
|
||||||
|
$adapter->addMultiplePsr0($map);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($data['autoload']['psr-4'])) {
|
||||||
|
$map = $this->transformMultiple($data['autoload']['psr-4']);
|
||||||
|
$adapter->addMultiplePsr4($map);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($data['autoload']['classmap'])) {
|
||||||
|
$this->addClassmapSources($adapter, (array)$data['autoload']['classmap']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($data['autoload']['files'])) {
|
||||||
|
foreach ($data['autoload']['files'] as $file) {
|
||||||
|
require $this->pathPrefix . $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $multiple
|
||||||
|
*
|
||||||
|
* @return array[]
|
||||||
|
*/
|
||||||
|
protected function transformMultiple(array $multiple) {
|
||||||
|
foreach ($multiple as &$paths) {
|
||||||
|
$paths = (array)$paths;
|
||||||
|
foreach ($paths as &$path) {
|
||||||
|
if ('' === $path || '/' !== $path[0]) {
|
||||||
|
$path = $this->pathPrefix . $path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $multiple;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string[] $include_paths
|
||||||
|
*/
|
||||||
|
protected function addIncludePaths(array $include_paths) {
|
||||||
|
foreach ($include_paths as &$path) {
|
||||||
|
$path = $this->pathPrefix . $path;
|
||||||
|
}
|
||||||
|
array_push($include_paths, get_include_path());
|
||||||
|
set_include_path(join(PATH_SEPARATOR, $include_paths));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ClassFinderAdapter $adapter
|
||||||
|
* @param string[] $sources_raw
|
||||||
|
* Array of files and folders to scan for class implementations.
|
||||||
|
*/
|
||||||
|
protected function addClassmapSources($adapter, array $sources_raw) {
|
||||||
|
foreach ($sources_raw as &$path) {
|
||||||
|
$path = $this->pathPrefix . $path;
|
||||||
|
}
|
||||||
|
$adapter->addClassmapSources($sources_raw);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,118 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\Discovery;
|
||||||
|
|
||||||
|
use Drupal\xautoload\Adapter\ClassFinderAdapter;
|
||||||
|
use Drupal\xautoload\DirectoryBehavior\DefaultDirectoryBehavior;
|
||||||
|
use Drupal\xautoload\DirectoryBehavior\Psr0DirectoryBehavior;
|
||||||
|
use Drupal\xautoload\Util;
|
||||||
|
|
||||||
|
class ComposerJsonTargetDir extends ComposerJson {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $targetDir;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $data
|
||||||
|
* @param string $path_prefix
|
||||||
|
*/
|
||||||
|
function __construct(array $data, $path_prefix) {
|
||||||
|
parent::__construct($data, $path_prefix);
|
||||||
|
$this->targetDir = rtrim($data['target-dir'], '/') . '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ClassFinderAdapter $adapter
|
||||||
|
*
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
function writeToAdapter(ClassFinderAdapter $adapter) {
|
||||||
|
|
||||||
|
$data = $this->data;
|
||||||
|
|
||||||
|
if (!empty($data['include-path'])) {
|
||||||
|
$paths = $this->pathsResolveTargetDir((array) $data['include-path']);
|
||||||
|
$this->addIncludePaths($paths, $this->pathPrefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($data['autoload']['psr-0'])) {
|
||||||
|
$this->addMultipleWithTargetDir($adapter, $data['autoload']['psr-0']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($data['autoload']['psr-4'])) {
|
||||||
|
throw new \Exception("PSR-4 is incompatible with target-dir.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($data['autoload']['classmap'])) {
|
||||||
|
$paths = $this->pathsResolveTargetDir($data['autoload']['classmap']);
|
||||||
|
$this->addClassmapSources($adapter, $paths);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($data['autoload']['files'])) {
|
||||||
|
$paths = $this->pathsResolveTargetDir($data['autoload']['files']);
|
||||||
|
foreach ($paths as $file) {
|
||||||
|
require $this->pathPrefix . $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string[] $paths
|
||||||
|
*
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
protected function pathsResolveTargetDir(array $paths) {
|
||||||
|
$strlen = strlen($this->targetDir);
|
||||||
|
foreach ($paths as &$path) {
|
||||||
|
if (0 === strpos($path, $this->targetDir)) {
|
||||||
|
$path = substr($path, $strlen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $paths;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ClassFinderAdapter $adapter
|
||||||
|
* @param array $prefixes
|
||||||
|
*/
|
||||||
|
protected function addMultipleWithTargetDir(ClassFinderAdapter $adapter, array $prefixes) {
|
||||||
|
$default_behavior = new DefaultDirectoryBehavior();
|
||||||
|
$psr0_behavior = new Psr0DirectoryBehavior();
|
||||||
|
$namespace_map = array();
|
||||||
|
$prefix_map = array();
|
||||||
|
$target_dir_strlen = strlen($this->targetDir);
|
||||||
|
foreach ($prefixes as $prefix => $paths) {
|
||||||
|
if (FALSE === strpos($prefix, '\\')) {
|
||||||
|
$logical_base_path = Util::prefixLogicalPath($prefix);
|
||||||
|
foreach ((array) $paths as $root_path) {
|
||||||
|
$deep_path = strlen($root_path)
|
||||||
|
? rtrim($root_path, '/') . '/' . $logical_base_path
|
||||||
|
: $logical_base_path;
|
||||||
|
if (0 !== strpos($deep_path, $this->targetDir)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$deep_path = $this->pathPrefix . substr($deep_path, $target_dir_strlen);
|
||||||
|
$prefix_map[$logical_base_path][$deep_path] = $default_behavior;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$logical_base_path = Util::namespaceLogicalPath($prefix);
|
||||||
|
foreach ((array) $paths as $root_path) {
|
||||||
|
$deep_path = strlen($root_path)
|
||||||
|
? rtrim($root_path, '/') . '/' . $logical_base_path
|
||||||
|
: $logical_base_path;
|
||||||
|
if (0 !== strpos($deep_path, $this->targetDir)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$deep_path = $this->pathPrefix . substr($deep_path, $target_dir_strlen);
|
||||||
|
$namespace_map[$logical_base_path][$deep_path] = $psr0_behavior;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!empty($prefix_map)) {
|
||||||
|
$adapter->getPrefixMap()->registerDeepPaths($prefix_map);
|
||||||
|
}
|
||||||
|
$adapter->getNamespaceMap()->registerDeepPaths($namespace_map);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,98 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\Discovery;
|
||||||
|
|
||||||
|
use RuntimeException;
|
||||||
|
|
||||||
|
class FileInspector {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $path
|
||||||
|
* @return string[]
|
||||||
|
* @throws RuntimeException
|
||||||
|
*/
|
||||||
|
static function inspectPhpFile($path) {
|
||||||
|
try {
|
||||||
|
$contents = php_strip_whitespace($path);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
throw new \RuntimeException(
|
||||||
|
'Could not scan for classes inside ' . $path . ": \n" . $e->getMessage(),
|
||||||
|
// The Exception code. Defaults to 0.
|
||||||
|
0,
|
||||||
|
// The previous exception used for exception chaining.
|
||||||
|
$e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::inspectFileContents($contents);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $contents
|
||||||
|
* The PHP file contents obtained with php_strip_whitespace($path).
|
||||||
|
*
|
||||||
|
* @return string[]
|
||||||
|
* Classes discovered in the file.
|
||||||
|
*/
|
||||||
|
protected static function inspectFileContents($contents) {
|
||||||
|
$traits = version_compare(PHP_VERSION, '5.4', '<')
|
||||||
|
? ''
|
||||||
|
: '|trait';
|
||||||
|
|
||||||
|
// return early if there is no chance of matching anything in this file
|
||||||
|
if (!preg_match('{\b(?:class|interface' . $traits . ')\s}i', $contents)) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
// strip heredocs/nowdocs
|
||||||
|
$contents = preg_replace(
|
||||||
|
'{<<<\'?(\w+)\'?(?:\r\n|\n|\r)(?:.*?)(?:\r\n|\n|\r)\\1(?=\r\n|\n|\r|;)}s',
|
||||||
|
'null',
|
||||||
|
$contents);
|
||||||
|
|
||||||
|
// strip strings
|
||||||
|
$contents = preg_replace(
|
||||||
|
'{"[^"\\\\]*(\\\\.[^"\\\\]*)*"|\'[^\'\\\\]*(\\\\.[^\'\\\\]*)*\'}s',
|
||||||
|
'null',
|
||||||
|
$contents);
|
||||||
|
|
||||||
|
// strip leading non-php code if needed
|
||||||
|
if (substr($contents, 0, 2) !== '<?') {
|
||||||
|
$contents = preg_replace('{^.+?<\?}s', '<?', $contents);
|
||||||
|
}
|
||||||
|
|
||||||
|
// strip non-php blocks in the file
|
||||||
|
$contents = preg_replace('{\?>.+<\?}s', '?><?', $contents);
|
||||||
|
|
||||||
|
// strip trailing non-php code if needed
|
||||||
|
$pos = strrpos($contents, '?>');
|
||||||
|
if (FALSE !== $pos && FALSE === strpos(substr($contents, $pos), '<?')) {
|
||||||
|
$contents = substr($contents, 0, $pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
preg_match_all(
|
||||||
|
'{
|
||||||
|
(?:
|
||||||
|
\b(?<![\$:>])(?P<type>class|interface' . $traits . ') \s+ (?P<name>[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)
|
||||||
|
| \b(?<![\$:>])(?P<ns>namespace) (?P<nsname>\s+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*(?:\s*\\\\\s*[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)*)? \s*[\{;]
|
||||||
|
)
|
||||||
|
}ix',
|
||||||
|
$contents,
|
||||||
|
$matches
|
||||||
|
);
|
||||||
|
|
||||||
|
$classes = array();
|
||||||
|
$namespace = '';
|
||||||
|
|
||||||
|
for ($i = 0, $len = count($matches['type']); $i < $len; $i++) {
|
||||||
|
if (!empty($matches['ns'][$i])) {
|
||||||
|
$namespace = str_replace(array(' ', "\t", "\r", "\n"), '', $matches['nsname'][$i])
|
||||||
|
. '\\';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$classes[] = ltrim($namespace . $matches['name'][$i], '\\');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $classes;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,211 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is autoloaded with the regular uncached xautoload.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\Discovery;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scan directories for wildcard files[] instructions in a module's info file.
|
||||||
|
*/
|
||||||
|
class WildcardFileFinder {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
* Info array for the wildcard string currently being processed.
|
||||||
|
* This value changes for each new wildcard being processed.
|
||||||
|
*/
|
||||||
|
protected $value;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var mixed[]
|
||||||
|
* $files array passed to hook_registry_files_alter().
|
||||||
|
*/
|
||||||
|
protected $files = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $paths
|
||||||
|
* Array keys are file paths or wildcard file paths.
|
||||||
|
*/
|
||||||
|
function addDrupalPaths(array $paths) {
|
||||||
|
foreach ($paths as $path => $value) {
|
||||||
|
if (1
|
||||||
|
&& FALSE !== strpos($path, '*')
|
||||||
|
&& preg_match('#^([^\*]*)/(.*\*.*)$#', $path, $m)
|
||||||
|
) {
|
||||||
|
// Resolve wildcards.
|
||||||
|
$this->value = $value;
|
||||||
|
list(, $base, $wildcard) = $m;
|
||||||
|
$this->scanDirectory($base, $wildcard);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Register the file directly.
|
||||||
|
$this->files[$path] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string[] $paths
|
||||||
|
* Array keys are file paths or wildcard file paths.
|
||||||
|
* @param mixed $value
|
||||||
|
*/
|
||||||
|
function addPaths(array $paths, $value = TRUE) {
|
||||||
|
foreach ($paths as $path) {
|
||||||
|
if (1
|
||||||
|
&& FALSE !== strpos($path, '*')
|
||||||
|
&& preg_match('#^([^\*]*)/(.*\*.*)$#', $path, $m)
|
||||||
|
) {
|
||||||
|
// Resolve wildcards.
|
||||||
|
$this->value = $value;
|
||||||
|
list(, $base, $wildcard) = $m;
|
||||||
|
$this->scanDirectory($base, $wildcard);
|
||||||
|
}
|
||||||
|
elseif (is_dir($path)) {
|
||||||
|
// Resolve wildcards.
|
||||||
|
$this->value = $value;
|
||||||
|
$this->scanDirectory($path . '/', '**/*.inc');
|
||||||
|
$this->scanDirectory($path . '/', '**/*.php');
|
||||||
|
}
|
||||||
|
elseif (is_file($path)) {
|
||||||
|
// Register the file directly.
|
||||||
|
$this->files[$path] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
function getFiles() {
|
||||||
|
return array_keys($this->files);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed[]
|
||||||
|
*/
|
||||||
|
function getDrupalFiles() {
|
||||||
|
return $this->files;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $dir
|
||||||
|
* Base folder, e.g. "sites/all/modules/foo/includes", which does NOT
|
||||||
|
* contain any asterisk ("*").
|
||||||
|
* @param string $wildcard
|
||||||
|
* Suffix which may contain asterisks.
|
||||||
|
*/
|
||||||
|
protected function scanDirectory($dir, $wildcard) {
|
||||||
|
if (!is_dir($dir)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (FALSE === strpos($wildcard, '*')) {
|
||||||
|
// $wildcard is a fixed string, not a wildcard.
|
||||||
|
$this->suggestFile($dir . '/' . $wildcard);
|
||||||
|
}
|
||||||
|
elseif ('**' === $wildcard) {
|
||||||
|
// Trick: "$a/**" == union of "$a/*" and "$a/*/**"
|
||||||
|
$this->scanDirectoryLevel($dir, '*');
|
||||||
|
$this->scanDirectoryLevel($dir, '*', '**');
|
||||||
|
}
|
||||||
|
elseif ('**/' === substr($wildcard, 0, 3)) {
|
||||||
|
// Trick: "$a/**/$b" == union of "$a/$b" and "$a/*/**/$b"
|
||||||
|
$remaining = substr($wildcard, 3);
|
||||||
|
$this->scanDirectory($dir, $remaining);
|
||||||
|
$this->scanDirectoryLevel($dir, '*', $wildcard);
|
||||||
|
}
|
||||||
|
elseif (FALSE !== ($slashpos = strpos($wildcard, '/'))) {
|
||||||
|
// $wildcard consists of more than one fragment.
|
||||||
|
$fragment = substr($wildcard, 0, $slashpos);
|
||||||
|
$remaining = substr($wildcard, $slashpos + 1);
|
||||||
|
if (FALSE === strpos($fragment, '*')) {
|
||||||
|
$this->scanDirectory($dir . '/' . $fragment, $remaining);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$this->scanDirectoryLevel($dir, $fragment, $remaining);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// $wildcard represents a file name.
|
||||||
|
$this->scanDirectoryLevel($dir, $wildcard);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $dir
|
||||||
|
* Base directory, not containing any wildcard.
|
||||||
|
* @param string $fragment
|
||||||
|
* Wildcard path fragment to be processed now. This is never '**', but it
|
||||||
|
* always contains at least one asterisk.
|
||||||
|
* @param null $remaining
|
||||||
|
* Optional rest of the wildcard string, that may contain path fragments to
|
||||||
|
* be processed later.
|
||||||
|
*
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
protected function scanDirectoryLevel($dir, $fragment, $remaining = NULL) {
|
||||||
|
|
||||||
|
if (!is_dir($dir)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('**' === $fragment) {
|
||||||
|
throw new \Exception("Fragment must not be '**'.");
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (scandir($dir) as $candidate) {
|
||||||
|
if (!$this->validateCandidate($candidate, $fragment)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($remaining)) {
|
||||||
|
$this->suggestFile($dir . '/' . $candidate);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$this->scanDirectory($dir . '/' . $candidate, $remaining);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $candidate
|
||||||
|
* String to be checked against the wildcard.
|
||||||
|
* @param $wildcard
|
||||||
|
* Wildcard string like '*', '*.*' or '*.inc'.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* TRUE, if $candidate matches $wildcard.
|
||||||
|
*/
|
||||||
|
protected function validateCandidate($candidate, $wildcard) {
|
||||||
|
|
||||||
|
if ($candidate == '.' || $candidate == '..') {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
if (strpos($candidate, '*') !== FALSE) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
if ($wildcard == '*' || $wildcard == '**') {
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// More complex wildcard string.
|
||||||
|
$fragments = array();
|
||||||
|
foreach (explode('*', $wildcard) as $fragment) {
|
||||||
|
$fragments[] = preg_quote($fragment);
|
||||||
|
}
|
||||||
|
$regex = implode('.*', $fragments);
|
||||||
|
|
||||||
|
return preg_match("/^$regex$/", $candidate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $path
|
||||||
|
* Add a new file path to $this->filesInRegistry().
|
||||||
|
*/
|
||||||
|
protected function suggestFile($path) {
|
||||||
|
if (is_file($path)) {
|
||||||
|
$this->files[$path] = $this->value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,178 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\DrupalSystem;
|
||||||
|
|
||||||
|
class DrupalSystem implements DrupalSystemInterface {
|
||||||
|
|
||||||
|
function __construct() {
|
||||||
|
if (!function_exists('drupal_get_filename')) {
|
||||||
|
throw new \Exception("This class works only within a working Drupal environment.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function variableSet($name, $value) {
|
||||||
|
variable_set($name, $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function variableGet($name, $default = NULL) {
|
||||||
|
return variable_get($name, $default);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function drupalGetFilename($type, $name) {
|
||||||
|
return DRUPAL_ROOT . '/' . drupal_get_filename($type, $name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function drupalGetPath($type, $name) {
|
||||||
|
return DRUPAL_ROOT . '/' . drupal_get_path($type, $name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function getExtensionTypes($extension_names) {
|
||||||
|
$q = db_select('system');
|
||||||
|
$q->condition('name', $extension_names);
|
||||||
|
$q->fields('system', array('name', 'type'));
|
||||||
|
|
||||||
|
return $q->execute()->fetchAllKeyed();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function getActiveExtensions() {
|
||||||
|
try {
|
||||||
|
// Doing this directly tends to be a lot faster than system_list().
|
||||||
|
return db_query("SELECT name, type from {system} WHERE status = 1")
|
||||||
|
->fetchAllKeyed();
|
||||||
|
}
|
||||||
|
catch (\DatabaseConnectionNotDefinedException $e) {
|
||||||
|
// During install, the database is not available.
|
||||||
|
// At this time only the system module is "installed".
|
||||||
|
/** See https://www.drupal.org/node/2393205 */
|
||||||
|
return array('system' => 'module');
|
||||||
|
}
|
||||||
|
catch (\PDOException $e) {
|
||||||
|
// Some time later during install, there is a database but not yet a system table.
|
||||||
|
// At this time only the system module is "installed".
|
||||||
|
// @todo Check if this is really a "Table 'system' doesn't exist'" exception.
|
||||||
|
return array('system' => 'module');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function moduleImplements($hook) {
|
||||||
|
return module_implements($hook);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper for module_list()
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
function moduleList() {
|
||||||
|
return module_list();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see libraries_info()
|
||||||
|
*
|
||||||
|
* @throws \Exception
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
function getLibrariesInfo() {
|
||||||
|
if (!function_exists('libraries_info')) {
|
||||||
|
// Libraries is at a lower version, which does not have this function.
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
# drupal_static_reset('libraries_info');
|
||||||
|
return libraries_info();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see libraries_get_path()
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* Name of the library.
|
||||||
|
*
|
||||||
|
* @throws \Exception
|
||||||
|
* @return string|false
|
||||||
|
*/
|
||||||
|
function librariesGetPath($name) {
|
||||||
|
if (!function_exists('libraries_get_path')) {
|
||||||
|
throw new \Exception('Function libraries_get_path() does not exist.');
|
||||||
|
}
|
||||||
|
return libraries_get_path($name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called from xautoload_install() to set the module weight.
|
||||||
|
*
|
||||||
|
* @param int $weight
|
||||||
|
* New module weight for xautoload.
|
||||||
|
*/
|
||||||
|
public function installSetModuleWeight($weight) {
|
||||||
|
db_update('system')
|
||||||
|
->fields(array('weight' => $weight))
|
||||||
|
->condition('name', 'xautoload')
|
||||||
|
->condition('type', 'module')
|
||||||
|
->execute();
|
||||||
|
system_list_reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $cid
|
||||||
|
* @param string $bin
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*
|
||||||
|
* @see cache_get()
|
||||||
|
*/
|
||||||
|
public function cacheGet($cid, $bin = 'cache') {
|
||||||
|
return cache_get($cid, $bin);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $cid
|
||||||
|
* @param mixed $data
|
||||||
|
* @param string $bin
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*
|
||||||
|
* @see cache_set()
|
||||||
|
*/
|
||||||
|
public function cacheSet($cid, $data, $bin = 'cache') {
|
||||||
|
cache_set($cid, $data, $bin);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string|null $cid
|
||||||
|
* @param string|null $bin
|
||||||
|
*
|
||||||
|
* @see cache_clear_all()
|
||||||
|
*/
|
||||||
|
public function cacheClearAll($cid = NULL, $bin = NULL) {
|
||||||
|
cache_clear_all($cid, $bin);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $key
|
||||||
|
*/
|
||||||
|
public function drupalStaticReset($key) {
|
||||||
|
\drupal_static_reset($key);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,145 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\DrupalSystem;
|
||||||
|
|
||||||
|
interface DrupalSystemInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper for variable_set()
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @param mixed $value
|
||||||
|
*/
|
||||||
|
function variableSet($name, $value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replacement of variable_get().
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @param mixed $default
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*
|
||||||
|
* @see variable_get()
|
||||||
|
*/
|
||||||
|
function variableGet($name, $default = NULL);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replacement of drupal_get_filename(), but returning an absolute file path.
|
||||||
|
*
|
||||||
|
* @param string $type
|
||||||
|
* @param string $name
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* The result of drupal_get_filename() with DRUPAL_ROOT . '/' prepended.
|
||||||
|
*
|
||||||
|
* @see drupal_get_filename()
|
||||||
|
*/
|
||||||
|
function drupalGetFilename($type, $name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replacement of drupal_get_path(), but returning an absolute directory path.
|
||||||
|
*
|
||||||
|
* @param string $type
|
||||||
|
* @param string $name
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*
|
||||||
|
* @see drupal_get_path()
|
||||||
|
*/
|
||||||
|
function drupalGetPath($type, $name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string[] $extension_names
|
||||||
|
* Extension names.
|
||||||
|
*
|
||||||
|
* @return string[]
|
||||||
|
* Extension types by extension name.
|
||||||
|
*/
|
||||||
|
function getExtensionTypes($extension_names);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets active extensions directly from the system table.
|
||||||
|
*
|
||||||
|
* @return string[]
|
||||||
|
* Extension types by extension name.
|
||||||
|
*/
|
||||||
|
function getActiveExtensions();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper for module_list()
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
function moduleList();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper for module_implements()
|
||||||
|
*
|
||||||
|
* @param string $hook
|
||||||
|
*
|
||||||
|
* @return array[]
|
||||||
|
*/
|
||||||
|
function moduleImplements($hook);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see libraries_info()
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
function getLibrariesInfo();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see libraries_get_path()
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* Name of the library.
|
||||||
|
*
|
||||||
|
* @return string|false
|
||||||
|
*/
|
||||||
|
function librariesGetPath($name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called from xautoload_install() to set the module weight.
|
||||||
|
*
|
||||||
|
* @param int $weight
|
||||||
|
* New module weight for xautoload.
|
||||||
|
*/
|
||||||
|
public function installSetModuleWeight($weight);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $cid
|
||||||
|
* @param string $bin
|
||||||
|
*
|
||||||
|
* @return object|false
|
||||||
|
* The cache or FALSE on failure.
|
||||||
|
*
|
||||||
|
* @see cache_get()
|
||||||
|
*/
|
||||||
|
public function cacheGet($cid, $bin = 'cache');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $cid
|
||||||
|
* @param mixed $data
|
||||||
|
* @param string $bin
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*
|
||||||
|
* @see cache_set()
|
||||||
|
*/
|
||||||
|
public function cacheSet($cid, $data, $bin = 'cache');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string|null $cid
|
||||||
|
* @param string|null $bin
|
||||||
|
*
|
||||||
|
* @see cache_clear_all()
|
||||||
|
*/
|
||||||
|
public function cacheClearAll($cid = NULL, $bin = NULL);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $key
|
||||||
|
*/
|
||||||
|
public function drupalStaticReset($key);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,127 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\Libraries;
|
||||||
|
|
||||||
|
|
||||||
|
use Drupal\xautoload\ClassFinder\ExtendedClassFinderInterface;
|
||||||
|
use Drupal\xautoload\ClassFinder\InjectedApi\InjectedApiInterface;
|
||||||
|
use Drupal\xautoload\ClassFinder\Plugin\FinderPluginInterface;
|
||||||
|
use Drupal\xautoload\DrupalSystem\DrupalSystemInterface;
|
||||||
|
use Drupal\xautoload\Phases\PhaseObserverInterface;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers autoload mappings from all libraries on hook_init(), or after the
|
||||||
|
* first cache miss.
|
||||||
|
*/
|
||||||
|
class LibrariesFinderPlugin implements FinderPluginInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ExtendedClassFinderInterface
|
||||||
|
*/
|
||||||
|
private $finder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var DrupalSystemInterface
|
||||||
|
*/
|
||||||
|
private $system;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ExtendedClassFinderInterface $finder
|
||||||
|
* @param DrupalSystemInterface $system
|
||||||
|
*/
|
||||||
|
function __construct(ExtendedClassFinderInterface $finder, DrupalSystemInterface $system) {
|
||||||
|
$this->finder = $finder;
|
||||||
|
$this->system = $system;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the file for a class that in PSR-0 or PEAR would be in
|
||||||
|
* $psr_0_root . '/' . $path_fragment . $path_suffix
|
||||||
|
*
|
||||||
|
* @param InjectedApiInterface $api
|
||||||
|
* @param string $logical_base_path
|
||||||
|
* @param string $relative_path
|
||||||
|
*
|
||||||
|
* @return bool|null
|
||||||
|
* TRUE, if the file was found.
|
||||||
|
* FALSE or NULL, otherwise.
|
||||||
|
*/
|
||||||
|
function findFile($api, $logical_base_path, $relative_path) {
|
||||||
|
|
||||||
|
// Prevent recursion if this is called from libraries_info().
|
||||||
|
// @todo Find a better way to do this?
|
||||||
|
$backtrace = defined('DEBUG_BACKTRACE_IGNORE_ARGS')
|
||||||
|
? debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS)
|
||||||
|
: debug_backtrace(FALSE);
|
||||||
|
foreach ($backtrace as $call) {
|
||||||
|
if ('libraries_info' === $call['function']) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->finder->getNamespaceMap()->unregisterDeepPath('', '');
|
||||||
|
$this->finder->getPrefixMap()->unregisterDeepPath('', '');
|
||||||
|
$this->registerAllLibraries();
|
||||||
|
return $this->finder->apiFindFile($api, $api->getClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers all libraries that have an "xautoload" setting.
|
||||||
|
*/
|
||||||
|
private function registerAllLibraries() {
|
||||||
|
$adapter = \xautoload_InjectedAPI_hookXautoload::create($this->finder, '');
|
||||||
|
foreach ($info = $this->getLibrariesXautoloadInfo() as $name => $pathAndCallback) {
|
||||||
|
list($path, $callback) = $pathAndCallback;
|
||||||
|
if (!is_callable($callback)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!is_dir($path)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$adapter->setExtensionDir($path);
|
||||||
|
call_user_func($callback, $adapter, $path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array[]
|
||||||
|
*/
|
||||||
|
private function getLibrariesXautoloadInfo() {
|
||||||
|
$cached = $this->system->cacheGet(XAUTOLOAD_CACHENAME_LIBRARIES_INFO);
|
||||||
|
if (FALSE !== $cached) {
|
||||||
|
return $cached->data;
|
||||||
|
}
|
||||||
|
$info = $this->buildLibrariesXautoloadInfo();
|
||||||
|
$this->system->cacheSet(XAUTOLOAD_CACHENAME_LIBRARIES_INFO, $info);
|
||||||
|
return $info;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array[]
|
||||||
|
*/
|
||||||
|
private function buildLibrariesXautoloadInfo() {
|
||||||
|
// @todo Reset drupal_static('libraries') ?
|
||||||
|
$all = array();
|
||||||
|
foreach ($this->system->getLibrariesInfo() as $name => $info) {
|
||||||
|
if (!isset($info['xautoload'])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$callback = $info['xautoload'];
|
||||||
|
if (!is_callable($callback)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/** See https://www.drupal.org/node/2473901 */
|
||||||
|
$path = isset($info['library path'])
|
||||||
|
? $info['library path']
|
||||||
|
: $this->system->librariesGetPath($name);
|
||||||
|
if (FALSE === $path) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$all[$name] = array($path, $callback);
|
||||||
|
}
|
||||||
|
return $all;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,55 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\Libraries;
|
||||||
|
|
||||||
|
|
||||||
|
class LibrariesInfoAlter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Empty constructor.
|
||||||
|
*
|
||||||
|
* Required for versions of PHP under 5.3.3, to prevent fallback to
|
||||||
|
* librariesInfoAlter() as the default constructor.
|
||||||
|
*/
|
||||||
|
function __construct() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $info
|
||||||
|
*
|
||||||
|
* @see hook_libraries_info_alter()
|
||||||
|
* @see xautoload_libraries_info_alter()
|
||||||
|
*/
|
||||||
|
function librariesInfoAlter(&$info) {
|
||||||
|
foreach ($info as $library_name => &$library_info) {
|
||||||
|
if (1
|
||||||
|
&& isset($library_info['xautoload'])
|
||||||
|
&& is_callable($library_info['xautoload'])
|
||||||
|
) {
|
||||||
|
$this->alterLibraryInfo($library_info, $library_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $library_info
|
||||||
|
* @param string $library_name
|
||||||
|
*/
|
||||||
|
private function alterLibraryInfo(&$library_info, $library_name) {
|
||||||
|
$callable = $library_info['xautoload'];
|
||||||
|
if ($callable instanceof \Closure) {
|
||||||
|
// Wrap the closure so it can be serialized.
|
||||||
|
$callable = new SerializableClosureWrapper(
|
||||||
|
$library_info['xautoload'],
|
||||||
|
// The module name and library name allow the closure to be recovered on
|
||||||
|
// unserialize.
|
||||||
|
$library_info['module'],
|
||||||
|
$library_name);
|
||||||
|
$library_info['xautoload'] = $callable;
|
||||||
|
}
|
||||||
|
# $library_info['callbacks']['pre-load'][] = new LibrariesPreLoadCallback($callable);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,91 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\Libraries;
|
||||||
|
|
||||||
|
|
||||||
|
use Drupal\xautoload\ClassFinder\ExtendedClassFinderInterface;
|
||||||
|
use Drupal\xautoload\DrupalSystem\DrupalSystemInterface;
|
||||||
|
use Drupal\xautoload\Phases\PhaseObserverInterface;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers autoload mappings from all libraries on hook_init(), or after the
|
||||||
|
* first cache miss.
|
||||||
|
*/
|
||||||
|
class LibrariesOnInit implements PhaseObserverInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var DrupalSystemInterface
|
||||||
|
*/
|
||||||
|
private $system;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ExtendedClassFinderInterface
|
||||||
|
*/
|
||||||
|
private $finder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param DrupalSystemInterface $system
|
||||||
|
*/
|
||||||
|
function __construct(DrupalSystemInterface $system) {
|
||||||
|
$this->system = $system;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wake up after a cache fail.
|
||||||
|
*
|
||||||
|
* @param ExtendedClassFinderInterface $finder
|
||||||
|
* @param string[] $extensions
|
||||||
|
* Extension type by extension name.
|
||||||
|
*/
|
||||||
|
public function wakeUp(ExtendedClassFinderInterface $finder, array $extensions) {
|
||||||
|
$this->finder = $finder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enter the boot phase of the request, where all bootstrap module files are included.
|
||||||
|
*/
|
||||||
|
public function enterBootPhase() {
|
||||||
|
// Nothing.
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enter the main phase of the request, where all module files are included.
|
||||||
|
*/
|
||||||
|
public function enterMainPhase() {
|
||||||
|
$this->registerLibrariesFinderPlugin();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* React to new extensions that were just enabled.
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @param string $type
|
||||||
|
*/
|
||||||
|
public function welcomeNewExtension($name, $type) {
|
||||||
|
// Nothing.
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* React to xautoload_modules_enabled()
|
||||||
|
*
|
||||||
|
* @param string[] $modules
|
||||||
|
* New module names.
|
||||||
|
*/
|
||||||
|
public function modulesEnabled($modules) {
|
||||||
|
$this->system->drupalStaticReset('libraries_info');
|
||||||
|
$this->system->cacheClearAll(XAUTOLOAD_CACHENAME_LIBRARIES_INFO, 'cache');
|
||||||
|
$this->registerLibrariesFinderPlugin();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers all libraries that have an "xautoload" setting.
|
||||||
|
*/
|
||||||
|
private function registerLibrariesFinderPlugin() {
|
||||||
|
$plugin = new LibrariesFinderPlugin($this->finder, $this->system);
|
||||||
|
$this->finder->getPrefixMap()->registerDeepPath('', '', $plugin);
|
||||||
|
$this->finder->getNamespaceMap()->registerDeepPath('', '', $plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\Libraries;
|
||||||
|
|
||||||
|
|
||||||
|
class LibrariesPreLoadCallback {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var callable
|
||||||
|
* A callable that is serializable, so not a closure.
|
||||||
|
* Can be a SerializableClosureWrapper.
|
||||||
|
*/
|
||||||
|
private $callable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param callable $callable
|
||||||
|
*/
|
||||||
|
function __construct($callable) {
|
||||||
|
$this->callable = $callable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback that is applied directly before the library is loaded. At this
|
||||||
|
* point the library contains variant-specific information, if specified. Note
|
||||||
|
* that in this group the 'variants' property is no longer available.
|
||||||
|
*
|
||||||
|
* @param array $library
|
||||||
|
* An array of library information belonging to the top-level library, a
|
||||||
|
* specific version, a specific variant or a specific variant of a specific
|
||||||
|
* version. Because library information such as the 'files' property (see
|
||||||
|
* above) can be declared in all these different locations of the library
|
||||||
|
* array, but a callback may have to act on all these different parts of the
|
||||||
|
* library, it is called recursively for each library with a certain part of
|
||||||
|
* the libraries array passed as $library each time.
|
||||||
|
* @param string|null $version
|
||||||
|
* If the $library array belongs to a certain version (see above), a string
|
||||||
|
* containing the version. This argument may be empty, so NULL should be
|
||||||
|
* specified as the default value.
|
||||||
|
* @param string|null $variant
|
||||||
|
* If the $library array belongs to a certain variant (see above), a string
|
||||||
|
* containing the variant name. This argument may be empty, so NULL should
|
||||||
|
* be specified as the default value.
|
||||||
|
*/
|
||||||
|
function __invoke($library, $version, $variant) {
|
||||||
|
if (!empty($library['installed'])) {
|
||||||
|
xautoload()->proxyFinder->observeFirstCacheMiss(
|
||||||
|
new LibraryCacheMissObserver($this->callable, $library['library path']));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\Libraries;
|
||||||
|
|
||||||
|
|
||||||
|
use Drupal\xautoload\CacheMissObserver\CacheMissObserverInterface;
|
||||||
|
use Drupal\xautoload\ClassFinder\ExtendedClassFinderInterface;
|
||||||
|
|
||||||
|
class LibraryCacheMissObserver implements CacheMissObserverInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var callable
|
||||||
|
*/
|
||||||
|
private $callable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $path;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param callable $callable
|
||||||
|
* @param string $path
|
||||||
|
*/
|
||||||
|
function __construct($callable, $path) {
|
||||||
|
$this->callable = $callable;
|
||||||
|
$this->path = $path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes the operation.
|
||||||
|
*
|
||||||
|
* This method will only be called if and when the "real" class finder is
|
||||||
|
* initialized.
|
||||||
|
*
|
||||||
|
* @param ExtendedClassFinderInterface $finder
|
||||||
|
* The class finder.
|
||||||
|
*/
|
||||||
|
function cacheMiss($finder) {
|
||||||
|
$adapter = \xautoload_InjectedAPI_hookXautoload::create($finder, $this->path);
|
||||||
|
call_user_func($this->callable, $adapter, $this->path);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,85 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\Libraries;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A wrapper that allows serialization of closures from hook_libraries_info().
|
||||||
|
*/
|
||||||
|
class SerializableClosureWrapper {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The closure
|
||||||
|
*
|
||||||
|
* @var \Closure
|
||||||
|
*/
|
||||||
|
private $closure;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Module that implements hook_libraries_info()
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $moduleName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name of the library that has this closure for xautoload.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $libraryName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \Closure $closure
|
||||||
|
* @param string $moduleName
|
||||||
|
* @param string $libraryName
|
||||||
|
*/
|
||||||
|
public function __construct($closure, $moduleName, $libraryName) {
|
||||||
|
$this->closure = $closure;
|
||||||
|
$this->moduleName = $moduleName;
|
||||||
|
$this->libraryName = $libraryName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __sleep() {
|
||||||
|
return array('moduleName', 'libraryName');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \Drupal\xautoload\Adapter\LocalDirectoryAdapter $adapter
|
||||||
|
*/
|
||||||
|
public function __invoke($adapter) {
|
||||||
|
$closure = $this->lazyGetClosure();
|
||||||
|
if ($closure instanceof \Closure) {
|
||||||
|
$closure($adapter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \Closure|FALSE
|
||||||
|
*/
|
||||||
|
private function lazyGetClosure() {
|
||||||
|
return isset($this->closure)
|
||||||
|
? $this->closure
|
||||||
|
: $this->closure = $this->loadClosure();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \Closure|FALSE
|
||||||
|
*/
|
||||||
|
private function loadClosure() {
|
||||||
|
$source_function = $this->moduleName . '_libraries_info';
|
||||||
|
if (!function_exists($source_function)) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
$module_libraries = $source_function();
|
||||||
|
if (!isset($module_libraries[$this->libraryName]['xautoload'])) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
$closure_candidate = $module_libraries[$this->libraryName]['xautoload'];
|
||||||
|
if (!$closure_candidate instanceof \Closure) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
return $closure_candidate;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
110
frontend/drupal/modules/xautoload/src/Main.php
Normal file
110
frontend/drupal/modules/xautoload/src/Main.php
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\xautoload;
|
||||||
|
|
||||||
|
use Drupal\xautoload\DIC\ServiceContainer;
|
||||||
|
use Drupal\xautoload\DIC\ServiceContainerInterface;
|
||||||
|
|
||||||
|
class Main implements ServiceContainerInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ServiceContainer
|
||||||
|
* The service container, similar to a DIC.
|
||||||
|
*/
|
||||||
|
protected $services;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ServiceContainer $services
|
||||||
|
*/
|
||||||
|
function __construct($services) {
|
||||||
|
$this->services = $services;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return ServiceContainer
|
||||||
|
*/
|
||||||
|
function getServiceContainer() {
|
||||||
|
return $this->services;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalidate all values stored in APC.
|
||||||
|
*/
|
||||||
|
function flushCache() {
|
||||||
|
$this->services->cacheManager->renewCachePrefix();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a module in early bootstrap, or from modulename.install.
|
||||||
|
*
|
||||||
|
* This is only needed for modules that need autoloading very early in the
|
||||||
|
* request, or e.g. during uninstall, or any situation that xautoload cannot
|
||||||
|
* catch up with.
|
||||||
|
*
|
||||||
|
* The method will register all autoloading schemes for this module that are
|
||||||
|
* supported by default:
|
||||||
|
* - PSR-0: "Drupal\\$module\\Foo" => "$module_dir/lib/Drupal/$module/Foo.php"
|
||||||
|
* - PEAR-FLAT: $module . "_Foo_Bar" => "$module_dir/lib/Foo/Bar.php"
|
||||||
|
*
|
||||||
|
* It will not register anything for PSR-4, since it is not clear whether this
|
||||||
|
* will be in "/lib/" or "/src/" or elsewhere.
|
||||||
|
*
|
||||||
|
* Suggested usage: (in your $modulename.module, or $modulename.install):
|
||||||
|
*
|
||||||
|
* xautoload()->registerModule(__FILE__);
|
||||||
|
*
|
||||||
|
* @param string $__FILE__
|
||||||
|
* File path to a *.module or *.install file.
|
||||||
|
* The basename of the file MUST be the module name.
|
||||||
|
* It is recommended to call this from the respective file itself, using the
|
||||||
|
* __FILE__ constant for this argument.
|
||||||
|
*/
|
||||||
|
function registerModule($__FILE__) {
|
||||||
|
$this->services->extensionNamespaces->registerExtension($__FILE__);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a module as PSR-4, in early bootstrap or from modulename.install
|
||||||
|
*
|
||||||
|
* This can be used while Drupal 8 is still undecided whether PSR-4 class
|
||||||
|
* files should live in "lib" or in "src" by default.
|
||||||
|
*
|
||||||
|
* Suggested usage: (in your $modulename.module, or $modulename.install):
|
||||||
|
*
|
||||||
|
* // E.g. "Drupal\\$module\\Foo" => "$module_dir/lib/Foo.php"
|
||||||
|
* xautoload()->registerModulePsr4(__FILE__, 'lib');
|
||||||
|
*
|
||||||
|
* or
|
||||||
|
*
|
||||||
|
* // E.g. "Drupal\\$module\\Foo" => "$module_dir/src/Foo.php"
|
||||||
|
* xautoload()->registerModulePsr4(__FILE__, 'src');
|
||||||
|
*
|
||||||
|
* or
|
||||||
|
*
|
||||||
|
* // E.g. "Drupal\\$module\\Foo" => "$module_dir/psr4/Foo.php"
|
||||||
|
* xautoload()->registerModulePsr4(__FILE__, 'psr4');
|
||||||
|
*
|
||||||
|
* @param string $__FILE__
|
||||||
|
* File path to a *.module or *.install file.
|
||||||
|
* The basename of the file MUST be the module name.
|
||||||
|
* It is recommended to call this from the respective file itself, using the
|
||||||
|
* __FILE__ constant for this argument.
|
||||||
|
* @param string $subdir
|
||||||
|
* The PSR-4 base directory for the module namespace, relative to the module
|
||||||
|
* directory. E.g. "src" or "lib".
|
||||||
|
*/
|
||||||
|
function registerModulePsr4($__FILE__, $subdir) {
|
||||||
|
$this->services->extensionNamespaces->registerExtensionPsr4($__FILE__, $subdir);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Magic getter for service objects. This lets this class act as a proxy for
|
||||||
|
* the service container.
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
function __get($key) {
|
||||||
|
return $this->services->__get($key);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\Phases;
|
||||||
|
|
||||||
|
use Drupal\xautoload\ClassFinder\ExtendedClassFinderInterface;
|
||||||
|
use Drupal\xautoload\ClassFinder\Plugin\DrupalCoreRegistryPlugin;
|
||||||
|
|
||||||
|
class DrupalCoreRegistryRegistrator implements PhaseObserverInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wake up after a cache fail.
|
||||||
|
*
|
||||||
|
* @param ExtendedClassFinderInterface $finder
|
||||||
|
* The class finder object, with any cache decorator peeled off.
|
||||||
|
* @param string[] $extensions
|
||||||
|
* Currently enabled extensions. Extension type by extension name.
|
||||||
|
*/
|
||||||
|
public function wakeUp(ExtendedClassFinderInterface $finder, array $extensions) {
|
||||||
|
$plugin = new DrupalCoreRegistryPlugin(DRUPAL_ROOT . '/');
|
||||||
|
$finder->getNamespaceMap()->registerDeepPath('', 'registry', $plugin);
|
||||||
|
$finder->getPrefixMap()->registerDeepPath('', 'registry', $plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enter the boot phase of the request, where all bootstrap module files are included.
|
||||||
|
*/
|
||||||
|
public function enterBootPhase() {
|
||||||
|
// Nothing.
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enter the main phase of the request, where all module files are included.
|
||||||
|
*/
|
||||||
|
public function enterMainPhase() {
|
||||||
|
// Nothing.
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* React to new extensions that were just enabled.
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @param string $type
|
||||||
|
*/
|
||||||
|
public function welcomeNewExtension($name, $type) {
|
||||||
|
// Nothing.
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* React to xautoload_modules_enabled()
|
||||||
|
*
|
||||||
|
* @param string[] $modules
|
||||||
|
* New module names.
|
||||||
|
*/
|
||||||
|
public function modulesEnabled($modules) {
|
||||||
|
// Nothing.
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,171 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\Phases;
|
||||||
|
|
||||||
|
use Drupal\xautoload\DrupalSystem\DrupalSystemInterface;
|
||||||
|
use Drupal\xautoload\CacheMissObserver\CacheMissObserverInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Records events during a Drupal request, and forwards them to the registered
|
||||||
|
* observers after the first class loader cache miss.
|
||||||
|
*/
|
||||||
|
class DrupalPhaseControl implements CacheMissObserverInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var DrupalSystemInterface
|
||||||
|
*/
|
||||||
|
private $system;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var PhaseObserverInterface[]
|
||||||
|
*/
|
||||||
|
private $observers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
* TRUE, if the class finder is no longer the cached one..
|
||||||
|
*/
|
||||||
|
private $awake = FALSE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string[]|null
|
||||||
|
* Extension type by extension name.
|
||||||
|
*/
|
||||||
|
private $extensions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
* TRUE, if in main phase.
|
||||||
|
*/
|
||||||
|
private $mainPhase = FALSE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
* TRUE, if in of after boot phase.
|
||||||
|
*/
|
||||||
|
private $bootPhase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param DrupalSystemInterface $system
|
||||||
|
* @param PhaseObserverInterface[] $observers
|
||||||
|
*/
|
||||||
|
public function __construct(DrupalSystemInterface $system, array $observers) {
|
||||||
|
$this->system = $system;
|
||||||
|
$this->observers = $observers;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function cacheMiss($finder) {
|
||||||
|
$this->extensions = $this->system->getActiveExtensions();
|
||||||
|
foreach ($this->observers as $observer) {
|
||||||
|
$observer->wakeUp($finder, $this->extensions);
|
||||||
|
}
|
||||||
|
$this->awake = TRUE;
|
||||||
|
if ($this->bootPhase) {
|
||||||
|
// We slipped into boot phase while asleep. Need to catch up.
|
||||||
|
foreach ($this->observers as $observer) {
|
||||||
|
$observer->enterBootPhase();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($this->mainPhase) {
|
||||||
|
// We slipped into main phase while asleep. Need to catch up.
|
||||||
|
foreach ($this->observers as $observer) {
|
||||||
|
$observer->enterMainPhase();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function enterBootPhase() {
|
||||||
|
if ($this->bootPhase) {
|
||||||
|
// We are already in the main phase. Nothing changes.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$this->bootPhase = TRUE;
|
||||||
|
if (!$this->awake) {
|
||||||
|
// The entire thing is not initialized yet.
|
||||||
|
// Postpone until operateOnFinder()
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
foreach ($this->observers as $observer) {
|
||||||
|
$observer->enterBootPhase();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiate the main phase.
|
||||||
|
*
|
||||||
|
* Called from
|
||||||
|
* @see xautoload_custom_theme()
|
||||||
|
* @see xautolaod_init()
|
||||||
|
*/
|
||||||
|
public function enterMainPhase() {
|
||||||
|
// Main phase implies boot phase.
|
||||||
|
$this->enterBootPhase();
|
||||||
|
if ($this->mainPhase) {
|
||||||
|
// We are already in the main phase. Nothing changes.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$this->mainPhase = TRUE;
|
||||||
|
if (!$this->awake) {
|
||||||
|
// The entire thing is not initialized yet.
|
||||||
|
// Postpone until operateOnFinder()
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
foreach ($this->observers as $observer) {
|
||||||
|
$observer->enterMainPhase();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if new extensions have been enabled, and registers them.
|
||||||
|
*
|
||||||
|
* This is called from xautoload_module_implements_alter(), which is called
|
||||||
|
* whenever a new module is enabled, but also some calls we need to ignore.
|
||||||
|
*/
|
||||||
|
public function checkNewExtensions() {
|
||||||
|
if (!$this->awake) {
|
||||||
|
// The entire thing is not initialized yet.
|
||||||
|
// Postpone until operateOnFinder()
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$activeExtensions = $this->system->getActiveExtensions();
|
||||||
|
if ($activeExtensions === $this->extensions) {
|
||||||
|
// Nothing actually changed. False alarm.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Now check all extensions to find out if any of them is new.
|
||||||
|
foreach ($activeExtensions as $name => $type) {
|
||||||
|
if (!isset($this->extensions[$name])) {
|
||||||
|
// This extension was freshly enabled.
|
||||||
|
if ('xautoload' === $name) {
|
||||||
|
// If xautoload is enabled in this request, then boot phase and main
|
||||||
|
// phase are not properly initialized yet.
|
||||||
|
$this->enterMainPhase();
|
||||||
|
}
|
||||||
|
// Notify observers about the new extension.
|
||||||
|
foreach ($this->observers as $observer) {
|
||||||
|
$observer->welcomeNewExtension($name, $type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called from @see xautoload_modules_enabled()
|
||||||
|
*
|
||||||
|
* @param $modules
|
||||||
|
*/
|
||||||
|
public function modulesEnabled($modules) {
|
||||||
|
if (!$this->awake) {
|
||||||
|
// No need to postpone.
|
||||||
|
// initMainPhase() will have these modules included.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
foreach ($this->observers as $observer) {
|
||||||
|
$observer->modulesEnabled($modules);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,253 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\Phases;
|
||||||
|
|
||||||
|
|
||||||
|
use Drupal\xautoload\ClassFinder\ExtendedClassFinderInterface;
|
||||||
|
use Drupal\xautoload\ClassFinder\Plugin\DrupalExtensionNamespaceFinderPlugin;
|
||||||
|
use Drupal\xautoload\ClassFinder\Plugin\DrupalExtensionUnderscoreFinderPlugin;
|
||||||
|
use Drupal\xautoload\DrupalSystem\DrupalSystemInterface;
|
||||||
|
|
||||||
|
class ExtensionNamespaces implements PhaseObserverInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ExtendedClassFinderInterface
|
||||||
|
*/
|
||||||
|
private $finder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var (string|false)[]
|
||||||
|
*/
|
||||||
|
private $queue = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Which modules are already registered.
|
||||||
|
*
|
||||||
|
* @var bool[]
|
||||||
|
*/
|
||||||
|
private $registered = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var DrupalSystemInterface
|
||||||
|
*/
|
||||||
|
private $system;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param DrupalSystemInterface $system
|
||||||
|
*/
|
||||||
|
public function __construct(DrupalSystemInterface $system) {
|
||||||
|
$this->system = $system;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers the namespaces for a module even though it might be currently
|
||||||
|
* disabled, or even though it might be early in the request.
|
||||||
|
*
|
||||||
|
* @param string $__FILE__
|
||||||
|
*/
|
||||||
|
public function registerExtension($__FILE__) {
|
||||||
|
if (NULL === $this->finder) {
|
||||||
|
// Sleeping..
|
||||||
|
$this->queue[$__FILE__] = FALSE;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$info = pathinfo($__FILE__);
|
||||||
|
$name = $info['filename'];
|
||||||
|
|
||||||
|
// Check if something was registered before.
|
||||||
|
if (isset($this->registered[$name])) {
|
||||||
|
// Already registered.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->_registerExtension($name, $info['dirname']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers the namespace with PSR-4 for a module even though it might be
|
||||||
|
* currently disabled, or even though it might be early in the request.
|
||||||
|
*
|
||||||
|
* @param string $__FILE__
|
||||||
|
* @param string $subdir
|
||||||
|
*/
|
||||||
|
public function registerExtensionPsr4($__FILE__, $subdir) {
|
||||||
|
if (NULL === $this->finder) {
|
||||||
|
// Sleeping..
|
||||||
|
$this->queue[$__FILE__] = $subdir;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$info = pathinfo($__FILE__);
|
||||||
|
$name = $info['filename'];
|
||||||
|
|
||||||
|
// Check if something was registered before.
|
||||||
|
if (isset($this->registered[$name])) {
|
||||||
|
if ('psr-4' === $this->registered[$name]) {
|
||||||
|
// It already happened.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Unregister the lazy plugins.
|
||||||
|
$this->finder->getNamespaceMap()->unregisterDeepPath(
|
||||||
|
'Drupal/' . $name . '/',
|
||||||
|
$name
|
||||||
|
);
|
||||||
|
$this->finder->getPrefixMap()->unregisterDeepPath(
|
||||||
|
str_replace('_', '/', $name) . '/',
|
||||||
|
$name
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$this->_registerExtensionPsr4($name, $info['dirname'], $subdir);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wake up after a cache fail.
|
||||||
|
*
|
||||||
|
* @param ExtendedClassFinderInterface $finder
|
||||||
|
* @param string[] $extensions
|
||||||
|
* Extension type by extension name.
|
||||||
|
*/
|
||||||
|
public function wakeUp(ExtendedClassFinderInterface $finder, array $extensions) {
|
||||||
|
$this->finder = $finder;
|
||||||
|
|
||||||
|
// Register queued extensions.
|
||||||
|
foreach ($this->queue as $__FILE__ => $subdir) {
|
||||||
|
$info = pathinfo($__FILE__);
|
||||||
|
$name = $info['filename'];
|
||||||
|
$dir = $info['dirname'];
|
||||||
|
if (FALSE === $subdir) {
|
||||||
|
// This is not PSR-4.
|
||||||
|
$this->_registerExtension($name, $dir);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// This is PSR-4.
|
||||||
|
$this->_registerExtensionPsr4($name, $dir, $subdir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$extensions = array_diff_key($extensions, $this->registered);
|
||||||
|
|
||||||
|
// Register remaining extensions, using the lazy plugins.
|
||||||
|
$this->_registerLazyExtensionPlugins($extensions);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enter the boot phase of the request, where all bootstrap module files are included.
|
||||||
|
*/
|
||||||
|
public function enterBootPhase() {
|
||||||
|
// Nothing.
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enter the main phase of the request, where all module files are included.
|
||||||
|
*/
|
||||||
|
public function enterMainPhase() {
|
||||||
|
// Nothing.
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* React to new extensions that were just enabled.
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @param string $type
|
||||||
|
*/
|
||||||
|
public function welcomeNewExtension($name, $type) {
|
||||||
|
if (isset($this->registered[$name])) {
|
||||||
|
// Already registered.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$dir = $this->system->drupalGetPath($type, $name);
|
||||||
|
$this->_registerExtension($name, $dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* React to xautoload_modules_enabled()
|
||||||
|
*
|
||||||
|
* @param string[] $modules
|
||||||
|
* New module names.
|
||||||
|
*/
|
||||||
|
public function modulesEnabled($modules) {
|
||||||
|
// Nothing.
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $name
|
||||||
|
* @param string $dir
|
||||||
|
*/
|
||||||
|
private function _registerExtension($name, $dir) {
|
||||||
|
if (is_dir($lib = $dir . '/lib')) {
|
||||||
|
$this->finder->addPsr0('Drupal\\' . $name . '\\', $lib);
|
||||||
|
$this->finder->addPearFlat($name . '_', $lib);
|
||||||
|
}
|
||||||
|
if (is_dir($src = $dir . '/src')) {
|
||||||
|
$this->finder->addPsr4('Drupal\\' . $name . '\\', $src);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->registered[$name] = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $name
|
||||||
|
* @param string $dir
|
||||||
|
* @param string $subdir
|
||||||
|
*/
|
||||||
|
private function _registerExtensionPsr4($name, $dir, $subdir) {
|
||||||
|
$this->finder->addPsr4('Drupal\\' . $name . '\\', $dir . '/' . $subdir);
|
||||||
|
|
||||||
|
// Re-add the PSR-0 test directory, for consistency's sake.
|
||||||
|
if (is_dir($lib_tests = $dir . '/lib/Drupal/' . $name . '/Tests')) {
|
||||||
|
$this->finder->addPsr0('Drupal\\' . $name . '\\Tests\\', $lib_tests);
|
||||||
|
}
|
||||||
|
$this->registered[$name] = 'psr-4';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register lazy plugins for enabled Drupal modules and themes, assuming that
|
||||||
|
* we don't know yet whether they use PSR-0, PEAR-Flat, or none of these.
|
||||||
|
*
|
||||||
|
* @param string[] $extensions
|
||||||
|
* An array where the keys are extension names, and the values are extension
|
||||||
|
* types like 'module' or 'theme'.
|
||||||
|
*/
|
||||||
|
private function _registerLazyExtensionPlugins(array $extensions) {
|
||||||
|
|
||||||
|
$namespaceBehaviors = array();
|
||||||
|
$prefixBehaviors = array();
|
||||||
|
foreach (array('module', 'theme') as $extension_type) {
|
||||||
|
$namespaceBehaviors[$extension_type] = new DrupalExtensionNamespaceFinderPlugin(
|
||||||
|
$extension_type,
|
||||||
|
$this->finder->getNamespaceMap(),
|
||||||
|
$this->finder->getPrefixMap(),
|
||||||
|
$this->system);
|
||||||
|
$prefixBehaviors[$extension_type] = new DrupalExtensionUnderscoreFinderPlugin(
|
||||||
|
$extension_type,
|
||||||
|
$this->finder->getNamespaceMap(),
|
||||||
|
$this->finder->getPrefixMap(),
|
||||||
|
$this->system);
|
||||||
|
}
|
||||||
|
|
||||||
|
$prefix_map = array();
|
||||||
|
$namespace_map = array();
|
||||||
|
foreach ($extensions as $name => $type) {
|
||||||
|
if (empty($namespaceBehaviors[$type])) {
|
||||||
|
// Unsupported extension type, e.g. "theme_engine".
|
||||||
|
// This can happen if a site was upgraded from Drupal 6.
|
||||||
|
// See https://drupal.org/comment/8503979#comment-8503979
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!empty($this->registered[$name])) {
|
||||||
|
// The extension has already been processed.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$namespace_map['Drupal/' . $name . '/'][$name] = $namespaceBehaviors[$type];
|
||||||
|
$prefix_map[str_replace('_', '/', $name) . '/'][$name] = $prefixBehaviors[$type];
|
||||||
|
$this->registered[$name] = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->finder->getNamespaceMap()->registerDeepPaths($namespace_map);
|
||||||
|
$this->finder->getPrefixMap()->registerDeepPaths($prefix_map);
|
||||||
|
}
|
||||||
|
}
|
111
frontend/drupal/modules/xautoload/src/Phases/HookXautoload.php
Normal file
111
frontend/drupal/modules/xautoload/src/Phases/HookXautoload.php
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\Phases;
|
||||||
|
|
||||||
|
|
||||||
|
use Drupal\xautoload\ClassFinder\ExtendedClassFinderInterface;
|
||||||
|
use Drupal\xautoload\DrupalSystem\DrupalSystemInterface;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A variation of hook_xautoload() that fires
|
||||||
|
* - when all *module file are included:
|
||||||
|
* - xautoload_custom_theme(), or
|
||||||
|
* - xautoload_init(), whichever is first.
|
||||||
|
* - when module_enable() has finished:
|
||||||
|
* - xautoload_modules_enabled()
|
||||||
|
*
|
||||||
|
* This can cause any implementation to be fired multiple times per request.
|
||||||
|
*/
|
||||||
|
class HookXautoload implements PhaseObserverInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ExtendedClassFinderInterface|null
|
||||||
|
*/
|
||||||
|
private $finder = NULL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var DrupalSystemInterface
|
||||||
|
*/
|
||||||
|
private $system;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string[]
|
||||||
|
*/
|
||||||
|
private $extensions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param DrupalSystemInterface $system
|
||||||
|
*/
|
||||||
|
public function __construct(DrupalSystemInterface $system) {
|
||||||
|
$this->system = $system;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wake up after a cache fail.
|
||||||
|
*
|
||||||
|
* @param ExtendedClassFinderInterface $finder
|
||||||
|
* @param string[] $extensions
|
||||||
|
* Extension type by extension name.
|
||||||
|
*/
|
||||||
|
public function wakeUp(ExtendedClassFinderInterface $finder, array $extensions) {
|
||||||
|
$this->finder = $finder;
|
||||||
|
$this->extensions = $extensions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enter the boot phase of the request, where all bootstrap module files are included.
|
||||||
|
*/
|
||||||
|
public function enterBootPhase() {
|
||||||
|
// Nothing.
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enter the main phase of the request, where all module files are included.
|
||||||
|
*/
|
||||||
|
public function enterMainPhase() {
|
||||||
|
$modules = $this->system->moduleImplements('xautoload');
|
||||||
|
$this->runHookXautoload($modules);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* New extensions were enabled/installed.
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* Extension type by name.
|
||||||
|
* @param string $type
|
||||||
|
*/
|
||||||
|
public function welcomeNewExtension($name, $type) {
|
||||||
|
// Nothing.
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* React to xautoload_modules_enabled()
|
||||||
|
*
|
||||||
|
* @param string[] $modules
|
||||||
|
* New module names.
|
||||||
|
*/
|
||||||
|
public function modulesEnabled($modules) {
|
||||||
|
$modules = $this->system->moduleImplements('xautoload');
|
||||||
|
$this->runHookXautoload($modules);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs hook_xautoload() on all enabled modules.
|
||||||
|
*
|
||||||
|
* This may occur multiple times in a request, if new modules are enabled.
|
||||||
|
*
|
||||||
|
* @param array $modules
|
||||||
|
*/
|
||||||
|
private function runHookXautoload(array $modules) {
|
||||||
|
// Let other modules register stuff to the finder via hook_xautoload().
|
||||||
|
$adapter = \xautoload_InjectedAPI_hookXautoload::create($this->finder, '');
|
||||||
|
foreach ($modules as $module) {
|
||||||
|
$adapter->setExtensionDir($dir = $this->system->drupalGetPath('module', $module));
|
||||||
|
$function = $module . '_xautoload';
|
||||||
|
$function($adapter, $dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,110 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\Phases;
|
||||||
|
|
||||||
|
|
||||||
|
use Drupal\xautoload\ClassFinder\ExtendedClassFinderInterface;
|
||||||
|
use Drupal\xautoload\DrupalSystem\DrupalSystemInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A variation of hook_xautoload() that fires very early, as soon as a *.module
|
||||||
|
* file is included, but only once per module / request.
|
||||||
|
*/
|
||||||
|
class HookXautoloadEarly implements PhaseObserverInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ExtendedClassFinderInterface|null
|
||||||
|
*/
|
||||||
|
private $finder = NULL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var DrupalSystemInterface
|
||||||
|
*/
|
||||||
|
private $system;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string[]
|
||||||
|
*/
|
||||||
|
private $extensions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param DrupalSystemInterface $system
|
||||||
|
*/
|
||||||
|
public function __construct(DrupalSystemInterface $system) {
|
||||||
|
$this->system = $system;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wake up after a cache fail.
|
||||||
|
*
|
||||||
|
* @param ExtendedClassFinderInterface $finder
|
||||||
|
* @param string[] $extensions
|
||||||
|
* Extension type by extension name.
|
||||||
|
*/
|
||||||
|
public function wakeUp(ExtendedClassFinderInterface $finder, array $extensions) {
|
||||||
|
$this->finder = $finder;
|
||||||
|
$this->extensions = $extensions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enter the boot phase of the request, where all bootstrap module files are included.
|
||||||
|
*/
|
||||||
|
public function enterBootPhase() {
|
||||||
|
// @todo Call hook_xautoload() on bootstrap modules, if in bootstrap phase.
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enter the main phase of the request, where all module files are included.
|
||||||
|
*/
|
||||||
|
public function enterMainPhase() {
|
||||||
|
// @todo Don't use moduleImplements(), to prevent hook_module_implements_alter()
|
||||||
|
$modules = $this->system->moduleImplements('xautoload');
|
||||||
|
// @todo Remove boot modules from the list.
|
||||||
|
$this->runHookXautoload($modules);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* New extensions were enabled/installed.
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* Extension type by name.
|
||||||
|
* @param string $type
|
||||||
|
*/
|
||||||
|
public function welcomeNewExtension($name, $type) {
|
||||||
|
$function = $name . '_xautoload';
|
||||||
|
if (!function_exists($function)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$dir = $this->system->drupalGetPath($type, $name);
|
||||||
|
$adapter = \xautoload_InjectedAPI_hookXautoload::create($this->finder, $dir);
|
||||||
|
$function($adapter, $dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* React to xautoload_modules_enabled()
|
||||||
|
*
|
||||||
|
* @param string[] $modules
|
||||||
|
* New module names.
|
||||||
|
*/
|
||||||
|
public function modulesEnabled($modules) {
|
||||||
|
// Nothing.
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs hook_xautoload() on all enabled modules.
|
||||||
|
*
|
||||||
|
* This may occur multiple times in a request, if new modules are enabled.
|
||||||
|
*
|
||||||
|
* @param array $modules
|
||||||
|
*/
|
||||||
|
private function runHookXautoload(array $modules) {
|
||||||
|
// Let other modules register stuff to the finder via hook_xautoload().
|
||||||
|
$adapter = \xautoload_InjectedAPI_hookXautoload::create($this->finder, '');
|
||||||
|
foreach ($modules as $module) {
|
||||||
|
$adapter->setExtensionDir($dir = $this->system->drupalGetPath('module', $module));
|
||||||
|
$function = $module . '_xautoload';
|
||||||
|
$function($adapter, $dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,63 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace Drupal\xautoload\Phases;
|
||||||
|
|
||||||
|
|
||||||
|
use Drupal\xautoload\ClassFinder\ExtendedClassFinderInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Observes switching into specific "phases" of a Drupal process.
|
||||||
|
*
|
||||||
|
* @see \Drupal\xautoload\Phases\DrupalPhaseControl
|
||||||
|
*
|
||||||
|
* These phases only fire after the class loader had a cache fail.
|
||||||
|
* This way, all the initialization logic, subscribing module namespaces etc,
|
||||||
|
* will not be executed on an average request.
|
||||||
|
*
|
||||||
|
* Phase switching events can hit the observer later in the request, even if the
|
||||||
|
* respective phase has already started long ago. E.g. the enterBootPhase() will
|
||||||
|
* still fire even if the cache miss happens during the "main phase".
|
||||||
|
*
|
||||||
|
* However, the order of phase events being called on an observer will always be
|
||||||
|
* the same. And wakeUp() will always be called before any other phase event,
|
||||||
|
* giving the observer the chance to set up the finder object.
|
||||||
|
*/
|
||||||
|
interface PhaseObserverInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wake up after a cache fail.
|
||||||
|
*
|
||||||
|
* @param ExtendedClassFinderInterface $finder
|
||||||
|
* The class finder object, with any cache decorator peeled off.
|
||||||
|
* @param string[] $extensions
|
||||||
|
* Currently enabled extensions. Extension type by extension name.
|
||||||
|
*/
|
||||||
|
public function wakeUp(ExtendedClassFinderInterface $finder, array $extensions);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enter the boot phase of the request, where all bootstrap module files are included.
|
||||||
|
*/
|
||||||
|
public function enterBootPhase();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enter the main phase of the request, where all module files are included.
|
||||||
|
*/
|
||||||
|
public function enterMainPhase();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* React to new extensions that were just enabled.
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @param string $type
|
||||||
|
*/
|
||||||
|
public function welcomeNewExtension($name, $type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* React to xautoload_modules_enabled()
|
||||||
|
*
|
||||||
|
* @param string[] $modules
|
||||||
|
* New module names.
|
||||||
|
*/
|
||||||
|
public function modulesEnabled($modules);
|
||||||
|
}
|
202
frontend/drupal/modules/xautoload/src/Util.php
Normal file
202
frontend/drupal/modules/xautoload/src/Util.php
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\xautoload;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A number of static methods that don't interact with any global state.
|
||||||
|
*/
|
||||||
|
class Util {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a random string made of uppercase and lowercase characters and numbers.
|
||||||
|
*
|
||||||
|
* @param int $length
|
||||||
|
* Length of the random string to generate
|
||||||
|
* @param string $chars
|
||||||
|
* Allowed characters
|
||||||
|
* @param string $chars_first
|
||||||
|
* Allowed characters for the first character.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* Random string of the specified length
|
||||||
|
*/
|
||||||
|
static function randomString($length = 30, $chars = NULL, $chars_first = NULL) {
|
||||||
|
|
||||||
|
if (!isset($chars)) {
|
||||||
|
$chars = 'abcdefghijklmnopqrstuvwxyz' .
|
||||||
|
'ABCDEFGHIJKLMNOPQRSTUVWXYZ' .
|
||||||
|
'1234567890';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($chars_first)) {
|
||||||
|
$chars_first = $chars;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the randomizer.
|
||||||
|
srand((double) microtime() * 1000000);
|
||||||
|
|
||||||
|
$str = substr($chars_first, rand() % strlen($chars_first), 1);
|
||||||
|
for ($i = 0; $i < $length; ++$i) {
|
||||||
|
$str .= substr($chars, rand() % strlen($chars), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $str;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a random string that is a valid PHP identifier.
|
||||||
|
*
|
||||||
|
* @param int $length
|
||||||
|
* Length of the random string to generate
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* Random string of the specified length
|
||||||
|
*/
|
||||||
|
static function randomIdentifier($length = 40) {
|
||||||
|
|
||||||
|
// Since PHP is case insensitive, we only user lowercase characters.
|
||||||
|
$chars_first = 'abcdefghijklmnopqrstuvwxyz_';
|
||||||
|
$chars = 'abcdefghijklmnopqrstuvwxyz_1234567890';
|
||||||
|
|
||||||
|
return self::randomString($length, $chars, $chars_first);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a random string that is a stream wrapper protocol.
|
||||||
|
*
|
||||||
|
* @param int $length
|
||||||
|
* Length of the random string to generate
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* Random string of the specified length
|
||||||
|
*/
|
||||||
|
static function randomProtocol($length = 40) {
|
||||||
|
|
||||||
|
// Since PHP is case insensitive, we only user lowercase characters.
|
||||||
|
$chars_first = 'abcdefghijklmnopqrstuvwxyz_';
|
||||||
|
$chars = 'abcdefghijklmnopqrstuvwxyz_1234567890';
|
||||||
|
|
||||||
|
return self::randomString($length, $chars, $chars_first);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a string representation of a callback for debug purposes.
|
||||||
|
*
|
||||||
|
* @param callback $callback
|
||||||
|
* A PHP callback, which could be an array or a string.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* A string representation to be displayed to a user, e.g.
|
||||||
|
* "Foo::staticMethod()", or "Foo->bar()"
|
||||||
|
*/
|
||||||
|
static function callbackToString($callback) {
|
||||||
|
if (is_array($callback)) {
|
||||||
|
list($obj, $method) = $callback;
|
||||||
|
if (is_object($obj)) {
|
||||||
|
$str = get_class($obj) . '->' . $method . '()';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$str = $obj . '::';
|
||||||
|
$str .= $method . '()';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$str = $callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $str;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert the underscores of a prefix into directory separators.
|
||||||
|
*
|
||||||
|
* @param string $prefix
|
||||||
|
* Prefix, without trailing underscore.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* Path fragment representing the prefix, with trailing '/'.
|
||||||
|
*/
|
||||||
|
static function prefixLogicalPath($prefix) {
|
||||||
|
if (!strlen($prefix)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
$pear_logical_path = str_replace('_', '/', rtrim($prefix, '_') . '_');
|
||||||
|
// Clean up surplus '/' resulting from duplicate underscores, or an
|
||||||
|
// underscore at the beginning of the class.
|
||||||
|
while (FALSE !== $pos = strrpos('/' . $pear_logical_path, '//')) {
|
||||||
|
$pear_logical_path[$pos] = '_';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $pear_logical_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace the namespace separator with directory separator.
|
||||||
|
*
|
||||||
|
* @param string $namespace
|
||||||
|
* Namespace without trailing namespace separator.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* Path fragment representing the namespace, with trailing '/'.
|
||||||
|
*/
|
||||||
|
static function namespaceLogicalPath($namespace) {
|
||||||
|
return
|
||||||
|
strlen($namespace)
|
||||||
|
? str_replace('\\', '/', rtrim($namespace, '\\') . '\\')
|
||||||
|
: '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a file exists, considering the full include path.
|
||||||
|
* Return the resolved path to that file.
|
||||||
|
*
|
||||||
|
* @param string $file
|
||||||
|
* The filepath
|
||||||
|
* @return boolean|string
|
||||||
|
* The resolved file path, if the file exists in the include path.
|
||||||
|
* FALSE, otherwise.
|
||||||
|
*/
|
||||||
|
static function findFileInIncludePath($file) {
|
||||||
|
if (function_exists('stream_resolve_include_path')) {
|
||||||
|
// Use the PHP 5.3.1+ way of doing this.
|
||||||
|
return stream_resolve_include_path($file);
|
||||||
|
}
|
||||||
|
elseif ($file[0] === '/') {
|
||||||
|
// That's an absolute path already.
|
||||||
|
return file_exists($file)
|
||||||
|
? $file
|
||||||
|
: FALSE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Manually loop all candidate paths.
|
||||||
|
foreach (explode(PATH_SEPARATOR, get_include_path()) as $base_dir) {
|
||||||
|
if (file_exists($base_dir . '/' . $file)) {
|
||||||
|
return $base_dir . '/' . $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether an identifier is defined as either a class, interface or
|
||||||
|
* trait. Does not trigger autoloading.
|
||||||
|
*
|
||||||
|
* @param string $class
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
static function classIsDefined($class) {
|
||||||
|
return class_exists($class, FALSE)
|
||||||
|
|| interface_exists($class, FALSE)
|
||||||
|
|| (PHP_VERSION_ID >= 50400 && trait_exists($class, FALSE));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dummy method to force autoloading this class (or an ancestor).
|
||||||
|
*/
|
||||||
|
static function forceAutoload() {
|
||||||
|
// Do nothing.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
37
frontend/drupal/modules/xautoload/tests/bootstrap.php
Normal file
37
frontend/drupal/modules/xautoload/tests/bootstrap.php
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Drupal\xautoload\Discovery\ClassMapGenerator;
|
||||||
|
|
||||||
|
require_once dirname(__DIR__) . '/xautoload.early.lib.inc';
|
||||||
|
|
||||||
|
_xautoload_register();
|
||||||
|
|
||||||
|
xautoload()->finder->addPsr4('Drupal\xautoload\Tests\\', __DIR__ . '/src/');
|
||||||
|
|
||||||
|
// Use a non-cached class map generator.
|
||||||
|
xautoload()->getServiceContainer()->set('classMapGenerator', new ClassMapGenerator());
|
||||||
|
|
||||||
|
// Register a one-off class loader for the test PSR-4 classes.
|
||||||
|
/*
|
||||||
|
call_user_func(
|
||||||
|
function() {
|
||||||
|
$addPsr4 = function($namespace, $src) {
|
||||||
|
$strlen = strlen($namespace);
|
||||||
|
spl_autoload_register(
|
||||||
|
function ($class) use ($src, $namespace, $strlen) {
|
||||||
|
if (0 === strpos($class, $namespace)) {
|
||||||
|
$file = $src . '/' . str_replace('\\', '/', substr($class, $strlen)) . '.php';
|
||||||
|
if (file_exists($file)) {
|
||||||
|
require_once $file;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
$addPsr4('Drupal\xautoload\Tests\\', __DIR__ . '/src');
|
||||||
|
$addPsr4('Drupal\xautoload\\', dirname(__DIR__) . '/src');
|
||||||
|
}
|
||||||
|
);
|
||||||
|
*/
|
8
frontend/drupal/modules/xautoload/tests/fixtures/.libraries/ComposerTargetDirTestLib/Foo.php
vendored
Normal file
8
frontend/drupal/modules/xautoload/tests/fixtures/.libraries/ComposerTargetDirTestLib/Foo.php
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Acme\ComposerTargetDirTestLib;
|
||||||
|
|
||||||
|
|
||||||
|
class Foo {
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"autoload": {
|
||||||
|
"psr-0": {
|
||||||
|
"Acme\\ComposerTargetDirTestLib\\": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"target-dir": "Acme/ComposerTargetDirTestLib/"
|
||||||
|
}
|
8
frontend/drupal/modules/xautoload/tests/fixtures/.libraries/ComposerTestLib/composer.json
vendored
Normal file
8
frontend/drupal/modules/xautoload/tests/fixtures/.libraries/ComposerTestLib/composer.json
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"ComposerTestLib\\": "psr4"
|
||||||
|
},
|
||||||
|
"classmap": ["other"]
|
||||||
|
}
|
||||||
|
}
|
9
frontend/drupal/modules/xautoload/tests/fixtures/.libraries/ComposerTestLib/other/Foo.php
vendored
Normal file
9
frontend/drupal/modules/xautoload/tests/fixtures/.libraries/ComposerTestLib/other/Foo.php
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace ComposerTestLib\Other;
|
||||||
|
|
||||||
|
|
||||||
|
class Foo {
|
||||||
|
|
||||||
|
}
|
9
frontend/drupal/modules/xautoload/tests/fixtures/.libraries/ComposerTestLib/psr4/Foo.php
vendored
Normal file
9
frontend/drupal/modules/xautoload/tests/fixtures/.libraries/ComposerTestLib/psr4/Foo.php
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace ComposerTestLib;
|
||||||
|
|
||||||
|
|
||||||
|
class Foo {
|
||||||
|
|
||||||
|
}
|
5
frontend/drupal/modules/xautoload/tests/fixtures/.libraries/testlib/src/Foo.php
vendored
Normal file
5
frontend/drupal/modules/xautoload/tests/fixtures/.libraries/testlib/src/Foo.php
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Acme\TestLib;
|
||||||
|
|
||||||
|
class Foo {}
|
12
frontend/drupal/modules/xautoload/tests/fixtures/.modules/libraries/libraries.module
vendored
Normal file
12
frontend/drupal/modules/xautoload/tests/fixtures/.modules/libraries/libraries.module
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Drupal\xautoload\Tests\VirtualDrupal\DrupalEnvironment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $name
|
||||||
|
*
|
||||||
|
* @see libraries_load()
|
||||||
|
*/
|
||||||
|
function libraries_load($name) {
|
||||||
|
DrupalEnvironment::getInstance()->librariesLoad($name);
|
||||||
|
}
|
1
frontend/drupal/modules/xautoload/tests/fixtures/.modules/system/system.module
vendored
Normal file
1
frontend/drupal/modules/xautoload/tests/fixtures/.modules/system/system.module
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
<?php
|
5
frontend/drupal/modules/xautoload/tests/fixtures/.modules/testmod/psr4/Foo.php
vendored
Normal file
5
frontend/drupal/modules/xautoload/tests/fixtures/.modules/testmod/psr4/Foo.php
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\testmod;
|
||||||
|
|
||||||
|
class Foo {}
|
16
frontend/drupal/modules/xautoload/tests/fixtures/.modules/testmod/testmod.install
vendored
Normal file
16
frontend/drupal/modules/xautoload/tests/fixtures/.modules/testmod/testmod.install
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
function testmod_enable() {
|
||||||
|
\Drupal\xautoload\Tests\Util\StaticCallLog::addCall();
|
||||||
|
# new \Drupal\testmod\Foo();
|
||||||
|
}
|
||||||
|
|
||||||
|
function testmod_install() {
|
||||||
|
\Drupal\xautoload\Tests\Util\StaticCallLog::addCall();
|
||||||
|
# new \Drupal\testmod\Foo();
|
||||||
|
}
|
||||||
|
|
||||||
|
function testmod_schema() {
|
||||||
|
\Drupal\xautoload\Tests\Util\StaticCallLog::addCall();
|
||||||
|
# new \Drupal\testmod\Foo();
|
||||||
|
}
|
91
frontend/drupal/modules/xautoload/tests/fixtures/.modules/testmod/testmod.module
vendored
Normal file
91
frontend/drupal/modules/xautoload/tests/fixtures/.modules/testmod/testmod.module
vendored
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Drupal\xautoload\Adapter\LocalDirectoryAdapter;
|
||||||
|
use Drupal\xautoload\Tests\Util\StaticCallLog;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_init()
|
||||||
|
*/
|
||||||
|
function testmod_init() {
|
||||||
|
StaticCallLog::addCall();
|
||||||
|
new \Drupal\testmod\Foo();
|
||||||
|
# libraries_load('testlib');
|
||||||
|
new \Acme\TestLib\Foo();
|
||||||
|
new \ComposerTestLib\Other\Foo();
|
||||||
|
new \ComposerTestLib\Foo();
|
||||||
|
new \Acme\ComposerTargetDirTestLib\Foo();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_modules_enabled()
|
||||||
|
*/
|
||||||
|
function testmod_modules_enabled() {
|
||||||
|
StaticCallLog::addCall();
|
||||||
|
# new \Drupal\testmod\Foo();
|
||||||
|
new \Acme\TestLib\Foo();
|
||||||
|
new \ComposerTestLib\Other\Foo();
|
||||||
|
new \ComposerTestLib\Foo();
|
||||||
|
new \Acme\ComposerTargetDirTestLib\Foo();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_watchdog()
|
||||||
|
*/
|
||||||
|
function testmod_watchdog() {
|
||||||
|
StaticCallLog::addCall();
|
||||||
|
# new \Drupal\testmod\Foo();
|
||||||
|
# new \Acme\TestLib\Foo();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_xautoload()
|
||||||
|
*
|
||||||
|
* @param LocalDirectoryAdapter $adapter
|
||||||
|
*/
|
||||||
|
function testmod_xautoload($adapter) {
|
||||||
|
StaticCallLog::addCall();
|
||||||
|
$adapter->addPsr4('Drupal\testmod\\', 'psr4');
|
||||||
|
new \Drupal\testmod\Foo();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_libraries_info()
|
||||||
|
*/
|
||||||
|
function testmod_libraries_info() {
|
||||||
|
StaticCallLog::addCall();
|
||||||
|
new \Drupal\testmod\Foo();
|
||||||
|
return array(
|
||||||
|
'testlib' => array(
|
||||||
|
'name' => 'Test library',
|
||||||
|
'xautoload' => '_testmod_libraries_testlib_xautoload',
|
||||||
|
),
|
||||||
|
'ComposerTestLib' => array(
|
||||||
|
'xautoload' => '_testmod_libraries_ComposerTestLib_xautoload',
|
||||||
|
),
|
||||||
|
'ComposerTargetDirTestLib' => array(
|
||||||
|
'xautoload' => '_testmod_libraries_ComposerTargetDirTestLib_xautoload',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param LocalDirectoryAdapter $adapter
|
||||||
|
*/
|
||||||
|
function _testmod_libraries_testlib_xautoload($adapter) {
|
||||||
|
StaticCallLog::addCall();
|
||||||
|
$adapter->addPsr4('Acme\TestLib\\', 'src');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param LocalDirectoryAdapter $adapter
|
||||||
|
*/
|
||||||
|
function _testmod_libraries_ComposerTestLib_xautoload($adapter) {
|
||||||
|
$adapter->composerJson('composer.json');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param LocalDirectoryAdapter $adapter
|
||||||
|
*/
|
||||||
|
function _testmod_libraries_ComposerTargetDirTestLib_xautoload($adapter) {
|
||||||
|
$adapter->composerJson('composer.json');
|
||||||
|
}
|
3
frontend/drupal/modules/xautoload/tests/fixtures/.modules/testmod_pearflat/lib/Foo.php
vendored
Normal file
3
frontend/drupal/modules/xautoload/tests/fixtures/.modules/testmod_pearflat/lib/Foo.php
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class testmod_pearflat_Foo {}
|
@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
function testmod_pearflat_enable() {
|
||||||
|
\Drupal\xautoload\Tests\Util\StaticCallLog::addCall();
|
||||||
|
new testmod_pearflat_Foo;
|
||||||
|
}
|
||||||
|
|
||||||
|
function testmod_pearflat_install() {
|
||||||
|
\Drupal\xautoload\Tests\Util\StaticCallLog::addCall();
|
||||||
|
new testmod_pearflat_Foo;
|
||||||
|
}
|
||||||
|
|
||||||
|
function testmod_pearflat_schema() {
|
||||||
|
\Drupal\xautoload\Tests\Util\StaticCallLog::addCall();
|
||||||
|
new testmod_pearflat_Foo;
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Drupal\xautoload\Tests\Util\StaticCallLog;
|
||||||
|
|
||||||
|
function testmod_pearflat_init() {
|
||||||
|
StaticCallLog::addCall();
|
||||||
|
new testmod_pearflat_Foo;
|
||||||
|
}
|
||||||
|
|
||||||
|
function testmod_pearflat_modules_enabled() {
|
||||||
|
StaticCallLog::addCall();
|
||||||
|
new testmod_pearflat_Foo;
|
||||||
|
}
|
||||||
|
|
||||||
|
function testmod_pearflat_watchdog() {
|
||||||
|
StaticCallLog::addCall();
|
||||||
|
new testmod_pearflat_Foo;
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\testmod_psr0_lib;
|
||||||
|
|
||||||
|
class Foo {}
|
@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Drupal\testmod_psr0_lib\Foo;
|
||||||
|
use Drupal\xautoload\Tests\Util\StaticCallLog;
|
||||||
|
|
||||||
|
function testmod_psr0_lib_enable() {
|
||||||
|
StaticCallLog::addCall();
|
||||||
|
new Foo;
|
||||||
|
}
|
||||||
|
|
||||||
|
function testmod_psr0_lib_install() {
|
||||||
|
StaticCallLog::addCall();
|
||||||
|
new Foo;
|
||||||
|
}
|
||||||
|
|
||||||
|
function testmod_psr0_lib_schema() {
|
||||||
|
StaticCallLog::addCall();
|
||||||
|
new Foo;
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Drupal\testmod_psr0_lib\Foo;
|
||||||
|
use Drupal\xautoload\Tests\Util\StaticCallLog;
|
||||||
|
|
||||||
|
function testmod_psr0_lib_init() {
|
||||||
|
StaticCallLog::addCall();
|
||||||
|
new Foo;
|
||||||
|
}
|
||||||
|
|
||||||
|
function testmod_psr0_lib_modules_enabled() {
|
||||||
|
StaticCallLog::addCall();
|
||||||
|
new Foo;
|
||||||
|
}
|
||||||
|
|
||||||
|
function testmod_psr0_lib_watchdog() {
|
||||||
|
StaticCallLog::addCall();
|
||||||
|
new Foo;
|
||||||
|
}
|
5
frontend/drupal/modules/xautoload/tests/fixtures/.modules/testmod_psr4_custom/psr4/Foo.php
vendored
Normal file
5
frontend/drupal/modules/xautoload/tests/fixtures/.modules/testmod_psr4_custom/psr4/Foo.php
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\testmod_psr4_custom;
|
||||||
|
|
||||||
|
class Foo {}
|
@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Drupal\testmod_psr4_custom\Foo;
|
||||||
|
use Drupal\xautoload\Tests\Util\StaticCallLog;
|
||||||
|
|
||||||
|
function testmod_psr4_custom_enable() {
|
||||||
|
StaticCallLog::addCall();
|
||||||
|
new Foo;
|
||||||
|
}
|
||||||
|
|
||||||
|
function testmod_psr4_custom_install() {
|
||||||
|
StaticCallLog::addCall();
|
||||||
|
new Foo;
|
||||||
|
}
|
||||||
|
|
||||||
|
function testmod_psr4_custom_schema() {
|
||||||
|
StaticCallLog::addCall();
|
||||||
|
new Foo;
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Drupal\testmod_psr4_custom\Foo;
|
||||||
|
use Drupal\xautoload\Tests\Util\StaticCallLog;
|
||||||
|
|
||||||
|
xautoload()->main->registerModulePsr4(__FILE__, 'psr4');
|
||||||
|
|
||||||
|
function testmod_psr4_custom_init() {
|
||||||
|
StaticCallLog::addCall();
|
||||||
|
new Foo;
|
||||||
|
}
|
||||||
|
|
||||||
|
function testmod_psr4_custom_modules_enabled() {
|
||||||
|
StaticCallLog::addCall();
|
||||||
|
new Foo;
|
||||||
|
}
|
||||||
|
|
||||||
|
function testmod_psr4_custom_watchdog() {
|
||||||
|
StaticCallLog::addCall();
|
||||||
|
new Foo;
|
||||||
|
}
|
5
frontend/drupal/modules/xautoload/tests/fixtures/.modules/testmod_psr4_src/src/Foo.php
vendored
Normal file
5
frontend/drupal/modules/xautoload/tests/fixtures/.modules/testmod_psr4_src/src/Foo.php
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\testmod_psr4_src;
|
||||||
|
|
||||||
|
class Foo {}
|
@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Drupal\testmod_psr4_src\Foo;
|
||||||
|
use Drupal\xautoload\Tests\Util\StaticCallLog;
|
||||||
|
|
||||||
|
function testmod_psr4_src_enable() {
|
||||||
|
StaticCallLog::addCall();
|
||||||
|
new Foo;
|
||||||
|
}
|
||||||
|
|
||||||
|
function testmod_psr4_src_install() {
|
||||||
|
StaticCallLog::addCall();
|
||||||
|
new Foo;
|
||||||
|
}
|
||||||
|
|
||||||
|
function testmod_psr4_src_schema() {
|
||||||
|
StaticCallLog::addCall();
|
||||||
|
new Foo;
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user