first commit

This commit is contained in:
2025-06-17 11:53:18 +02:00
commit 9f0f7ba12b
8804 changed files with 1369176 additions and 0 deletions

View File

@ -0,0 +1,340 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Library 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Library General
Public License instead of this License.

View File

@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
use Rector\CodeQuality\Rector\Class_\InlineConstructorDefaultToPropertyRector;
use Rector\Config\RectorConfig;
use Rector\Php71\Rector\ClassConst\PublicConstantVisibilityRector;
use Rector\Set\ValueObject\LevelSetList;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->paths([
__DIR__ . '/src',
__DIR__ . '/Tests',
]);
$rectorConfig->importNames(true);
$rectorConfig->importShortClasses(false);
$rectorConfig->rule(PublicConstantVisibilityRector::class);
};

View File

@ -0,0 +1,214 @@
<?php
/**
* Part of the Joomla Framework Application Package
*
* @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Application;
use Joomla\Application\Event\ApplicationErrorEvent;
use Joomla\Application\Event\ApplicationEvent;
use Joomla\Event\DispatcherAwareInterface;
use Joomla\Event\DispatcherAwareTrait;
use Joomla\Event\EventInterface;
use Joomla\Registry\Registry;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
/**
* Joomla Framework Base Application Class
*
* @since 1.0.0
*/
abstract class AbstractApplication implements
ConfigurationAwareApplicationInterface,
LoggerAwareInterface,
DispatcherAwareInterface
{
use LoggerAwareTrait;
use DispatcherAwareTrait;
/**
* The application configuration object.
*
* @var Registry
* @since 1.0.0
*/
protected $config;
/**
* Class constructor.
*
* @param Registry|null $config An optional argument to provide dependency injection for the
* application's config object. If the argument is a Registry
* object that object will become the application's config object,
* otherwise a default config object is created.
*
* @since 1.0.0
*/
public function __construct(Registry $config = null)
{
$this->config = $config ?: new Registry();
// Set the execution datetime and timestamp;
$this->set('execution.datetime', \gmdate('Y-m-d H:i:s'));
$this->set('execution.timestamp', \time());
$this->set('execution.microtimestamp', \microtime(true));
$this->initialise();
}
/**
* Method to close the application.
*
* @param integer $code The exit code (optional; default is 0).
*
* @return void
*
* @codeCoverageIgnore
* @since 1.0.0
*/
public function close($code = 0)
{
exit($code);
}
/**
* Dispatches an application event if the dispatcher has been set.
*
* @param string $eventName The event to dispatch.
* @param EventInterface|null $event The event object.
*
* @return EventInterface|null The dispatched event or null if no dispatcher is set
*
* @since 2.0.0
*/
protected function dispatchEvent(string $eventName, ?EventInterface $event = null): ?EventInterface
{
try {
$dispatcher = $this->getDispatcher();
} catch (\UnexpectedValueException $exception) {
return null;
}
return $dispatcher->dispatch($eventName, $event ?: new ApplicationEvent($eventName, $this));
}
/**
* Method to run the application routines.
*
* Most likely you will want to instantiate a controller and execute it, or perform some sort of task directly.
*
* @return mixed
*
* @since 1.0.0
*/
abstract protected function doExecute();
/**
* Execute the application.
*
* @return void
*
* @since 1.0.0
*/
public function execute()
{
try {
$this->dispatchEvent(ApplicationEvents::BEFORE_EXECUTE);
// Perform application routines.
$this->doExecute();
$this->dispatchEvent(ApplicationEvents::AFTER_EXECUTE);
} catch (\Throwable $throwable) {
$this->dispatchEvent(ApplicationEvents::ERROR, new ApplicationErrorEvent($throwable, $this));
}
}
/**
* Returns a property of the object or the default value if the property is not set.
*
* @param string $key The name of the property.
* @param mixed $default The default value (optional) if none is set.
*
* @return mixed The value of the configuration.
*
* @since 1.0.0
*/
public function get($key, $default = null)
{
return $this->config->get($key, $default);
}
/**
* Get the logger.
*
* @return LoggerInterface
*
* @since 1.0.0
*/
public function getLogger()
{
// If a logger hasn't been set, use NullLogger
if (!($this->logger instanceof LoggerInterface)) {
$this->setLogger(new NullLogger());
}
return $this->logger;
}
/**
* Custom initialisation method.
*
* Called at the end of the AbstractApplication::__construct method.
* This is for developers to inject initialisation code for their application classes.
*
* @return void
*
* @codeCoverageIgnore
* @since 1.0.0
*/
protected function initialise()
{
}
/**
* Modifies a property of the object, creating it if it does not already exist.
*
* @param string $key The name of the property.
* @param mixed $value The value of the property to set (optional).
*
* @return mixed Previous value of the property
*
* @since 1.0.0
*/
public function set($key, $value = null)
{
$previous = $this->config->get($key);
$this->config->set($key, $value);
return $previous;
}
/**
* Sets the configuration for the application.
*
* @param Registry $config A registry object holding the configuration.
*
* @return $this
*
* @since 1.0.0
*/
public function setConfiguration(Registry $config)
{
$this->config = $config;
return $this;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,60 @@
<?php
/**
* Part of the Joomla Framework Application Package
*
* @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Application;
/**
* Class defining the events available in the application.
*
* @since 2.0.0
*/
final class ApplicationEvents
{
/**
* The ERROR event is an event triggered when a Throwable is uncaught.
*
* This event allows you to inspect the Throwable and implement additional error handling/reporting mechanisms.
*
* @var string
* @since 2.0.0
*/
public const ERROR = 'application.error';
/**
* The BEFORE_EXECUTE event is an event triggered before the application is executed.
*
* @var string
* @since 2.0.0
*/
public const BEFORE_EXECUTE = 'application.before_execute';
/**
* The AFTER_EXECUTE event is an event triggered after the application is executed.
*
* @var string
* @since 2.0.0
*/
public const AFTER_EXECUTE = 'application.after_execute';
/**
* The BEFORE_RESPOND event is an event triggered before the application response is sent.
*
* @var string
* @since 2.0.0
*/
public const BEFORE_RESPOND = 'application.before_respond';
/**
* The AFTER_RESPOND event is an event triggered after the application response is sent.
*
* @var string
* @since 2.0.0
*/
public const AFTER_RESPOND = 'application.after_respond';
}

View File

@ -0,0 +1,38 @@
<?php
/**
* Part of the Joomla Framework Application Package
*
* @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Application;
/**
* Joomla Framework Application Interface
*
* @since 2.0.0
*/
interface ApplicationInterface
{
/**
* Method to close the application.
*
* @param integer $code The exit code (optional; default is 0).
*
* @return void
*
* @since 2.0.0
*/
public function close($code = 0);
/**
* Execute the application.
*
* @return void
*
* @since 2.0.0
*/
public function execute();
}

View File

@ -0,0 +1,55 @@
<?php
/**
* Part of the Joomla Framework Application Package
*
* @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Application;
use Joomla\Registry\Registry;
/**
* Application sub-interface defining an application class which is aware of its configuration
*
* @since 2.0.0
*/
interface ConfigurationAwareApplicationInterface extends ApplicationInterface
{
/**
* Returns a property of the object or the default value if the property is not set.
*
* @param string $key The name of the property.
* @param mixed $default The default value (optional) if none is set.
*
* @return mixed The value of the configuration.
*
* @since 2.0.0
*/
public function get($key, $default = null);
/**
* Modifies a property of the object, creating it if it does not already exist.
*
* @param string $key The name of the property.
* @param mixed $value The value of the property to set (optional).
*
* @return mixed Previous value of the property
*
* @since 2.0.0
*/
public function set($key, $value = null);
/**
* Sets the configuration for the application.
*
* @param Registry $config A registry object holding the configuration.
*
* @return $this
*
* @since 2.0.0
*/
public function setConfiguration(Registry $config);
}

View File

@ -0,0 +1,60 @@
<?php
/**
* Part of the Joomla Framework Application Package
*
* @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Application\Controller;
use Psr\Container\ContainerInterface;
/**
* Controller resolver which supports creating controllers from a PSR-11 compatible container
*
* Controllers must be registered in the container using their FQCN as a service key
*
* @since 2.0.0
*/
class ContainerControllerResolver extends ControllerResolver
{
/**
* The container to search for controllers in
*
* @var ContainerInterface
* @since 2.0.0
*/
private $container;
/**
* Constructor
*
* @param ContainerInterface $container The container to search for controllers in
*
* @since 2.0.0
*/
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
/**
* Instantiate a controller class
*
* @param string $class The class to instantiate
*
* @return object Controller class instance
*
* @since 2.0.0
*/
protected function instantiateController(string $class): object
{
if ($this->container->has($class)) {
return $this->container->get($class);
}
return parent::instantiateController($class);
}
}

View File

@ -0,0 +1,123 @@
<?php
/**
* Part of the Joomla Framework Application Package
*
* @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Application\Controller;
use Joomla\Controller\ControllerInterface;
use Joomla\Router\ResolvedRoute;
/**
* Resolves a controller for the given route.
*
* @since 2.0.0
*/
class ControllerResolver implements ControllerResolverInterface
{
/**
* Resolve the controller for a route
*
* @param ResolvedRoute $route The route to resolve the controller for
*
* @return callable
*
* @throws \InvalidArgumentException
* @since 2.0.0
*/
public function resolve(ResolvedRoute $route): callable
{
$controller = $route->getController();
// Try to resolve a callable defined as an array
if (\is_array($controller)) {
if (isset($controller[0]) && \is_string($controller[0]) && isset($controller[1])) {
if (!\class_exists($controller[0])) {
throw new \InvalidArgumentException(
\sprintf('Cannot resolve controller for URI `%s`', $route->getUri())
);
}
try {
$controller[0] = $this->instantiateController($controller[0]);
} catch (\ArgumentCountError $error) {
throw new \InvalidArgumentException(
\sprintf(
'Controller `%s` has required constructor arguments, cannot instantiate the class',
$controller[0]
),
0,
$error
);
}
}
if (!\is_callable($controller)) {
throw new \InvalidArgumentException(
\sprintf('Cannot resolve controller for URI `%s`', $route->getUri())
);
}
return $controller;
}
// Try to resolve an invocable object
if (\is_object($controller)) {
if (!\is_callable($controller)) {
throw new \InvalidArgumentException(
\sprintf('Cannot resolve controller for URI `%s`', $route->getUri())
);
}
return $controller;
}
// Try to resolve a known function
if (\function_exists($controller)) {
return $controller;
}
// Try to resolve a class name if it implements our ControllerInterface
if (\is_string($controller) && \interface_exists(ControllerInterface::class)) {
if (!\class_exists($controller)) {
throw new \InvalidArgumentException(
\sprintf('Cannot resolve controller for URI `%s`', $route->getUri())
);
}
try {
return [$this->instantiateController($controller), 'execute'];
} catch (\ArgumentCountError $error) {
throw new \InvalidArgumentException(
\sprintf(
'Controller `%s` has required constructor arguments, cannot instantiate the class',
$controller
),
0,
$error
);
}
}
// Unsupported resolution
throw new \InvalidArgumentException(\sprintf('Cannot resolve controller for URI `%s`', $route->getUri()));
}
/**
* Instantiate a controller class
*
* @param string $class The class to instantiate
*
* @return object Controller class instance
*
* @since 2.0.0
*/
protected function instantiateController(string $class): object
{
return new $class();
}
}

View File

@ -0,0 +1,32 @@
<?php
/**
* Part of the Joomla Framework Application Package
*
* @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Application\Controller;
use Joomla\Router\ResolvedRoute;
/**
* Interface defining a controller resolver.
*
* @since 2.0.0
*/
interface ControllerResolverInterface
{
/**
* Resolve the controller for a route
*
* @param ResolvedRoute $route The route to resolve the controller for
*
* @return callable
*
* @since 2.0.0
* @throws \InvalidArgumentException
*/
public function resolve(ResolvedRoute $route): callable;
}

View File

@ -0,0 +1,70 @@
<?php
/**
* Part of the Joomla Framework Application Package
*
* @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Application\Event;
use Joomla\Application\AbstractApplication;
use Joomla\Application\ApplicationEvents;
/**
* Event class thrown when an application error occurs.
*
* @since 2.0.0
*/
class ApplicationErrorEvent extends ApplicationEvent
{
/**
* The Throwable object with the error data.
*
* @var \Throwable
* @since 2.0.0
*/
private $error;
/**
* Event constructor.
*
* @param \Throwable $error The Throwable object with the error data.
* @param AbstractApplication $application The active application.
*
* @since 2.0.0
*/
public function __construct(\Throwable $error, AbstractApplication $application)
{
parent::__construct(ApplicationEvents::ERROR, $application);
$this->error = $error;
}
/**
* Get the error object.
*
* @return \Throwable
*
* @since 2.0.0
*/
public function getError(): \Throwable
{
return $this->error;
}
/**
* Set the error object.
*
* @param \Throwable $error The error object to set to the event.
*
* @return void
*
* @since 2.0.0
*/
public function setError(\Throwable $error): void
{
$this->error = $error;
}
}

View File

@ -0,0 +1,56 @@
<?php
/**
* Part of the Joomla Framework Application Package
*
* @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Application\Event;
use Joomla\Application\AbstractApplication;
use Joomla\Event\Event;
/**
* Base event class for application events.
*
* @since 2.0.0
*/
class ApplicationEvent extends Event
{
/**
* The active application.
*
* @var AbstractApplication
* @since 2.0.0
*/
private $application;
/**
* Event constructor.
*
* @param string $name The event name.
* @param AbstractApplication $application The active application.
*
* @since 2.0.0
*/
public function __construct(string $name, AbstractApplication $application)
{
parent::__construct($name);
$this->application = $application;
}
/**
* Get the active application.
*
* @return AbstractApplication
*
* @since 2.0.0
*/
public function getApplication(): AbstractApplication
{
return $this->application;
}
}

View File

@ -0,0 +1,19 @@
<?php
/**
* Part of the Joomla Framework Application Package
*
* @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Application\Exception;
/**
* Exception thrown when the application can't write to the response body
*
* @since 2.0.0
*/
class UnableToWriteBody extends \DomainException
{
}

View File

@ -0,0 +1,62 @@
<?php
/**
* Part of the Joomla Framework Application Package
*
* @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Application;
use Joomla\Session\SessionInterface;
/**
* Application sub-interface defining a web application class which supports sessions
*
* @since 2.0.0
*/
interface SessionAwareWebApplicationInterface extends WebApplicationInterface
{
/**
* Method to get the application session object.
*
* @return SessionInterface The session object
*
* @since 2.0.0
*/
public function getSession();
/**
* Sets the session for the application to use, if required.
*
* @param SessionInterface $session A session object.
*
* @return $this
*
* @since 2.0.0
*/
public function setSession(SessionInterface $session);
/**
* Checks for a form token in the request.
*
* @param string $method The request method in which to look for the token key.
*
* @return boolean
*
* @since 2.0.0
*/
public function checkToken($method = 'post');
/**
* Method to determine a hash for anti-spoofing variable names
*
* @param boolean $forceNew If true, force a new token to be created
*
* @return string Hashed var name
*
* @since 2.0.0
*/
public function getFormToken($forceNew = false);
}

View File

@ -0,0 +1,111 @@
<?php
/**
* Part of the Joomla Framework Application Package
*
* @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Application;
use Joomla\Input\Input;
use Joomla\Session\SessionInterface;
/**
* Trait which helps implementing `Joomla\Application\SessionAwareWebApplicationInterface` in a web application class.
*
* @since 2.0.0
*/
trait SessionAwareWebApplicationTrait
{
/**
* The application session object.
*
* @var SessionInterface
* @since 2.0.0
*/
protected $session;
/**
* Method to get the application input object.
*
* @return Input
*
* @since 2.0.0
*/
abstract public function getInput(): Input;
/**
* Method to get the application session object.
*
* @return SessionInterface The session object
*
* @since 2.0.0
*/
public function getSession()
{
if ($this->session === null) {
throw new \RuntimeException(\sprintf('A %s object has not been set.', SessionInterface::class));
}
return $this->session;
}
/**
* Sets the session for the application to use, if required.
*
* @param SessionInterface $session A session object.
*
* @return $this
*
* @since 2.0.0
*/
public function setSession(SessionInterface $session)
{
$this->session = $session;
return $this;
}
/**
* Checks for a form token in the request.
*
* @param string $method The request method in which to look for the token key.
*
* @return boolean
*
* @since 2.0.0
*/
public function checkToken($method = 'post')
{
$token = $this->getFormToken();
// Support a token sent via the X-CSRF-Token header, then fall back to a token in the request
$requestToken = $this->getInput()->server->get(
'HTTP_X_CSRF_TOKEN',
$this->getInput()->$method->get($token, '', 'alnum'),
'alnum'
);
if (!$requestToken) {
return false;
}
return $this->getSession()->hasToken($token);
}
/**
* Method to determine a hash for anti-spoofing variable names
*
* @param boolean $forceNew If true, force a new token to be created
*
* @return string Hashed var name
*
* @since 2.0.0
*/
public function getFormToken($forceNew = false)
{
return $this->getSession()->getToken($forceNew);
}
}

View File

@ -0,0 +1,574 @@
<?php
/**
* Part of the Joomla Framework Application Package
*
* @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Application\Web;
/**
* Class to model a Web Client.
*
* @property-read integer $platform The detected platform on which the web client runs.
* @property-read boolean $mobile True if the web client is a mobile device.
* @property-read integer $engine The detected rendering engine used by the web client.
* @property-read integer $browser The detected browser used by the web client.
* @property-read string $browserVersion The detected browser version used by the web client.
* @property-read array $languages The priority order detected accepted languages for the client.
* @property-read array $encodings The priority order detected accepted encodings for the client.
* @property-read string $userAgent The web client's user agent string.
* @property-read string $acceptEncoding The web client's accepted encoding string.
* @property-read string $acceptLanguage The web client's accepted languages string.
* @property-read array $detection An array of flags determining whether a detection routine has been run.
* @property-read boolean $robot True if the web client is a robot
* @property-read array $headers An array of all headers sent by client
*
* @since 1.0.0
*/
class WebClient
{
public const WINDOWS = 1;
public const WINDOWS_PHONE = 2;
public const WINDOWS_CE = 3;
public const IPHONE = 4;
public const IPAD = 5;
public const IPOD = 6;
public const MAC = 7;
public const BLACKBERRY = 8;
public const ANDROID = 9;
public const LINUX = 10;
public const TRIDENT = 11;
public const WEBKIT = 12;
public const GECKO = 13;
public const PRESTO = 14;
public const KHTML = 15;
public const AMAYA = 16;
public const IE = 17;
public const FIREFOX = 18;
public const CHROME = 19;
public const SAFARI = 20;
public const OPERA = 21;
public const ANDROIDTABLET = 22;
public const EDGE = 23;
public const BLINK = 24;
public const EDG = 25;
/**
* The detected platform on which the web client runs.
*
* @var integer
* @since 1.0.0
*/
protected $platform;
/**
* True if the web client is a mobile device.
*
* @var boolean
* @since 1.0.0
*/
protected $mobile = false;
/**
* The detected rendering engine used by the web client.
*
* @var integer
* @since 1.0.0
*/
protected $engine;
/**
* The detected browser used by the web client.
*
* @var integer
* @since 1.0.0
*/
protected $browser;
/**
* The detected browser version used by the web client.
*
* @var string
* @since 1.0.0
*/
protected $browserVersion;
/**
* The priority order detected accepted languages for the client.
*
* @var array
* @since 1.0.0
*/
protected $languages = [];
/**
* The priority order detected accepted encodings for the client.
*
* @var array
* @since 1.0.0
*/
protected $encodings = [];
/**
* The web client's user agent string.
*
* @var string
* @since 1.0.0
*/
protected $userAgent;
/**
* The web client's accepted encoding string.
*
* @var string
* @since 1.0.0
*/
protected $acceptEncoding;
/**
* The web client's accepted languages string.
*
* @var string
* @since 1.0.0
*/
protected $acceptLanguage;
/**
* True if the web client is a robot.
*
* @var boolean
* @since 1.0.0
*/
protected $robot = false;
/**
* An array of flags determining whether or not a detection routine has been run.
*
* @var array
* @since 1.0.0
*/
protected $detection = [];
/**
* An array of headers sent by client.
*
* @var array
* @since 1.3.0
*/
protected $headers;
/**
* Class constructor.
*
* @param string $userAgent The optional user-agent string to parse.
* @param string $acceptEncoding The optional client accept encoding string to parse.
* @param string $acceptLanguage The optional client accept language string to parse.
*
* @since 1.0.0
*/
public function __construct($userAgent = null, $acceptEncoding = null, $acceptLanguage = null)
{
// If no explicit user agent string was given attempt to use the implicit one from server environment.
if (empty($userAgent) && isset($_SERVER['HTTP_USER_AGENT'])) {
$this->userAgent = $_SERVER['HTTP_USER_AGENT'];
} else {
$this->userAgent = $userAgent;
}
// If no explicit acceptable encoding string was given attempt to use the implicit one from server environment.
if (empty($acceptEncoding) && isset($_SERVER['HTTP_ACCEPT_ENCODING'])) {
$this->acceptEncoding = $_SERVER['HTTP_ACCEPT_ENCODING'];
} else {
$this->acceptEncoding = $acceptEncoding;
}
// If no explicit acceptable languages string was given attempt to use the implicit one from server environment.
if (empty($acceptLanguage) && isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
$this->acceptLanguage = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
} else {
$this->acceptLanguage = $acceptLanguage;
}
}
/**
* Magic method to get an object property's value by name.
*
* @param string $name Name of the property for which to return a value.
*
* @return mixed The requested value if it exists.
*
* @since 1.0.0
*/
public function __get($name)
{
switch ($name) {
case 'mobile':
case 'platform':
if (empty($this->detection['platform'])) {
$this->detectPlatform($this->userAgent);
}
break;
case 'engine':
if (empty($this->detection['engine'])) {
$this->detectEngine($this->userAgent);
}
break;
case 'browser':
case 'browserVersion':
if (empty($this->detection['browser'])) {
$this->detectBrowser($this->userAgent);
}
break;
case 'languages':
if (empty($this->detection['acceptLanguage'])) {
$this->detectLanguage($this->acceptLanguage);
}
break;
case 'encodings':
if (empty($this->detection['acceptEncoding'])) {
$this->detectEncoding($this->acceptEncoding);
}
break;
case 'robot':
if (empty($this->detection['robot'])) {
$this->detectRobot($this->userAgent);
}
break;
case 'headers':
if (empty($this->detection['headers'])) {
$this->detectHeaders();
}
break;
}
// Return the property if it exists.
if (\property_exists($this, $name)) {
return $this->$name;
}
$trace = \debug_backtrace();
\trigger_error(
'Undefined property via \__get(): ' . $name . ' in ' . $trace[0]['file'] . ' on line ' . $trace[0]['line'],
E_USER_NOTICE
);
}
/**
* Detects the client browser and version in a user agent string.
*
* @param string $userAgent The user-agent string to parse.
*
* @return void
*
* @since 1.0.0
*/
protected function detectBrowser($userAgent)
{
$patternBrowser = '';
// Attempt to detect the browser type. Obviously we are only worried about major browsers.
if ((\stripos($userAgent, 'MSIE') !== false) && (\stripos($userAgent, 'Opera') === false)) {
$this->browser = self::IE;
$patternBrowser = 'MSIE';
} elseif (\stripos($userAgent, 'Trident') !== false) {
$this->browser = self::IE;
$patternBrowser = ' rv';
} elseif (\stripos($userAgent, 'Edge') !== false) {
$this->browser = self::EDGE;
$patternBrowser = 'Edge';
} elseif (\stripos($userAgent, 'Edg') !== false) {
$this->browser = self::EDG;
$patternBrowser = 'Edg';
} elseif ((\stripos($userAgent, 'Firefox') !== false) && (\stripos($userAgent, 'like Firefox') === false)) {
$this->browser = self::FIREFOX;
$patternBrowser = 'Firefox';
} elseif (\stripos($userAgent, 'OPR') !== false) {
$this->browser = self::OPERA;
$patternBrowser = 'OPR';
} elseif (\stripos($userAgent, 'Chrome') !== false) {
$this->browser = self::CHROME;
$patternBrowser = 'Chrome';
} elseif (\stripos($userAgent, 'Safari') !== false) {
$this->browser = self::SAFARI;
$patternBrowser = 'Safari';
} elseif (\stripos($userAgent, 'Opera') !== false) {
$this->browser = self::OPERA;
$patternBrowser = 'Opera';
}
// If we detected a known browser let's attempt to determine the version.
if ($this->browser) {
// Build the REGEX pattern to match the browser version string within the user agent string.
$pattern = '#(?<browser>Version|' . $patternBrowser . ')[/ :]+(?<version>[0-9.|a-zA-Z.]*)#';
// Attempt to find version strings in the user agent string.
$matches = [];
if (\preg_match_all($pattern, $userAgent, $matches)) {
// Do we have both a Version and browser match?
if (\count($matches['browser']) == 2) {
// See whether Version or browser came first, and use the number accordingly.
if (\strripos($userAgent, 'Version') < \strripos($userAgent, $patternBrowser)) {
$this->browserVersion = $matches['version'][0];
} else {
$this->browserVersion = $matches['version'][1];
}
} elseif (\count($matches['browser']) > 2) {
$key = \array_search('Version', $matches['browser']);
if ($key) {
$this->browserVersion = $matches['version'][$key];
}
} else {
// We only have a Version or a browser so use what we have.
$this->browserVersion = $matches['version'][0];
}
}
}
// Mark this detection routine as run.
$this->detection['browser'] = true;
}
/**
* Method to detect the accepted response encoding by the client.
*
* @param string $acceptEncoding The client accept encoding string to parse.
*
* @return void
*
* @since 1.0.0
*/
protected function detectEncoding($acceptEncoding)
{
// Parse the accepted encodings.
$this->encodings = \array_map('trim', (array) \explode(',', (string) $acceptEncoding));
// Mark this detection routine as run.
$this->detection['acceptEncoding'] = true;
}
/**
* Detects the client rendering engine in a user agent string.
*
* @param string $userAgent The user-agent string to parse.
*
* @return void
*
* @since 1.0.0
*/
protected function detectEngine($userAgent)
{
if (\stripos($userAgent, 'MSIE') !== false || \stripos($userAgent, 'Trident') !== false) {
// Attempt to detect the client engine -- starting with the most popular ... for now.
$this->engine = self::TRIDENT;
} elseif (\stripos($userAgent, 'Edge') !== false || \stripos($userAgent, 'EdgeHTML') !== false) {
$this->engine = self::EDGE;
} elseif (\stripos($userAgent, 'Edg') !== false) {
$this->engine = self::BLINK;
} elseif (\stripos($userAgent, 'Chrome') !== false) {
$result = \explode('/', \stristr($userAgent, 'Chrome'));
$version = \explode(' ', $result[1]);
if ($version[0] >= 28) {
$this->engine = self::BLINK;
} else {
$this->engine = self::WEBKIT;
}
} elseif (\stripos($userAgent, 'AppleWebKit') !== false || \stripos($userAgent, 'blackberry') !== false) {
if (\stripos($userAgent, 'AppleWebKit') !== false) {
$result = \explode('/', \stristr($userAgent, 'AppleWebKit'));
$version = \explode(' ', $result[1]);
if ($version[0] === 537.36) {
// AppleWebKit/537.36 is Blink engine specific, exception is Blink emulated IEMobile, Trident or Edge
$this->engine = self::BLINK;
}
}
// Evidently blackberry uses WebKit and doesn't necessarily report it. Bad RIM.
$this->engine = self::WEBKIT;
} elseif (\stripos($userAgent, 'Gecko') !== false && \stripos($userAgent, 'like Gecko') === false) {
// We have to check for like Gecko because some other browsers spoof Gecko.
$this->engine = self::GECKO;
} elseif (\stripos($userAgent, 'Opera') !== false || \stripos($userAgent, 'Presto') !== false) {
$version = false;
if (\preg_match('/Opera[\/| ]?([0-9.]+)/u', $userAgent, $match)) {
$version = (float) ($match[1]);
}
if (\preg_match('/Version\/([0-9.]+)/u', $userAgent, $match)) {
if ((float) ($match[1]) >= 10) {
$version = (float) ($match[1]);
}
}
if ($version !== false && $version >= 15) {
$this->engine = self::BLINK;
} else {
$this->engine = self::PRESTO;
}
} elseif (\stripos($userAgent, 'KHTML') !== false) {
// *sigh*
$this->engine = self::KHTML;
} elseif (\stripos($userAgent, 'Amaya') !== false) {
// Lesser known engine but it finishes off the major list from Wikipedia :-)
$this->engine = self::AMAYA;
}
// Mark this detection routine as run.
$this->detection['engine'] = true;
}
/**
* Method to detect the accepted languages by the client.
*
* @param mixed $acceptLanguage The client accept language string to parse.
*
* @return void
*
* @since 1.0.0
*/
protected function detectLanguage($acceptLanguage)
{
// Parse the accepted encodings.
$this->languages = \array_map('trim', (array) \explode(',', $acceptLanguage));
// Mark this detection routine as run.
$this->detection['acceptLanguage'] = true;
}
/**
* Detects the client platform in a user agent string.
*
* @param string $userAgent The user-agent string to parse.
*
* @return void
*
* @since 1.0.0
*/
protected function detectPlatform($userAgent)
{
// Attempt to detect the client platform.
if (\stripos($userAgent, 'Windows') !== false) {
$this->platform = self::WINDOWS;
// Let's look at the specific mobile options in the Windows space.
if (\stripos($userAgent, 'Windows Phone') !== false) {
$this->mobile = true;
$this->platform = self::WINDOWS_PHONE;
} elseif (\stripos($userAgent, 'Windows CE') !== false) {
$this->mobile = true;
$this->platform = self::WINDOWS_CE;
}
} elseif (\stripos($userAgent, 'iPhone') !== false) {
// Interestingly 'iPhone' is present in all iOS devices so far including iPad and iPods.
$this->mobile = true;
$this->platform = self::IPHONE;
// Let's look at the specific mobile options in the iOS space.
if (\stripos($userAgent, 'iPad') !== false) {
$this->platform = self::IPAD;
} elseif (\stripos($userAgent, 'iPod') !== false) {
$this->platform = self::IPOD;
}
} elseif (\stripos($userAgent, 'iPad') !== false) {
// In case where iPhone is not mentioed in iPad user agent string
$this->mobile = true;
$this->platform = self::IPAD;
} elseif (\stripos($userAgent, 'iPod') !== false) {
// In case where iPhone is not mentioed in iPod user agent string
$this->mobile = true;
$this->platform = self::IPOD;
} elseif (\preg_match('/macintosh|mac os x/i', $userAgent)) {
// This has to come after the iPhone check because mac strings are also present in iOS devices.
$this->platform = self::MAC;
} elseif (\stripos($userAgent, 'Blackberry') !== false) {
$this->mobile = true;
$this->platform = self::BLACKBERRY;
} elseif (\stripos($userAgent, 'Android') !== false) {
$this->mobile = true;
$this->platform = self::ANDROID;
/*
* Attempt to distinguish between Android phones and tablets
* There is no totally foolproof method but certain rules almost always hold
* Android 3.x is only used for tablets
* Some devices and browsers encourage users to change their UA string to include Tablet.
* Google encourages manufacturers to exclude the string Mobile from tablet device UA strings.
* In some modes Kindle Android devices include the string Mobile but they include the string Silk.
*/
if (
\stripos($userAgent, 'Android 3') !== false || \stripos($userAgent, 'Tablet') !== false
|| \stripos($userAgent, 'Mobile') === false || \stripos($userAgent, 'Silk') !== false
) {
$this->platform = self::ANDROIDTABLET;
}
} elseif (\stripos($userAgent, 'Linux') !== false) {
$this->platform = self::LINUX;
}
// Mark this detection routine as run.
$this->detection['platform'] = true;
}
/**
* Determines if the browser is a robot or not.
*
* @param string $userAgent The user-agent string to parse.
*
* @return void
*
* @since 1.0.0
*/
protected function detectRobot($userAgent)
{
$this->robot = (bool) \preg_match('/http|bot|robot|spider|crawler|curl|^$/i', $userAgent);
$this->detection['robot'] = true;
}
/**
* Fills internal array of headers
*
* @return void
*
* @since 1.3.0
*/
protected function detectHeaders()
{
if (\function_exists('getallheaders')) {
// If php is working under Apache, there is a special function
$this->headers = \getallheaders();
} else {
// Else we fill headers from $_SERVER variable
$this->headers = [];
foreach ($_SERVER as $name => $value) {
if (\substr($name, 0, 5) == 'HTTP_') {
$this->headers[\str_replace(' ', '-', \ucwords(\strtolower(\str_replace('_', ' ', \substr($name, 5)))))] = $value;
}
}
}
// Mark this detection routine as run.
$this->detection['headers'] = true;
}
}

View File

@ -0,0 +1,105 @@
<?php
/**
* Part of the Joomla Framework Application Package
*
* @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Application;
use Joomla\Application\Controller\ControllerResolverInterface;
use Joomla\Application\Web\WebClient;
use Joomla\Input\Input;
use Joomla\Registry\Registry;
use Joomla\Router\RouterInterface;
use Psr\Http\Message\ResponseInterface;
/**
* A basic web application class for handing HTTP requests.
*
* @since 2.0.0
*/
class WebApplication extends AbstractWebApplication implements SessionAwareWebApplicationInterface
{
use SessionAwareWebApplicationTrait;
/**
* The application's controller resolver.
*
* @var ControllerResolverInterface
* @since 2.0.0
*/
protected $controllerResolver;
/**
* The application's router.
*
* @var RouterInterface
* @since 2.0.0
*/
protected $router;
/**
* Class constructor.
*
* @param ControllerResolverInterface $controllerResolver The application's controller resolver
* @param RouterInterface $router The application's router
* @param Input ?$input An optional argument to provide dependency injection
* for the application's input object. If the argument
* is an Input object that object will become the
* application's input object, otherwise a default input
* object is created.
* @param Registry ?$config An optional argument to provide dependency injection
* for the application's config object. If the argument
* is a Registry object that object will become the
* application's config object, otherwise a default
* config object is created.
* @param Web\WebClient ?$client An optional argument to provide dependency injection
* for the application's client object. If the argument
* is a Web\WebClient object that object will become the
* application's client object, otherwise a default
* client object is created.
* @param ResponseInterface ?$response An optional argument to provide dependency injection
* for the application's response object. If the
* argument is a ResponseInterface object that object
* will become the application's response object,
* otherwise a default response object is created.
*
* @since 2.0.0
*/
public function __construct(
ControllerResolverInterface $controllerResolver,
RouterInterface $router,
Input $input = null,
Registry $config = null,
WebClient $client = null,
ResponseInterface $response = null
) {
$this->controllerResolver = $controllerResolver;
$this->router = $router;
// Call the constructor as late as possible (it runs `initialise`).
parent::__construct($input, $config, $client, $response);
}
/**
* Method to run the application routines.
*
* @return void
*
* @since 2.0.0
*/
protected function doExecute(): void
{
$route = $this->router->parseRoute($this->get('uri.route'), $this->input->getMethod());
// Add variables to the input if not already set
foreach ($route->getRouteVariables() as $key => $value) {
$this->input->def($key, $value);
}
\call_user_func($this->controllerResolver->resolve($route));
}
}

View File

@ -0,0 +1,184 @@
<?php
/**
* Part of the Joomla Framework Application Package
*
* @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Application;
use Joomla\Input\Input;
use Psr\Http\Message\ResponseInterface;
/**
* Application sub-interface defining a web application class
*
* @since 2.0.0
*/
interface WebApplicationInterface extends ApplicationInterface
{
/**
* Method to get the application input object.
*
* @return Input
*
* @since 2.0.0
*/
public function getInput(): Input;
/**
* Redirect to another URL.
*
* If the headers have not been sent the redirect will be accomplished using a "301 Moved Permanently" or "303 See Other" code in the header
* pointing to the new location. If the headers have already been sent this will be accomplished using a JavaScript statement.
*
* @param string $url The URL to redirect to. Can only be http/https URL
* @param integer|boolean $status The HTTP status code to be provided. 303 is assumed by default.
*
* @return void
*
* @since 2.0.0
* @throws \InvalidArgumentException
*/
public function redirect($url, $status = 303);
/**
* Set/get cachable state for the response.
*
* If $allow is set, sets the cachable state of the response. Always returns the current state.
*
* @param boolean $allow True to allow browser caching.
*
* @return boolean
*
* @since 2.0.0
*/
public function allowCache($allow = null);
/**
* Method to set a response header.
*
* If the replace flag is set then all headers with the given name will be replaced by the new one.
* The headers are stored in an internal array to be sent when the site is sent to the browser.
*
* @param string $name The name of the header to set.
* @param string $value The value of the header to set.
* @param boolean $replace True to replace any headers with the same name.
*
* @return $this
*
* @since 2.0.0
*/
public function setHeader($name, $value, $replace = false);
/**
* Method to get the array of response headers to be sent when the response is sent to the client.
*
* @return array
*
* @since 2.0.0
*/
public function getHeaders();
/**
* Method to clear any set response headers.
*
* @return $this
*
* @since 2.0.0
*/
public function clearHeaders();
/**
* Send the response headers.
*
* @return $this
*
* @since 2.0.0
*/
public function sendHeaders();
/**
* Set body content. If body content already defined, this will replace it.
*
* @param string $content The content to set as the response body.
*
* @return $this
*
* @since 2.0.0
*/
public function setBody($content);
/**
* Prepend content to the body content
*
* @param string $content The content to prepend to the response body.
*
* @return $this
*
* @since 2.0.0
*/
public function prependBody($content);
/**
* Append content to the body content
*
* @param string $content The content to append to the response body.
*
* @return $this
*
* @since 2.0.0
*/
public function appendBody($content);
/**
* Return the body content
*
* @return mixed The response body as a string.
*
* @since 2.0.0
*/
public function getBody();
/**
* Get the PSR-7 Response Object.
*
* @return ResponseInterface
*
* @since 2.0.0
*/
public function getResponse(): ResponseInterface;
/**
* Check if the value is a valid HTTP status code
*
* @param integer $code The potential status code
*
* @return boolean
*
* @since 2.0.0
*/
public function isValidHttpStatus($code);
/**
* Set the PSR-7 Response Object.
*
* @param ResponseInterface $response The response object
*
* @return void
*
* @since 2.0.0
*/
public function setResponse(ResponseInterface $response): void;
/**
* Determine if we are using a secure (SSL) connection.
*
* @return boolean True if using SSL, false if not.
*
* @since 2.0.0
*/
public function isSslConnection();
}

340
libraries/vendor/joomla/archive/LICENSE vendored Normal file
View File

@ -0,0 +1,340 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Library 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Library General
Public License instead of this License.

View File

@ -0,0 +1,210 @@
<?php
/**
* Part of the Joomla Framework Archive Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Archive;
use Joomla\Archive\Exception\UnknownArchiveException;
use Joomla\Archive\Exception\UnsupportedArchiveException;
use Joomla\Filesystem\File;
use Joomla\Filesystem\Folder;
/**
* An Archive handling class
*
* @since 1.0
*/
class Archive
{
/**
* The array of instantiated archive adapters.
*
* @var ExtractableInterface[]
* @since 1.0
*/
protected $adapters = [];
/**
* Holds the options array.
*
* @var array|\ArrayAccess
* @since 1.0
*/
public $options = [];
/**
* Create a new Archive object.
*
* @param array|\ArrayAccess $options An array of options
*
* @since 1.0
* @throws \InvalidArgumentException
*/
public function __construct($options = [])
{
if (!\is_array($options) && !($options instanceof \ArrayAccess)) {
throw new \InvalidArgumentException(
'The options param must be an array or implement the ArrayAccess interface.'
);
}
// Make sure we have a tmp directory.
isset($options['tmp_path']) || $options['tmp_path'] = realpath(sys_get_temp_dir());
$this->options = $options;
}
/**
* Extract an archive file to a directory.
*
* @param string $archivename The name of the archive file
* @param string $extractdir Directory to unpack into
*
* @return boolean True for success
*
* @since 1.0
* @throws UnknownArchiveException if the archive type is not supported
*/
public function extract($archivename, $extractdir)
{
$ext = pathinfo($archivename, \PATHINFO_EXTENSION);
$path = pathinfo($archivename, \PATHINFO_DIRNAME);
$filename = pathinfo($archivename, \PATHINFO_FILENAME);
switch (strtolower($ext)) {
case 'zip':
$result = $this->getAdapter('zip')->extract($archivename, $extractdir);
break;
case 'tar':
$result = $this->getAdapter('tar')->extract($archivename, $extractdir);
break;
case 'tgz':
case 'gz':
case 'gzip':
// This may just be an individual file (e.g. sql script)
$tmpfname = $this->options['tmp_path'] . '/' . uniqid('gzip');
try {
$this->getAdapter('gzip')->extract($archivename, $tmpfname);
} catch (\RuntimeException $exception) {
@unlink($tmpfname);
return false;
}
if ($ext === 'tgz' || stripos($filename, '.tar') !== false) {
$result = $this->getAdapter('tar')->extract($tmpfname, $extractdir);
} else {
Folder::create($extractdir);
$result = File::copy($tmpfname, $extractdir . '/' . $filename, null, false);
}
@unlink($tmpfname);
break;
case 'tbz2':
case 'bz2':
case 'bzip2':
// This may just be an individual file (e.g. sql script)
$tmpfname = $this->options['tmp_path'] . '/' . uniqid('bzip2');
try {
$this->getAdapter('bzip2')->extract($archivename, $tmpfname);
} catch (\RuntimeException $exception) {
@unlink($tmpfname);
return false;
}
if ($ext === 'tbz2' || stripos($filename, '.tar') !== false) {
$result = $this->getAdapter('tar')->extract($tmpfname, $extractdir);
} else {
Folder::create($extractdir);
$result = File::copy($tmpfname, $extractdir . '/' . $filename, null, false);
}
@unlink($tmpfname);
break;
default:
throw new UnknownArchiveException(sprintf('Unsupported archive type: %s', $ext));
}
return $result;
}
/**
* Method to override the provided adapter with your own implementation.
*
* @param string $type Name of the adapter to set.
* @param string $class FQCN of your class which implements ExtractableInterface.
* @param boolean $override True to force override the adapter type.
*
* @return $this
*
* @since 1.0
* @throws UnsupportedArchiveException if the adapter type is not supported
*/
public function setAdapter($type, $class, $override = true)
{
if ($override || !isset($this->adapters[$type])) {
if (!\is_object($class) && !class_exists($class)) {
throw new UnsupportedArchiveException($type, sprintf('Archive adapter "%s" (class "%s") not found.', $type, $class));
}
if (!$class::isSupported()) {
throw new UnsupportedArchiveException($type, sprintf('Archive adapter "%s" (class "%s") not supported.', $type, $class));
}
$object = new $class($this->options);
if (!($object instanceof ExtractableInterface)) {
throw new UnsupportedArchiveException(
$type,
sprintf(
'The provided adapter "%s" (class "%s") must implement %s',
$type,
$class,
ExtractableInterface::class
)
);
}
$this->adapters[$type] = $object;
}
return $this;
}
/**
* Get a file compression adapter.
*
* @param string $type The type of adapter (bzip2|gzip|tar|zip).
*
* @return ExtractableInterface Adapter for the requested type
*
* @since 1.0
*/
public function getAdapter($type)
{
$type = strtolower($type);
if (!isset($this->adapters[$type])) {
// Try to load the adapter object
$this->setAdapter($type, __NAMESPACE__ . '\\' . ucfirst($type));
}
return $this->adapters[$type];
}
}

View File

@ -0,0 +1,139 @@
<?php
/**
* Part of the Joomla Framework Archive Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Archive;
use Joomla\Filesystem\File;
use Joomla\Filesystem\Stream;
/**
* Bzip2 format adapter for the Archive package
*
* @since 1.0
*/
class Bzip2 implements ExtractableInterface
{
/**
* Bzip2 file data buffer
*
* @var string
* @since 1.0
*/
private $data;
/**
* Holds the options array.
*
* @var array|\ArrayAccess
* @since 1.0
*/
protected $options = [];
/**
* Create a new Archive object.
*
* @param array|\ArrayAccess $options An array of options
*
* @since 1.0
* @throws \InvalidArgumentException
*/
public function __construct($options = [])
{
if (!\is_array($options) && !($options instanceof \ArrayAccess)) {
throw new \InvalidArgumentException(
'The options param must be an array or implement the ArrayAccess interface.'
);
}
$this->options = $options;
}
/**
* Extract a Bzip2 compressed file to a given path
*
* @param string $archive Path to Bzip2 archive to extract
* @param string $destination Path to extract archive to
*
* @return boolean True if successful
*
* @since 1.0
* @throws \RuntimeException
*/
public function extract($archive, $destination)
{
$this->data = null;
if (!isset($this->options['use_streams']) || $this->options['use_streams'] == false) {
// Old style: read the whole file and then parse it
$this->data = file_get_contents($archive);
if (!$this->data) {
throw new \RuntimeException('Unable to read archive');
}
$buffer = bzdecompress($this->data);
unset($this->data);
if (empty($buffer)) {
throw new \RuntimeException('Unable to decompress data');
}
if (!File::write($destination, $buffer)) {
throw new \RuntimeException('Unable to write archive to file ' . $destination);
}
} else {
// New style! streams!
$input = Stream::getStream();
// Use bzip
$input->set('processingmethod', 'bz');
if (!$input->open($archive)) {
throw new \RuntimeException('Unable to read archive');
}
$output = Stream::getStream();
if (!$output->open($destination, 'w')) {
$input->close();
throw new \RuntimeException('Unable to open file "' . $destination . '" for writing');
}
do {
$this->data = $input->read($input->get('chunksize', 8196));
if ($this->data) {
if (!$output->write($this->data)) {
$input->close();
throw new \RuntimeException('Unable to write archive to file ' . $destination);
}
}
} while ($this->data);
$output->close();
$input->close();
}
return true;
}
/**
* Tests whether this adapter can unpack files on this computer.
*
* @return boolean True if supported
*
* @since 1.0
*/
public static function isSupported()
{
return \extension_loaded('bz2');
}
}

View File

@ -0,0 +1,19 @@
<?php
/**
* Part of the Joomla Framework Archive Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Archive\Exception;
/**
* Exception class defining an unknown archive type
*
* @since 2.0.0
*/
class UnknownArchiveException extends \InvalidArgumentException
{
}

View File

@ -0,0 +1,55 @@
<?php
/**
* Part of the Joomla Framework Archive Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Archive\Exception;
/**
* Exception class defining an unsupported archive adapter
*
* @since 2.0.0
*/
class UnsupportedArchiveException extends \InvalidArgumentException
{
/**
* The unsupported archive adapter name
*
* @var string
* @since 2.0.0-beta2
*/
protected $adapterType = '';
/**
* Constructor
*
* @param string $adapterType The unsupported adapter type.
* @param string $message The Exception message to throw.
* @param int $code The Exception code.
* @param ?\Throwable $previous The previous throwable used for the exception chaining.
*
* @since 2.0.0-beta2
*/
public function __construct(string $adapterType, string $message = '', int $code = 0, ?\Throwable $previous = null)
{
$this->adapterType = $adapterType;
parent::__construct($message, $code, $previous);
}
/**
* Gets the name of the adapter type that was unsupported
*
* @return string
*
* @since 2.0.0-beta2
*/
public function getUnsupportedAdapterType(): string
{
return $this->adapterType;
}
}

View File

@ -0,0 +1,40 @@
<?php
/**
* Part of the Joomla Framework Archive Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Archive;
/**
* Archive class interface
*
* @since 1.0
*/
interface ExtractableInterface
{
/**
* Extract a compressed file to a given path
*
* @param string $archive Path to archive to extract
* @param string $destination Path to extract archive to
*
* @return boolean True if successful
*
* @since 1.0
* @throws \RuntimeException
*/
public function extract($archive, $destination);
/**
* Tests whether this adapter can unpack files on this computer.
*
* @return boolean True if supported
*
* @since 1.0
*/
public static function isSupported();
}

View File

@ -0,0 +1,197 @@
<?php
/**
* Part of the Joomla Framework Archive Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Archive;
use Joomla\Filesystem\File;
use Joomla\Filesystem\Stream;
/**
* Gzip format adapter for the Archive package
*
* This class is inspired from and draws heavily in code and concept from the Compress package of
* The Horde Project <http://www.horde.org>
*
* @contributor Michael Slusarz <slusarz@horde.org>
* @contributor Michael Cochrane <mike@graftonhall.co.nz>
*
* @since 1.0
*/
class Gzip implements ExtractableInterface
{
/**
* Gzip file flags.
*
* @var array
* @since 1.0
*/
private const FLAGS = ['FTEXT' => 0x01, 'FHCRC' => 0x02, 'FEXTRA' => 0x04, 'FNAME' => 0x08, 'FCOMMENT' => 0x10];
/**
* Gzip file data buffer
*
* @var string
* @since 1.0
*/
private $data;
/**
* Holds the options array.
*
* @var array|\ArrayAccess
* @since 1.0
*/
protected $options = [];
/**
* Create a new Archive object.
*
* @param array|\ArrayAccess $options An array of options
*
* @since 1.0
* @throws \InvalidArgumentException
*/
public function __construct($options = [])
{
if (!\is_array($options) && !($options instanceof \ArrayAccess)) {
throw new \InvalidArgumentException(
'The options param must be an array or implement the ArrayAccess interface.'
);
}
$this->options = $options;
}
/**
* Extract a Gzip compressed file to a given path
*
* @param string $archive Path to ZIP archive to extract
* @param string $destination Path to extract archive to
*
* @return boolean True if successful
*
* @since 1.0
* @throws \RuntimeException
*/
public function extract($archive, $destination)
{
$this->data = null;
if (!isset($this->options['use_streams']) || $this->options['use_streams'] == false) {
$this->data = file_get_contents($archive);
if (!$this->data) {
throw new \RuntimeException('Unable to read archive');
}
$position = $this->getFilePosition();
$buffer = gzinflate(substr($this->data, $position, \strlen($this->data) - $position));
if (empty($buffer)) {
throw new \RuntimeException('Unable to decompress data');
}
if (!File::write($destination, $buffer)) {
throw new \RuntimeException('Unable to write archive to file ' . $destination);
}
} else {
// New style! streams!
$input = Stream::getStream();
// Use gz
$input->set('processingmethod', 'gz');
if (!$input->open($archive)) {
throw new \RuntimeException('Unable to read archive');
}
$output = Stream::getStream();
if (!$output->open($destination, 'w')) {
$input->close();
throw new \RuntimeException('Unable to open file "' . $destination . '" for writing');
}
do {
$this->data = $input->read($input->get('chunksize', 8196));
if ($this->data) {
if (!$output->write($this->data)) {
$input->close();
throw new \RuntimeException('Unable to write archive to file ' . $destination);
}
}
} while ($this->data);
$output->close();
$input->close();
}
return true;
}
/**
* Tests whether this adapter can unpack files on this computer.
*
* @return boolean True if supported
*
* @since 1.0
*/
public static function isSupported()
{
return \extension_loaded('zlib');
}
/**
* Get file data offset for archive
*
* @return integer Data position marker for archive
*
* @since 1.0
* @throws \RuntimeException
*/
public function getFilePosition()
{
// Gzipped file... unpack it first
$position = 0;
$info = @ unpack('CCM/CFLG/VTime/CXFL/COS', substr($this->data, $position + 2));
if (!$info) {
throw new \RuntimeException('Unable to decompress data.');
}
$position += 10;
if ($info['FLG'] & self::FLAGS['FEXTRA']) {
$XLEN = unpack('vLength', substr($this->data, $position + 0, 2));
$XLEN = $XLEN['Length'];
$position += $XLEN + 2;
}
if ($info['FLG'] & self::FLAGS['FNAME']) {
$filenamePos = strpos($this->data, "\x0", $position);
$position = $filenamePos + 1;
}
if ($info['FLG'] & self::FLAGS['FCOMMENT']) {
$commentPos = strpos($this->data, "\x0", $position);
$position = $commentPos + 1;
}
if ($info['FLG'] & self::FLAGS['FHCRC']) {
$hcrc = unpack('vCRC', substr($this->data, $position + 0, 2));
$hcrc = $hcrc['CRC'];
$position += 2;
}
return $position;
}
}

View File

@ -0,0 +1,255 @@
<?php
/**
* Part of the Joomla Framework Archive Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Archive;
use Joomla\Filesystem\File;
use Joomla\Filesystem\Folder;
use Joomla\Filesystem\Path;
/**
* Tar format adapter for the Archive package
*
* This class is inspired from and draws heavily in code and concept from the Compress package of
* The Horde Project <http://www.horde.org>
*
* @contributor Michael Slusarz <slusarz@horde.org>
* @contributor Michael Cochrane <mike@graftonhall.co.nz>
*
* @since 1.0
*/
class Tar implements ExtractableInterface
{
/**
* Tar file types.
*
* @var array
* @since 1.0
*/
private const TYPES = [
0x0 => 'Unix file',
0x30 => 'File',
0x31 => 'Link',
0x32 => 'Symbolic link',
0x33 => 'Character special file',
0x34 => 'Block special file',
0x35 => 'Directory',
0x36 => 'FIFO special file',
0x37 => 'Contiguous file',
];
/**
* Tar file data buffer
*
* @var string
* @since 1.0
*/
private $data;
/**
* Tar file metadata array
*
* @var array
* @since 1.0
*/
private $metadata;
/**
* Holds the options array.
*
* @var array|\ArrayAccess
* @since 1.0
*/
protected $options = [];
/**
* Create a new Archive object.
*
* @param array|\ArrayAccess $options An array of options or an object that implements \ArrayAccess
*
* @since 1.0
* @throws \InvalidArgumentException
*/
public function __construct($options = [])
{
if (!\is_array($options) && !($options instanceof \ArrayAccess)) {
throw new \InvalidArgumentException(
'The options param must be an array or implement the ArrayAccess interface.'
);
}
$this->options = $options;
}
/**
* Extract a ZIP compressed file to a given path
*
* @param string $archive Path to ZIP archive to extract
* @param string $destination Path to extract archive into
*
* @return boolean True if successful
*
* @since 1.0
* @throws \RuntimeException
*/
public function extract($archive, $destination)
{
$this->metadata = [];
$this->data = file_get_contents($archive);
if (!$this->data) {
throw new \RuntimeException('Unable to read archive');
}
$this->getTarInfo($this->data);
for ($i = 0, $n = \count($this->metadata); $i < $n; $i++) {
$type = strtolower($this->metadata[$i]['type']);
if ($type == 'file' || $type == 'unix file') {
$buffer = $this->metadata[$i]['data'];
$path = Path::clean($destination . '/' . $this->metadata[$i]['name']);
if (!$this->isBelow($destination, $destination . '/' . $this->metadata[$i]['name'])) {
throw new \OutOfBoundsException('Unable to write outside of destination path', 100);
}
// Make sure the destination folder exists
if (!Folder::create(\dirname($path))) {
throw new \RuntimeException('Unable to create destination folder ' . \dirname($path));
}
if (!File::write($path, $buffer)) {
throw new \RuntimeException('Unable to write entry to file ' . $path);
}
}
}
return true;
}
/**
* Tests whether this adapter can unpack files on this computer.
*
* @return boolean True if supported
*
* @since 1.0
*/
public static function isSupported()
{
return true;
}
/**
* Get the list of files/data from a Tar archive buffer and builds a metadata array.
*
* Array structure:
* <pre>
* KEY: Position in the array
* VALUES: 'attr' -- File attributes
* 'data' -- Raw file contents
* 'date' -- File modification time
* 'name' -- Filename
* 'size' -- Original file size
* 'type' -- File type
* </pre>
*
* @param string $data The Tar archive buffer.
*
* @return void
*
* @since 1.0
* @throws \RuntimeException
*/
protected function getTarInfo(&$data)
{
$position = 0;
$returnArray = [];
while ($position < \strlen($data)) {
$info = @unpack(
'Z100filename/Z8mode/Z8uid/Z8gid/Z12size/Z12mtime/Z8checksum/Ctypeflag/Z100link/Z6magic/Z2version/Z32uname/Z32gname/Z8devmajor/Z8devminor',
substr($data, $position)
);
/*
* This variable has been set in the previous loop, meaning that the filename was present in the previous block
* to allow more than 100 characters - see below
*/
if (isset($longlinkfilename)) {
$info['filename'] = $longlinkfilename;
unset($longlinkfilename);
}
if (!$info) {
throw new \RuntimeException('Unable to decompress data');
}
$position += 512;
$contents = substr($data, $position, octdec($info['size']));
$position += ceil(octdec($info['size']) / 512) * 512;
if ($info['filename']) {
$file = [
'attr' => null,
'data' => null,
'date' => octdec($info['mtime']),
'name' => trim($info['filename']),
'size' => octdec($info['size']),
'type' => self::TYPES[$info['typeflag']] ?? null,
];
if (($info['typeflag'] == 0) || ($info['typeflag'] == 0x30) || ($info['typeflag'] == 0x35)) {
// File or folder.
$file['data'] = $contents;
$mode = hexdec(substr($info['mode'], 4, 3));
$file['attr'] = (($info['typeflag'] == 0x35) ? 'd' : '-')
. (($mode & 0x400) ? 'r' : '-')
. (($mode & 0x200) ? 'w' : '-')
. (($mode & 0x100) ? 'x' : '-')
. (($mode & 0x040) ? 'r' : '-')
. (($mode & 0x020) ? 'w' : '-')
. (($mode & 0x010) ? 'x' : '-')
. (($mode & 0x004) ? 'r' : '-')
. (($mode & 0x002) ? 'w' : '-')
. (($mode & 0x001) ? 'x' : '-');
} elseif (\chr($info['typeflag']) == 'L' && $info['filename'] == '././@LongLink') {
// GNU tar ././@LongLink support - the filename is actually in the contents, set a variable here so we can test in the next loop
$longlinkfilename = $contents;
// And the file contents are in the next block so we'll need to skip this
continue;
}
$returnArray[] = $file;
}
}
$this->metadata = $returnArray;
}
/**
* Check if a path is below a given destination path
*
* @param string $destination The destination path
* @param string $path The path to be checked
*
* @return boolean
*
* @since 2.0.1
*/
private function isBelow($destination, $path): bool
{
$absoluteRoot = Path::clean(Path::resolve($destination));
$absolutePath = Path::clean(Path::resolve($path));
return strpos($absolutePath, $absoluteRoot) === 0;
}
}

View File

@ -0,0 +1,661 @@
<?php
/**
* Part of the Joomla Framework Archive Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Archive;
use Joomla\Filesystem\File;
use Joomla\Filesystem\Folder;
use Joomla\Filesystem\Path;
/**
* ZIP format adapter for the Archive package
*
* The ZIP compression code is partially based on code from:
* Eric Mueller <eric@themepark.com>
* http://www.zend.com/codex.php?id=535&single=1
*
* Deins125 <webmaster@atlant.ru>
* http://www.zend.com/codex.php?id=470&single=1
*
* The ZIP compression date code is partially based on code from
* Peter Listiak <mlady@users.sourceforge.net>
*
* This class is inspired from and draws heavily in code and concept from the Compress package of
* The Horde Project <http://www.horde.org>
*
* @contributor Chuck Hagenbuch <chuck@horde.org>
* @contributor Michael Slusarz <slusarz@horde.org>
* @contributor Michael Cochrane <mike@graftonhall.co.nz>
*
* @since 1.0
*/
class Zip implements ExtractableInterface
{
/**
* ZIP compression methods.
*
* @var array
* @since 1.0
*/
private const METHODS = [
0x0 => 'None',
0x1 => 'Shrunk',
0x2 => 'Super Fast',
0x3 => 'Fast',
0x4 => 'Normal',
0x5 => 'Maximum',
0x6 => 'Imploded',
0x8 => 'Deflated',
];
/**
* Beginning of central directory record.
*
* @var string
* @since 1.0
*/
private const CTRL_DIR_HEADER = "\x50\x4b\x01\x02";
/**
* End of central directory record.
*
* @var string
* @since 1.0
*/
private const CTRL_DIR_END = "\x50\x4b\x05\x06\x00\x00\x00\x00";
/**
* Beginning of file contents.
*
* @var string
* @since 1.0
*/
private const FILE_HEADER = "\x50\x4b\x03\x04";
/**
* ZIP file data buffer
*
* @var string
* @since 1.0
*/
private $data;
/**
* ZIP file metadata array
*
* @var array
* @since 1.0
*/
private $metadata;
/**
* Holds the options array.
*
* @var array|\ArrayAccess
* @since 1.0
*/
protected $options = [];
/**
* Create a new Archive object.
*
* @param array|\ArrayAccess $options An array of options or an object that implements \ArrayAccess
*
* @since 1.0
* @throws \InvalidArgumentException
*/
public function __construct($options = [])
{
if (!\is_array($options) && !($options instanceof \ArrayAccess)) {
throw new \InvalidArgumentException(
'The options param must be an array or implement the ArrayAccess interface.'
);
}
$this->options = $options;
}
/**
* Create a ZIP compressed file from an array of file data.
*
* @param string $archive Path to save archive.
* @param array $files Array of files to add to archive.
*
* @return boolean True if successful.
*
* @since 1.0
* @todo Finish Implementation
*/
public function create($archive, $files)
{
$contents = [];
$ctrldir = [];
foreach ($files as $file) {
$this->addToZipFile($file, $contents, $ctrldir);
}
return $this->createZipFile($contents, $ctrldir, $archive);
}
/**
* Extract a ZIP compressed file to a given path
*
* @param string $archive Path to ZIP archive to extract
* @param string $destination Path to extract archive into
*
* @return boolean True if successful
*
* @since 1.0
* @throws \RuntimeException
*/
public function extract($archive, $destination)
{
if (!is_file($archive)) {
throw new \RuntimeException('Archive does not exist at ' . $archive);
}
if (static::hasNativeSupport()) {
return $this->extractNative($archive, $destination);
}
return $this->extractCustom($archive, $destination);
}
/**
* Tests whether this adapter can unpack files on this computer.
*
* @return boolean True if supported
*
* @since 1.0
*/
public static function isSupported()
{
return self::hasNativeSupport() || \extension_loaded('zlib');
}
/**
* Method to determine if the server has native zip support for faster handling
*
* @return boolean True if php has native ZIP support
*
* @since 1.0
*/
public static function hasNativeSupport()
{
return \extension_loaded('zip');
}
/**
* Checks to see if the data is a valid ZIP file.
*
* @param string $data ZIP archive data buffer.
*
* @return boolean True if valid, false if invalid.
*
* @since 1.0
*/
public function checkZipData($data)
{
return strpos($data, self::FILE_HEADER) !== false;
}
/**
* Extract a ZIP compressed file to a given path using a php based algorithm that only requires zlib support
*
* @param string $archive Path to ZIP archive to extract.
* @param string $destination Path to extract archive into.
*
* @return boolean True if successful
*
* @since 1.0
* @throws \RuntimeException
*/
protected function extractCustom($archive, $destination)
{
$this->metadata = [];
$this->data = file_get_contents($archive);
if (!$this->data) {
throw new \RuntimeException('Unable to read archive');
}
if (!$this->readZipInfo($this->data)) {
throw new \RuntimeException('Get ZIP Information failed');
}
foreach ($this->metadata as $i => $metadata) {
$lastPathCharacter = substr($metadata['name'], -1, 1);
if ($lastPathCharacter !== '/' && $lastPathCharacter !== '\\') {
$buffer = $this->getFileData($i);
$path = Path::clean($destination . '/' . $metadata['name']);
if (!$this->isBelow($destination, $destination . '/' . $metadata['name'])) {
throw new \OutOfBoundsException('Unable to write outside of destination path', 100);
}
// Make sure the destination folder exists
if (!Folder::create(\dirname($path))) {
throw new \RuntimeException('Unable to create destination folder');
}
if (!File::write($path, $buffer)) {
throw new \RuntimeException('Unable to write file');
}
}
}
return true;
}
/**
* Extract a ZIP compressed file to a given path using native php api calls for speed
*
* @param string $archive Path to ZIP archive to extract
* @param string $destination Path to extract archive into
*
* @return boolean True on success
*
* @throws \RuntimeException
* @since 1.0
*/
protected function extractNative($archive, $destination)
{
$zip = new \ZipArchive();
if ($zip->open($archive) !== true) {
throw new \RuntimeException('Unable to open archive');
}
// Make sure the destination folder exists
if (!Folder::create($destination)) {
throw new \RuntimeException('Unable to create destination folder ' . \dirname($destination));
}
// Read files in the archive
for ($index = 0; $index < $zip->numFiles; $index++) {
$file = $zip->getNameIndex($index);
if (substr($file, -1) === '/') {
continue;
}
$buffer = $zip->getFromIndex($index);
if ($buffer === false) {
throw new \RuntimeException('Unable to read ZIP entry');
}
if (!$this->isBelow($destination, $destination . '/' . $file)) {
throw new \RuntimeException('Unable to write outside of destination path', 100);
}
if (File::write($destination . '/' . $file, $buffer) === false) {
throw new \RuntimeException('Unable to write ZIP entry to file ' . $destination . '/' . $file);
}
}
$zip->close();
return true;
}
/**
* Get the list of files/data from a ZIP archive buffer.
*
* <pre>
* KEY: Position in zipfile
* VALUES: 'attr' -- File attributes
* 'crc' -- CRC checksum
* 'csize' -- Compressed file size
* 'date' -- File modification time
* 'name' -- Filename
* 'method'-- Compression method
* 'size' -- Original file size
* 'type' -- File type
* </pre>
*
* @param string $data The ZIP archive buffer.
*
* @return boolean True on success
*
* @since 1.0
* @throws \RuntimeException
*/
private function readZipInfo($data)
{
$entries = [];
// Find the last central directory header entry
$fhLast = strpos($data, self::CTRL_DIR_END);
do {
$last = $fhLast;
} while (($fhLast = strpos($data, self::CTRL_DIR_END, $fhLast + 1)) !== false);
// Find the central directory offset
$offset = 0;
if ($last) {
$endOfCentralDirectory = unpack(
'vNumberOfDisk/vNoOfDiskWithStartOfCentralDirectory/vNoOfCentralDirectoryEntriesOnDisk/' .
'vTotalCentralDirectoryEntries/VSizeOfCentralDirectory/VCentralDirectoryOffset/vCommentLength',
substr($data, $last + 4)
);
$offset = $endOfCentralDirectory['CentralDirectoryOffset'];
}
// Get details from central directory structure.
$fhStart = strpos($data, self::CTRL_DIR_HEADER, $offset);
$dataLength = \strlen($data);
do {
if ($dataLength < $fhStart + 31) {
throw new \RuntimeException('Invalid ZIP Data');
}
$info = unpack('vMethod/VTime/VCRC32/VCompressed/VUncompressed/vLength', substr($data, $fhStart + 10, 20));
$name = substr($data, $fhStart + 46, $info['Length']);
$entries[$name] = [
'attr' => null,
'crc' => sprintf('%08s', dechex($info['CRC32'])),
'csize' => $info['Compressed'],
'date' => null,
'_dataStart' => null,
'name' => $name,
'method' => self::METHODS[$info['Method']],
'_method' => $info['Method'],
'size' => $info['Uncompressed'],
'type' => null,
];
$entries[$name]['date'] = mktime(
($info['Time'] >> 11) & 0x1f,
($info['Time'] >> 5) & 0x3f,
($info['Time'] << 1) & 0x3e,
($info['Time'] >> 21) & 0x07,
($info['Time'] >> 16) & 0x1f,
(($info['Time'] >> 25) & 0x7f) + 1980
);
if ($dataLength < $fhStart + 43) {
throw new \RuntimeException('Invalid ZIP data');
}
$info = unpack('vInternal/VExternal/VOffset', substr($data, $fhStart + 36, 10));
$entries[$name]['type'] = ($info['Internal'] & 0x01) ? 'text' : 'binary';
$entries[$name]['attr'] = (($info['External'] & 0x10) ? 'D' : '-') . (($info['External'] & 0x20) ? 'A' : '-')
. (($info['External'] & 0x03) ? 'S' : '-') . (($info['External'] & 0x02) ? 'H' : '-') . (($info['External'] & 0x01) ? 'R' : '-');
$entries[$name]['offset'] = $info['Offset'];
// Get details from local file header since we have the offset
$lfhStart = strpos($data, self::FILE_HEADER, $entries[$name]['offset']);
if ($dataLength < $lfhStart + 34) {
throw new \RuntimeException('Invalid ZIP Data');
}
$info = unpack('vMethod/VTime/VCRC32/VCompressed/VUncompressed/vLength/vExtraLength', substr($data, $lfhStart + 8, 25));
$name = substr($data, $lfhStart + 30, $info['Length']);
$entries[$name]['_dataStart'] = $lfhStart + 30 + $info['Length'] + $info['ExtraLength'];
// Bump the max execution time because not using the built in php zip libs makes this process slow.
@set_time_limit(ini_get('max_execution_time'));
} while (($fhStart = strpos($data, self::CTRL_DIR_HEADER, $fhStart + 46)) !== false);
$this->metadata = array_values($entries);
return true;
}
/**
* Returns the file data for a file by offset in the ZIP archive
*
* @param integer $key The position of the file in the archive.
*
* @return string Uncompressed file data buffer.
*
* @since 1.0
*/
private function getFileData(int $key): string
{
if ($this->metadata[$key]['_method'] == 0x8) {
return gzinflate(substr($this->data, $this->metadata[$key]['_dataStart'], $this->metadata[$key]['csize']));
}
if ($this->metadata[$key]['_method'] == 0x0) {
// Files that aren't compressed.
return substr($this->data, $this->metadata[$key]['_dataStart'], $this->metadata[$key]['csize']);
}
if ($this->metadata[$key]['_method'] == 0x12) {
// If bz2 extension is loaded use it
if (\extension_loaded('bz2')) {
return bzdecompress(substr($this->data, $this->metadata[$key]['_dataStart'], $this->metadata[$key]['csize']));
}
}
return '';
}
/**
* Converts a UNIX timestamp to a 4-byte DOS date and time format (date in high 2-bytes, time in low 2-bytes allowing magnitude comparison).
*
* @param integer $unixtime The current UNIX timestamp.
*
* @return integer The current date in a 4-byte DOS format.
*
* @since 1.0
*/
protected function unix2DosTime($unixtime = null)
{
$timearray = $unixtime === null ? getdate() : getdate($unixtime);
if ($timearray['year'] < 1980) {
$timearray['year'] = 1980;
$timearray['mon'] = 1;
$timearray['mday'] = 1;
$timearray['hours'] = 0;
$timearray['minutes'] = 0;
$timearray['seconds'] = 0;
}
return (($timearray['year'] - 1980) << 25) | ($timearray['mon'] << 21) | ($timearray['mday'] << 16) | ($timearray['hours'] << 11) |
($timearray['minutes'] << 5) | ($timearray['seconds'] >> 1);
}
/**
* Adds a "file" to the ZIP archive.
*
* @param array $file File data array to add
* @param array $contents An array of existing zipped files.
* @param array $ctrldir An array of central directory information.
*
* @return void
*
* @since 1.0
* @todo Review and finish implementation
*/
private function addToZipFile(array &$file, array &$contents, array &$ctrldir): void
{
$data = &$file['data'];
$name = str_replace('\\', '/', $file['name']);
// See if time/date information has been provided.
$ftime = null;
if (isset($file['time'])) {
$ftime = $file['time'];
}
// Get the hex time.
$dtime = dechex($this->unix2DosTime($ftime));
$hexdtime = \chr(hexdec($dtime[6] . $dtime[7])) . \chr(hexdec($dtime[4] . $dtime[5])) . \chr(hexdec($dtime[2] . $dtime[3]))
. \chr(hexdec($dtime[0] . $dtime[1]));
// Begin creating the ZIP data.
$fr = self::FILE_HEADER;
// Version needed to extract.
$fr .= "\x14\x00";
// General purpose bit flag.
$fr .= "\x00\x00";
// Compression method.
$fr .= "\x08\x00";
// Last modification time/date.
$fr .= $hexdtime;
// "Local file header" segment.
$uncLen = \strlen($data);
$crc = crc32($data);
$zdata = gzcompress($data);
$zdata = substr(substr($zdata, 0, -4), 2);
$cLen = \strlen($zdata);
// CRC 32 information.
$fr .= pack('V', $crc);
// Compressed filesize.
$fr .= pack('V', $cLen);
// Uncompressed filesize.
$fr .= pack('V', $uncLen);
// Length of filename.
$fr .= pack('v', \strlen($name));
// Extra field length.
$fr .= pack('v', 0);
// File name.
$fr .= $name;
// "File data" segment.
$fr .= $zdata;
// Add this entry to array.
$oldOffset = \strlen(implode('', $contents));
$contents[] = &$fr;
// Add to central directory record.
$cdrec = self::CTRL_DIR_HEADER;
// Version made by.
$cdrec .= "\x00\x00";
// Version needed to extract
$cdrec .= "\x14\x00";
// General purpose bit flag
$cdrec .= "\x00\x00";
// Compression method
$cdrec .= "\x08\x00";
// Last mod time/date.
$cdrec .= $hexdtime;
// CRC 32 information.
$cdrec .= pack('V', $crc);
// Compressed filesize.
$cdrec .= pack('V', $cLen);
// Uncompressed filesize.
$cdrec .= pack('V', $uncLen);
// Length of filename.
$cdrec .= pack('v', \strlen($name));
// Extra field length.
$cdrec .= pack('v', 0);
// File comment length.
$cdrec .= pack('v', 0);
// Disk number start.
$cdrec .= pack('v', 0);
// Internal file attributes.
$cdrec .= pack('v', 0);
// External file attributes -'archive' bit set.
$cdrec .= pack('V', 32);
// Relative offset of local header.
$cdrec .= pack('V', $oldOffset);
// File name.
$cdrec .= $name;
// Save to central directory array.
$ctrldir[] = &$cdrec;
}
/**
* Creates the ZIP file.
*
* Official ZIP file format: http://www.pkware.com/appnote.txt
*
* @param array $contents An array of existing zipped files.
* @param array $ctrlDir An array of central directory information.
* @param string $path The path to store the archive.
*
* @return boolean True if successful
*
* @since 1.0
* @todo Review and finish implementation
*/
private function createZipFile(array $contents, array $ctrlDir, string $path): bool
{
$data = implode('', $contents);
$dir = implode('', $ctrlDir);
/*
* Buffer data:
* Total # of entries "on this disk".
* Total # of entries overall.
* Size of central directory.
* Offset to start of central dir.
* ZIP file comment length.
*/
$buffer = $data . $dir . self::CTRL_DIR_END .
pack('v', \count($ctrlDir)) .
pack('v', \count($ctrlDir)) .
pack('V', \strlen($dir)) .
pack('V', \strlen($data)) .
"\x00\x00";
return File::write($path, $buffer);
}
/**
* Check if a path is below a given destination path
*
* @param string $destination The destination path
* @param string $path The path to be checked
*
* @return boolean
*
* @since 1.1.10
*/
private function isBelow($destination, $path): bool
{
$absoluteRoot = Path::clean(Path::resolve($destination));
$absolutePath = Path::clean(Path::resolve($path));
return strpos($absolutePath, $absoluteRoot) === 0;
}
}

View File

@ -0,0 +1,504 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 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.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
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 and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, 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 library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete 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 distribute a copy of this License along with the
Library.
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 Library or any portion
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
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 Library, 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 Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you 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.
If distribution of 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 satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be 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.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library 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.
9. 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 Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
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 with
this License.
11. 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 Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library 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 Library.
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.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library 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.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser 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 Library
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 Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
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
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "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
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. 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 LIBRARY 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
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. 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 library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; 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.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!

View File

@ -0,0 +1,119 @@
<?php
/**
* Part of the Joomla Framework Authentication Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Authentication;
use Joomla\Authentication\Password\BCryptHandler;
use Joomla\Authentication\Password\HandlerInterface;
/**
* Abstract AuthenticationStrategy for username/password based authentication
*
* @since 1.1.0
*/
abstract class AbstractUsernamePasswordAuthenticationStrategy implements AuthenticationStrategyInterface
{
/**
* The password handler to validate the password against.
*
* @var HandlerInterface
* @since 1.2.0
*/
protected $passwordHandler;
/**
* The last authentication status.
*
* @var integer
* @since 1.1.0
*/
protected $status;
/**
* Constructor.
*
* @param ?HandlerInterface $passwordHandler The password handler.
*
* @since 1.2.0
*/
public function __construct(?HandlerInterface $passwordHandler = null)
{
$this->passwordHandler = $passwordHandler ?: new BCryptHandler();
}
/**
* Attempt to authenticate the username and password pair.
*
* @param string $username The username to authenticate.
* @param string $password The password to attempt authentication with.
*
* @return string|boolean A string containing a username if authentication is successful, false otherwise.
*
* @since 1.1.0
*/
protected function doAuthenticate($username, $password)
{
$hashedPassword = $this->getHashedPassword($username);
if ($hashedPassword === false) {
$this->status = Authentication::NO_SUCH_USER;
return false;
}
if (!$this->verifyPassword($username, $password, $hashedPassword)) {
$this->status = Authentication::INVALID_CREDENTIALS;
return false;
}
$this->status = Authentication::SUCCESS;
return $username;
}
/**
* Retrieve the hashed password for the specified user.
*
* @param string $username Username to lookup.
*
* @return string|boolean Hashed password on success or boolean false on failure.
*
* @since 1.1.0
*/
abstract protected function getHashedPassword($username);
/**
* Get the status of the last authentication attempt.
*
* @return integer Authentication class constant result.
*
* @since 1.1.0
*/
public function getResult()
{
return $this->status;
}
/**
* Attempt to verify the username and password pair.
*
* @param string $username The username to authenticate.
* @param string $password The password to attempt authentication with.
* @param string $hashedPassword The hashed password to attempt authentication against.
*
* @return boolean
*
* @since 1.1.0
*/
protected function verifyPassword($username, $password, $hashedPassword)
{
return $this->passwordHandler->validatePassword($password, $hashedPassword);
}
}

View File

@ -0,0 +1,142 @@
<?php
/**
* Part of the Joomla Framework Authentication Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Authentication;
/**
* Joomla Framework Authentication Class
*
* @since 1.0
*/
class Authentication
{
/**
* Authentication was successful.
*
* @since 1.0
*/
public const SUCCESS = 1;
/**
* Credentials were provided but they were invalid.
*
* @since 1.0
*/
public const INVALID_CREDENTIALS = 2;
/**
* Credentials were provided but the user did not exist in the credential store.
*
* @since 1.0
*/
public const NO_SUCH_USER = 3;
/**
* There were no credentials found.
*
* @since 1.0
*/
public const NO_CREDENTIALS = 4;
/**
* There were partial credentials found but they were not complete.
*
* @since 1.0
*/
public const INCOMPLETE_CREDENTIALS = 5;
/**
* The array of strategies.
*
* @var AuthenticationStrategyInterface[]
* @since 1.0
*/
private $strategies = [];
/**
* The array of results.
*
* @var integer[]
* @since 1.0
*/
private $results = [];
/**
* Register a new strategy
*
* @param string $strategyName The name to use for the strategy.
* @param AuthenticationStrategyInterface $strategy The authentication strategy object to add.
*
* @return void
*
* @since 1.0
*/
public function addStrategy($strategyName, AuthenticationStrategyInterface $strategy)
{
$this->strategies[$strategyName] = $strategy;
}
/**
* Perform authentication
*
* @param string[] $strategies Array of strategies to try - empty to try all strategies.
*
* @return string|boolean A string containing a username if authentication is successful, false otherwise.
*
* @since 1.0
* @throws \RuntimeException
*/
public function authenticate(array $strategies = [])
{
if (empty($strategies)) {
$strategyObjects = $this->strategies;
} else {
$strategyObjects = [];
foreach ($strategies as $strategy) {
if (!isset($this->strategies[$strategy])) {
throw new \RuntimeException('Authentication Strategy Not Found');
}
$strategyObjects[$strategy] = $this->strategies[$strategy];
}
}
if (empty($strategyObjects)) {
throw new \RuntimeException('No strategies have been set');
}
/** @var AuthenticationStrategyInterface $strategyObject */
foreach ($strategyObjects as $strategy => $strategyObject) {
$username = $strategyObject->authenticate();
$this->results[$strategy] = $strategyObject->getResult();
if (\is_string($username)) {
return $username;
}
}
return false;
}
/**
* Get authentication results.
*
* Use this if you want to get more detailed information about the results of an authentication attempts.
*
* @return integer[] An array containing authentication results.
*
* @since 1.0
*/
public function getResults()
{
return $this->results;
}
}

View File

@ -0,0 +1,36 @@
<?php
/**
* Part of the Joomla Framework Authentication Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Authentication;
/**
* Joomla Framework AuthenticationStrategy Interface
*
* @since 1.0
*/
interface AuthenticationStrategyInterface
{
/**
* Attempt authentication.
*
* @return string|boolean A string containing a username if authentication is successful, false otherwise.
*
* @since 1.0
*/
public function authenticate();
/**
* Get last authentication result.
*
* @return integer An integer from Authentication class constants with the authentication result.
*
* @since 1.0
*/
public function getResult();
}

View File

@ -0,0 +1,19 @@
<?php
/**
* Part of the Joomla Framework Authentication Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Authentication\Exception;
/**
* Exception class defining an unsupported password handler
*
* @since 2.0.0
*/
class UnsupportedPasswordHandlerException extends \LogicException
{
}

View File

@ -0,0 +1,125 @@
<?php
/**
* Part of the Joomla Framework Authentication Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Authentication\Password;
use Joomla\Authentication\Exception\UnsupportedPasswordHandlerException;
/**
* Password handler for Argon2i hashed passwords
*
* @since 1.2.0
*/
class Argon2iHandler implements HandlerInterface
{
/**
* Generate a hash for a plaintext password
*
* @param string $plaintext The plaintext password to validate
* @param array $options Options for the hashing operation
*
* @return string
*
* @since 1.2.0
* @throws UnsupportedPasswordHandlerException if the password handler is not supported
*/
public function hashPassword($plaintext, array $options = [])
{
// Use the password extension if able
if (\defined('PASSWORD_ARGON2I')) {
return password_hash($plaintext, \PASSWORD_ARGON2I, $options);
}
// Use the sodium extension (PHP 7.2 native or PECL 2.x) if able
if (\function_exists('sodium_crypto_pwhash_str_verify')) {
$hash = sodium_crypto_pwhash_str(
$plaintext,
\SODIUM_CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE,
\SODIUM_CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE
);
sodium_memzero($plaintext);
return $hash;
}
// Use the libsodium extension (PECL 1.x) if able
if (\extension_loaded('libsodium')) {
$hash = \Sodium\crypto_pwhash_str(
$plaintext,
\Sodium\CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE,
\Sodium\CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE
);
\Sodium\memzero($plaintext);
return $hash;
}
throw new UnsupportedPasswordHandlerException('Argon2i algorithm is not supported.');
}
/**
* Check that the password handler is supported in this environment
*
* @return boolean
*
* @since 1.2.0
*/
public static function isSupported()
{
// Check for native PHP engine support in the password extension
if (\defined('PASSWORD_ARGON2I')) {
return true;
}
// Check if the sodium_compat polyfill is installed and look for compatibility through that
if (class_exists('\\ParagonIE_Sodium_Compat') && method_exists('\\ParagonIE_Sodium_Compat', 'crypto_pwhash_is_available')) {
return \ParagonIE_Sodium_Compat::crypto_pwhash_is_available();
}
// Check for support from the (lib)sodium extension
return \function_exists('sodium_crypto_pwhash_str') || \extension_loaded('libsodium');
}
/**
* Validate a password
*
* @param string $plaintext The plain text password to validate
* @param string $hashed The password hash to validate against
*
* @return boolean
*
* @since 1.2.0
* @throws UnsupportedPasswordHandlerException if the password handler is not supported
*/
public function validatePassword($plaintext, $hashed)
{
// Use the password extension if able
if (\defined('PASSWORD_ARGON2I')) {
return password_verify($plaintext, $hashed);
}
// Use the sodium extension (PHP 7.2 native or PECL 2.x) if able
if (\function_exists('sodium_crypto_pwhash_str_verify')) {
$valid = sodium_crypto_pwhash_str_verify($hashed, $plaintext);
sodium_memzero($plaintext);
return $valid;
}
// Use the libsodium extension (PECL 1.x) if able
if (\extension_loaded('libsodium')) {
$valid = \Sodium\crypto_pwhash_str_verify($hashed, $plaintext);
\Sodium\memzero($plaintext);
return $valid;
}
throw new UnsupportedPasswordHandlerException('Argon2i algorithm is not supported.');
}
}

View File

@ -0,0 +1,79 @@
<?php
/**
* Part of the Joomla Framework Authentication Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Authentication\Password;
use Joomla\Authentication\Exception\UnsupportedPasswordHandlerException;
/**
* Password handler for Argon2id hashed passwords
*
* @since 1.3.0
*/
class Argon2idHandler implements HandlerInterface
{
/**
* Generate a hash for a plaintext password
*
* @param string $plaintext The plaintext password to validate
* @param array $options Options for the hashing operation
*
* @return string
*
* @since 1.3.0
* @throws UnsupportedPasswordHandlerException if the password handler is not supported
*/
public function hashPassword($plaintext, array $options = [])
{
// Use the password extension if able
if (version_compare(\PHP_VERSION, '7.3', '>=') && \defined('PASSWORD_ARGON2ID')) {
return password_hash($plaintext, \PASSWORD_ARGON2ID, $options);
}
throw new UnsupportedPasswordHandlerException('Argon2id algorithm is not supported.');
}
/**
* Check that the password handler is supported in this environment
*
* @return boolean
*
* @since 1.3.0
*/
public static function isSupported()
{
// Check for native PHP engine support in the password extension
if (version_compare(\PHP_VERSION, '7.3', '>=') && \defined('PASSWORD_ARGON2ID')) {
return true;
}
return false;
}
/**
* Validate a password
*
* @param string $plaintext The plain text password to validate
* @param string $hashed The password hash to validate against
*
* @return boolean
*
* @since 1.3.0
* @throws UnsupportedPasswordHandlerException if the password handler is not supported
*/
public function validatePassword($plaintext, $hashed)
{
// Use the password extension if able
if (version_compare(\PHP_VERSION, '7.3', '>=') && \defined('PASSWORD_ARGON2ID')) {
return password_verify($plaintext, $hashed);
}
throw new UnsupportedPasswordHandlerException('Argon2id algorithm is not supported.');
}
}

View File

@ -0,0 +1,61 @@
<?php
/**
* Part of the Joomla Framework Authentication Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Authentication\Password;
/**
* Password handler for BCrypt hashed passwords
*
* @since 1.2.0
*/
class BCryptHandler implements HandlerInterface
{
/**
* Generate a hash for a plaintext password
*
* @param string $plaintext The plaintext password to validate
* @param array $options Options for the hashing operation
*
* @return string
*
* @since 1.2.0
*/
public function hashPassword($plaintext, array $options = [])
{
return password_hash($plaintext, \PASSWORD_BCRYPT, $options);
}
/**
* Check that the password handler is supported in this environment
*
* @return boolean
*
* @since 1.2.0
*/
public static function isSupported()
{
// Check the password_verify() function exists, either as part of PHP core or through a polyfill
return \function_exists('password_verify');
}
/**
* Validate a password
*
* @param string $plaintext The plain text password to validate
* @param string $hashed The password hash to validate against
*
* @return boolean
*
* @since 1.2.0
*/
public function validatePassword($plaintext, $hashed)
{
return password_verify($plaintext, $hashed);
}
}

View File

@ -0,0 +1,51 @@
<?php
/**
* Part of the Joomla Framework Authentication Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Authentication\Password;
/**
* Interface defining a password handler
*
* @since 1.2.0
*/
interface HandlerInterface
{
/**
* Generate a hash for a plaintext password
*
* @param string $plaintext The plaintext password to validate
* @param array $options Options for the hashing operation
*
* @return string
*
* @since 1.2.0
*/
public function hashPassword($plaintext, array $options = []);
/**
* Check that the password handler is supported in this environment
*
* @return boolean
*
* @since 1.2.0
*/
public static function isSupported();
/**
* Validate a password
*
* @param string $plaintext The plain text password to validate
* @param string $hashed The password hash to validate against
*
* @return boolean
*
* @since 1.2.0
*/
public function validatePassword($plaintext, $hashed);
}

View File

@ -0,0 +1,123 @@
<?php
/**
* Part of the Joomla Framework Authentication Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Authentication\Strategies;
use Joomla\Authentication\AbstractUsernamePasswordAuthenticationStrategy;
use Joomla\Authentication\Authentication;
use Joomla\Authentication\Password\HandlerInterface;
use Joomla\Database\DatabaseInterface;
use Joomla\Input\Input;
/**
* Joomla Framework Database Strategy Authentication class
*
* @since 1.1.0
*/
class DatabaseStrategy extends AbstractUsernamePasswordAuthenticationStrategy
{
/**
* DatabaseInterface object
*
* @var DatabaseInterface
* @since 1.1.0
*/
private $db;
/**
* Database connection options
*
* @var array
* @since 1.1.0
*/
private $dbOptions;
/**
* The Input object
*
* @var Input
* @since 1.1.0
*/
private $input;
/**
* Strategy Constructor
*
* @param Input $input The input object from which to retrieve the request credentials.
* @param DatabaseInterface $database DatabaseDriver for retrieving user credentials.
* @param array $options Optional options array for configuring the credential storage connection.
* @param ?HandlerInterface $passwordHandler The password handler.
*
* @since 1.1.0
*/
public function __construct(Input $input, DatabaseInterface $database, array $options = [], ?HandlerInterface $passwordHandler = null)
{
parent::__construct($passwordHandler);
$this->input = $input;
$this->db = $database;
$options['database_table'] = $options['database_table'] ?? '#__users';
$options['username_column'] = $options['username_column'] ?? 'username';
$options['password_column'] = $options['password_column'] ?? 'password';
$this->dbOptions = $options;
}
/**
* Attempt to authenticate the username and password pair.
*
* @return string|boolean A string containing a username if authentication is successful, false otherwise.
*
* @since 1.1.0
*/
public function authenticate()
{
$username = $this->input->get('username', false, 'username');
$password = $this->input->get('password', false, 'raw');
if (!$username || !$password) {
$this->status = Authentication::NO_CREDENTIALS;
return false;
}
return $this->doAuthenticate($username, $password);
}
/**
* Retrieve the hashed password for the specified user.
*
* @param string $username Username to lookup.
*
* @return string|boolean Hashed password on success or boolean false on failure.
*
* @since 1.1.0
*/
protected function getHashedPassword($username)
{
try {
$password = $this->db->setQuery(
$this->db->getQuery(true)
->select($this->db->quoteName($this->dbOptions['password_column']))
->from($this->db->quoteName($this->dbOptions['database_table']))
->where($this->db->quoteName($this->dbOptions['username_column']) . ' = ?')
->bind(1, $username)
)->loadResult();
} catch (\RuntimeException $exception) {
return false;
}
if (!$password) {
return false;
}
return $password;
}
}

View File

@ -0,0 +1,91 @@
<?php
/**
* Part of the Joomla Framework Authentication Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Authentication\Strategies;
use Joomla\Authentication\AbstractUsernamePasswordAuthenticationStrategy;
use Joomla\Authentication\Authentication;
use Joomla\Authentication\Password\HandlerInterface;
use Joomla\Input\Input;
/**
* Joomla Framework Local Strategy Authentication class
*
* @since 1.0
*/
class LocalStrategy extends AbstractUsernamePasswordAuthenticationStrategy
{
/**
* The credential store.
*
* @var array
* @since 1.0
*/
private $credentialStore;
/**
* The Input object
*
* @var Input
* @since 1.0
*/
private $input;
/**
* Strategy Constructor
*
* @param Input $input The input object from which to retrieve the request credentials.
* @param array $credentialStore Hash of username and hash pairs.
* @param ?HandlerInterface $passwordHandler The password handler.
*
* @since 1.0
*/
public function __construct(Input $input, array $credentialStore = [], ?HandlerInterface $passwordHandler = null)
{
parent::__construct($passwordHandler);
$this->credentialStore = $credentialStore;
$this->input = $input;
}
/**
* Attempt to authenticate the username and password pair.
*
* @return string|boolean A string containing a username if authentication is successful, false otherwise.
*
* @since 1.0
*/
public function authenticate()
{
$username = $this->input->get('username', false, 'username');
$password = $this->input->get('password', false, 'raw');
if (!$username || !$password) {
$this->status = Authentication::NO_CREDENTIALS;
return false;
}
return $this->doAuthenticate($username, $password);
}
/**
* Retrieve the hashed password for the specified user.
*
* @param string $username Username to lookup.
*
* @return string|boolean Hashed password on success or boolean false on failure.
*
* @since 1.1.0
*/
protected function getHashedPassword($username)
{
return $this->credentialStore[$username] ?? false;
}
}

View File

@ -0,0 +1,106 @@
local volumes = [
{
name: "composer-cache",
path: "/tmp/composer-cache",
},
];
local hostvolumes = [
{
name: "composer-cache",
host: {path: "/tmp/composer-cache"}
},
];
local composer(phpversion, params) = {
name: "composer",
image: "joomlaprojects/docker-images:php" + phpversion,
volumes: volumes,
commands: [
"php -v",
"composer update " + params,
]
};
local phpunit(phpversion) = {
name: "PHPUnit",
image: "joomlaprojects/docker-images:php" + phpversion,
[if phpversion == "8.3" then "failure"]: "ignore",
commands: ["vendor/bin/phpunit"]
};
local pipeline(name, phpversion, params) = {
kind: "pipeline",
name: "PHP " + name,
volumes: hostvolumes,
steps: [
composer(phpversion, params),
phpunit(phpversion)
],
};
[
{
kind: "pipeline",
name: "Codequality",
volumes: hostvolumes,
steps: [
{
name: "composer",
image: "joomlaprojects/docker-images:php8.1",
volumes: volumes,
commands: [
"php -v",
"composer update",
]
},
{
name: "phpcs",
image: "joomlaprojects/docker-images:php8.1",
depends: [ "composer" ],
commands: [
"vendor/bin/phpcs --standard=ruleset.xml src/"
]
},
{
name: "phan",
image: "joomlaprojects/docker-images:php8.1-ast",
depends: [ "composer" ],
failure: "ignore",
commands: [
"vendor/bin/phan"
]
},
{
name: "phpstan",
image: "joomlaprojects/docker-images:php8.1",
depends: [ "composer" ],
failure: "ignore",
commands: [
"vendor/bin/phpstan analyse src",
]
},
{
name: "phploc",
image: "joomlaprojects/docker-images:php8.1",
depends: [ "composer" ],
failure: "ignore",
commands: [
"phploc src",
]
},
{
name: "phpcpd",
image: "joomlaprojects/docker-images:php8.1",
depends: [ "composer" ],
failure: "ignore",
commands: [
"phpcpd src",
]
}
]
},
pipeline("8.1 lowest", "8.1", "--prefer-stable --prefer-lowest"),
pipeline("8.1", "8.1", "--prefer-stable"),
pipeline("8.2", "8.2", "--prefer-stable"),
]

View File

@ -0,0 +1,115 @@
---
kind: pipeline
name: Codequality
steps:
- commands:
- php -v
- composer update
image: joomlaprojects/docker-images:php8.1
name: composer
volumes:
- name: composer-cache
path: /tmp/composer-cache
- commands:
- vendor/bin/phpcs --standard=ruleset.xml src/
depends:
- composer
image: joomlaprojects/docker-images:php8.1
name: phpcs
- commands:
- vendor/bin/phan
depends:
- composer
failure: ignore
image: joomlaprojects/docker-images:php8.1-ast
name: phan
- commands:
- vendor/bin/phpstan analyse src
depends:
- composer
failure: ignore
image: joomlaprojects/docker-images:php8.1
name: phpstan
- commands:
- phploc src
depends:
- composer
failure: ignore
image: joomlaprojects/docker-images:php8.1
name: phploc
- commands:
- phpcpd src
depends:
- composer
failure: ignore
image: joomlaprojects/docker-images:php8.1
name: phpcpd
volumes:
- host:
path: /tmp/composer-cache
name: composer-cache
---
kind: pipeline
name: PHP 8.1 lowest
steps:
- commands:
- php -v
- composer update --prefer-stable --prefer-lowest
image: joomlaprojects/docker-images:php8.1
name: composer
volumes:
- name: composer-cache
path: /tmp/composer-cache
- commands:
- vendor/bin/phpunit
image: joomlaprojects/docker-images:php8.1
name: PHPUnit
volumes:
- host:
path: /tmp/composer-cache
name: composer-cache
---
kind: pipeline
name: PHP 8.1
steps:
- commands:
- php -v
- composer update --prefer-stable
image: joomlaprojects/docker-images:php8.1
name: composer
volumes:
- name: composer-cache
path: /tmp/composer-cache
- commands:
- vendor/bin/phpunit
image: joomlaprojects/docker-images:php8.1
name: PHPUnit
volumes:
- host:
path: /tmp/composer-cache
name: composer-cache
---
kind: pipeline
name: PHP 8.2
steps:
- commands:
- php -v
- composer update --prefer-stable
image: joomlaprojects/docker-images:php8.2
name: composer
volumes:
- name: composer-cache
path: /tmp/composer-cache
- commands:
- vendor/bin/phpunit
image: joomlaprojects/docker-images:php8.2
name: PHPUnit
volumes:
- host:
path: /tmp/composer-cache
name: composer-cache
---
kind: signature
hmac: c9820ee9949d145df5c2e47d5941d1649f380abd0ac1e5261370115fbf12729b
...

340
libraries/vendor/joomla/console/LICENSE vendored Normal file
View File

@ -0,0 +1,340 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Library 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Library General
Public License instead of this License.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,580 @@
<?php
/**
* Part of the Joomla Framework Console Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Console\Command;
use Joomla\Console\Application;
use Symfony\Component\Console\Helper\HelperSet;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Base command class for a Joomla! command line application.
*
* @since 2.0.0
*/
abstract class AbstractCommand
{
/**
* The default command name
*
* @var string|null
* @since 2.0.0
*/
protected static $defaultName;
/**
* The command's aliases.
*
* @var string[]
* @since 2.0.0
*/
private $aliases = [];
/**
* The application running this command.
*
* @var Application|null
* @since 2.0.0
*/
private $application;
/**
* Flag tracking whether the application definition has been merged to this command.
*
* @var boolean
* @since 2.0.0
*/
private $applicationDefinitionMerged = false;
/**
* Flag tracking whether the application definition with arguments has been merged to this command.
*
* @var boolean
* @since 2.0.0
*/
private $applicationDefinitionMergedWithArgs = false;
/**
* The command's input definition.
*
* @var InputDefinition
* @since 2.0.0
*/
private $definition;
/**
* The command's description.
*
* @var string
* @since 2.0.0
*/
private $description = '';
/**
* The command's help.
*
* @var string
* @since 2.0.0
*/
private $help = '';
/**
* The command's input helper set.
*
* @var HelperSet|null
* @since 2.0.0
*/
private $helperSet;
/**
* Flag tracking whether the command is hidden from the command listing.
*
* @var boolean
* @since 2.0.0
*/
private $hidden = false;
/**
* The command's name.
*
* @var string
* @since 2.0.0
*/
private $name;
/**
* The command's synopses.
*
* @var string[]
* @since 2.0.0
*/
private $synopsis = [];
/**
* Command constructor.
*
* @param string|null $name The name of the command; if the name is empty and no default is set, a name must be set in the configure() method
*
* @since 2.0.0
*/
public function __construct(?string $name = null)
{
$this->definition = new InputDefinition();
if ($name !== null || null !== $name = static::getDefaultName()) {
$this->setName($name);
}
$this->configure();
}
/**
* Adds an argument to the input definition.
*
* @param string $name The argument name
* @param ?integer $mode The argument mode: InputArgument::REQUIRED or InputArgument::OPTIONAL
* @param string $description A description text
* @param mixed $default The default value (for InputArgument::OPTIONAL mode only)
*
* @return $this
*
* @since 2.0.0
*/
public function addArgument(string $name, ?int $mode = null, string $description = '', $default = null): self
{
$this->definition->addArgument(new InputArgument($name, $mode, $description, $default));
return $this;
}
/**
* Adds an option to the input definition.
*
* @param string $name The option name
* @param string|array $shortcut The shortcuts, can be null, a string of shortcuts delimited by | or an array of shortcuts
* @param ?integer $mode The option mode: One of the VALUE_* constants
* @param string $description A description text
* @param ?mixed $default The default value (must be null for InputOption::VALUE_NONE)
*
* @return $this
*
* @since 2.0.0
*/
public function addOption(string $name, $shortcut = null, ?int $mode = null, $description = '', $default = null): self
{
$this->definition->addOption(new InputOption($name, $shortcut, $mode, $description, $default));
return $this;
}
/**
* Configure the command.
*
* @return void
*
* @since 2.0.0
*/
protected function configure(): void
{
}
/**
* Internal function to execute the command.
*
* @param InputInterface $input The input to inject into the command.
* @param OutputInterface $output The output to inject into the command.
*
* @return integer The command exit code
*
* @since 2.0.0
*/
abstract protected function doExecute(InputInterface $input, OutputInterface $output): int;
/**
* Executes the command.
*
* @param InputInterface $input The input to inject into the command.
* @param OutputInterface $output The output to inject into the command.
*
* @return integer The command exit code
*
* @since 2.0.0
*/
public function execute(InputInterface $input, OutputInterface $output): int
{
// Force the creation of the synopsis before the merge with the app definition
$this->getSynopsis(true);
$this->getSynopsis(false);
// Add the application arguments and options
$this->mergeApplicationDefinition();
// Bind the input against the command specific arguments/options
$input->bind($this->getDefinition());
$this->initialise($input, $output);
// Ensure that the command has a `command` argument so it does not fail validation
if ($input->hasArgument('command') && $input->getArgument('command') === null) {
$input->setArgument('command', $this->getName());
}
$input->validate();
return $this->doExecute($input, $output);
}
/**
* Get the command's aliases.
*
* @return string[]
*
* @since 2.0.0
*/
public function getAliases(): array
{
return $this->aliases;
}
/**
* Get the application object.
*
* @return Application The application object.
*
* @since 2.0.0
* @throws \UnexpectedValueException if the application has not been set.
*/
public function getApplication(): Application
{
if ($this->application) {
return $this->application;
}
throw new \UnexpectedValueException('Application not set in ' . \get_class($this));
}
/**
* Get the default command name for this class.
*
* This allows a command name to defined and referenced without instantiating the full command class.
*
* @return string|null
*
* @since 2.0.0
*/
public static function getDefaultName(): ?string
{
$class = \get_called_class();
$r = new \ReflectionProperty($class, 'defaultName');
return $class === $r->class ? static::$defaultName : null;
}
/**
* Gets the InputDefinition attached to this command.
*
* @return InputDefinition
*
* @since 2.0.0
*/
public function getDefinition(): InputDefinition
{
return $this->definition;
}
/**
* Get the command's description.
*
* @return string
*
* @since 2.0.0
*/
public function getDescription(): string
{
return $this->description;
}
/**
* Get the command's help.
*
* @return string
*
* @since 2.0.0
*/
public function getHelp(): string
{
return $this->help;
}
/**
* Get the command's input helper set.
*
* @return HelperSet|null
*
* @since 2.0.0
*/
public function getHelperSet(): ?HelperSet
{
return $this->helperSet;
}
/**
* Get the command's name.
*
* @return string|null
*
* @since 2.0.0
*/
public function getName(): ?string
{
return $this->name;
}
/**
* Returns the processed help for the command.
*
* This method is used to replace placeholders in commands with the real values.
* By default, this supports `%command.name%` and `%command.full_name`.
*
* @return string
*
* @since 2.0.0
*/
public function getProcessedHelp(): string
{
$name = $this->getName();
$placeholders = [
'%command.name%',
'%command.full_name%',
];
$replacements = [
$name,
$_SERVER['PHP_SELF'] . ' ' . $name,
];
return str_replace($placeholders, $replacements, $this->getHelp() ?: $this->getDescription());
}
/**
* Get the command's synopsis.
*
* @param boolean $short Flag indicating whether the short or long version of the synopsis should be returned
*
* @return string
*
* @since 2.0.0
*/
public function getSynopsis(bool $short = false): string
{
$key = $short ? 'short' : 'long';
if (!isset($this->synopsis[$key])) {
$this->synopsis[$key] = trim(sprintf('%s %s', $this->getName(), $this->getDefinition()->getSynopsis($short)));
}
return $this->synopsis[$key];
}
/**
* Internal hook to initialise the command after the input has been bound and before the input is validated.
*
* @param InputInterface $input The input to inject into the command.
* @param OutputInterface $output The output to inject into the command.
*
* @return void
*
* @since 2.0.0
*/
protected function initialise(InputInterface $input, OutputInterface $output): void
{
}
/**
* Check if the command is enabled in this environment.
*
* @return boolean
*
* @since 2.0.0
*/
public function isEnabled(): bool
{
return true;
}
/**
* Check if the command is hidden from the command listing.
*
* @return boolean
*
* @since 2.0.0
*/
public function isHidden(): bool
{
return $this->hidden;
}
/**
* Merges the application definition with the command definition.
*
* @param boolean $mergeArgs Flag indicating whether the application's definition arguments should be merged
*
* @return void
*
* @since 2.0.0
* @internal This method should not be relied on as part of the public API
*/
final public function mergeApplicationDefinition(bool $mergeArgs = true): void
{
if (!$this->application || ($this->applicationDefinitionMerged && ($this->applicationDefinitionMergedWithArgs || !$mergeArgs))) {
return;
}
$this->getDefinition()->addOptions($this->getApplication()->getDefinition()->getOptions());
$this->applicationDefinitionMerged = true;
if ($mergeArgs) {
$currentArguments = $this->getDefinition()->getArguments();
$this->getDefinition()->setArguments($this->getApplication()->getDefinition()->getArguments());
$this->getDefinition()->addArguments($currentArguments);
$this->applicationDefinitionMergedWithArgs = true;
}
}
/**
* Set the command's aliases.
*
* @param string[] $aliases The command aliases
*
* @return void
*
* @since 2.0.0
*/
public function setAliases(array $aliases): void
{
$this->aliases = $aliases;
}
/**
* Set the command's application.
*
* @param ?Application $application The command's application
*
* @return void
*
* @since 2.0.0
*/
public function setApplication(?Application $application = null): void
{
$this->application = $application;
if ($application) {
$this->setHelperSet($application->getHelperSet());
} else {
$this->helperSet = null;
}
}
/**
* Sets the input definition for the command.
*
* @param array|InputDefinition $definition Either an InputDefinition object or an array of objects to write to the definition.
*
* @return void
*
* @since 2.0.0
*/
public function setDefinition($definition): void
{
if ($definition instanceof InputDefinition) {
$this->definition = $definition;
} else {
$this->definition->setDefinition($definition);
}
$this->applicationDefinitionMerged = false;
}
/**
* Sets the description for the command.
*
* @param string $description The description for the command
*
* @return void
*
* @since 2.0.0
*/
public function setDescription(string $description): void
{
$this->description = $description;
}
/**
* Sets the help for the command.
*
* @param string $help The help for the command
*
* @return void
*
* @since 2.0.0
*/
public function setHelp(string $help): void
{
$this->help = $help;
}
/**
* Set the command's input helper set.
*
* @param HelperSet $helperSet The helper set.
*
* @return void
*
* @since 2.0.0
*/
public function setHelperSet(HelperSet $helperSet): void
{
$this->helperSet = $helperSet;
}
/**
* Set whether this command is hidden from the command listing.
*
* @param boolean $hidden Flag if this command is hidden.
*
* @return void
*
* @since 2.0.0
*/
public function setHidden(bool $hidden): void
{
$this->hidden = $hidden;
}
/**
* Set the command's name.
*
* @param string $name The command name
*
* @return void
*
* @since 2.0.0
*/
public function setName(string $name): void
{
$this->name = $name;
}
}

View File

@ -0,0 +1,103 @@
<?php
/**
* Part of the Joomla Framework Console Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Console\Command;
use Joomla\Console\Helper\DescriptorHelper;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Command to render a command's help data.
*
* @since 2.0.0
*/
class HelpCommand extends AbstractCommand
{
/**
* The default command name
*
* @var string
* @since 2.0.0
*/
protected static $defaultName = 'help';
/**
* The command to process help for
*
* @var AbstractCommand|null
* @since 2.0.0
*/
private $command;
/**
* Configure the command.
*
* @return void
*
* @since 2.0.0
*/
protected function configure(): void
{
$this->setDescription('Show the help for a command');
$this->setHelp(
<<<'EOF'
The <info>%command.name%</info> command displays a command's help information:
<info>php %command.full_name% list</info>
To display the list of available commands, please use the <info>list</info> command.
EOF
);
$this->addArgument('command_name', InputArgument::OPTIONAL, 'The command name', 'help');
}
/**
* Internal function to execute the command.
*
* @param InputInterface $input The input to inject into the command.
* @param OutputInterface $output The output to inject into the command.
*
* @return integer The command exit code
*
* @since 2.0.0
*/
protected function doExecute(InputInterface $input, OutputInterface $output): int
{
if (!$this->command) {
$this->command = $this->getApplication()->getCommand($input->getArgument('command_name'));
}
$descriptor = new DescriptorHelper();
if ($this->getHelperSet() !== null) {
$this->getHelperSet()->set($descriptor);
}
$descriptor->describe($output, $this->command);
return 0;
}
/**
* Set the command whose help is being presented.
*
* @param AbstractCommand $command The command to process help for.
*
* @return void
*
* @since 2.0.0
*/
public function setCommand(AbstractCommand $command): void
{
$this->command = $command;
}
}

View File

@ -0,0 +1,80 @@
<?php
/**
* Part of the Joomla Framework Console Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Console\Command;
use Joomla\Console\Helper\DescriptorHelper;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Command listing all available commands.
*
* @since 2.0.0
*/
class ListCommand extends AbstractCommand
{
/**
* The default command name
*
* @var string
* @since 2.0.0
*/
protected static $defaultName = 'list';
/**
* Configure the command.
*
* @return void
*
* @since 2.0.0
*/
protected function configure(): void
{
$this->setDescription("List the application's available commands");
$this->addArgument('namespace', InputArgument::OPTIONAL, 'The namespace name');
$this->setHelp(
<<<'EOF'
The <info>%command.name%</info> command lists all of the application's commands:
<info>php %command.full_name%</info>
EOF
);
}
/**
* Internal function to execute the command.
*
* @param InputInterface $input The input to inject into the command.
* @param OutputInterface $output The output to inject into the command.
*
* @return integer The command exit code
*
* @since 2.0.0
*/
protected function doExecute(InputInterface $input, OutputInterface $output): int
{
$descriptor = new DescriptorHelper();
if ($this->getHelperSet() !== null) {
$this->getHelperSet()->set($descriptor);
}
$descriptor->describe(
$output,
$this->getApplication(),
[
'namespace' => $input->getArgument('namespace'),
]
);
return 0;
}
}

View File

@ -0,0 +1,60 @@
<?php
/**
* Part of the Joomla Framework Console Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Console;
/**
* Class defining the events available in the console application.
*
* @since 2.0.0
*/
final class ConsoleEvents
{
/**
* The APPLICATION_ERROR event is an event triggered when an uncaught Throwable is received at the main application executor.
*
* This event allows developers to handle the Throwable.
*
* @var string
* @since 2.0.0
*/
public const APPLICATION_ERROR = 'console.application_error';
/**
* The BEFORE_COMMAND_EXECUTE event is an event triggered before a command is executed.
*
* This event allows developers to modify information about the command or the command's
* dependencies prior to the command being executed.
*
* @var string
* @since 2.0.0
*/
public const BEFORE_COMMAND_EXECUTE = 'console.before_command_execute';
/**
* The COMMAND_ERROR event is an event triggered when an uncaught Throwable from a command is received.
*
* This event allows developers to handle the Throwable.
*
* @var string
* @since 2.0.0
*/
public const COMMAND_ERROR = 'console.command_error';
/**
* The TERMINATE event is an event triggered immediately before the application is exited.
*
* This event allows developers to perform any post-process actions and to manipulate
* the process' exit code.
*
* @var string
* @since 2.0.0
*/
public const TERMINATE = 'console.terminate';
}

View File

@ -0,0 +1,235 @@
<?php
/**
* Part of the Joomla Framework Console Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Console\Descriptor;
use Joomla\Console\Application;
use Joomla\Console\Command\AbstractCommand;
use Symfony\Component\Console\Exception\CommandNotFoundException;
/**
* Describes an application.
*
* @since 2.0.0
*/
final class ApplicationDescription
{
/**
* Placeholder for commands in the global namespace.
*
* @var string
* @since 2.0.0
*/
public const GLOBAL_NAMESPACE = '_global';
/**
* The application's aliased commands.
*
* @var AbstractCommand[]
* @since 2.0.0
*/
private $aliases;
/**
* The application being described.
*
* @var Application
* @since 2.0.0
*/
private $application;
/**
* The application's commands.
*
* @var AbstractCommand[]
* @since 2.0.0
*/
private $commands;
/**
* The command namespace to process.
*
* @var string
* @since 2.0.0
*/
private $namespace = '';
/**
* The application's command namespaces.
*
* @var array[]
* @since 2.0.0
*/
private $namespaces;
/**
* Flag indicating hidden commands should be displayed.
*
* @var boolean
* @since 2.0.0
*/
private $showHidden;
/**
* Constructor.
*
* @param Application $application The application being described.
* @param string $namespace The command namespace to process.
* @param boolean $showHidden Flag indicating hidden commands should be displayed.
*
* @since 2.0.0
*/
public function __construct(Application $application, string $namespace = '', bool $showHidden = false)
{
$this->application = $application;
$this->namespace = $namespace;
$this->showHidden = $showHidden;
}
/**
* Get the application's command namespaces.
*
* @return array[]
*
* @since 2.0.0
*/
public function getNamespaces(): array
{
if ($this->namespaces === null) {
$this->inspectApplication();
}
return $this->namespaces;
}
/**
* Get the application's commands.
*
* @return AbstractCommand[]
*
* @since 2.0.0
*/
public function getCommands(): array
{
if ($this->commands === null) {
$this->inspectApplication();
}
return $this->commands;
}
/**
* Get a command by name.
*
* @param string $name The name of the command to retrieve.
*
* @return AbstractCommand
*
* @since 2.0.0
* @throws CommandNotFoundException
*/
public function getCommand(string $name): AbstractCommand
{
if (!isset($this->commands[$name]) && !isset($this->aliases[$name])) {
throw new CommandNotFoundException(sprintf('Command %s does not exist.', $name));
}
return $this->commands[$name] ?? $this->aliases[$name];
}
/**
* Returns the namespace part of the command name.
*
* @param string $name The command name to process
* @param integer $limit The maximum number of parts of the namespace
*
* @return string
*
* @since 2.0.0
*/
private function extractNamespace(string $name, ?int $limit = null): string
{
$parts = explode(':', $name);
array_pop($parts);
return implode(':', $limit === null ? $parts : \array_slice($parts, 0, $limit));
}
/**
* Inspects the application.
*
* @return void
*
* @since 2.0.0
*/
private function inspectApplication(): void
{
$this->commands = [];
$this->namespaces = [];
$all = $this->application->getAllCommands($this->namespace ? $this->application->findNamespace($this->namespace) : '');
foreach ($this->sortCommands($all) as $namespace => $commands) {
$names = [];
foreach ($commands as $name => $command) {
if (!$command->getName() || (!$this->showHidden && $command->isHidden())) {
continue;
}
if ($command->getName() === $name) {
$this->commands[$name] = $command;
} else {
$this->aliases[$name] = $command;
}
$names[] = $name;
}
$this->namespaces[$namespace] = ['id' => $namespace, 'commands' => $names];
}
}
/**
* Sort a set of commands.
*
* @param AbstractCommand[] $commands The commands to sort.
*
* @return AbstractCommand[][]
*
* @since 2.0.0
*/
private function sortCommands(array $commands): array
{
$namespacedCommands = [];
$globalCommands = [];
foreach ($commands as $name => $command) {
$key = $this->extractNamespace($name, 1);
if (!$key) {
$globalCommands[self::GLOBAL_NAMESPACE][$name] = $command;
} else {
$namespacedCommands[$key][$name] = $command;
}
}
ksort($namespacedCommands);
$namespacedCommands = array_merge($globalCommands, $namespacedCommands);
foreach ($namespacedCommands as &$commandsSet) {
ksort($commandsSet);
}
// Unset reference to keep scope clear
unset($commandsSet);
return $namespacedCommands;
}
}

View File

@ -0,0 +1,262 @@
<?php
/**
* Part of the Joomla Framework Console Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Console\Descriptor;
use Joomla\Console\Application;
use Joomla\Console\Command\AbstractCommand;
use Joomla\String\StringHelper;
use Symfony\Component\Console\Descriptor\TextDescriptor as SymfonyTextDescriptor;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Text object descriptor.
*
* @since 2.0.0
*/
final class TextDescriptor extends SymfonyTextDescriptor
{
/**
* Describes an object.
*
* @param OutputInterface $output The output object to use.
* @param object $object The object to describe.
* @param array $options Descriptor options.
*
* @return void
*
* @since 2.0.0
*/
public function describe(OutputInterface $output, object $object, array $options = []): void
{
$this->output = $output;
switch (true) {
case $object instanceof Application:
$this->describeJoomlaApplication($object, $options);
break;
case $object instanceof AbstractCommand:
$this->describeConsoleCommand($object, $options);
break;
default:
parent::describe($output, $object, $options);
}
}
/**
* Formats command aliases to show them in the command description.
*
* @param AbstractCommand $command The command to process
*
* @return string
*
* @since 2.0.0
*/
private function getCommandAliasesText(AbstractCommand $command): string
{
$text = '';
$aliases = $command->getAliases();
if ($aliases) {
$text = '[' . implode('|', $aliases) . '] ';
}
return $text;
}
/**
* Describes a command.
*
* @param AbstractCommand $command The command being described.
* @param array $options Descriptor options.
*
* @return void
*
* @since 2.0.0
*/
private function describeConsoleCommand(AbstractCommand $command, array $options): void
{
$command->getSynopsis(true);
$command->getSynopsis(false);
$command->mergeApplicationDefinition(false);
$this->writeText('<comment>Usage:</comment>', $options);
foreach (array_merge([$command->getSynopsis(true)], $command->getAliases()) as $usage) {
$this->writeText("\n");
$this->writeText(' ' . $usage, $options);
}
$this->writeText("\n");
$definition = $command->getDefinition();
if ($definition->getOptions() || $definition->getArguments()) {
$this->writeText("\n");
$this->describeInputDefinition($definition, $options);
$this->writeText("\n");
}
if ($help = $command->getProcessedHelp()) {
$this->writeText("\n");
$this->writeText('<comment>Help:</comment>', $options);
$this->writeText("\n");
$this->writeText(' ' . str_replace("\n", "\n ", $help), $options);
$this->writeText("\n");
}
}
/**
* Describes an application.
*
* @param Application $app The application being described.
* @param array $options Descriptor options.
*
* @return void
*
* @since 2.0.0
*/
private function describeJoomlaApplication(Application $app, array $options): void
{
$describedNamespace = $options['namespace'] ?? '';
$description = new ApplicationDescription($app, $describedNamespace);
$version = $app->getLongVersion();
if ($version !== '') {
$this->writeText("$version\n\n", $options);
}
$this->writeText("<comment>Usage:</comment>\n");
$this->writeText(" command [options] [arguments]\n\n");
$this->describeInputDefinition(new InputDefinition($app->getDefinition()->getOptions()), $options);
$this->writeText("\n");
$this->writeText("\n");
$commands = $description->getCommands();
$namespaces = $description->getNamespaces();
if ($describedNamespace && $namespaces) {
// Ensure all aliased commands are included when describing a specific namespace
$describedNamespaceInfo = reset($namespaces);
foreach ($describedNamespaceInfo['commands'] as $name) {
$commands[$name] = $description->getCommand($name);
}
}
$width = $this->getColumnWidth(
\call_user_func_array(
'array_merge',
array_map(
function ($namespace) use ($commands) {
return array_intersect($namespace['commands'], array_keys($commands));
},
array_values($namespaces)
)
)
);
if ($describedNamespace) {
$this->writeText(sprintf('<comment>Available commands for the "%s" namespace:</comment>', $describedNamespace), $options);
} else {
$this->writeText('<comment>Available commands:</comment>', $options);
}
foreach ($namespaces as $namespace) {
$namespace['commands'] = array_filter(
$namespace['commands'],
function ($name) use ($commands) {
return isset($commands[$name]);
}
);
if (!$namespace['commands']) {
continue;
}
if (!$describedNamespace && $namespace['id'] !== ApplicationDescription::GLOBAL_NAMESPACE) {
$this->writeText("\n");
$this->writeText(' <comment>' . $namespace['id'] . '</comment>', $options);
}
foreach ($namespace['commands'] as $name) {
$this->writeText("\n");
$spacingWidth = $width - StringHelper::strlen($name);
$command = $commands[$name];
$commandAliases = $name === $command->getName() ? $this->getCommandAliasesText($command) : '';
$this->writeText(
sprintf(
' <info>%s</info>%s%s',
$name,
str_repeat(' ', $spacingWidth),
$commandAliases . $command->getDescription()
),
$options
);
}
}
$this->writeText("\n");
}
/**
* Calculate the column width for a group of commands.
*
* @param AbstractCommand[]|string[] $commands The commands to use for processing a width.
*
* @return integer
*
* @since 2.0.0
*/
private function getColumnWidth(array $commands): int
{
$widths = [];
foreach ($commands as $command) {
if ($command instanceof AbstractCommand) {
$widths[] = StringHelper::strlen($command->getName());
foreach ($command->getAliases() as $alias) {
$widths[] = StringHelper::strlen($alias);
}
} else {
$widths[] = StringHelper::strlen($command);
}
}
return $widths ? max($widths) + 2 : 0;
}
/**
* Writes text to the output.
*
* @param string $content The message.
* @param array $options The options to use for formatting the output.
*
* @return void
*
* @since 2.0.0
*/
private function writeText($content, array $options = []): void
{
$this->write(
isset($options['raw_text']) && $options['raw_text'] ? strip_tags($content) : $content,
isset($options['raw_output']) ? !$options['raw_output'] : true
);
}
}

View File

@ -0,0 +1,110 @@
<?php
/**
* Part of the Joomla Framework Console Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Console\Event;
use Joomla\Console\Application;
use Joomla\Console\Command\AbstractCommand;
use Joomla\Console\ConsoleEvents;
/**
* Event triggered when an uncaught Throwable is received by the application.
*
* @since 2.0.0
*/
class ApplicationErrorEvent extends ConsoleEvent
{
/**
* The Throwable object with the error data.
*
* @var \Throwable
* @since 2.0.0
*/
private $error;
/**
* The exit code to use for the application.
*
* @var integer|null
* @since 2.0.0
*/
private $exitCode;
/**
* Event constructor.
*
* @param \Throwable $error The Throwable object with the error data.
* @param Application $application The active application.
* @param AbstractCommand|null $command The command being executed.
*
* @since 2.0.0
*/
public function __construct(\Throwable $error, Application $application, ?AbstractCommand $command = null)
{
parent::__construct(ConsoleEvents::APPLICATION_ERROR, $application, $command);
$this->error = $error;
}
/**
* Get the error object.
*
* @return \Throwable
*
* @since 2.0.0
*/
public function getError(): \Throwable
{
return $this->error;
}
/**
* Gets the exit code.
*
* @return integer
*
* @since 2.0.0
*/
public function getExitCode(): int
{
return $this->exitCode ?: (\is_int($this->error->getCode()) && $this->error->getCode() !== 0 ? $this->error->getCode() : 1);
}
/**
* Set the error object.
*
* @param \Throwable $error The error object to set to the event.
*
* @return void
*
* @since 2.0.0
*/
public function setError(\Throwable $error): void
{
$this->error = $error;
}
/**
* Sets the exit code.
*
* @param integer $exitCode The command exit code.
*
* @return void
*
* @since 2.0.0
*/
public function setExitCode(int $exitCode): void
{
$this->exitCode = $exitCode;
$r = new \ReflectionProperty($this->error, 'code');
$r->setAccessible(true);
$r->setValue($this->error, $this->exitCode);
}
}

View File

@ -0,0 +1,91 @@
<?php
/**
* Part of the Joomla Framework Console Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Console\Event;
use Joomla\Console\Application;
use Joomla\Console\Command\AbstractCommand;
use Joomla\Console\ConsoleEvents;
/**
* Event triggered before a command is executed.
*
* @since 2.0.0
*/
class BeforeCommandExecuteEvent extends ConsoleEvent
{
/**
* The return code for a command disabled by this event.
*
* @var integer
* @since 2.0.0
*/
public const RETURN_CODE_DISABLED = 113;
/**
* Flag indicating the command is enabled
*
* @var boolean
* @since 2.0.0
*/
private $commandEnabled = true;
/**
* Event constructor.
*
* @param Application $application The active application.
* @param AbstractCommand|null $command The command being executed.
*
* @since 2.0.0
*/
public function __construct(Application $application, ?AbstractCommand $command = null)
{
parent::__construct(ConsoleEvents::BEFORE_COMMAND_EXECUTE, $application, $command);
if ($command) {
$this->commandEnabled = $command->isEnabled();
}
}
/**
* Disable the command.
*
* @return void
*
* @since 2.0.0
*/
public function disableCommand(): void
{
$this->commandEnabled = false;
}
/**
* Enable the command.
*
* @return void
*
* @since 2.0.0
*/
public function enableCommand(): void
{
$this->commandEnabled = false;
}
/**
* Check if the command is enabled.
*
* @return boolean
*
* @since 2.0.0
*/
public function isCommandEnabled(): bool
{
return $this->commandEnabled;
}
}

View File

@ -0,0 +1,114 @@
<?php
/**
* Part of the Joomla Framework Console Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Console\Event;
use Joomla\Console\Application;
use Joomla\Console\Command\AbstractCommand;
use Joomla\Console\ConsoleEvents;
/**
* Event triggered when an uncaught Throwable is received by the application from a command.
*
* @since 2.0.0
*/
class CommandErrorEvent extends ConsoleEvent
{
/**
* The Throwable object with the error data.
*
* @var \Throwable
* @since 2.0.0
*/
private $error;
/**
* The exit code to use for the application.
*
* @var integer|null
* @since 2.0.0
*/
private $exitCode;
/**
* Event constructor.
*
* @param \Throwable $error The Throwable object with the error data.
* @param Application $application The active application.
* @param AbstractCommand|null $command The command being executed.
*
* @since 2.0.0
*/
public function __construct(\Throwable $error, Application $application, ?AbstractCommand $command = null)
{
parent::__construct(ConsoleEvents::COMMAND_ERROR, $application, $command);
$this->error = $error;
}
/**
* Get the error object.
*
* @return \Throwable
*
* @since 2.0.0
*/
public function getError(): \Throwable
{
return $this->error;
}
/**
* Gets the exit code.
*
* @return integer
*
* @since 2.0.0
*/
public function getExitCode(): int
{
if ($this->exitCode !== null) {
return $this->exitCode;
}
return \is_int($this->error->getCode()) && $this->error->getCode() !== 0 ? $this->error->getCode() : 1;
}
/**
* Set the error object.
*
* @param \Throwable $error The error object to set to the event.
*
* @return void
*
* @since 2.0.0
*/
public function setError(\Throwable $error): void
{
$this->error = $error;
}
/**
* Sets the exit code.
*
* @param integer $exitCode The command exit code.
*
* @return void
*
* @since 2.0.0
*/
public function setExitCode(int $exitCode): void
{
$this->exitCode = $exitCode;
$r = new \ReflectionProperty($this->error, 'code');
$r->setAccessible(true);
$r->setValue($this->error, $this->exitCode);
}
}

View File

@ -0,0 +1,79 @@
<?php
/**
* Part of the Joomla Framework Console Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Console\Event;
use Joomla\Console\Application;
use Joomla\Console\Command\AbstractCommand;
use Joomla\Event\Event;
/**
* Base event class for console events.
*
* @since 2.0.0
*/
class ConsoleEvent extends Event
{
/**
* The active application.
*
* @var Application
* @since 2.0.0
*/
private $application;
/**
* The command being executed.
*
* @var AbstractCommand|null
* @since 2.0.0
*/
private $command;
/**
* Event constructor.
*
* @param string $name The event name.
* @param Application $application The active application.
* @param AbstractCommand|null $command The command being executed.
*
* @since 2.0.0
*/
public function __construct(string $name, Application $application, ?AbstractCommand $command = null)
{
parent::__construct($name);
$this->application = $application;
$this->command = $command;
}
/**
* Get the active application.
*
* @return Application
*
* @since 2.0.0
*/
public function getApplication(): Application
{
return $this->application;
}
/**
* Get the command being executed.
*
* @return AbstractCommand|null
*
* @since 2.0.0
*/
public function getCommand(): ?AbstractCommand
{
return $this->command;
}
}

View File

@ -0,0 +1,72 @@
<?php
/**
* Part of the Joomla Framework Console Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Console\Event;
use Joomla\Console\Application;
use Joomla\Console\Command\AbstractCommand;
use Joomla\Console\ConsoleEvents;
/**
* Event triggered immediately before the process is terminated.
*
* @since 2.0.0
*/
class TerminateEvent extends ConsoleEvent
{
/**
* The exit code to use for the application.
*
* @var integer
* @since 2.0.0
*/
private $exitCode;
/**
* Event constructor.
*
* @param integer $exitCode The Throwable object with the error data.
* @param Application $application The active application.
* @param AbstractCommand|null $command The command being executed.
*
* @since 2.0.0
*/
public function __construct(int $exitCode, Application $application, ?AbstractCommand $command = null)
{
parent::__construct(ConsoleEvents::TERMINATE, $application, $command);
$this->exitCode = $exitCode;
}
/**
* Gets the exit code.
*
* @return integer
*
* @since 2.0.0
*/
public function getExitCode(): int
{
return $this->exitCode;
}
/**
* Sets the exit code.
*
* @param integer $exitCode The command exit code.
*
* @return void
*
* @since 2.0.0
*/
public function setExitCode(int $exitCode): void
{
$this->exitCode = $exitCode;
}
}

View File

@ -0,0 +1,21 @@
<?php
/**
* Part of the Joomla Framework Console Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Console\Exception;
use Symfony\Component\Console\Exception\CommandNotFoundException;
/**
* Exception indicating a missing command namespace.
*
* @since 2.0.0
*/
class NamespaceNotFoundException extends CommandNotFoundException
{
}

View File

@ -0,0 +1,50 @@
<?php
/**
* Part of the Joomla Framework Console Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Console\Helper;
use Joomla\Console\Descriptor\TextDescriptor;
use Symfony\Component\Console\Helper\Helper;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Describes an object.
*
* @since 2.0.0
*/
class DescriptorHelper extends Helper
{
/**
* Describes an object if supported.
*
* @param OutputInterface $output The output object to use.
* @param object $object The object to describe.
* @param array $options Options for the descriptor.
*
* @return void
*
* @since 2.0.0
*/
public function describe(OutputInterface $output, $object, array $options = [])
{
(new TextDescriptor())->describe($output, $object, $options);
}
/**
* Returns the canonical name of this helper.
*
* @return string The canonical name
*
* @since 2.0.0
*/
public function getName()
{
return 'descriptor';
}
}

View File

@ -0,0 +1,97 @@
<?php
/**
* Part of the Joomla Framework Console Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Console\Loader;
use Joomla\Console\Command\AbstractCommand;
use Psr\Container\ContainerInterface;
use Symfony\Component\Console\Exception\CommandNotFoundException;
/**
* PSR-11 compatible command loader.
*
* @since 2.0.0
*/
final class ContainerLoader implements LoaderInterface
{
/**
* The service container.
*
* @var ContainerInterface
* @since 2.0.0
*/
private $container;
/**
* The command name to service ID map.
*
* @var string[]
* @since 2.0.0
*/
private $commandMap;
/**
* Constructor.
*
* @param ContainerInterface $container A container from which to load command services.
* @param array $commandMap An array with command names as keys and service IDs as values.
*
* @since 2.0.0
*/
public function __construct(ContainerInterface $container, array $commandMap)
{
$this->container = $container;
$this->commandMap = $commandMap;
}
/**
* Loads a command.
*
* @param string $name The command to load.
*
* @return AbstractCommand
*
* @since 2.0.0
* @throws CommandNotFoundException
*/
public function get(string $name): AbstractCommand
{
if (!$this->has($name)) {
throw new CommandNotFoundException(sprintf('Command "%s" does not exist.', $name));
}
return $this->container->get($this->commandMap[$name]);
}
/**
* Get the names of the registered commands.
*
* @return string[]
*
* @since 2.0.0
*/
public function getNames(): array
{
return array_keys($this->commandMap);
}
/**
* Checks if a command exists.
*
* @param string $name The command to check.
*
* @return boolean
*
* @since 2.0.0
*/
public function has($name): bool
{
return isset($this->commandMap[$name]) && $this->container->has($this->commandMap[$name]);
}
}

View File

@ -0,0 +1,53 @@
<?php
/**
* Part of the Joomla Framework Console Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Console\Loader;
use Joomla\Console\Command\AbstractCommand;
use Symfony\Component\Console\Exception\CommandNotFoundException;
/**
* Interface defining a command loader.
*
* @since 2.0.0
*/
interface LoaderInterface
{
/**
* Loads a command.
*
* @param string $name The command to load.
*
* @return AbstractCommand
*
* @since 2.0.0
* @throws CommandNotFoundException
*/
public function get(string $name): AbstractCommand;
/**
* Get the names of the registered commands.
*
* @return string[]
*
* @since 2.0.0
*/
public function getNames(): array;
/**
* Checks if a command exists.
*
* @param string $name The command to check.
*
* @return boolean
*
* @since 2.0.0
*/
public function has($name): bool;
}

340
libraries/vendor/joomla/crypt/LICENSE vendored Normal file
View File

@ -0,0 +1,340 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Library 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Library General
Public License instead of this License.

View File

@ -0,0 +1,127 @@
<?php
/**
* Part of the Joomla Framework Crypt Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Crypt\Cipher;
use Defuse\Crypto\Crypto as DefuseCrypto;
use Defuse\Crypto\Exception\EnvironmentIsBrokenException;
use Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException;
use Defuse\Crypto\Key as DefuseKey;
use Defuse\Crypto\RuntimeTests;
use Joomla\Crypt\CipherInterface;
use Joomla\Crypt\Exception\DecryptionException;
use Joomla\Crypt\Exception\EncryptionException;
use Joomla\Crypt\Exception\InvalidKeyException;
use Joomla\Crypt\Exception\InvalidKeyTypeException;
use Joomla\Crypt\Key;
/**
* Joomla cipher for encryption, decryption and key generation via the php-encryption library.
*
* @since 2.0.0
*/
class Crypto implements CipherInterface
{
/**
* Method to decrypt a data string.
*
* @param string $data The encrypted string to decrypt.
* @param Key $key The key object to use for decryption.
*
* @return string The decrypted data string.
*
* @since 2.0.0
* @throws DecryptionException if the data cannot be decrypted
* @throws InvalidKeyTypeException if the key is not valid for the cipher
*/
public function decrypt($data, Key $key)
{
// Validate key.
if ($key->getType() !== 'crypto') {
throw new InvalidKeyTypeException('crypto', $key->getType());
}
// Decrypt the data.
try {
return DefuseCrypto::decrypt($data, DefuseKey::loadFromAsciiSafeString($key->getPrivate()));
} catch (WrongKeyOrModifiedCiphertextException $ex) {
throw new DecryptionException('DANGER! DANGER! The ciphertext has been tampered with!', $ex->getCode(), $ex);
} catch (EnvironmentIsBrokenException $ex) {
throw new DecryptionException('Cannot safely perform decryption', $ex->getCode(), $ex);
}
}
/**
* Method to encrypt a data string.
*
* @param string $data The data string to encrypt.
* @param Key $key The key object to use for encryption.
*
* @return string The encrypted data string.
*
* @since 2.0.0
* @throws EncryptionException if the data cannot be encrypted
* @throws InvalidKeyTypeException if the key is not valid for the cipher
*/
public function encrypt($data, Key $key)
{
// Validate key.
if ($key->getType() !== 'crypto') {
throw new InvalidKeyTypeException('crypto', $key->getType());
}
// Encrypt the data.
try {
return DefuseCrypto::encrypt($data, DefuseKey::loadFromAsciiSafeString($key->getPrivate()));
} catch (EnvironmentIsBrokenException $ex) {
throw new EncryptionException('Cannot safely perform encryption', $ex->getCode(), $ex);
}
}
/**
* Method to generate a new encryption key object.
*
* @param array $options Key generation options.
*
* @return Key
*
* @since 2.0.0
* @throws InvalidKeyException if the key cannot be generated
*/
public function generateKey(array $options = [])
{
// Generate the encryption key.
try {
$public = DefuseKey::createNewRandomKey();
} catch (EnvironmentIsBrokenException $ex) {
throw new InvalidKeyException('Cannot safely create a key', $ex->getCode(), $ex);
}
// Create the new encryption key object.
return new Key('crypto', $public->saveToAsciiSafeString(), $public->getRawBytes());
}
/**
* Check if the cipher is supported in this environment.
*
* @return boolean
*
* @since 2.0.0
*/
public static function isSupported(): bool
{
try {
RuntimeTests::runtimeTest();
return true;
} catch (EnvironmentIsBrokenException $e) {
return false;
}
}
}

View File

@ -0,0 +1,144 @@
<?php
/**
* Part of the Joomla Framework Crypt Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Crypt\Cipher;
use Joomla\Crypt\CipherInterface;
use Joomla\Crypt\Exception\DecryptionException;
use Joomla\Crypt\Exception\EncryptionException;
use Joomla\Crypt\Exception\InvalidKeyException;
use Joomla\Crypt\Exception\InvalidKeyTypeException;
use Joomla\Crypt\Key;
/**
* Joomla cipher for encryption, decryption and key generation via the openssl extension.
*
* @since 2.0.0
*/
class OpenSSL implements CipherInterface
{
/**
* Initialisation vector for key generator method.
*
* @var string
* @since 2.0.0
*/
private $iv;
/**
* Method to use for encryption.
*
* @var string
* @since 2.0.0
*/
private $method;
/**
* Instantiate the cipher.
*
* @param string $iv The initialisation vector to use
* @param string $method The encryption method to use
*
* @since 2.0.0
*/
public function __construct(string $iv, string $method)
{
$this->iv = $iv;
$this->method = $method;
}
/**
* Method to decrypt a data string.
*
* @param string $data The encrypted string to decrypt.
* @param Key $key The key object to use for decryption.
*
* @return string The decrypted data string.
*
* @since 2.0.0
* @throws DecryptionException if the data cannot be decrypted
* @throws InvalidKeyTypeException if the key is not valid for the cipher
*/
public function decrypt($data, Key $key)
{
// Validate key.
if ($key->getType() !== 'openssl') {
throw new InvalidKeyTypeException('openssl', $key->getType());
}
$cleartext = openssl_decrypt($data, $this->method, $key->getPrivate(), true, $this->iv);
if ($cleartext === false) {
throw new DecryptionException('Failed to decrypt data');
}
return $cleartext;
}
/**
* Method to encrypt a data string.
*
* @param string $data The data string to encrypt.
* @param Key $key The key object to use for encryption.
*
* @return string The encrypted data string.
*
* @since 2.0.0
* @throws EncryptionException if the data cannot be encrypted
* @throws InvalidKeyTypeException if the key is not valid for the cipher
*/
public function encrypt($data, Key $key)
{
// Validate key.
if ($key->getType() !== 'openssl') {
throw new InvalidKeyTypeException('openssl', $key->getType());
}
$encrypted = openssl_encrypt($data, $this->method, $key->getPrivate(), true, $this->iv);
if ($encrypted === false) {
throw new EncryptionException('Unable to encrypt data');
}
return $encrypted;
}
/**
* Method to generate a new encryption key object.
*
* @param array $options Key generation options.
*
* @return Key
*
* @since 2.0.0
* @throws InvalidKeyException if the key cannot be generated
*/
public function generateKey(array $options = [])
{
$passphrase = $options['passphrase'] ?? false;
if ($passphrase === false) {
throw new InvalidKeyException('Missing passphrase file');
}
return new Key('openssl', $passphrase, 'unused');
}
/**
* Check if the cipher is supported in this environment.
*
* @return boolean
*
* @since 2.0.0
*/
public static function isSupported(): bool
{
return \extension_loaded('openssl');
}
}

View File

@ -0,0 +1,209 @@
<?php
/**
* Part of the Joomla Framework Crypt Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Crypt\Cipher;
use Joomla\Crypt\CipherInterface;
use Joomla\Crypt\Exception\DecryptionException;
use Joomla\Crypt\Exception\EncryptionException;
use Joomla\Crypt\Exception\InvalidKeyException;
use Joomla\Crypt\Exception\InvalidKeyTypeException;
use Joomla\Crypt\Exception\UnsupportedCipherException;
use Joomla\Crypt\Key;
use ParagonIE\Sodium\Compat;
/**
* Cipher for sodium algorithm encryption, decryption and key generation.
*
* @since 1.4.0
*/
class Sodium implements CipherInterface
{
/**
* The message nonce to be used with encryption/decryption
*
* @var string
* @since 1.4.0
*/
private $nonce;
/**
* Method to decrypt a data string.
*
* @param string $data The encrypted string to decrypt.
* @param Key $key The key object to use for decryption.
*
* @return string The decrypted data string.
*
* @since 1.4.0
* @throws DecryptionException if the data cannot be decrypted
* @throws InvalidKeyTypeException if the key is not valid for the cipher
*/
public function decrypt($data, Key $key)
{
// Validate key.
if ($key->getType() !== 'sodium') {
throw new InvalidKeyTypeException('sodium', $key->getType());
}
if (!$this->nonce) {
throw new DecryptionException('Missing nonce to decrypt data');
}
// Use the sodium extension (PHP 7.2 native, PECL 2.x, or paragonie/sodium_compat) if able
if (\function_exists('sodium_crypto_box_open')) {
try {
$decrypted = sodium_crypto_box_open(
$data,
$this->nonce,
sodium_crypto_box_keypair_from_secretkey_and_publickey($key->getPrivate(), $key->getPublic())
);
if ($decrypted === false) {
throw new DecryptionException('Malformed message or invalid MAC');
}
} catch (\SodiumException $exception) {
throw new DecryptionException('Malformed message or invalid MAC', $exception->getCode(), $exception);
}
return $decrypted;
}
// Use the libsodium extension (PECL 1.x) if able; purposefully skipping sodium_compat fallback here as that will match the above check
if (\extension_loaded('libsodium')) {
$decrypted = \Sodium\crypto_box_open(
$data,
$this->nonce,
\Sodium\crypto_box_keypair_from_secretkey_and_publickey($key->getPrivate(), $key->getPublic())
);
if ($decrypted === false) {
throw new DecryptionException('Malformed message or invalid MAC');
}
return $decrypted;
}
// Well this is awkward
throw new UnsupportedCipherException(static::class);
}
/**
* Method to encrypt a data string.
*
* @param string $data The data string to encrypt.
* @param Key $key The key object to use for encryption.
*
* @return string The encrypted data string.
*
* @since 1.4.0
* @throws EncryptionException if the data cannot be encrypted
* @throws InvalidKeyTypeException if the key is not valid for the cipher
*/
public function encrypt($data, Key $key)
{
// Validate key.
if ($key->getType() !== 'sodium') {
throw new InvalidKeyTypeException('sodium', $key->getType());
}
if (!$this->nonce) {
throw new EncryptionException('Missing nonce to decrypt data');
}
// Use the sodium extension (PHP 7.2 native, PECL 2.x, or paragonie/sodium_compat) if able
if (\function_exists('sodium_crypto_box')) {
try {
return sodium_crypto_box(
$data,
$this->nonce,
sodium_crypto_box_keypair_from_secretkey_and_publickey($key->getPrivate(), $key->getPublic())
);
} catch (\SodiumException $exception) {
throw new EncryptionException('Could not encrypt file.', $exception->getCode(), $exception);
}
}
// Use the libsodium extension (PECL 1.x) if able; purposefully skipping sodium_compat fallback here as that will match the above check
if (\extension_loaded('libsodium')) {
return \Sodium\crypto_box(
$data,
$this->nonce,
\Sodium\crypto_box_keypair_from_secretkey_and_publickey($key->getPrivate(), $key->getPublic())
);
}
// Well this is awkward
throw new UnsupportedCipherException(static::class);
}
/**
* Method to generate a new encryption key object.
*
* @param array $options Key generation options.
*
* @return Key
*
* @since 1.4.0
* @throws InvalidKeyException if the key cannot be generated
* @throws UnsupportedCipherException if the cipher is not supported on the current environment
*/
public function generateKey(array $options = [])
{
// Use the sodium extension (PHP 7.2 native, PECL 2.x, or paragonie/sodium_compat) if able
if (\function_exists('sodium_crypto_box_keypair')) {
try {
// Generate the encryption key.
$pair = sodium_crypto_box_keypair();
return new Key('sodium', sodium_crypto_box_secretkey($pair), sodium_crypto_box_publickey($pair));
} catch (\SodiumException $exception) {
throw new InvalidKeyException('Could not generate encryption key.', $exception->getCode(), $exception);
}
}
// Use the libsodium extension (PECL 1.x) if able; purposefully skipping sodium_compat fallback here as that will match the above check
if (\extension_loaded('libsodium')) {
// Generate the encryption key.
$pair = \Sodium\crypto_box_keypair();
return new Key('sodium', \Sodium\crypto_box_secretkey($pair), \Sodium\crypto_box_publickey($pair));
}
// Well this is awkward
throw new UnsupportedCipherException(static::class);
}
/**
* Check if the cipher is supported in this environment.
*
* @return boolean
*
* @since 2.0.0
*/
public static function isSupported(): bool
{
// Prefer ext/sodium, then ext/libsodium, then presence of paragonie/sodium_compat
return \function_exists('sodium_crypto_box') || \extension_loaded('libsodium') || class_exists(Compat::class);
}
/**
* Set the nonce to use for encrypting/decrypting messages
*
* @param string $nonce The message nonce
*
* @return void
*
* @since 1.4.0
*/
public function setNonce($nonce)
{
$this->nonce = $nonce;
}
}

View File

@ -0,0 +1,76 @@
<?php
/**
* Part of the Joomla Framework Crypt Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Crypt;
use Joomla\Crypt\Exception\DecryptionException;
use Joomla\Crypt\Exception\EncryptionException;
use Joomla\Crypt\Exception\InvalidKeyException;
use Joomla\Crypt\Exception\InvalidKeyTypeException;
use Joomla\Crypt\Exception\UnsupportedCipherException;
/**
* Joomla Framework Cipher interface.
*
* @since 1.0
*/
interface CipherInterface
{
/**
* Method to decrypt a data string.
*
* @param string $data The encrypted string to decrypt.
* @param Key $key The key[/pair] object to use for decryption.
*
* @return string The decrypted data string.
*
* @since 1.0
* @throws DecryptionException if the data cannot be decrypted
* @throws InvalidKeyTypeException if the key is not valid for the cipher
* @throws UnsupportedCipherException if the cipher is not supported on the current environment
*/
public function decrypt($data, Key $key);
/**
* Method to encrypt a data string.
*
* @param string $data The data string to encrypt.
* @param Key $key The key[/pair] object to use for encryption.
*
* @return string The encrypted data string.
*
* @since 1.0
* @throws EncryptionException if the data cannot be encrypted
* @throws InvalidKeyTypeException if the key is not valid for the cipher
* @throws UnsupportedCipherException if the cipher is not supported on the current environment
*/
public function encrypt($data, Key $key);
/**
* Method to generate a new encryption key[/pair] object.
*
* @param array $options Key generation options.
*
* @return Key
*
* @since 1.0
* @throws InvalidKeyException if the key cannot be generated
* @throws UnsupportedCipherException if the cipher is not supported on the current environment
*/
public function generateKey(array $options = []);
/**
* Check if the cipher is supported in this environment.
*
* @return boolean
*
* @since 2.0.0
*/
public static function isSupported(): bool;
}

View File

@ -0,0 +1,139 @@
<?php
/**
* Part of the Joomla Framework Crypt Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Crypt;
use Joomla\Crypt\Cipher\Crypto;
use Joomla\Crypt\Exception\DecryptionException;
use Joomla\Crypt\Exception\EncryptionException;
use Joomla\Crypt\Exception\InvalidKeyException;
use Joomla\Crypt\Exception\InvalidKeyTypeException;
use Joomla\Crypt\Exception\UnsupportedCipherException;
/**
* Crypt is a Joomla Framework class for handling basic encryption/decryption of data.
*
* @since 1.0
*/
class Crypt
{
/**
* The encryption cipher object.
*
* @var CipherInterface
* @since 1.0
*/
private $cipher;
/**
* The encryption key[/pair)].
*
* @var Key
* @since 1.0
*/
private $key;
/**
* Object Constructor takes an optional key to be used for encryption/decryption. If no key is given then the
* secret word from the configuration object is used.
*
* @param ?CipherInterface $cipher The encryption cipher object.
* @param ?Key $key The encryption key[/pair)].
*
* @since 1.0
*/
public function __construct(?CipherInterface $cipher = null, ?Key $key = null)
{
// Set the encryption cipher.
$this->cipher = $cipher ?: new Crypto();
// Set the encryption key[/pair)].
$this->key = $key ?: $this->generateKey();
}
/**
* Method to decrypt a data string.
*
* @param string $data The encrypted string to decrypt.
*
* @return string The decrypted data string.
*
* @since 1.0
* @throws DecryptionException if the data cannot be decrypted
* @throws InvalidKeyTypeException if the key is not valid for the cipher
* @throws UnsupportedCipherException if the cipher is not supported on the current environment
*/
public function decrypt($data)
{
return $this->cipher->decrypt($data, $this->key);
}
/**
* Method to encrypt a data string.
*
* @param string $data The data string to encrypt.
*
* @return string The encrypted data string.
*
* @since 1.0
* @throws EncryptionException if the data cannot be encrypted
* @throws InvalidKeyTypeException if the key is not valid for the cipher
* @throws UnsupportedCipherException if the cipher is not supported on the current environment
*/
public function encrypt($data)
{
return $this->cipher->encrypt($data, $this->key);
}
/**
* Method to generate a new encryption key[/pair] object.
*
* @param array $options Key generation options.
*
* @return Key
*
* @since 1.0
* @throws InvalidKeyException if the key cannot be generated
* @throws UnsupportedCipherException if the cipher is not supported on the current environment
*/
public function generateKey(array $options = [])
{
return $this->cipher->generateKey($options);
}
/**
* Method to set the encryption key[/pair] object.
*
* @param Key $key The key object to set.
*
* @return Crypt Instance of $this to allow chaining.
*
* @since 1.0
*/
public function setKey(Key $key)
{
$this->key = $key;
return $this;
}
/**
* Generate random bytes.
*
* @param integer $length Length of the random data to generate
*
* @return string Random binary data
*
* @since 1.0
*/
public static function genRandomBytes($length = 16)
{
return random_bytes($length);
}
}

View File

@ -0,0 +1,19 @@
<?php
/**
* Part of the Joomla Framework Crypt Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Crypt\Exception;
/**
* Interface defining all crypt package exceptions
*
* @since 2.0.0
*/
interface CryptExceptionInterface extends \Throwable
{
}

View File

@ -0,0 +1,19 @@
<?php
/**
* Part of the Joomla Framework Crypt Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Crypt\Exception;
/**
* Exception representing an error decrypting data
*
* @since 2.0.0
*/
class DecryptionException extends \RuntimeException implements CryptExceptionInterface
{
}

View File

@ -0,0 +1,19 @@
<?php
/**
* Part of the Joomla Framework Crypt Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Crypt\Exception;
/**
* Exception representing an error encrypting data
*
* @since 2.0.0
*/
class EncryptionException extends \RuntimeException implements CryptExceptionInterface
{
}

View File

@ -0,0 +1,19 @@
<?php
/**
* Part of the Joomla Framework Crypt Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Crypt\Exception;
/**
* Exception representing an error generating an encryption key
*
* @since 2.0.0
*/
class InvalidKeyException extends \RuntimeException implements CryptExceptionInterface
{
}

View File

@ -0,0 +1,31 @@
<?php
/**
* Part of the Joomla Framework Crypt Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Crypt\Exception;
/**
* Exception representing an invalid Joomla\Crypt\Key type for a cipher
*
* @since 1.4.0
*/
class InvalidKeyTypeException extends \InvalidArgumentException implements CryptExceptionInterface
{
/**
* InvalidKeyTypeException constructor.
*
* @param string $expectedKeyType The expected key type.
* @param string $actualKeyType The actual key type.
*
* @since 1.4.0
*/
public function __construct($expectedKeyType, $actualKeyType)
{
parent::__construct("Invalid key of type: $actualKeyType. Expected $expectedKeyType.");
}
}

View File

@ -0,0 +1,30 @@
<?php
/**
* Part of the Joomla Framework Crypt Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Crypt\Exception;
/**
* Exception representing an error encrypting data
*
* @since 2.0.0
*/
class UnsupportedCipherException extends \LogicException implements CryptExceptionInterface
{
/**
* UnsupportedCipherException constructor.
*
* @param string $class The class name of the unsupported cipher.
*
* @since 2.0.0
*/
public function __construct(string $class)
{
parent::__construct("The '$class' cipher is not supported in this environment.");
}
}

View File

@ -0,0 +1,97 @@
<?php
/**
* Part of the Joomla Framework Crypt Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Crypt;
/**
* Encryption key object for the Joomla Framework.
*
* @since 1.0
*/
class Key
{
/**
* The private key.
*
* @var string
* @since 1.0
*/
private $private;
/**
* The public key.
*
* @var string
* @since 1.0
*/
private $public;
/**
* The key type.
*
* @var string
* @since 1.0
*/
private $type;
/**
* Constructor.
*
* @param string $type The key type.
* @param string $private The private key.
* @param string $public The public key.
*
* @since 1.0
*/
public function __construct(string $type, string $private, string $public)
{
// Set the key type.
$this->type = $type;
// Set the public/private key strings.
$this->private = $private;
$this->public = $public;
}
/**
* Retrieve the private key
*
* @return string
*
* @since 2.0.0
*/
public function getPrivate(): string
{
return $this->private;
}
/**
* Retrieve the public key
*
* @return string
*
* @since 2.0.0
*/
public function getPublic(): string
{
return $this->public;
}
/**
* Retrieve the key type
*
* @return string
*
* @since 2.0.0
*/
public function getType(): string
{
return $this->type;
}
}

340
libraries/vendor/joomla/data/LICENSE vendored Normal file
View File

@ -0,0 +1,340 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Library 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Library General
Public License instead of this License.

View File

@ -0,0 +1,311 @@
<?php
/**
* Part of the Joomla Framework Data Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Data;
use Joomla\Registry\Registry;
/**
* DataObject is a class that is used to store data but allowing you to access the data by mimicking the way PHP handles class properties.
*
* @since 1.0
*/
class DataObject implements DumpableInterface, \IteratorAggregate, \JsonSerializable, \Countable
{
/**
* The data object properties.
*
* @var array
* @since 1.0
*/
private $properties = [];
/**
* The class constructor.
*
* @param mixed $properties Either an associative array or another object by which to set the initial properties of the new object.
*
* @since 1.0
* @throws \InvalidArgumentException
*/
public function __construct($properties = [])
{
// Check the properties input.
if (!empty($properties)) {
// Bind the properties.
$this->bind($properties);
}
}
/**
* The magic get method is used to get a data property.
*
* This method is a public proxy for the protected getProperty method.
*
* Note: Magic __get does not allow recursive calls. This can be tricky because the error generated by recursing into
* __get is "Undefined property: {CLASS}::{PROPERTY}" which is misleading. This is relevant for this class because
* requesting a non-visible property can trigger a call to a sub-function. If that references the property directly in
* the object, it will cause a recursion into __get.
*
* @param string $property The name of the data property.
*
* @return mixed The value of the data property, or null if the data property does not exist.
*
* @see DataObject::getProperty()
* @since 1.0
*/
public function __get($property)
{
return $this->getProperty($property);
}
/**
* The magic isset method is used to check the state of an object property.
*
* @param string $property The name of the data property.
*
* @return boolean
*
* @since 1.0
*/
public function __isset($property)
{
return isset($this->properties[$property]);
}
/**
* The magic set method is used to set a data property.
*
* This is a public proxy for the protected setProperty method.
*
* @param string $property The name of the data property.
* @param mixed $value The value to give the data property.
*
* @return void
*
* @see DataObject::setProperty()
* @since 1.0
*/
public function __set($property, $value)
{
$this->setProperty($property, $value);
}
/**
* The magic unset method is used to unset a data property.
*
* @param string $property The name of the data property.
*
* @return void
*
* @since 1.0
*/
public function __unset($property)
{
unset($this->properties[$property]);
}
/**
* Binds an array or object to this object.
*
* @param mixed $properties An associative array of properties or an object.
* @param boolean $updateNulls True to bind null values, false to ignore null values.
*
* @return $this
*
* @since 1.0
* @throws \InvalidArgumentException
*/
public function bind($properties, $updateNulls = true)
{
// Check the properties data type.
if (!\is_array($properties) && !\is_object($properties)) {
throw new \InvalidArgumentException(
sprintf('The $properties argument must be an array or object, a %s was given.', \gettype($properties))
);
}
// Check if the object is traversable.
if ($properties instanceof \Traversable) {
// Convert iterator to array.
$properties = iterator_to_array($properties);
} elseif (\is_object($properties)) {
// Convert properties to an array.
$properties = (array) $properties;
}
// Bind the properties.
foreach ($properties as $property => $value) {
// Check if the value is null and should be bound.
if ($value === null && !$updateNulls) {
continue;
}
// Set the property.
$this->setProperty($property, $value);
}
return $this;
}
/**
* Dumps the data properties into an object, recursively if appropriate.
*
* @param integer $depth The maximum depth of recursion (default = 3).
* For example, a depth of 0 will return a stdClass with all the properties in native
* form. A depth of 1 will recurse into the first level of properties only.
* @param ?\SplObjectStorage $dumped An array of already serialized objects that is used to avoid infinite loops.
*
* @return \stdClass
*
* @since 1.0
*/
public function dump($depth = 3, \SplObjectStorage $dumped = null)
{
// Check if we should initialise the recursion tracker.
if ($dumped === null) {
$dumped = new \SplObjectStorage();
}
// Add this object to the dumped stack.
$dumped->attach($this);
// Setup a container.
$dump = new \stdClass();
// Dump all object properties.
foreach (array_keys($this->properties) as $property) {
// Get the property.
$dump->$property = $this->dumpProperty($property, $depth, $dumped);
}
return $dump;
}
/**
* Gets this object represented as an ArrayIterator.
*
* This allows the data properties to be access via a foreach statement.
*
* @return \ArrayIterator
*
* @see IteratorAggregate::getIterator()
* @since 1.0
*/
public function getIterator()
{
return new \ArrayIterator($this->dump(0));
}
/**
* Gets the data properties in a form that can be serialised to JSON format.
*
* @return mixed
*
* @since 1.0
*/
public function jsonSerialize()
{
return $this->dump();
}
/**
* Dumps a data property.
*
* If recursion is set, this method will dump any object implementing DumpableInterface (like DataObject and DataSet); it will
* convert a DateTimeInterface object to a string; and it will convert a Joomla\Registry\Registry to an object.
*
* @param string $property The name of the data property.
* @param integer $depth The current depth of recursion (a value of 0 will ignore recursion).
* @param \SplObjectStorage $dumped An array of already serialized objects that is used to avoid infinite loops.
*
* @return mixed The value of the dumped property.
*
* @since 1.0
*/
protected function dumpProperty($property, $depth, \SplObjectStorage $dumped)
{
$value = $this->getProperty($property);
if ($depth > 0) {
// Check if the object is also a dumpable object.
if ($value instanceof DumpableInterface) {
// Do not dump the property if it has already been dumped.
if (!$dumped->contains($value)) {
$value = $value->dump($depth - 1, $dumped);
}
}
// Check if the object is a date.
if ($value instanceof \DateTimeInterface) {
$value = $value->format('Y-m-d H:i:s');
} elseif ($value instanceof Registry) {
$value = $value->toObject();
}
}
return $value;
}
/**
* Gets a data property.
*
* @param string $property The name of the data property.
*
* @return mixed The value of the data property.
*
* @see DataObject::__get()
* @since 1.0
*/
protected function getProperty($property)
{
return $this->properties[$property] ?? null;
}
/**
* Sets a data property.
*
* If the name of the property starts with a null byte, this method will return null.
*
* @param string $property The name of the data property.
* @param mixed $value The value to give the data property.
*
* @return mixed The value of the data property.
*
* @see DataObject::__set()
* @since 1.0
*/
protected function setProperty($property, $value)
{
/*
* Check if the property starts with a null byte. If so, discard it because a later attempt to try to access it
* can cause a fatal error. See http://www.php.net/manual/en/language.types.array.php#language.types.array.casting
*/
if (strpos($property, "\0") === 0) {
return;
}
// Set the value.
$this->properties[$property] = $value;
return $value;
}
/**
* Count the number of data properties.
*
* @return integer The number of data properties.
*
* @since 1.0
*/
public function count()
{
return \count($this->properties);
}
}

View File

@ -0,0 +1,573 @@
<?php
/**
* Part of the Joomla Framework Data Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Data;
/**
* DataSet is a collection class that allows the developer to operate on a set of DataObjects as if they were in a typical PHP array.
*
* @since 1.0
*/
class DataSet implements DumpableInterface, \ArrayAccess, \Countable, \Iterator
{
/**
* The current position of the iterator.
*
* @var integer
* @since 1.0
*/
private $current = false;
/**
* The iterator objects.
*
* @var DataObject[]
* @since 1.0
*/
private $objects = [];
/**
* The class constructor.
*
* @param DataObject[] $objects An array of DataObject objects to bind to the data set.
*
* @since 1.0
* @throws \InvalidArgumentException if an object is not a DataObject.
*/
public function __construct(array $objects = [])
{
// Set the objects.
$this->initialise($objects);
}
/**
* The magic call method is used to call object methods using the iterator.
*
* Example: $array = $objectList->foo('bar');
*
* The object list will iterate over its objects and see if each object has a callable 'foo' method.
* If so, it will pass the argument list and assemble any return values. If an object does not have
* a callable method no return value is recorded.
* The keys of the objects and the result array are maintained.
*
* @param string $method The name of the method called.
* @param array $arguments The arguments of the method called.
*
* @return array An array of values returned by the methods called on the objects in the data set.
*
* @since 1.0
*/
public function __call($method, $arguments = [])
{
$return = [];
// Iterate through the objects.
foreach ($this->objects as $key => $object) {
// Create the object callback.
$callback = [$object, $method];
// Check if the callback is callable.
if (\is_callable($callback)) {
// Call the method for the object.
$return[$key] = \call_user_func_array($callback, $arguments);
}
}
return $return;
}
/**
* The magic get method is used to get a list of properties from the objects in the data set.
*
* Example: $array = $dataSet->foo;
*
* This will return a column of the values of the 'foo' property in all the objects
* (or values determined by custom property setters in the individual DataObject's).
* The result array will contain an entry for each object in the list (compared to __call which may not).
* The keys of the objects and the result array are maintained.
*
* @param string $property The name of the data property.
*
* @return array An associative array of the values.
*
* @since 1.0
*/
public function __get($property)
{
$return = [];
// Iterate through the objects.
foreach ($this->objects as $key => $object) {
// Get the property.
$return[$key] = $object->$property;
}
return $return;
}
/**
* The magic isset method is used to check the state of an object property using the iterator.
*
* Example: $array = isset($objectList->foo);
*
* @param string $property The name of the property.
*
* @return boolean True if the property is set in any of the objects in the data set.
*
* @since 1.0
*/
public function __isset($property)
{
$return = [];
// Iterate through the objects.
foreach ($this->objects as $object) {
// Check the property.
$return[] = isset($object->$property);
}
return \in_array(true, $return, true);
}
/**
* The magic set method is used to set an object property using the iterator.
*
* Example: $objectList->foo = 'bar';
*
* This will set the 'foo' property to 'bar' in all of the objects
* (or a value determined by custom property setters in the DataObject).
*
* @param string $property The name of the property.
* @param mixed $value The value to give the data property.
*
* @return void
*
* @since 1.0
*/
public function __set($property, $value)
{
// Iterate through the objects.
foreach ($this->objects as $object) {
// Set the property.
$object->$property = $value;
}
}
/**
* The magic unset method is used to unset an object property using the iterator.
*
* Example: unset($objectList->foo);
*
* This will unset all of the 'foo' properties in the list of DataObject's.
*
* @param string $property The name of the property.
*
* @return void
*
* @since 1.0
*/
public function __unset($property)
{
// Iterate through the objects.
foreach ($this->objects as $object) {
unset($object->$property);
}
}
/**
* Gets an array of keys, existing in objects
*
* @param string $type Selection type 'all' or 'common'
*
* @return array Array of keys
*
* @since 1.2.0
* @throws \InvalidArgumentException
*/
public function getObjectsKeys($type = 'all')
{
$keys = null;
if ($type == 'all') {
$function = 'array_merge';
} elseif ($type == 'common') {
$function = 'array_intersect_key';
} else {
throw new \InvalidArgumentException("Unknown selection type: $type");
}
foreach ($this->objects as $object) {
$objectVars = json_decode(json_encode($object), true);
$keys = ($keys === null) ? $objectVars : $function($keys, $objectVars);
}
return array_keys($keys);
}
/**
* Gets all objects as an array
*
* @param boolean $associative Option to set return mode: associative or numeric array.
* @param string ...$keys Unlimited optional property names to extract from objects.
*
* @return array Returns an array according to defined options.
*
* @since 1.2.0
*/
public function toArray($associative = true, ...$keys)
{
if (empty($keys)) {
$keys = $this->getObjectsKeys();
}
$return = [];
$i = 0;
foreach ($this->objects as $key => $object) {
$arrayItem = [];
$key = ($associative) ? $key : $i++;
$j = 0;
foreach ($keys as $property) {
$propertyKey = ($associative) ? $property : $j++;
$arrayItem[$propertyKey] = $object->$property ?? null;
}
$return[$key] = $arrayItem;
}
return $return;
}
/**
* Gets the number of data objects in the set.
*
* @return integer The number of objects.
*
* @since 1.0
*/
public function count()
{
return \count($this->objects);
}
/**
* Clears the objects in the data set.
*
* @return DataSet Returns itself to allow chaining.
*
* @since 1.0
*/
public function clear()
{
$this->objects = [];
$this->rewind();
return $this;
}
/**
* Get the current data object in the set.
*
* @return DataObject|false The current object, or false if the array is empty or the pointer is beyond the end of the elements.
*
* @since 1.0
*/
public function current()
{
return is_scalar($this->current) ? $this->objects[$this->current] : false;
}
/**
* Dumps the data object in the set, recursively if appropriate.
*
* @param integer $depth The maximum depth of recursion (default = 3).
* For example, a depth of 0 will return a stdClass with all the properties in native
* form. A depth of 1 will recurse into the first level of properties only.
* @param ?\SplObjectStorage $dumped An array of already serialized objects that is used to avoid infinite loops.
*
* @return array An associative array of the data objects in the set, dumped as a simple PHP stdClass object.
*
* @see DataObject::dump()
* @since 1.0
*/
public function dump($depth = 3, \SplObjectStorage $dumped = null)
{
// Check if we should initialise the recursion tracker.
if ($dumped === null) {
$dumped = new \SplObjectStorage();
}
// Add this object to the dumped stack.
$dumped->attach($this);
$objects = [];
// Make sure that we have not reached our maximum depth.
if ($depth > 0) {
// Handle JSON serialization recursively.
foreach ($this->objects as $key => $object) {
$objects[$key] = $object->dump($depth, $dumped);
}
}
return $objects;
}
/**
* Gets the data set in a form that can be serialised to JSON format.
*
* Note that this method will not return an associative array, otherwise it would be encoded into an object.
* JSON decoders do not consistently maintain the order of associative keys, whereas they do maintain the order of arrays.
*
* @return array
*
* @since 1.0
*/
public function jsonSerialize()
{
$return = [];
// Iterate through the objects.
foreach ($this->objects as $object) {
// Call the method for the object.
$return[] = $object;
}
return $return;
}
/**
* Gets the key of the current object in the iterator.
*
* @return integer|false The object key on success; false on failure.
*
* @since 1.0
*/
public function key()
{
return $this->current;
}
/**
* Gets the array of keys for all the objects in the iterator (emulates array_keys).
*
* @return array The array of keys
*
* @since 1.0
*/
public function keys()
{
return array_keys($this->objects);
}
/**
* Applies a function to every object in the set (emulates array_walk).
*
* @param callable $funcname Callback function.
*
* @return boolean
*
* @since 1.2.0
* @throws \InvalidArgumentException
*/
public function walk(callable $funcname)
{
foreach ($this->objects as $key => $object) {
$funcname($object, $key);
}
return true;
}
/**
* Advances the iterator to the next object in the iterator.
*
* @return void
*
* @since 1.0
*/
public function next()
{
// Get the object offsets.
$keys = $this->keys();
// Check if _current has been set to false but offsetUnset.
if ($this->current === false && isset($keys[0])) {
// This is a special case where offsetUnset was used in a foreach loop and the first element was unset.
$this->current = $keys[0];
} else {
// Get the current key.
$position = array_search($this->current, $keys);
// Check if there is an object after the current object.
if ($position !== false && isset($keys[$position + 1])) {
// Get the next id.
$this->current = $keys[$position + 1];
} else {
// That was the last object or the internal properties have become corrupted.
$this->current = null;
}
}
}
/**
* Checks whether an offset exists in the iterator.
*
* @param mixed $offset The object offset.
*
* @return boolean
*
* @since 1.0
*/
public function offsetExists($offset)
{
return isset($this->objects[$offset]);
}
/**
* Gets an offset in the iterator.
*
* @param mixed $offset The object offset.
*
* @return DataObject|null
*
* @since 1.0
*/
public function offsetGet($offset)
{
return $this->objects[$offset] ?? null;
}
/**
* Sets an offset in the iterator.
*
* @param mixed $offset The object offset.
* @param DataObject $object The object object.
*
* @return void
*
* @since 1.0
* @throws \InvalidArgumentException if an object is not an instance of DataObject.
*/
public function offsetSet($offset, $object)
{
if (!($object instanceof DataObject)) {
throw new \InvalidArgumentException(
sprintf(
'The $object argument must be an instance of "%s", a %s was given.',
DataObject::class,
\gettype($object) === 'object' ? \get_class($object) : \gettype($object)
)
);
}
// Set the offset.
if ($offset === null) {
$this->objects[] = $object;
} else {
$this->objects[$offset] = $object;
}
}
/**
* Unsets an offset in the iterator.
*
* @param mixed $offset The object offset.
*
* @return void
*
* @since 1.0
*/
public function offsetUnset($offset)
{
if (!isset($this[$offset])) {
// Do nothing if the offset does not exist.
return;
}
// Check for special handling of unsetting the current position.
if ($offset == $this->current) {
// Get the current position.
$keys = $this->keys();
$position = array_search($this->current, $keys);
// Check if there is an object before the current object.
if ($position > 0) {
// Move the current position back one.
$this->current = $keys[$position - 1];
} else {
// We are at the start of the keys AND let's assume we are in a foreach loop and `next` is going to be called.
$this->current = false;
}
}
unset($this->objects[$offset]);
}
/**
* Rewinds the iterator to the first object.
*
* @return void
*
* @since 1.0
*/
public function rewind()
{
// Set the current position to the first object.
if (empty($this->objects)) {
$this->current = false;
} else {
$keys = $this->keys();
$this->current = array_shift($keys);
}
}
/**
* Validates the iterator.
*
* @return boolean
*
* @since 1.0
*/
public function valid()
{
// Check the current position.
if (!is_scalar($this->current) || !isset($this->objects[$this->current])) {
return false;
}
return true;
}
/**
* Initialises the list with an array of objects.
*
* @param array $input An array of objects.
*
* @return void
*
* @since 1.0
* @throws \InvalidArgumentException if an object is not a DataObject.
*/
private function initialise(array $input = [])
{
foreach ($input as $key => $object) {
if ($object !== null) {
$this[$key] = $object;
}
}
$this->rewind();
}
}

View File

@ -0,0 +1,32 @@
<?php
/**
* Part of the Joomla Framework Data Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Data;
/**
* An interface to define if an object is dumpable.
*
* @since 1.0
*/
interface DumpableInterface
{
/**
* Dumps the data properties into an object, recursively if appropriate.
*
* @param integer $depth The maximum depth of recursion.
* For example, a depth of 0 will return a stdClass with all the properties in native
* form. A depth of 1 will recurse into the first level of properties only.
* @param ?\SplObjectStorage $dumped An array of already serialized objects that is used to avoid infinite loops.
*
* @return \stdClass
*
* @since 1.0
*/
public function dump($depth = 3, \SplObjectStorage $dumped = null);
}

View File

@ -0,0 +1,275 @@
local volumes = [
{
name: 'composer-cache',
path: '/tmp/composer-cache',
},
];
local hostvolumes = [
{
name: 'composer-cache',
host: { path: '/tmp/composer-cache' },
},
];
local composer(phpversion, params) = {
name: 'Composer',
image: 'joomlaprojects/docker-images:php' + phpversion,
volumes: volumes,
commands: [
'php -v',
'sleep 20',
'composer update ' + params,
],
};
local phpunit_common(phpversion) = {
name: 'PHPUnit',
image: 'joomlaprojects/docker-images:php' + phpversion,
[if phpversion == '8.3' then 'failure']: 'ignore',
commands: [
'vendor/bin/phpunit --configuration phpunit.xml.dist --testdox',
],
};
local phpunit_mysql(phpversion, driver) = {
name: 'PHPUnit',
image: 'joomlaprojects/docker-images:php' + phpversion,
[if phpversion == '8.3' then 'failure']: 'ignore',
commands: [
'php --ri ' + driver + ' || true',
'sleep 20',
'vendor/bin/phpunit --configuration phpunit.' + driver + '.xml.dist --testdox',
],
};
local phpunit(phpversion, driver) = {
name: 'PHPUnit',
image: 'joomlaprojects/docker-images:php' + phpversion,
[if phpversion == '8.3' then 'failure']: 'ignore',
commands: [
'php --ri ' + driver + ' || true',
'vendor/bin/phpunit --configuration phpunit.' + driver + '.xml.dist --testdox',
],
};
local phpunit_sqlsrv(phpversion) = {
name: 'PHPUnit with MS SQL Server',
image: 'joomlaprojects/docker-images:php' + phpversion,
commands: [
'apt-get update',
'apt-get install -y software-properties-common lsb-release gnupg',
'curl https://packages.microsoft.com/keys/microsoft.asc | apt-key add -',
'echo "deb [arch=amd64,armhf,arm64] https://packages.microsoft.com/ubuntu/22.04/prod jammy main" >> /etc/apt/sources.list',
'apt-get update',
'ACCEPT_EULA=Y apt-get install -y msodbcsql18 unixodbc-dev',
'pecl install sqlsrv && docker-php-ext-enable sqlsrv',
'pecl install pdo_sqlsrv && docker-php-ext-enable pdo_sqlsrv',
'php --ri sqlsrv',
'php --ri pdo_sqlsrv',
'vendor/bin/phpunit --configuration phpunit.sqlsrv.xml.dist --testdox',
],
};
local pipeline_sqlite(phpversion, driver, params) = {
kind: 'pipeline',
name: 'PHP ' + phpversion + ' with SQLite (' + driver + ')',
environment: { DB: driver },
volumes: hostvolumes,
steps: [
composer(phpversion, params),
phpunit(phpversion, driver),
],
};
local pipeline_mysql(phpversion, driver, dbversion, params) = {
kind: 'pipeline',
name: 'PHP ' + phpversion + ' with MySQL ' + dbversion + ' (' + driver + ')',
environment: { DB: driver },
volumes: hostvolumes,
steps: [
composer(phpversion, params),
phpunit_mysql(phpversion, driver),
],
services: [
{
name: driver,
image: 'bitnami/mysql:' + dbversion,
environment: {
ALLOW_EMPTY_PASSWORD: 'yes',
MYSQL_DATABASE: 'joomla_ut',
MYSQL_ROOT_PASSWORD: '',
MYSQL_AUTHENTICATION_PLUGIN: 'mysql_native_password',
},
ports: [
{
container: 3306,
host: 3306,
},
],
},
],
};
local pipeline_mariadb(phpversion, driver, dbversion, params) = {
kind: 'pipeline',
name: 'PHP ' + phpversion + ' with MariaDB ' + dbversion + ' (' + driver + ')',
environment: { DB: driver },
volumes: hostvolumes,
steps: [
composer(phpversion, params),
phpunit(phpversion, driver),
],
services: [
{
name: driver,
image: 'mariadb:' + dbversion,
environment: {
MARIADB_ALLOW_EMPTY_ROOT_PASSWORD: 'yes',
MARIADB_DATABASE: 'joomla_ut',
MARIADB_ROOT_PASSWORD: '',
# Provide MySQL environments variables for MariaDB < 10.2.
MYSQL_ALLOW_EMPTY_PASSWORD: 'yes',
MYSQL_DATABASE: 'joomla_ut',
MYSQL_ROOT_PASSWORD: '',
},
ports: [
{
container: 3306,
host: 3306,
},
],
},
],
};
local pipeline_postgres(phpversion, driver, dbversion, params) = {
kind: 'pipeline',
name: 'PHP ' + phpversion + ' with PostgreSQL ' + dbversion + ' (' + driver + ')',
environment: { DB: driver },
volumes: hostvolumes,
steps: [
composer(phpversion, params),
phpunit(phpversion, driver),
],
services: [
{
name: driver,
image: 'postgres:' + dbversion,
environment: {
POSTGRES_HOST_AUTH_METHOD: 'trust',
POSTGRES_PASSWORD: '',
POSTGRES_USER: 'postgres',
},
ports: [
{
container: 5432,
host: 5432,
},
],
commands: [
'psql -U postgres -c ',
'psql -U postgres -d joomla_ut -a -f Tests/Stubs/Schema/pgsql.sql',
],
},
],
};
local pipeline_sqlsrv(phpversion, driver, dbversion, params) = {
kind: 'pipeline',
name: 'PHP ' + phpversion + ' with MS SQL Server ' + dbversion + ' (' + driver + ')',
environment: { DB: driver },
volumes: hostvolumes,
steps: [
composer(phpversion, params),
phpunit_sqlsrv(phpversion),
],
services: [
{
name: driver,
image: 'mcr.microsoft.com/mssql/server:' + dbversion,
environment: {
ACCEPT_EULA: 'Y',
SA_PASSWORD: 'JoomlaFramework123',
},
ports: [
{
container: 1433,
host: 1433,
},
],
},
],
};
[
{
kind: 'pipeline',
name: 'Codequality',
volumes: hostvolumes,
steps: [
{
name: 'composer',
image: 'joomlaprojects/docker-images:php8.1',
volumes: volumes,
commands: [
'php -v',
'composer update'
],
},
{
name: 'phpcs',
image: 'joomlaprojects/docker-images:php8.1',
depends: [ 'composer' ],
commands: [
'vendor/bin/phpcs --standard=ruleset.xml src/',
],
},
{
name: 'phan',
image: 'joomlaprojects/docker-images:php8.1-ast',
depends: [ 'composer' ],
failure: 'ignore',
commands: [
'vendor/bin/phan'
],
},
{
name: 'phpstan',
image: 'joomlaprojects/docker-images:php8.1',
depends: [ 'composer' ],
failure: 'ignore',
commands: [
'vendor/bin/phpstan analyse src',
],
},
{
name: 'phploc',
image: 'joomlaprojects/docker-images:php8.1',
depends: [ 'composer' ],
failure: 'ignore',
commands: [
'phploc src',
],
},
],
},
pipeline_sqlite('8.1', 'sqlite', '--prefer-stable'),
pipeline_sqlite('8.2', 'sqlite', '--prefer-stable'),
pipeline_mysql('8.1', 'mysql', '5.7', '--prefer-stable'),
pipeline_mysql('8.2', 'mysql', '5.7', '--prefer-stable'),
pipeline_mysql('8.1', 'mysql', '8.0', '--prefer-stable'),
pipeline_mysql('8.2', 'mysql', '8.0', '--prefer-stable'),
pipeline_mysql('8.1', 'mysqli', '5.7', '--prefer-stable'),
pipeline_mysql('8.2', 'mysqli', '5.7', '--prefer-stable'),
pipeline_mysql('8.1', 'mysqli', '8.0', '--prefer-stable'),
pipeline_mysql('8.2', 'mysqli', '8.0', '--prefer-stable'),
pipeline_mariadb('8.1', 'mariadb', '10.2', '--prefer-stable'),
pipeline_mariadb('8.2', 'mariadb', '10.2', '--prefer-stable'),
pipeline_postgres('8.1', 'pgsql', '10', '--prefer-stable'),
pipeline_postgres('8.2', 'pgsql', '10', '--prefer-stable'),
pipeline_postgres('8.1', 'pgsql', '11', '--prefer-stable'),
pipeline_postgres('8.2', 'pgsql', '11', '--prefer-stable'),
pipeline_sqlsrv('8.1', 'sqlsrv', '2017-latest', '--prefer-stable'),
pipeline_sqlsrv('8.2', 'sqlsrv', '2017-latest', '--prefer-stable'),
]

View File

@ -0,0 +1,692 @@
---
kind: pipeline
name: Codequality
steps:
- commands:
- php -v
- composer update
image: joomlaprojects/docker-images:php8.1
name: composer
volumes:
- name: composer-cache
path: /tmp/composer-cache
- commands:
- vendor/bin/phpcs --standard=ruleset.xml src/
depends:
- composer
image: joomlaprojects/docker-images:php8.1
name: phpcs
- commands:
- vendor/bin/phan
depends:
- composer
failure: ignore
image: joomlaprojects/docker-images:php8.1-ast
name: phan
- commands:
- vendor/bin/phpstan analyse src
depends:
- composer
failure: ignore
image: joomlaprojects/docker-images:php8.1
name: phpstan
- commands:
- phploc src
depends:
- composer
failure: ignore
image: joomlaprojects/docker-images:php8.1
name: phploc
volumes:
- host:
path: /tmp/composer-cache
name: composer-cache
---
environment:
DB: sqlite
kind: pipeline
name: PHP 8.1 with SQLite (sqlite)
steps:
- commands:
- php -v
- sleep 20
- composer update --prefer-stable
image: joomlaprojects/docker-images:php8.1
name: Composer
volumes:
- name: composer-cache
path: /tmp/composer-cache
- commands:
- php --ri sqlite || true
- vendor/bin/phpunit --configuration phpunit.sqlite.xml.dist --testdox
image: joomlaprojects/docker-images:php8.1
name: PHPUnit
volumes:
- host:
path: /tmp/composer-cache
name: composer-cache
---
environment:
DB: sqlite
kind: pipeline
name: PHP 8.2 with SQLite (sqlite)
steps:
- commands:
- php -v
- sleep 20
- composer update --prefer-stable
image: joomlaprojects/docker-images:php8.2
name: Composer
volumes:
- name: composer-cache
path: /tmp/composer-cache
- commands:
- php --ri sqlite || true
- vendor/bin/phpunit --configuration phpunit.sqlite.xml.dist --testdox
image: joomlaprojects/docker-images:php8.2
name: PHPUnit
volumes:
- host:
path: /tmp/composer-cache
name: composer-cache
---
environment:
DB: mysql
kind: pipeline
name: PHP 8.1 with MySQL 5.7 (mysql)
services:
- environment:
ALLOW_EMPTY_PASSWORD: "yes"
MYSQL_AUTHENTICATION_PLUGIN: mysql_native_password
MYSQL_DATABASE: joomla_ut
MYSQL_ROOT_PASSWORD: ""
image: bitnami/mysql:5.7
name: mysql
ports:
- container: 3306
host: 3306
steps:
- commands:
- php -v
- sleep 20
- composer update --prefer-stable
image: joomlaprojects/docker-images:php8.1
name: Composer
volumes:
- name: composer-cache
path: /tmp/composer-cache
- commands:
- php --ri mysql || true
- sleep 20
- vendor/bin/phpunit --configuration phpunit.mysql.xml.dist --testdox
image: joomlaprojects/docker-images:php8.1
name: PHPUnit
volumes:
- host:
path: /tmp/composer-cache
name: composer-cache
---
environment:
DB: mysql
kind: pipeline
name: PHP 8.2 with MySQL 5.7 (mysql)
services:
- environment:
ALLOW_EMPTY_PASSWORD: "yes"
MYSQL_AUTHENTICATION_PLUGIN: mysql_native_password
MYSQL_DATABASE: joomla_ut
MYSQL_ROOT_PASSWORD: ""
image: bitnami/mysql:5.7
name: mysql
ports:
- container: 3306
host: 3306
steps:
- commands:
- php -v
- sleep 20
- composer update --prefer-stable
image: joomlaprojects/docker-images:php8.2
name: Composer
volumes:
- name: composer-cache
path: /tmp/composer-cache
- commands:
- php --ri mysql || true
- sleep 20
- vendor/bin/phpunit --configuration phpunit.mysql.xml.dist --testdox
image: joomlaprojects/docker-images:php8.2
name: PHPUnit
volumes:
- host:
path: /tmp/composer-cache
name: composer-cache
---
environment:
DB: mysql
kind: pipeline
name: PHP 8.1 with MySQL 8.0 (mysql)
services:
- environment:
ALLOW_EMPTY_PASSWORD: "yes"
MYSQL_AUTHENTICATION_PLUGIN: mysql_native_password
MYSQL_DATABASE: joomla_ut
MYSQL_ROOT_PASSWORD: ""
image: bitnami/mysql:8.0
name: mysql
ports:
- container: 3306
host: 3306
steps:
- commands:
- php -v
- sleep 20
- composer update --prefer-stable
image: joomlaprojects/docker-images:php8.1
name: Composer
volumes:
- name: composer-cache
path: /tmp/composer-cache
- commands:
- php --ri mysql || true
- sleep 20
- vendor/bin/phpunit --configuration phpunit.mysql.xml.dist --testdox
image: joomlaprojects/docker-images:php8.1
name: PHPUnit
volumes:
- host:
path: /tmp/composer-cache
name: composer-cache
---
environment:
DB: mysql
kind: pipeline
name: PHP 8.2 with MySQL 8.0 (mysql)
services:
- environment:
ALLOW_EMPTY_PASSWORD: "yes"
MYSQL_AUTHENTICATION_PLUGIN: mysql_native_password
MYSQL_DATABASE: joomla_ut
MYSQL_ROOT_PASSWORD: ""
image: bitnami/mysql:8.0
name: mysql
ports:
- container: 3306
host: 3306
steps:
- commands:
- php -v
- sleep 20
- composer update --prefer-stable
image: joomlaprojects/docker-images:php8.2
name: Composer
volumes:
- name: composer-cache
path: /tmp/composer-cache
- commands:
- php --ri mysql || true
- sleep 20
- vendor/bin/phpunit --configuration phpunit.mysql.xml.dist --testdox
image: joomlaprojects/docker-images:php8.2
name: PHPUnit
volumes:
- host:
path: /tmp/composer-cache
name: composer-cache
---
environment:
DB: mysqli
kind: pipeline
name: PHP 8.1 with MySQL 5.7 (mysqli)
services:
- environment:
ALLOW_EMPTY_PASSWORD: "yes"
MYSQL_AUTHENTICATION_PLUGIN: mysql_native_password
MYSQL_DATABASE: joomla_ut
MYSQL_ROOT_PASSWORD: ""
image: bitnami/mysql:5.7
name: mysqli
ports:
- container: 3306
host: 3306
steps:
- commands:
- php -v
- sleep 20
- composer update --prefer-stable
image: joomlaprojects/docker-images:php8.1
name: Composer
volumes:
- name: composer-cache
path: /tmp/composer-cache
- commands:
- php --ri mysqli || true
- sleep 20
- vendor/bin/phpunit --configuration phpunit.mysqli.xml.dist --testdox
image: joomlaprojects/docker-images:php8.1
name: PHPUnit
volumes:
- host:
path: /tmp/composer-cache
name: composer-cache
---
environment:
DB: mysqli
kind: pipeline
name: PHP 8.2 with MySQL 5.7 (mysqli)
services:
- environment:
ALLOW_EMPTY_PASSWORD: "yes"
MYSQL_AUTHENTICATION_PLUGIN: mysql_native_password
MYSQL_DATABASE: joomla_ut
MYSQL_ROOT_PASSWORD: ""
image: bitnami/mysql:5.7
name: mysqli
ports:
- container: 3306
host: 3306
steps:
- commands:
- php -v
- sleep 20
- composer update --prefer-stable
image: joomlaprojects/docker-images:php8.2
name: Composer
volumes:
- name: composer-cache
path: /tmp/composer-cache
- commands:
- php --ri mysqli || true
- sleep 20
- vendor/bin/phpunit --configuration phpunit.mysqli.xml.dist --testdox
image: joomlaprojects/docker-images:php8.2
name: PHPUnit
volumes:
- host:
path: /tmp/composer-cache
name: composer-cache
---
environment:
DB: mysqli
kind: pipeline
name: PHP 8.1 with MySQL 8.0 (mysqli)
services:
- environment:
ALLOW_EMPTY_PASSWORD: "yes"
MYSQL_AUTHENTICATION_PLUGIN: mysql_native_password
MYSQL_DATABASE: joomla_ut
MYSQL_ROOT_PASSWORD: ""
image: bitnami/mysql:8.0
name: mysqli
ports:
- container: 3306
host: 3306
steps:
- commands:
- php -v
- sleep 20
- composer update --prefer-stable
image: joomlaprojects/docker-images:php8.1
name: Composer
volumes:
- name: composer-cache
path: /tmp/composer-cache
- commands:
- php --ri mysqli || true
- sleep 20
- vendor/bin/phpunit --configuration phpunit.mysqli.xml.dist --testdox
image: joomlaprojects/docker-images:php8.1
name: PHPUnit
volumes:
- host:
path: /tmp/composer-cache
name: composer-cache
---
environment:
DB: mysqli
kind: pipeline
name: PHP 8.2 with MySQL 8.0 (mysqli)
services:
- environment:
ALLOW_EMPTY_PASSWORD: "yes"
MYSQL_AUTHENTICATION_PLUGIN: mysql_native_password
MYSQL_DATABASE: joomla_ut
MYSQL_ROOT_PASSWORD: ""
image: bitnami/mysql:8.0
name: mysqli
ports:
- container: 3306
host: 3306
steps:
- commands:
- php -v
- sleep 20
- composer update --prefer-stable
image: joomlaprojects/docker-images:php8.2
name: Composer
volumes:
- name: composer-cache
path: /tmp/composer-cache
- commands:
- php --ri mysqli || true
- sleep 20
- vendor/bin/phpunit --configuration phpunit.mysqli.xml.dist --testdox
image: joomlaprojects/docker-images:php8.2
name: PHPUnit
volumes:
- host:
path: /tmp/composer-cache
name: composer-cache
---
environment:
DB: mariadb
kind: pipeline
name: PHP 8.1 with MariaDB 10.2 (mariadb)
services:
- environment:
MARIADB_ALLOW_EMPTY_ROOT_PASSWORD: "yes"
MARIADB_DATABASE: joomla_ut
MARIADB_ROOT_PASSWORD: ""
MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
MYSQL_DATABASE: joomla_ut
MYSQL_ROOT_PASSWORD: ""
image: mariadb:10.2
name: mariadb
ports:
- container: 3306
host: 3306
steps:
- commands:
- php -v
- sleep 20
- composer update --prefer-stable
image: joomlaprojects/docker-images:php8.1
name: Composer
volumes:
- name: composer-cache
path: /tmp/composer-cache
- commands:
- php --ri mariadb || true
- vendor/bin/phpunit --configuration phpunit.mariadb.xml.dist --testdox
image: joomlaprojects/docker-images:php8.1
name: PHPUnit
volumes:
- host:
path: /tmp/composer-cache
name: composer-cache
---
environment:
DB: mariadb
kind: pipeline
name: PHP 8.2 with MariaDB 10.2 (mariadb)
services:
- environment:
MARIADB_ALLOW_EMPTY_ROOT_PASSWORD: "yes"
MARIADB_DATABASE: joomla_ut
MARIADB_ROOT_PASSWORD: ""
MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
MYSQL_DATABASE: joomla_ut
MYSQL_ROOT_PASSWORD: ""
image: mariadb:10.2
name: mariadb
ports:
- container: 3306
host: 3306
steps:
- commands:
- php -v
- sleep 20
- composer update --prefer-stable
image: joomlaprojects/docker-images:php8.2
name: Composer
volumes:
- name: composer-cache
path: /tmp/composer-cache
- commands:
- php --ri mariadb || true
- vendor/bin/phpunit --configuration phpunit.mariadb.xml.dist --testdox
image: joomlaprojects/docker-images:php8.2
name: PHPUnit
volumes:
- host:
path: /tmp/composer-cache
name: composer-cache
---
environment:
DB: pgsql
kind: pipeline
name: PHP 8.1 with PostgreSQL 10 (pgsql)
services:
- commands:
- 'psql -U postgres -c '
- psql -U postgres -d joomla_ut -a -f Tests/Stubs/Schema/pgsql.sql
environment:
POSTGRES_HOST_AUTH_METHOD: trust
POSTGRES_PASSWORD: ""
POSTGRES_USER: postgres
image: postgres:10
name: pgsql
ports:
- container: 5432
host: 5432
steps:
- commands:
- php -v
- sleep 20
- composer update --prefer-stable
image: joomlaprojects/docker-images:php8.1
name: Composer
volumes:
- name: composer-cache
path: /tmp/composer-cache
- commands:
- php --ri pgsql || true
- vendor/bin/phpunit --configuration phpunit.pgsql.xml.dist --testdox
image: joomlaprojects/docker-images:php8.1
name: PHPUnit
volumes:
- host:
path: /tmp/composer-cache
name: composer-cache
---
environment:
DB: pgsql
kind: pipeline
name: PHP 8.2 with PostgreSQL 10 (pgsql)
services:
- commands:
- 'psql -U postgres -c '
- psql -U postgres -d joomla_ut -a -f Tests/Stubs/Schema/pgsql.sql
environment:
POSTGRES_HOST_AUTH_METHOD: trust
POSTGRES_PASSWORD: ""
POSTGRES_USER: postgres
image: postgres:10
name: pgsql
ports:
- container: 5432
host: 5432
steps:
- commands:
- php -v
- sleep 20
- composer update --prefer-stable
image: joomlaprojects/docker-images:php8.2
name: Composer
volumes:
- name: composer-cache
path: /tmp/composer-cache
- commands:
- php --ri pgsql || true
- vendor/bin/phpunit --configuration phpunit.pgsql.xml.dist --testdox
image: joomlaprojects/docker-images:php8.2
name: PHPUnit
volumes:
- host:
path: /tmp/composer-cache
name: composer-cache
---
environment:
DB: pgsql
kind: pipeline
name: PHP 8.1 with PostgreSQL 11 (pgsql)
services:
- commands:
- 'psql -U postgres -c '
- psql -U postgres -d joomla_ut -a -f Tests/Stubs/Schema/pgsql.sql
environment:
POSTGRES_HOST_AUTH_METHOD: trust
POSTGRES_PASSWORD: ""
POSTGRES_USER: postgres
image: postgres:11
name: pgsql
ports:
- container: 5432
host: 5432
steps:
- commands:
- php -v
- sleep 20
- composer update --prefer-stable
image: joomlaprojects/docker-images:php8.1
name: Composer
volumes:
- name: composer-cache
path: /tmp/composer-cache
- commands:
- php --ri pgsql || true
- vendor/bin/phpunit --configuration phpunit.pgsql.xml.dist --testdox
image: joomlaprojects/docker-images:php8.1
name: PHPUnit
volumes:
- host:
path: /tmp/composer-cache
name: composer-cache
---
environment:
DB: pgsql
kind: pipeline
name: PHP 8.2 with PostgreSQL 11 (pgsql)
services:
- commands:
- 'psql -U postgres -c '
- psql -U postgres -d joomla_ut -a -f Tests/Stubs/Schema/pgsql.sql
environment:
POSTGRES_HOST_AUTH_METHOD: trust
POSTGRES_PASSWORD: ""
POSTGRES_USER: postgres
image: postgres:11
name: pgsql
ports:
- container: 5432
host: 5432
steps:
- commands:
- php -v
- sleep 20
- composer update --prefer-stable
image: joomlaprojects/docker-images:php8.2
name: Composer
volumes:
- name: composer-cache
path: /tmp/composer-cache
- commands:
- php --ri pgsql || true
- vendor/bin/phpunit --configuration phpunit.pgsql.xml.dist --testdox
image: joomlaprojects/docker-images:php8.2
name: PHPUnit
volumes:
- host:
path: /tmp/composer-cache
name: composer-cache
---
environment:
DB: sqlsrv
kind: pipeline
name: PHP 8.1 with MS SQL Server 2017-latest (sqlsrv)
services:
- environment:
ACCEPT_EULA: "Y"
SA_PASSWORD: JoomlaFramework123
image: mcr.microsoft.com/mssql/server:2017-latest
name: sqlsrv
ports:
- container: 1433
host: 1433
steps:
- commands:
- php -v
- sleep 20
- composer update --prefer-stable
image: joomlaprojects/docker-images:php8.1
name: Composer
volumes:
- name: composer-cache
path: /tmp/composer-cache
- commands:
- apt-get update
- apt-get install -y software-properties-common lsb-release gnupg
- curl https://packages.microsoft.com/keys/microsoft.asc | apt-key add -
- echo "deb [arch=amd64,armhf,arm64] https://packages.microsoft.com/ubuntu/22.04/prod
jammy main" >> /etc/apt/sources.list
- apt-get update
- ACCEPT_EULA=Y apt-get install -y msodbcsql18 unixodbc-dev
- pecl install sqlsrv && docker-php-ext-enable sqlsrv
- pecl install pdo_sqlsrv && docker-php-ext-enable pdo_sqlsrv
- php --ri sqlsrv
- php --ri pdo_sqlsrv
- vendor/bin/phpunit --configuration phpunit.sqlsrv.xml.dist --testdox
image: joomlaprojects/docker-images:php8.1
name: PHPUnit with MS SQL Server
volumes:
- host:
path: /tmp/composer-cache
name: composer-cache
---
environment:
DB: sqlsrv
kind: pipeline
name: PHP 8.2 with MS SQL Server 2017-latest (sqlsrv)
services:
- environment:
ACCEPT_EULA: "Y"
SA_PASSWORD: JoomlaFramework123
image: mcr.microsoft.com/mssql/server:2017-latest
name: sqlsrv
ports:
- container: 1433
host: 1433
steps:
- commands:
- php -v
- sleep 20
- composer update --prefer-stable
image: joomlaprojects/docker-images:php8.2
name: Composer
volumes:
- name: composer-cache
path: /tmp/composer-cache
- commands:
- apt-get update
- apt-get install -y software-properties-common lsb-release gnupg
- curl https://packages.microsoft.com/keys/microsoft.asc | apt-key add -
- echo "deb [arch=amd64,armhf,arm64] https://packages.microsoft.com/ubuntu/22.04/prod
jammy main" >> /etc/apt/sources.list
- apt-get update
- ACCEPT_EULA=Y apt-get install -y msodbcsql18 unixodbc-dev
- pecl install sqlsrv && docker-php-ext-enable sqlsrv
- pecl install pdo_sqlsrv && docker-php-ext-enable pdo_sqlsrv
- php --ri sqlsrv
- php --ri pdo_sqlsrv
- vendor/bin/phpunit --configuration phpunit.sqlsrv.xml.dist --testdox
image: joomlaprojects/docker-images:php8.2
name: PHPUnit with MS SQL Server
volumes:
- host:
path: /tmp/composer-cache
name: composer-cache
---
kind: signature
hmac: e511351199fd2fa0a45c01a540e0c31ecbe7c72b30075fa2dea06b11ca5c7f58
...

340
libraries/vendor/joomla/database/LICENSE vendored Normal file
View File

@ -0,0 +1,340 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Library 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Library General
Public License instead of this License.

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="Tests/bootstrap.php" colors="false">
<php>
<env name="JOOMLA_TEST_DB_DRIVER" value="sqlsrv" />
<env name="JOOMLA_TEST_DB_HOST" value="(local)\SQL2012SP1" />
<env name="JOOMLA_TEST_DB_PORT" value="" />
<env name="JOOMLA_TEST_DB_USER" value="sa" />
<env name="JOOMLA_TEST_DB_PASSWORD" value="Password12!" />
<env name="JOOMLA_TEST_DB_DATABASE" value="joomla_ut" />
<env name="JOOMLA_TEST_DB_PREFIX" value="" />
<env name="SYMFONY_DEPRECATIONS_HELPER" value="weak" />
</php>
<testsuites>
<testsuite name="Unit">
<directory>Tests</directory>
</testsuite>
</testsuites>
</phpunit>

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="Tests/bootstrap.php" colors="false">
<php>
<env name="JOOMLA_TEST_DB_DRIVER" value="sqlsrv" />
<env name="JOOMLA_TEST_DB_HOST" value="(local)\SQL2014" />
<env name="JOOMLA_TEST_DB_PORT" value="" />
<env name="JOOMLA_TEST_DB_USER" value="sa" />
<env name="JOOMLA_TEST_DB_PASSWORD" value="Password12!" />
<env name="JOOMLA_TEST_DB_DATABASE" value="joomla_ut" />
<env name="JOOMLA_TEST_DB_PREFIX" value="" />
<env name="SYMFONY_DEPRECATIONS_HELPER" value="weak" />
</php>
<testsuites>
<testsuite name="Unit">
<directory>Tests</directory>
</testsuite>
</testsuites>
</phpunit>

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="Tests/bootstrap.php" colors="false">
<php>
<env name="JOOMLA_TEST_DB_DRIVER" value="sqlsrv" />
<env name="JOOMLA_TEST_DB_HOST" value="(local)\SQL2017" />
<env name="JOOMLA_TEST_DB_PORT" value="" />
<env name="JOOMLA_TEST_DB_USER" value="sa" />
<env name="JOOMLA_TEST_DB_PASSWORD" value="Password12!" />
<env name="JOOMLA_TEST_DB_DATABASE" value="joomla_ut" />
<env name="JOOMLA_TEST_DB_PREFIX" value="" />
<env name="SYMFONY_DEPRECATIONS_HELPER" value="weak" />
</php>
<testsuites>
<testsuite name="Unit">
<directory>Tests</directory>
</testsuite>
</testsuites>
</phpunit>

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="Tests/bootstrap.php" colors="false">
<php>
<env name="JOOMLA_TEST_DB_DRIVER" value="mysqli" />
<env name="JOOMLA_TEST_DB_HOST" value="mariadb" />
<env name="JOOMLA_TEST_DB_PORT" value="3306" />
<env name="JOOMLA_TEST_DB_USER" value="root" />
<env name="JOOMLA_TEST_DB_PASSWORD" value="" />
<env name="JOOMLA_TEST_DB_DATABASE" value="joomla_ut" />
<env name="JOOMLA_TEST_DB_PREFIX" value="" />
<env name="SYMFONY_DEPRECATIONS_HELPER" value="weak" />
</php>
<testsuites>
<testsuite name="Unit">
<directory>Tests/Mysqli</directory>
</testsuite>
</testsuites>
</phpunit>

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="Tests/bootstrap.php" colors="false">
<php>
<env name="JOOMLA_TEST_DB_DRIVER" value="mysql" />
<env name="JOOMLA_TEST_DB_HOST" value="mysql" />
<env name="JOOMLA_TEST_DB_PORT" value="3306" />
<env name="JOOMLA_TEST_DB_USER" value="root" />
<env name="JOOMLA_TEST_DB_PASSWORD" value="" />
<env name="JOOMLA_TEST_DB_DATABASE" value="joomla_ut" />
<env name="JOOMLA_TEST_DB_PREFIX" value="" />
<env name="SYMFONY_DEPRECATIONS_HELPER" value="weak" />
</php>
<testsuites>
<testsuite name="Unit">
<directory>Tests/Mysql</directory>
</testsuite>
</testsuites>
</phpunit>

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="Tests/bootstrap.php" colors="false">
<php>
<env name="JOOMLA_TEST_DB_DRIVER" value="mysqli" />
<env name="JOOMLA_TEST_DB_HOST" value="mysqli" />
<env name="JOOMLA_TEST_DB_PORT" value="3306" />
<env name="JOOMLA_TEST_DB_USER" value="root" />
<env name="JOOMLA_TEST_DB_PASSWORD" value="" />
<env name="JOOMLA_TEST_DB_DATABASE" value="joomla_ut" />
<env name="JOOMLA_TEST_DB_PREFIX" value="" />
<env name="SYMFONY_DEPRECATIONS_HELPER" value="weak" />
</php>
<testsuites>
<testsuite name="Unit">
<directory>Tests/Mysqli</directory>
</testsuite>
</testsuites>
</phpunit>

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="Tests/bootstrap.php" colors="false">
<php>
<env name="JOOMLA_TEST_DB_DRIVER" value="pgsql" />
<env name="JOOMLA_TEST_DB_HOST" value="pgsql" />
<env name="JOOMLA_TEST_DB_PORT" value="5433" />
<env name="JOOMLA_TEST_DB_USER" value="postgres" />
<env name="JOOMLA_TEST_DB_PASSWORD" value="" />
<env name="JOOMLA_TEST_DB_DATABASE" value="joomla_ut" />
<env name="JOOMLA_TEST_DB_PREFIX" value="" />
<env name="JOOMLA_TEST_DB_SELECT" value="yes" />
<env name="SYMFONY_DEPRECATIONS_HELPER" value="weak" />
</php>
<testsuites>
<testsuite name="Unit">
<directory>Tests/Pgsql</directory>
</testsuite>
</testsuites>
</phpunit>

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="Tests/bootstrap.php" colors="false">
<php>
<env name="JOOMLA_TEST_DB_DRIVER" value="sqlite" />
<env name="JOOMLA_TEST_DB_HOST" value="" />
<env name="JOOMLA_TEST_DB_PORT" value="" />
<env name="JOOMLA_TEST_DB_USER" value="" />
<env name="JOOMLA_TEST_DB_PASSWORD" value="" />
<env name="JOOMLA_TEST_DB_DATABASE" value=":memory:" />
<env name="JOOMLA_TEST_DB_PREFIX" value="" />
<env name="SYMFONY_DEPRECATIONS_HELPER" value="weak" />
</php>
<testsuites>
<testsuite name="Unit">
<directory>Tests/Sqlite</directory>
</testsuite>
</testsuites>
</phpunit>

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="Tests/bootstrap.php" colors="false">
<php>
<env name="JOOMLA_TEST_DB_DRIVER" value="sqlsrv" />
<env name="JOOMLA_TEST_DB_HOST" value="sqlsrv" />
<env name="JOOMLA_TEST_DB_PORT" value="1433" />
<env name="JOOMLA_TEST_DB_USER" value="sa" />
<env name="JOOMLA_TEST_DB_PASSWORD" value="JoomlaFramework123" />
<env name="JOOMLA_TEST_DB_DATABASE" value="joomla_ut" />
<env name="JOOMLA_TEST_DB_PREFIX" value="" />
<env name="SYMFONY_DEPRECATIONS_HELPER" value="weak" />
</php>
<testsuites>
<testsuite name="Unit">
<directory>Tests/Sqlsrv</directory>
</testsuite>
</testsuites>
</phpunit>

View File

@ -0,0 +1,168 @@
<?php
/**
* Part of the Joomla Framework Database Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Database\Command;
use Joomla\Archive\Archive;
use Joomla\Archive\Zip;
use Joomla\Console\Command\AbstractCommand;
use Joomla\Database\DatabaseDriver;
use Joomla\Database\Exception\UnsupportedAdapterException;
use Joomla\Filesystem\File;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Console\Input\InputOption;
/**
* Console command for exporting the database
*
* @since 2.0.0
*/
class ExportCommand extends AbstractCommand
{
/**
* The default command name
*
* @var string
* @since 2.0.0
*/
protected static $defaultName = 'database:export';
/**
* Database connector
*
* @var DatabaseDriver
* @since 2.0.0
*/
private $db;
/**
* Instantiate the command.
*
* @param DatabaseDriver $db Database connector
*
* @since 2.0.0
*/
public function __construct(DatabaseDriver $db)
{
$this->db = $db;
parent::__construct();
}
/**
* Internal function to execute the command.
*
* @param InputInterface $input The input to inject into the command.
* @param OutputInterface $output The output to inject into the command.
*
* @return integer The command exit code
*
* @since 2.0.0
*/
protected function doExecute(InputInterface $input, OutputInterface $output): int
{
$symfonyStyle = new SymfonyStyle($input, $output);
$symfonyStyle->title('Exporting Database');
$totalTime = microtime(true);
if (!class_exists(File::class)) {
$symfonyStyle->error('The "joomla/filesystem" Composer package is not installed, cannot create an export.');
return 1;
}
// Make sure the database supports exports before we get going
try {
$exporter = $this->db->getExporter()
->withStructure();
} catch (UnsupportedAdapterException $e) {
$symfonyStyle->error(sprintf('The "%s" database driver does not support exporting data.', $this->db->getName()));
return 1;
}
$folderPath = $input->getOption('folder');
$tableName = $input->getOption('table');
$zip = $input->getOption('zip');
$zipFile = $folderPath . '/data_exported_' . date("Y-m-d\TH-i-s") . '.zip';
$tables = $this->db->getTableList();
$prefix = $this->db->getPrefix();
if ($tableName) {
if (!\in_array($tableName, $tables)) {
$symfonyStyle->error(sprintf('The %s table does not exist in the database.', $tableName));
return 1;
}
$tables = [$tableName];
}
if ($zip) {
if (!class_exists(Archive::class)) {
$symfonyStyle->error('The "joomla/archive" Composer package is not installed, cannot create ZIP files.');
return 1;
}
/** @var Zip $zipArchive */
$zipArchive = (new Archive())->getAdapter('zip');
}
foreach ($tables as $table) {
// If an empty prefix is in use then we will dump all tables, otherwise the prefix must match
if (strlen($prefix) === 0 || strpos(substr($table, 0, strlen($prefix)), $prefix) !== false) {
$taskTime = microtime(true);
$filename = $folderPath . '/' . $table . '.xml';
$symfonyStyle->text(sprintf('Processing the %s table', $table));
$data = (string) $exporter->from($table)->withData(true);
if (file_exists($filename)) {
File::delete($filename);
}
File::write($filename, $data);
if ($zip) {
$zipFilesArray = [['name' => $table . '.xml', 'data' => $data]];
$zipArchive->create($zipFile, $zipFilesArray);
File::delete($filename);
}
$symfonyStyle->text(sprintf('Exported data for %s in %d seconds', $table, round(microtime(true) - $taskTime, 3)));
}
}
$symfonyStyle->success(sprintf('Export completed in %d seconds', round(microtime(true) - $totalTime, 3)));
return 0;
}
/**
* Configure the command.
*
* @return void
*
* @since 2.0.0
*/
protected function configure(): void
{
$this->setDescription('Export the database');
$this->addOption('folder', null, InputOption::VALUE_OPTIONAL, 'Path to write the export files to', '.');
$this->addOption('table', null, InputOption::VALUE_REQUIRED, 'The name of the database table to export');
$this->addOption('zip', null, InputOption::VALUE_NONE, 'Flag indicating the export will be saved to a ZIP archive');
}
}

View File

@ -0,0 +1,251 @@
<?php
/**
* Part of the Joomla Framework Database Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
// phpcs:disable Generic.PHP.DeprecatedFunctions.Deprecated
namespace Joomla\Database\Command;
use Joomla\Archive\Archive;
use Joomla\Archive\Exception\UnknownArchiveException;
use Joomla\Console\Command\AbstractCommand;
use Joomla\Database\DatabaseDriver;
use Joomla\Database\Exception\ExecutionFailureException;
use Joomla\Database\Exception\UnsupportedAdapterException;
use Joomla\Filesystem\Exception\FilesystemException;
use Joomla\Filesystem\File;
use Joomla\Filesystem\Folder;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
/**
* Console command for importing the database
*
* @since 2.0.0
*/
class ImportCommand extends AbstractCommand
{
/**
* The default command name
*
* @var string
* @since 2.0.0
*/
protected static $defaultName = 'database:import';
/**
* Database connector
*
* @var DatabaseDriver
* @since 2.0.0
*/
private $db;
/**
* Instantiate the command.
*
* @param DatabaseDriver $db Database connector
*
* @since 2.0.0
*/
public function __construct(DatabaseDriver $db)
{
$this->db = $db;
parent::__construct();
}
/**
* Checks if the zip file contains database export files
*
* @param string $archive A zip archive to analyze
*
* @return void
*
* @since 2.0.0
* @throws \RuntimeException
*/
private function checkZipFile(string $archive): void
{
if (!extension_loaded('zip')) {
throw new \RuntimeException('The PHP zip extension is not installed or is disabled');
}
$zip = zip_open($archive);
if (!\is_resource($zip)) {
throw new \RuntimeException('Unable to open archive');
}
while ($file = @zip_read($zip)) {
if (strpos(zip_entry_name($file), $this->db->getPrefix()) === false) {
zip_entry_close($file);
@zip_close($zip);
throw new \RuntimeException('Unable to find table matching database prefix');
}
zip_entry_close($file);
}
@zip_close($zip);
}
/**
* Internal function to execute the command.
*
* @param InputInterface $input The input to inject into the command.
* @param OutputInterface $output The output to inject into the command.
*
* @return integer The command exit code
*
* @since 2.0.0
*/
protected function doExecute(InputInterface $input, OutputInterface $output): int
{
$symfonyStyle = new SymfonyStyle($input, $output);
$symfonyStyle->title('Importing Database');
$totalTime = microtime(true);
// Make sure the database supports imports before we get going
try {
$importer = $this->db->getImporter()
->withStructure()
->asXml();
} catch (UnsupportedAdapterException $e) {
$symfonyStyle->error(sprintf('The "%s" database driver does not support importing data.', $this->db->getName()));
return 1;
}
$folderPath = $input->getOption('folder');
$tableName = $input->getOption('table');
$zipFile = $input->getOption('zip');
if ($zipFile) {
if (!class_exists(File::class)) {
$symfonyStyle->error('The "joomla/filesystem" Composer package is not installed, cannot process ZIP files.');
return 1;
}
if (!class_exists(Archive::class)) {
$symfonyStyle->error('The "joomla/archive" Composer package is not installed, cannot process ZIP files.');
return 1;
}
$zipPath = $folderPath . '/' . $zipFile;
try {
$this->checkZipFile($zipPath);
} catch (\RuntimeException $e) {
$symfonyStyle->error($e->getMessage());
return 1;
}
$folderPath .= File::stripExt($zipFile);
try {
Folder::create($folderPath);
} catch (FilesystemException $e) {
$symfonyStyle->error($e->getMessage());
return 1;
}
try {
(new Archive())->extract($zipPath, $folderPath);
} catch (UnknownArchiveException $e) {
$symfonyStyle->error($e->getMessage());
Folder::delete($folderPath);
return 1;
}
}
if ($tableName) {
$tables = [$tableName . '.xml'];
} else {
$tables = Folder::files($folderPath, '\.xml$');
}
foreach ($tables as $table) {
$taskTime = microtime(true);
$percorso = $folderPath . '/' . $table;
// Check file
if (!file_exists($percorso)) {
$symfonyStyle->error(sprintf('The %s file does not exist.', $table));
return 1;
}
$tableName = str_replace('.xml', '', $table);
$symfonyStyle->text(sprintf('Importing %1$s from %2$s', $tableName, $table));
$importer->from(file_get_contents($percorso));
$symfonyStyle->text(sprintf('Processing the %s table', $tableName));
try {
$this->db->dropTable($tableName, true);
} catch (ExecutionFailureException $e) {
$symfonyStyle->error(sprintf('Error executing the DROP TABLE statement for %1$s: %2$s', $tableName, $e->getMessage()));
return 1;
}
try {
$importer->mergeStructure();
} catch (\Exception $e) {
$symfonyStyle->error(sprintf('Error merging the structure for %1$s: %2$s', $tableName, $e->getMessage()));
return 1;
}
try {
$importer->importData();
} catch (\Exception $e) {
$symfonyStyle->error(sprintf('Error importing the data for %1$s: %2$s', $tableName, $e->getMessage()));
return 1;
}
$symfonyStyle->text(sprintf('Imported data for %s in %d seconds', $table, round(microtime(true) - $taskTime, 3)));
}
if ($zipFile) {
Folder::delete($folderPath);
}
$symfonyStyle->success(sprintf('Import completed in %d seconds', round(microtime(true) - $totalTime, 3)));
return 0;
}
/**
* Configure the command.
*
* @return void
*
* @since 2.0.0
*/
protected function configure(): void
{
$this->setDescription('Import the database');
$this->addOption('folder', null, InputOption::VALUE_OPTIONAL, 'Path to the folder containing files to import', '.');
$this->addOption('zip', null, InputOption::VALUE_REQUIRED, 'The name of a ZIP file to import');
$this->addOption('table', null, InputOption::VALUE_REQUIRED, 'The name of the database table to import');
}
}

View File

@ -0,0 +1,29 @@
<?php
/**
* Part of the Joomla Framework Database Package
*
* @copyright Copyright (C) 2022 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Database;
/**
* Defines the interface for a DatabaseInterface aware class.
*
* @since 2.1.0
*/
interface DatabaseAwareInterface
{
/**
* Set the database.
*
* @param DatabaseInterface $db The database.
*
* @return void
*
* @since 2.1.0
*/
public function setDatabase(DatabaseInterface $db): void;
}

View File

@ -0,0 +1,59 @@
<?php
/**
* Part of the Joomla Framework Database Package
*
* @copyright Copyright (C) 2022 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Database;
use Joomla\Database\Exception\DatabaseNotFoundException;
/**
* Defines the trait for a Database Aware Class.
*
* @since 2.1.0
*/
trait DatabaseAwareTrait
{
/**
* Database
*
* @var DatabaseInterface
* @since 2.1.0
*/
private $databaseAwareTraitDatabase;
/**
* Get the database.
*
* @return DatabaseInterface
*
* @since 2.1.0
* @throws DatabaseNotFoundException May be thrown if the database has not been set.
*/
protected function getDatabase(): DatabaseInterface
{
if ($this->databaseAwareTraitDatabase) {
return $this->databaseAwareTraitDatabase;
}
throw new DatabaseNotFoundException('Database not set in ' . \get_class($this));
}
/**
* Set the database.
*
* @param DatabaseInterface $db The database.
*
* @return void
*
* @since 2.1.0
*/
public function setDatabase(DatabaseInterface $db): void
{
$this->databaseAwareTraitDatabase = $db;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,47 @@
<?php
/**
* Part of the Joomla Framework Database Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Database;
/**
* Class defining the events dispatched by the database API
*
* @since 2.0.0
*/
final class DatabaseEvents
{
/**
* Private constructor to prevent instantiation of this class
*
* @since 2.0.0
*/
private function __construct()
{
}
/**
* Database event which is dispatched after the connection to the database server is opened.
*
* Listeners to this event receive a `Joomla\Database\Event\ConnectionEvent` object.
*
* @var string
* @since 2.0.0
*/
public const POST_CONNECT = 'onAfterConnect';
/**
* Database event which is dispatched after the connection to the database server is closed.
*
* Listeners to this event receive a `Joomla\Database\Event\ConnectionEvent` object.
*
* @var string
* @since 2.0.0
*/
public const POST_DISCONNECT = 'onAfterDisconnect';
}

View File

@ -0,0 +1,308 @@
<?php
/**
* Part of the Joomla Framework Database Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Database;
/**
* Joomla Framework Database Exporter Class
*
* @since 1.0
*/
abstract class DatabaseExporter
{
/**
* The type of output format.
*
* @var string
* @since 1.0
*/
protected $asFormat = 'xml';
/**
* An array of cached data.
*
* @var array
* @since 1.0
*/
protected $cache = ['columns' => [], 'keys' => []];
/**
* The database connector to use for exporting structure and/or data.
*
* @var DatabaseInterface
* @since 1.0
*/
protected $db;
/**
* An array input sources (table names).
*
* @var string[]
* @since 1.0
*/
protected $from = [];
/**
* An array of options for the exporter.
*
* @var \stdClass
* @since 1.0
*/
protected $options;
/**
* Constructor.
*
* Sets up the default options for the exporter.
*
* @since 1.0
*/
public function __construct()
{
$this->options = new \stdClass();
// Set up the class defaults:
// Export not only structure
$this->withStructure();
$this->withData();
// Export as xml.
$this->asXml();
// Default destination is a string using $output = (string) $exporter;
}
/**
* Magic function to exports the data to a string.
*
* @return string
*
* @since 1.0
*/
public function __toString()
{
$buffer = '';
try {
// Check everything is ok to run first.
$this->check();
// Get the format.
switch ($this->asFormat) {
case 'xml':
default:
$buffer = $this->buildXml();
break;
}
} catch (\Exception $e) {
// Do nothing
}
return $buffer;
}
/**
* Set the output option for the exporter to XML format.
*
* @return $this
*
* @since 1.0
*/
public function asXml()
{
$this->asFormat = 'xml';
return $this;
}
/**
* Builds the XML data for the tables to export.
*
* @return string An XML string
*
* @since 1.0
* @throws \Exception if an error occurs.
*/
abstract protected function buildXml();
/**
* Builds the XML structure to export.
*
* @return array An array of XML lines (strings).
*
* @since 1.0
* @throws \Exception if an error occurs.
*/
abstract protected function buildXmlStructure();
/**
* Checks if all data and options are in order prior to exporting.
*
* @return $this
*
* @since 1.0
* @throws \Exception if an error is encountered.
*/
abstract public function check();
/**
* Specifies a list of table names to export.
*
* @param string[]|string $from The name of a single table, or an array of the table names to export.
*
* @return $this
*
* @since 1.0
* @throws \InvalidArgumentException
*/
public function from($from)
{
if (\is_string($from)) {
$this->from = [$from];
} elseif (\is_array($from)) {
$this->from = $from;
} else {
throw new \InvalidArgumentException('The exporter requires either a single table name or array of table names');
}
return $this;
}
/**
* Get the generic name of the table, converting the database prefix to the wildcard string.
*
* @param string $table The name of the table.
*
* @return string The name of the table with the database prefix replaced with #__.
*
* @since 1.0
*/
protected function getGenericTableName($table)
{
$prefix = $this->db->getPrefix();
// Replace the magic prefix if found.
return preg_replace("|^$prefix|", '#__', $table);
}
/**
* Sets the database connector to use for importing structure and/or data.
*
* @param DatabaseInterface $db The database connector.
*
* @return $this
*
* @since 1.0
*/
public function setDbo(DatabaseInterface $db)
{
$this->db = $db;
return $this;
}
/**
* Sets an internal option to export the structure of the input table(s).
*
* @param boolean $setting True to export the structure, false to not.
*
* @return $this
*
* @since 1.0
*/
public function withStructure($setting = true)
{
$this->options->withStructure = (bool) $setting;
return $this;
}
/**
* Sets an internal option to export the data of the input table(s).
*
* @param boolean $setting True to export the data, false to not.
*
* @return $this
*
* @since 2.0.0
*/
public function withData($setting = false)
{
$this->options->withData = (bool) $setting;
return $this;
}
/**
* Builds the XML data to export.
*
* @return array An array of XML lines (strings).
*
* @since 2.0.0
* @throws \Exception if an error occurs.
*/
protected function buildXmlData()
{
$buffer = [];
foreach ($this->from as $table) {
// Replace the magic prefix if found.
$table = $this->getGenericTableName($table);
// Get the details columns information.
$fields = $this->db->getTableColumns($table, false);
$colblob = [];
foreach ($fields as $field) {
// Catch blob for conversion xml
if ($field->Type == 'mediumblob') {
$colblob[] = $field->Field;
}
}
$this->db->setQuery(
$this->db->getQuery(true)
->select($this->db->quoteName(array_keys($fields)))
->from($this->db->quoteName($table))
);
$rows = $this->db->loadObjectList();
if (!count($rows)) {
continue;
}
$buffer[] = ' <table_data name="' . $table . '">';
foreach ($rows as $row) {
$buffer[] = ' <row>';
foreach ($row as $key => $value) {
if (!in_array($key, $colblob)) {
if (is_null($value)) {
$buffer[] = ' <field name="' . $key . '" value_is_null></field>';
} else {
$buffer[] = ' <field name="' . $key . '">' . htmlspecialchars($value, ENT_COMPAT, 'UTF-8') . '</field>';
}
} else {
$buffer[] = ' <field name="' . $key . '">' . base64_encode($value) . '</field>';
}
}
$buffer[] = ' </row>';
}
$buffer[] = ' </table_data>';
}
return $buffer;
}
}

View File

@ -0,0 +1,171 @@
<?php
/**
* Part of the Joomla Framework Database Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Database;
/**
* Joomla Framework Database Factory class
*
* @since 1.0
*/
class DatabaseFactory
{
/**
* Method to return a database driver based on the given options.
*
* There are three global options and then the rest are specific to the database driver. The 'database' option determines which database is to
* be used for the connection. The 'select' option determines whether the connector should automatically select the chosen database.
*
* @param string $name Name of the database driver you'd like to instantiate
* @param array $options Parameters to be passed to the database driver.
*
* @return DatabaseInterface
*
* @since 1.0
* @throws Exception\UnsupportedAdapterException if there is not a compatible database driver
*/
public function getDriver(string $name = 'mysqli', array $options = []): DatabaseInterface
{
// Sanitize the database connector options.
$options['driver'] = preg_replace('/[^A-Z0-9_\.-]/i', '', $name);
$options['database'] = $options['database'] ?? null;
$options['select'] = $options['select'] ?? true;
$options['factory'] = $options['factory'] ?? $this;
// Derive the class name from the driver.
$class = __NAMESPACE__ . '\\' . ucfirst(strtolower($options['driver'])) . '\\' . ucfirst(strtolower($options['driver'])) . 'Driver';
// If the class still doesn't exist we have nothing left to do but throw an exception. We did our best.
if (!class_exists($class)) {
throw new Exception\UnsupportedAdapterException(sprintf('Unable to load Database Driver: %s', $options['driver']));
}
return new $class($options);
}
/**
* Gets an exporter class object.
*
* @param string $name Name of the driver you want an exporter for.
* @param DatabaseInterface|null $db Optional database driver to inject into the query object.
*
* @return DatabaseExporter
*
* @since 1.0
* @throws Exception\UnsupportedAdapterException if there is not a compatible database exporter
*/
public function getExporter(string $name, ?DatabaseInterface $db = null): DatabaseExporter
{
// Derive the class name from the driver.
$class = __NAMESPACE__ . '\\' . ucfirst(strtolower($name)) . '\\' . ucfirst(strtolower($name)) . 'Exporter';
// Make sure we have an exporter class for this driver.
if (!class_exists($class)) {
// If it doesn't exist we are at an impasse so throw an exception.
throw new Exception\UnsupportedAdapterException('Database Exporter not found.');
}
/** @var DatabaseExporter $o */
$o = new $class();
if ($db) {
$o->setDbo($db);
}
return $o;
}
/**
* Gets an importer class object.
*
* @param string $name Name of the driver you want an importer for.
* @param DatabaseInterface|null $db Optional database driver to inject into the query object.
*
* @return DatabaseImporter
*
* @since 1.0
* @throws Exception\UnsupportedAdapterException if there is not a compatible database importer
*/
public function getImporter(string $name, ?DatabaseInterface $db = null): DatabaseImporter
{
// Derive the class name from the driver.
$class = __NAMESPACE__ . '\\' . ucfirst(strtolower($name)) . '\\' . ucfirst(strtolower($name)) . 'Importer';
// Make sure we have an importer class for this driver.
if (!class_exists($class)) {
// If it doesn't exist we are at an impasse so throw an exception.
throw new Exception\UnsupportedAdapterException('Database importer not found.');
}
/** @var DatabaseImporter $o */
$o = new $class();
if ($db) {
$o->setDbo($db);
}
return $o;
}
/**
* Get a new iterator on the current query.
*
* @param string $name Name of the driver you want an iterator for.
* @param StatementInterface $statement Statement holding the result set to be iterated.
* @param string|null $column An optional column to use as the iterator key.
* @param string $class The class of object that is returned.
*
* @return DatabaseIterator
*
* @since 2.0.0
*/
public function getIterator(
string $name,
StatementInterface $statement,
?string $column = null,
string $class = \stdClass::class
): DatabaseIterator {
// Derive the class name from the driver.
$iteratorClass = __NAMESPACE__ . '\\' . ucfirst($name) . '\\' . ucfirst($name) . 'Iterator';
// Make sure we have an iterator class for this driver.
if (!class_exists($iteratorClass)) {
// We can work with the base iterator class so use that
$iteratorClass = DatabaseIterator::class;
}
// Return a new iterator
return new $iteratorClass($statement, $column, $class);
}
/**
* Get the current query object or a new Query object.
*
* @param string $name Name of the driver you want an query object for.
* @param DatabaseInterface|null $db Optional database driver to inject into the query object.
*
* @return QueryInterface
*
* @since 1.0
* @throws Exception\UnsupportedAdapterException if there is not a compatible database query object
*/
public function getQuery(string $name, ?DatabaseInterface $db = null): QueryInterface
{
// Derive the class name from the driver.
$class = __NAMESPACE__ . '\\' . ucfirst(strtolower($name)) . '\\' . ucfirst(strtolower($name)) . 'Query';
// Make sure we have a query class for this driver.
if (!class_exists($class)) {
// If it doesn't exist we are at an impasse so throw an exception.
throw new Exception\UnsupportedAdapterException('Database Query class not found');
}
return new $class($db);
}
}

View File

@ -0,0 +1,376 @@
<?php
/**
* Part of the Joomla Framework Database Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Database;
/**
* Joomla Framework Database Importer Class
*
* @since 1.0
*/
abstract class DatabaseImporter
{
/**
* An array of cached data.
*
* @var array
* @since 1.0
*/
protected $cache = ['columns' => [], 'keys' => []];
/**
* The database connector to use for exporting structure and/or data.
*
* @var DatabaseInterface
* @since 1.0
*/
protected $db;
/**
* The input source.
*
* @var mixed
* @since 1.0
*/
protected $from = [];
/**
* The type of input format.
*
* @var string
* @since 1.0
*/
protected $asFormat = 'xml';
/**
* An array of options for the exporter.
*
* @var \stdClass
* @since 1.0
*/
protected $options;
/**
* Constructor.
*
* Sets up the default options for the importer.
*
* @since 1.0
*/
public function __construct()
{
$this->options = new \stdClass();
// Set up the class defaults:
// Import with only structure
$this->withStructure();
// Export as XML.
$this->asXml();
// Default destination is a string using $output = (string) $importer;
}
/**
* Set the output option for the importer to XML format.
*
* @return $this
*
* @since 1.0
*/
public function asXml()
{
$this->asFormat = 'xml';
return $this;
}
/**
* Checks if all data and options are in order prior to importer.
*
* @return $this
*
* @since 1.0
* @throws \RuntimeException
*/
abstract public function check();
/**
* Specifies the data source to import.
*
* @param \SimpleXMLElement|string $from The data source to import, either as a SimpleXMLElement object or XML string.
*
* @return $this
*
* @since 1.0
*/
public function from($from)
{
$this->from = $from;
return $this;
}
/**
* Get the SQL syntax to add a column.
*
* @param string $table The table name.
* @param \SimpleXMLElement $field The XML field definition.
*
* @return string
*
* @since 1.0
*/
protected function getAddColumnSql($table, \SimpleXMLElement $field)
{
return 'ALTER TABLE ' . $this->db->quoteName($table) . ' ADD COLUMN ' . $this->getColumnSQL($field);
}
/**
* Get alters for table if there is a difference.
*
* @param \SimpleXMLElement $structure The XML structure of the table.
*
* @return array
*
* @since 2.0.0
*/
abstract protected function getAlterTableSql(\SimpleXMLElement $structure);
/**
* Get the syntax to alter a column.
*
* @param string $table The name of the database table to alter.
* @param \SimpleXMLElement $field The XML definition for the field.
*
* @return string
*
* @since 1.0
*/
protected function getChangeColumnSql($table, \SimpleXMLElement $field)
{
return 'ALTER TABLE ' . $this->db->quoteName($table) . ' CHANGE COLUMN ' . $this->db->quoteName((string) $field['Field']) . ' '
. $this->getColumnSQL($field);
}
/**
* Get the SQL syntax for a single column that would be included in a table create or alter statement.
*
* @param \SimpleXMLElement $field The XML field definition.
*
* @return string
*
* @since 1.0
*/
abstract protected function getColumnSql(\SimpleXMLElement $field);
/**
* Get the SQL syntax to drop a column.
*
* @param string $table The table name.
* @param string $name The name of the field to drop.
*
* @return string
*
* @since 1.0
*/
protected function getDropColumnSql($table, $name)
{
return 'ALTER TABLE ' . $this->db->quoteName($table) . ' DROP COLUMN ' . $this->db->quoteName($name);
}
/**
* Get the details list of keys for a table.
*
* @param array $keys An array of objects that comprise the keys for the table.
*
* @return array The lookup array. array({key name} => array(object, ...))
*
* @since 1.0
*/
protected function getKeyLookup($keys)
{
// First pass, create a lookup of the keys.
$lookup = [];
foreach ($keys as $key) {
if ($key instanceof \SimpleXMLElement) {
$kName = (string) $key['Key_name'];
} else {
$kName = $key->Key_name;
}
if (empty($lookup[$kName])) {
$lookup[$kName] = [];
}
$lookup[$kName][] = $key;
}
return $lookup;
}
/**
* Get the real name of the table, converting the prefix wildcard string if present.
*
* @param string $table The name of the table.
*
* @return string The real name of the table.
*
* @since 1.0
*/
protected function getRealTableName($table)
{
$prefix = $this->db->getPrefix();
// Replace the magic prefix if found.
$table = preg_replace('|^#__|', $prefix, $table);
return $table;
}
/**
* Import the data from the source into the existing tables.
*
* @return void
*
* @note Currently only supports XML format.
* @since 2.0.0
* @throws \RuntimeException on error.
*/
public function importData()
{
if ($this->from instanceof \SimpleXMLElement) {
$xml = $this->from;
} else {
$xml = new \SimpleXMLElement($this->from);
}
// Get all the table definitions.
$xmlTables = $xml->xpath('database/table_data');
foreach ($xmlTables as $table) {
// Convert the magic prefix into the real table name.
$tableName = $this->getRealTableName((string) $table['name']);
$rows = $table->children();
foreach ($rows as $row) {
if ($row->getName() == 'row') {
$entry = new \stdClass();
foreach ($row->children() as $data) {
if (isset($data['value_is_null'])) {
$entry->{(string) $data['name']} = null;
} else {
$entry->{(string) $data['name']} = (string) $data;
}
}
$this->db->insertObject($tableName, $entry);
}
}
}
}
/**
* Merges the incoming structure definition with the existing structure.
*
* @return void
*
* @note Currently only supports XML format.
* @since 1.0
* @throws \RuntimeException on error.
*/
public function mergeStructure()
{
$tables = $this->db->getTableList();
if ($this->from instanceof \SimpleXMLElement) {
$xml = $this->from;
} else {
$xml = new \SimpleXMLElement($this->from);
}
// Get all the table definitions.
$xmlTables = $xml->xpath('database/table_structure');
foreach ($xmlTables as $table) {
// Convert the magic prefix into the real table name.
$tableName = $this->getRealTableName((string) $table['name']);
if (\in_array($tableName, $tables, true)) {
// The table already exists. Now check if there is any difference.
if ($queries = $this->getAlterTableSql($table)) {
// Run the queries to upgrade the data structure.
foreach ($queries as $query) {
$this->db->setQuery((string) $query);
$this->db->execute();
}
}
} else {
// This is a new table.
$sql = $this->xmlToCreate($table);
$queries = explode(';', (string) $sql);
foreach ($queries as $query) {
if (!empty($query)) {
$this->db->setQuery((string) $query);
$this->db->execute();
}
}
}
}
}
/**
* Sets the database connector to use for exporting structure and/or data.
*
* @param DatabaseInterface $db The database connector.
*
* @return $this
*
* @since 1.0
*/
public function setDbo(DatabaseInterface $db)
{
$this->db = $db;
return $this;
}
/**
* Sets an internal option to merge the structure based on the input data.
*
* @param boolean $setting True to import the structure, false to not.
*
* @return $this
*
* @since 1.0
*/
public function withStructure($setting = true)
{
$this->options->withStructure = (bool) $setting;
return $this;
}
/**
* Get the SQL syntax to add a table.
*
* @param \SimpleXMLElement $table The table information.
*
* @return string
*
* @since 2.0.0
* @throws \RuntimeException
*/
abstract protected function xmlToCreate(\SimpleXMLElement $table);
}

View File

@ -0,0 +1,619 @@
<?php
/**
* Part of the Joomla Framework Database Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Database;
/**
* Joomla Framework Database Interface
*
* @since 1.0
*/
interface DatabaseInterface
{
/**
* Connects to the database if needed.
*
* @return void
*
* @since 2.0.0
* @throws \RuntimeException
*/
public function connect();
/**
* Determines if the connection to the server is active.
*
* @return boolean
*
* @since 2.0.0
*/
public function connected();
/**
* Create a new database using information from $options object.
*
* @param \stdClass $options Object used to pass user and database name to database driver. This object must have "db_name" and "db_user" set.
* @param boolean $utf True if the database supports the UTF-8 character set.
*
* @return boolean|resource
*
* @since 2.0.0
* @throws \RuntimeException
*/
public function createDatabase($options, $utf = true);
/**
* Replace special placeholder representing binary field with the original string.
*
* @param string|resource $data Encoded string or resource.
*
* @return string The original string.
*
* @since 1.7.0
*/
public function decodeBinary($data);
/**
* Disconnects the database.
*
* @return void
*
* @since 2.0.0
*/
public function disconnect();
/**
* Drops a table from the database.
*
* @param string $table The name of the database table to drop.
* @param boolean $ifExists Optionally specify that the table must exist before it is dropped.
*
* @return $this
*
* @since 2.0.0
* @throws \RuntimeException
*/
public function dropTable($table, $ifExists = true);
/**
* Escapes a string for usage in an SQL statement.
*
* @param string $text The string to be escaped.
* @param boolean $extra Optional parameter to provide extra escaping.
*
* @return string The escaped string.
*
* @since 2.0.0
*/
public function escape($text, $extra = false);
/**
* Execute the SQL statement.
*
* @return boolean
*
* @since 2.0.0
* @throws \RuntimeException
*/
public function execute();
/**
* Get the number of affected rows for the previous executed SQL statement.
*
* @return integer
*
* @since 2.0.0
*/
public function getAffectedRows();
/**
* Method to get the database collation in use by sampling a text field of a table in the database.
*
* @return string|boolean The collation in use by the database or boolean false if not supported.
*
* @since 2.0.0
*/
public function getCollation();
/**
* Method that provides access to the underlying database connection.
*
* @return resource The underlying database connection resource.
*
* @since 2.0.0
*/
public function getConnection();
/**
* Method to get the database connection collation, as reported by the driver.
*
* If the connector doesn't support reporting this value please return an empty string.
*
* @return string
*
* @since 2.0.0
*/
public function getConnectionCollation();
/**
* Method to get the database encryption details (cipher and protocol) in use.
*
* @return string The database encryption details.
*
* @since 2.0.0
*/
public function getConnectionEncryption(): string;
/**
* Method to test if the database TLS connections encryption are supported.
*
* @return boolean Whether the database supports TLS connections encryption.
*
* @since 2.0.0
*/
public function isConnectionEncryptionSupported(): bool;
/**
* Method to check whether the installed database version is supported by the database driver
*
* @return boolean True if the database version is supported
*
* @since 2.0.0
*/
public function isMinimumVersion();
/**
* Get the total number of SQL statements executed by the database driver.
*
* @return integer
*
* @since 2.0.0
*/
public function getCount();
/**
* Returns a PHP date() function compliant date format for the database driver.
*
* @return string
*
* @since 2.0.0
*/
public function getDateFormat();
/**
* Get the minimum supported database version.
*
* @return string
*
* @since 2.0.0
*/
public function getMinimum();
/**
* Get the name of the database driver.
*
* @return string
*
* @since 2.0.0
*/
public function getName();
/**
* Get the null or zero representation of a timestamp for the database driver.
*
* @return string
*
* @since 2.0.0
*/
public function getNullDate();
/**
* Get the common table prefix for the database driver.
*
* @return string The common database table prefix.
*
* @since 3.0
*/
public function getPrefix();
/**
* Get the number of returned rows for the previous executed SQL statement.
*
* @return integer
*
* @since 2.0.0
*/
public function getNumRows();
/**
* Get the current query object or a new QueryInterface object.
*
* @param boolean $new False to return the current query object, True to return a new QueryInterface object.
*
* @return QueryInterface
*
* @since 2.0.0
* @throws \RuntimeException
*/
public function getQuery($new = false);
/**
* Get the server family type.
*
* @return string
*
* @since 2.0.0
*/
public function getServerType();
/**
* Retrieves field information about the given tables.
*
* @param string $table The name of the database table.
* @param boolean $typeOnly True (default) to only return field types.
*
* @return array
*
* @since 2.0.0
* @throws \RuntimeException
*/
public function getTableColumns($table, $typeOnly = true);
/**
* Retrieves field information about the given tables.
*
* @param mixed $tables A table name or a list of table names.
*
* @return array
*
* @since 2.0.0
* @throws \RuntimeException
*/
public function getTableKeys($tables);
/**
* Method to get an array of all tables in the database.
*
* @return array
*
* @since 2.0.0
* @throws \RuntimeException
*/
public function getTableList();
/**
* Get the version of the database connector.
*
* @return string
*
* @since 2.0.0
*/
public function getVersion();
/**
* Determine whether or not the database engine supports UTF-8 character encoding.
*
* @return boolean True if the database engine supports UTF-8 character encoding.
*
* @since 2.0.0
*/
public function hasUtfSupport();
/**
* Method to get the auto-incremented value from the last INSERT statement.
*
* @return mixed The value of the auto-increment field from the last inserted row.
*
* @since 2.0.0
*/
public function insertid();
/**
* Inserts a row into a table based on an object's properties.
*
* @param string $table The name of the database table to insert into.
* @param object $object A reference to an object whose public properties match the table fields.
* @param string $key The name of the primary key. If provided the object property is updated.
*
* @return boolean
*
* @since 2.0.0
* @throws \RuntimeException
*/
public function insertObject($table, &$object, $key = null);
/**
* Test to see if the connector is available.
*
* @return boolean
*
* @since 1.0
*/
public static function isSupported();
/**
* Method to get the first row of the result set from the database query as an associative array of ['field_name' => 'row_value'].
*
* @return mixed The return value or null if the query failed.
*
* @since 2.0.0
* @throws \RuntimeException
*/
public function loadAssoc();
/**
* Method to get an array of the result set rows from the database query where each row is an associative array
* of ['field_name' => 'row_value']. The array of rows can optionally be keyed by a field name, but defaults to
* a sequential numeric array.
*
* NOTE: Choosing to key the result array by a non-unique field name can result in unwanted
* behavior and should be avoided.
*
* @param string $key The name of a field on which to key the result array.
* @param string $column An optional column name. Instead of the whole row, only this column value will be in the result array.
*
* @return mixed The return value or null if the query failed.
*
* @since 2.0.0
* @throws \RuntimeException
*/
public function loadAssocList($key = null, $column = null);
/**
* Method to get an array of values from the <var>$offset</var> field in each row of the result set from the database query.
*
* @param integer $offset The row offset to use to build the result array.
*
* @return mixed The return value or null if the query failed.
*
* @since 2.0.0
* @throws \RuntimeException
*/
public function loadColumn($offset = 0);
/**
* Method to get the first row of the result set from the database query as an object.
*
* @param string $class The class name to use for the returned row object.
*
* @return mixed The return value or null if the query failed.
*
* @since 2.0.0
* @throws \RuntimeException
*/
public function loadObject($class = \stdClass::class);
/**
* Method to get an array of the result set rows from the database query where each row is an object. The array
* of objects can optionally be keyed by a field name, but defaults to a sequential numeric array.
*
* NOTE: Choosing to key the result array by a non-unique field name can result in unwanted behavior and should be avoided.
*
* @param string $key The name of a field on which to key the result array.
* @param string $class The class name to use for the returned row objects.
*
* @return mixed The return value or null if the query failed.
*
* @since 2.0.0
* @throws \RuntimeException
*/
public function loadObjectList($key = '', $class = \stdClass::class);
/**
* Method to get the first field of the first row of the result set from the database query.
*
* @return mixed The return value or null if the query failed.
*
* @since 2.0.0
* @throws \RuntimeException
*/
public function loadResult();
/**
* Method to get the first row of the result set from the database query as an array.
*
* Columns are indexed numerically so the first column in the result set would be accessible via <var>$row[0]</var>, etc.
*
* @return mixed The return value or null if the query failed.
*
* @since 2.0.0
* @throws \RuntimeException
*/
public function loadRow();
/**
* Method to get an array of the result set rows from the database query where each row is an array. The array
* of objects can optionally be keyed by a field offset, but defaults to a sequential numeric array.
*
* NOTE: Choosing to key the result array by a non-unique field can result in unwanted behavior and should be avoided.
*
* @param string $key The name of a field on which to key the result array.
*
* @return mixed The return value or null if the query failed.
*
* @since 2.0.0
* @throws \RuntimeException
*/
public function loadRowList($key = null);
/**
* Locks a table in the database.
*
* @param string $tableName The name of the table to unlock.
*
* @return $this
*
* @since 2.0.0
* @throws \RuntimeException
*/
public function lockTable($tableName);
/**
* Quotes and optionally escapes a string to database requirements for use in database queries.
*
* @param array|string $text A string or an array of strings to quote.
* @param boolean $escape True (default) to escape the string, false to leave it unchanged.
*
* @return string
*
* @since 2.0.0
*/
public function quote($text, $escape = true);
/**
* Quotes a binary string to database requirements for use in database queries.
*
* @param string $data A binary string to quote.
*
* @return string The binary quoted input string.
*
* @since 1.7.0
*/
public function quoteBinary($data);
/**
* Wrap an SQL statement identifier name such as column, table or database names in quotes to prevent injection
* risks and reserved word conflicts.
*
* @param array|string $name The identifier name to wrap in quotes, or an array of identifier names to wrap in quotes.
* Each type supports dot-notation name.
* @param array|string $as The AS query part associated to $name. It can be string or array, in latter case it has to be
* same length of $name; if is null there will not be any AS part for string or array element.
*
* @return array|string The quote wrapped name, same type of $name.
*
* @since 2.0.0
*/
public function quoteName($name, $as = null);
/**
* Renames a table in the database.
*
* @param string $oldTable The name of the table to be renamed
* @param string $newTable The new name for the table.
* @param string $backup Table prefix
* @param string $prefix For the table - used to rename constraints in non-mysql databases
*
* @return $this
*
* @since 2.0.0
* @throws \RuntimeException
*/
public function renameTable($oldTable, $newTable, $backup = null, $prefix = null);
/**
* This function replaces a string identifier with the configured table prefix.
*
* @param string $sql The SQL statement to prepare.
* @param string $prefix The table prefix.
*
* @return string The processed SQL statement.
*
* @since 2.0.0
*/
public function replacePrefix($sql, $prefix = '#__');
/**
* Select a database for use.
*
* @param string $database The name of the database to select for use.
*
* @return boolean
*
* @since 2.0.0
* @throws \RuntimeException
*/
public function select($database);
/**
* Sets the SQL statement string for later execution.
*
* @param mixed $query The SQL statement to set either as a Query object or a string.
* @param integer $offset The affected row offset to set. {@deprecated 3.0 Use LimitableInterface::setLimit() instead}
* @param integer $limit The maximum affected rows to set. {@deprecated 3.0 Use LimitableInterface::setLimit() instead}
*
* @return $this
*
* @since 2.0.0
*/
public function setQuery($query, $offset = 0, $limit = 0);
/**
* Method to commit a transaction.
*
* @param boolean $toSavepoint If true, commit to the last savepoint.
*
* @return void
*
* @since 2.0.0
* @throws \RuntimeException
*/
public function transactionCommit($toSavepoint = false);
/**
* Method to roll back a transaction.
*
* @param boolean $toSavepoint If true, rollback to the last savepoint.
*
* @return void
*
* @since 2.0.0
* @throws \RuntimeException
*/
public function transactionRollback($toSavepoint = false);
/**
* Method to initialize a transaction.
*
* @param boolean $asSavepoint If true and a transaction is already active, a savepoint will be created.
*
* @return void
*
* @since 2.0.0
* @throws \RuntimeException
*/
public function transactionStart($asSavepoint = false);
/**
* Method to truncate a table.
*
* @param string $table The table to truncate
*
* @return void
*
* @since 2.0.0
* @throws \RuntimeException
*/
public function truncateTable($table);
/**
* Unlocks tables in the database.
*
* @return $this
*
* @since 2.0.0
* @throws \RuntimeException
*/
public function unlockTables();
/**
* Updates a row in a table based on an object's properties.
*
* @param string $table The name of the database table to update.
* @param object $object A reference to an object whose public properties match the table fields.
* @param array|string $key The name of the primary key.
* @param boolean $nulls True to update null fields or false to ignore them.
*
* @return boolean
*
* @since 2.0.0
* @throws \RuntimeException
*/
public function updateObject($table, &$object, $key, $nulls = false);
}

View File

@ -0,0 +1,246 @@
<?php
/**
* Part of the Joomla Framework Database Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Database;
/**
* Joomla Framework Database Driver Class
*
* @since 1.0
*/
class DatabaseIterator implements \Countable, \Iterator
{
/**
* The class of object to create.
*
* @var string
* @since 1.0
*/
protected $class;
/**
* The name of the column to use for the key of the database record.
*
* @var mixed
* @since 1.0
*/
private $column;
/**
* The current database record.
*
* @var mixed
* @since 1.0
*/
private $current;
/**
* A numeric or string key for the current database record.
*
* @var scalar
* @since 1.0
*/
private $key;
/**
* The number of fetched records.
*
* @var integer
* @since 1.0
*/
private $fetched = 0;
/**
* The statement holding the result set to iterate.
*
* @var StatementInterface
* @since 1.0
*/
protected $statement;
/**
* Database iterator constructor.
*
* @param StatementInterface $statement The statement holding the result set to iterate.
* @param string $column An option column to use as the iterator key.
* @param string $class The class of object that is returned.
*
* @since 1.0
* @throws \InvalidArgumentException
*/
public function __construct(StatementInterface $statement, $column = null, $class = \stdClass::class)
{
if (!class_exists($class)) {
throw new \InvalidArgumentException(sprintf('new %s(*%s*, cursor)', \get_class($this), \gettype($class)));
}
if ($statement) {
$fetchMode = $class === \stdClass::class ? FetchMode::STANDARD_OBJECT : FetchMode::CUSTOM_OBJECT;
// PDO doesn't allow extra arguments for \PDO::FETCH_CLASS, so only forward the class for the custom object mode
if ($fetchMode === FetchMode::STANDARD_OBJECT) {
$statement->setFetchMode($fetchMode);
} else {
$statement->setFetchMode($fetchMode, $class);
}
}
$this->statement = $statement;
$this->class = $class;
$this->column = $column;
$this->fetched = 0;
$this->next();
}
/**
* Database iterator destructor.
*
* @since 1.0
*/
public function __destruct()
{
if ($this->statement) {
$this->freeResult();
}
}
/**
* Get the number of rows in the result set for the executed SQL given by the cursor.
*
* @return integer The number of rows in the result set.
*
* @see Countable::count()
* @since 1.0
*/
#[\ReturnTypeWillChange]
public function count()
{
if ($this->statement) {
return $this->statement->rowCount();
}
return 0;
}
/**
* The current element in the iterator.
*
* @return object
*
* @see Iterator::current()
* @since 1.0
*/
#[\ReturnTypeWillChange]
public function current()
{
return $this->current;
}
/**
* The key of the current element in the iterator.
*
* @return scalar
*
* @see Iterator::key()
* @since 1.0
*/
#[\ReturnTypeWillChange]
public function key()
{
return $this->key;
}
/**
* Moves forward to the next result from the SQL query.
*
* @return void
*
* @see Iterator::next()
* @since 1.0
*/
#[\ReturnTypeWillChange]
public function next()
{
// Set the default key as being the number of fetched object
$this->key = $this->fetched;
// Try to get an object
$this->current = $this->fetchObject();
// If an object has been found
if ($this->current) {
// Set the key as being the indexed column (if it exists)
if ($this->column && isset($this->current->{$this->column})) {
$this->key = $this->current->{$this->column};
}
// Update the number of fetched object
$this->fetched++;
}
}
/**
* Rewinds the iterator.
*
* This iterator cannot be rewound.
*
* @return void
*
* @see Iterator::rewind()
* @since 1.0
*/
#[\ReturnTypeWillChange]
public function rewind()
{
}
/**
* Checks if the current position of the iterator is valid.
*
* @return boolean
*
* @see Iterator::valid()
* @since 1.0
*/
#[\ReturnTypeWillChange]
public function valid()
{
return (bool) $this->current;
}
/**
* Method to fetch a row from the result set cursor as an object.
*
* @return mixed Either the next row from the result set or false if there are no more rows.
*
* @since 1.0
*/
protected function fetchObject()
{
if ($this->statement) {
return $this->statement->fetch();
}
return false;
}
/**
* Method to free up the memory used for the result set.
*
* @return void
*
* @since 1.0
*/
protected function freeResult()
{
if ($this->statement) {
$this->statement->closeCursor();
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,56 @@
<?php
/**
* Part of the Joomla Framework Database Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Database\Event;
use Joomla\Database\DatabaseInterface;
use Joomla\Event\Event;
/**
* Database connection event
*
* @since 2.0.0
*/
class ConnectionEvent extends Event
{
/**
* DatabaseInterface object for this event
*
* @var DatabaseInterface
* @since 2.0.0
*/
private $driver;
/**
* Constructor.
*
* @param string $name The event name.
* @param DatabaseInterface $driver The DatabaseInterface object for this event.
*
* @since 2.0.0
*/
public function __construct(string $name, DatabaseInterface $driver)
{
parent::__construct($name);
$this->driver = $driver;
}
/**
* Retrieve the DatabaseInterface object attached to this event.
*
* @return DatabaseInterface
*
* @since 2.0.0
*/
public function getDriver(): DatabaseInterface
{
return $this->driver;
}
}

View File

@ -0,0 +1,35 @@
<?php
/**
* Part of the Joomla Framework Database Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Database\Exception;
/**
* Exception class defining an error connecting to the database platform
*
* @since 1.5.0
*/
class ConnectionFailureException extends \RuntimeException
{
/**
* Construct the exception
*
* @param string $message The Exception message to throw. [optional]
* @param integer $code The Exception code. [optional]
* @param ?\Exception $previous The previous exception used for the exception chaining. [optional]
*
* @since 2.0.0
*/
public function __construct($message = '', $code = 0, \Exception $previous = null)
{
// PDO uses strings for exception codes, PHP forces numeric codes, so "force" the string code to be used
parent::__construct($message, 0, $previous);
$this->code = $code;
}
}

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