mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
Merge pull request #10643 from creative-commoners/pulls/5/replace-simpletest
DEP Replace thirdparty simpletest with symfony domcrawler
This commit is contained in:
commit
0ff7623230
@ -39,6 +39,7 @@
|
||||
"sminnee/callbacklist": "^0.1.1",
|
||||
"symfony/cache": "^6.1",
|
||||
"symfony/config": "^6.1",
|
||||
"symfony/dom-crawler": "^6.1",
|
||||
"symfony/filesystem": "^6.1",
|
||||
"symfony/mailer": "^6.1",
|
||||
"symfony/mime": "^6.1",
|
||||
|
@ -191,12 +191,12 @@ abstract class FunctionalTest extends SapphireTest implements TestOnly
|
||||
* @param string $formID HTML 'id' attribute of a form (loaded through a previous response)
|
||||
* @param string $button HTML 'name' attribute of the button (NOT the 'id' attribute)
|
||||
* @param array $data Map of GET/POST data.
|
||||
* @return HTTPResponse
|
||||
* @param bool $withSecurityToken Submit with the form's security token if there is one.
|
||||
*/
|
||||
public function submitForm($formID, $button = null, $data = [])
|
||||
public function submitForm(string $formID, string $button = null, array $data = [], bool $withSecurityToken = true): HTTPResponse
|
||||
{
|
||||
$this->cssParser = null;
|
||||
$response = $this->mainSession->submitForm($formID, $button, $data);
|
||||
$response = $this->mainSession->submitForm($formID, $button, $data, $withSecurityToken);
|
||||
if ($this->autoFollowRedirection && is_object($response) && $response->getHeader('Location')) {
|
||||
$response = $this->mainSession->followRedirection();
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
namespace SilverStripe\Dev;
|
||||
|
||||
use Exception;
|
||||
use InvalidArgumentException;
|
||||
use SilverStripe\Control\Controller;
|
||||
use SilverStripe\Control\Cookie_Backend;
|
||||
use SilverStripe\Control\Director;
|
||||
@ -12,9 +13,7 @@ use SilverStripe\Control\HTTPResponse_Exception;
|
||||
use SilverStripe\Control\Session;
|
||||
use SilverStripe\Core\Extensible;
|
||||
use SilverStripe\Core\Injector\Injector;
|
||||
use SimpleByName;
|
||||
use SimplePage;
|
||||
use SimplePageBuilder;
|
||||
use Symfony\Component\DomCrawler\Crawler;
|
||||
|
||||
/**
|
||||
* Represents a test usage session of a web-app
|
||||
@ -202,44 +201,54 @@ class TestSession
|
||||
* Wrong: array('mycheckboxvalues' => array(1 => 'one', 2 => 'two'))
|
||||
* Right: array('mycheckboxvalues[1]' => 'one', 'mycheckboxvalues[2]' => 'two')
|
||||
*
|
||||
* @see http://www.simpletest.org/en/form_testing_documentation.html
|
||||
*
|
||||
* @param string $formID HTML 'id' attribute of a form (loaded through a previous response)
|
||||
* @param string $button HTML 'name' attribute of the button (NOT the 'id' attribute)
|
||||
* @param array $data Map of GET/POST data.
|
||||
* @return HTTPResponse
|
||||
* @throws Exception
|
||||
* @param bool $withSecurityToken Submit with the form's security token if there is one.
|
||||
*/
|
||||
public function submitForm($formID, $button = null, $data = [])
|
||||
public function submitForm(string $formID, string $button = null, array $data = [], bool $withSecurityToken = true): HTTPResponse
|
||||
{
|
||||
/** @var Crawler $page */
|
||||
$page = $this->lastPage();
|
||||
if ($page) {
|
||||
$form = $page->getFormById($formID);
|
||||
if (!$form) {
|
||||
try {
|
||||
$formCrawler = $page->filterXPath("//form[@id='$formID']");
|
||||
$form = $formCrawler->form();
|
||||
} catch (InvalidArgumentException $e) {
|
||||
user_error("TestSession::submitForm failed to find the form {$formID}");
|
||||
}
|
||||
|
||||
foreach ($data as $k => $v) {
|
||||
$form->setField(new SimpleByName($k), $v);
|
||||
foreach ($data as $fieldName => $value) {
|
||||
if ($form->has($fieldName)) {
|
||||
$form->get($fieldName)->setValue($value);
|
||||
}
|
||||
}
|
||||
|
||||
// Add security token to submitted values
|
||||
if ($withSecurityToken && $form->has('SecurityID')) {
|
||||
$securityField = $page->filterXPath("//input[@id='{$formID}_SecurityID']");
|
||||
$form->get('SecurityID')->setValue($securityField->attr('value'));
|
||||
}
|
||||
|
||||
$values = $form->getPhpValues();
|
||||
|
||||
// Add button to submitted values
|
||||
if ($button) {
|
||||
$submission = $form->submitButton(new SimpleByName($button));
|
||||
if (!$submission) {
|
||||
$btnXpath = "//button[@name='$button'] | //input[@name='$button'][@type='button' or @type='submit']";
|
||||
if (!$formCrawler->children()->filterXPath($btnXpath)->count()) {
|
||||
throw new Exception("Can't find button '$button' to submit as part of test.");
|
||||
}
|
||||
} else {
|
||||
$submission = $form->submit();
|
||||
$values[$button] = true;
|
||||
}
|
||||
|
||||
$url = Director::makeRelative($form->getAction()->asString());
|
||||
|
||||
$postVars = [];
|
||||
parse_str($submission->_encode() ?? '', $postVars);
|
||||
return $this->post($url, $postVars);
|
||||
return $this->sendRequest(
|
||||
$form->getMethod(),
|
||||
Director::makeRelative($form->getUri()),
|
||||
$values
|
||||
);
|
||||
} else {
|
||||
user_error("TestSession::submitForm called when there is no form loaded."
|
||||
. " Visit the page with the form first", E_USER_WARNING);
|
||||
. " Visit the page with the form first", E_USER_WARNING);
|
||||
}
|
||||
}
|
||||
|
||||
@ -313,24 +322,11 @@ class TestSession
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the last response as a SimplePage object
|
||||
*
|
||||
* @return SimplePage The response if available
|
||||
* Get a DOM Crawler for the last response
|
||||
*/
|
||||
public function lastPage()
|
||||
public function lastPage(): Crawler
|
||||
{
|
||||
require_once("simpletest/http.php");
|
||||
require_once("simpletest/page.php");
|
||||
require_once("simpletest/form.php");
|
||||
|
||||
$builder = new SimplePageBuilder();
|
||||
if ($this->lastResponse) {
|
||||
$page = &$builder->parse(new TestSession_STResponseWrapper($this->lastResponse));
|
||||
$builder->free();
|
||||
unset($builder);
|
||||
|
||||
return $page;
|
||||
}
|
||||
return new Crawler($this->lastContent(), Director::absoluteURL($this->lastUrl()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,78 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Dev;
|
||||
|
||||
use SilverStripe\Control\HTTPResponse;
|
||||
|
||||
/**
|
||||
* Wrapper around HTTPResponse to make it look like a SimpleHTTPResposne
|
||||
*/
|
||||
class TestSession_STResponseWrapper
|
||||
{
|
||||
|
||||
/**
|
||||
* @var HTTPResponse
|
||||
*/
|
||||
private $response;
|
||||
|
||||
public function __construct(HTTPResponse $response)
|
||||
{
|
||||
$this->response = $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getContent()
|
||||
{
|
||||
return $this->response->getBody();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getError()
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null
|
||||
*/
|
||||
public function getSent()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getHeaders()
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string 'GET'
|
||||
*/
|
||||
public function getMethod()
|
||||
{
|
||||
return "GET";
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getUrl()
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null
|
||||
*/
|
||||
public function getRequestData()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
@ -33,8 +33,6 @@ define('TEST_SERVICES', __DIR__ . '/AopProxyServiceTest');
|
||||
/**
|
||||
* Tests for the dependency injector
|
||||
*
|
||||
* Note that these are SS conversions of the existing Simpletest unit tests
|
||||
*
|
||||
* @author marcus@silverstripe.com.au
|
||||
* @license BSD License http://silverstripe.org/bsd-license/
|
||||
*/
|
||||
|
@ -60,9 +60,7 @@ class EmailFieldTest extends FunctionalTest
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that input type='email' fields are submitted by SimpleTest
|
||||
*
|
||||
* @see SimpleTagBuilder::_createInputTag()
|
||||
* Check that input type='email' fields are submitted
|
||||
*/
|
||||
public function testEmailFieldPopulation()
|
||||
{
|
||||
|
@ -593,7 +593,7 @@ class FormTest extends FunctionalTest
|
||||
);
|
||||
}
|
||||
|
||||
public function testDisableSecurityTokenAcceptsSubmissionWithoutToken()
|
||||
public function testDisableSecurityTokenAcceptsSubmissionWithoutToken(): void
|
||||
{
|
||||
SecurityToken::enable();
|
||||
$expectedToken = SecurityToken::inst()->getValue();
|
||||
@ -646,16 +646,55 @@ class FormTest extends FunctionalTest
|
||||
count($tokenEls ?? []),
|
||||
'Token form field added for controller without disableSecurityToken()'
|
||||
);
|
||||
$token = (string)$tokenEls[0];
|
||||
$response = $this->submitForm(
|
||||
'Form_Form',
|
||||
null,
|
||||
[
|
||||
'Email' => 'test@test.com',
|
||||
'SecurityID' => $token
|
||||
]
|
||||
);
|
||||
$this->assertEquals(200, $response->getStatusCode(), 'Submission succeeds with security token');
|
||||
}
|
||||
|
||||
public function provideFormsSet()
|
||||
{
|
||||
return [
|
||||
'with security token' =>
|
||||
[
|
||||
['Form_Form', null, [ 'Email' => 'test@test.com' ], true],
|
||||
200,
|
||||
'Submission succeeds with security token',
|
||||
],
|
||||
'without security token' =>
|
||||
[
|
||||
['Form_Form', null, [ 'Email' => 'test@test.com' ], false],
|
||||
200,
|
||||
'Cannot submit form without security token',
|
||||
],
|
||||
'button with wrong name' =>
|
||||
[
|
||||
['Form_Form', 'undefined', [ 'Email' => 'test@test.com' ], true],
|
||||
null,
|
||||
"Can't find button 'undefined' to submit as part of test.",
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideFormsSet
|
||||
*/
|
||||
public function testSubmitFormWithSpecifiedParameters(
|
||||
array $formData,
|
||||
?int $statusCode,
|
||||
string $testMessage
|
||||
): void {
|
||||
|
||||
$this->get('FormTest_ControllerWithSecurityToken');
|
||||
|
||||
[ $form, $button, $data, $withSecurityToken ] = [ ...$formData ];
|
||||
|
||||
if (is_null($button)) {
|
||||
$response = $this->submitForm($form, $button, $data, $withSecurityToken);
|
||||
$this->assertEquals($statusCode, $response->getStatusCode(), $testMessage);
|
||||
} else {
|
||||
// Test nonexistent button Exceptions
|
||||
$this->expectException(\Exception::class);
|
||||
$this->expectExceptionMessage($testMessage);
|
||||
|
||||
$this->submitForm($form, $button, $data, $withSecurityToken);
|
||||
}
|
||||
}
|
||||
|
||||
public function testStrictFormMethodChecking()
|
||||
|
502
thirdparty/simpletest/LICENSE
vendored
502
thirdparty/simpletest/LICENSE
vendored
@ -1,502 +0,0 @@
|
||||
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!
|
1
thirdparty/simpletest/VERSION
vendored
1
thirdparty/simpletest/VERSION
vendored
@ -1 +0,0 @@
|
||||
1.0.1
|
173
thirdparty/simpletest/compatibility.php
vendored
173
thirdparty/simpletest/compatibility.php
vendored
@ -1,173 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* base include file for SimpleTest
|
||||
* @package SimpleTest
|
||||
* @version $Id: compatibility.php 1723 2008-04-08 00:34:10Z lastcraft $
|
||||
*/
|
||||
|
||||
/**
|
||||
* Static methods for compatibility between different
|
||||
* PHP versions.
|
||||
* @package SimpleTest
|
||||
*/
|
||||
class SimpleTestCompatibility {
|
||||
|
||||
/**
|
||||
* Creates a copy whether in PHP5 or PHP4.
|
||||
* @param object $object Thing to copy.
|
||||
* @return object A copy.
|
||||
* @access public
|
||||
* @static
|
||||
*/
|
||||
static function copy($object) {
|
||||
if (version_compare(phpversion() ?? '', '5') >= 0) {
|
||||
eval('$copy = clone $object;');
|
||||
return $copy;
|
||||
}
|
||||
return $object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Identity test. Drops back to equality + types for PHP5
|
||||
* objects as the === operator counts as the
|
||||
* stronger reference constraint.
|
||||
* @param mixed $first Test subject.
|
||||
* @param mixed $second Comparison object.
|
||||
* @return boolean True if identical.
|
||||
* @access public
|
||||
* @static
|
||||
*/
|
||||
function isIdentical($first, $second) {
|
||||
if (version_compare(phpversion() ?? '', '5') >= 0) {
|
||||
return SimpleTestCompatibility::_isIdenticalType($first, $second);
|
||||
}
|
||||
if ($first != $second) {
|
||||
return false;
|
||||
}
|
||||
return ($first === $second);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursive type test.
|
||||
* @param mixed $first Test subject.
|
||||
* @param mixed $second Comparison object.
|
||||
* @return boolean True if same type.
|
||||
* @access private
|
||||
* @static
|
||||
*/
|
||||
function _isIdenticalType($first, $second) {
|
||||
if (gettype($first) != gettype($second)) {
|
||||
return false;
|
||||
}
|
||||
if (is_object($first) && is_object($second)) {
|
||||
if (get_class($first) != get_class($second)) {
|
||||
return false;
|
||||
}
|
||||
return SimpleTestCompatibility::_isArrayOfIdenticalTypes(
|
||||
get_object_vars($first),
|
||||
get_object_vars($second));
|
||||
}
|
||||
if (is_array($first) && is_array($second)) {
|
||||
return SimpleTestCompatibility::_isArrayOfIdenticalTypes($first, $second);
|
||||
}
|
||||
if ($first !== $second) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursive type test for each element of an array.
|
||||
* @param mixed $first Test subject.
|
||||
* @param mixed $second Comparison object.
|
||||
* @return boolean True if identical.
|
||||
* @access private
|
||||
* @static
|
||||
*/
|
||||
function _isArrayOfIdenticalTypes($first, $second) {
|
||||
if (array_keys($first ?? []) != array_keys($second ?? [])) {
|
||||
return false;
|
||||
}
|
||||
foreach (array_keys($first ?? []) as $key) {
|
||||
$is_identical = SimpleTestCompatibility::_isIdenticalType(
|
||||
$first[$key],
|
||||
$second[$key]);
|
||||
if (! $is_identical) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for two variables being aliases.
|
||||
* @param mixed $first Test subject.
|
||||
* @param mixed $second Comparison object.
|
||||
* @return boolean True if same.
|
||||
* @access public
|
||||
* @static
|
||||
*/
|
||||
function isReference(&$first, &$second) {
|
||||
if (version_compare(phpversion() ?? '', '5', '>=') && is_object($first)) {
|
||||
return ($first === $second);
|
||||
}
|
||||
if (is_object($first) && is_object($second)) {
|
||||
$id = uniqid("test");
|
||||
$first->$id = true;
|
||||
$is_ref = isset($second->$id);
|
||||
unset($first->$id);
|
||||
return $is_ref;
|
||||
}
|
||||
$temp = $first;
|
||||
$first = uniqid("test");
|
||||
$is_ref = ($first === $second);
|
||||
$first = $temp;
|
||||
return $is_ref;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to see if an object is a member of a
|
||||
* class hiearchy.
|
||||
* @param object $object Object to test.
|
||||
* @param string $class Root name of hiearchy.
|
||||
* @return boolean True if class in hiearchy.
|
||||
* @access public
|
||||
* @static
|
||||
*/
|
||||
function isA($object, $class) {
|
||||
if (version_compare(phpversion() ?? '', '5') >= 0) {
|
||||
if (! class_exists($class ?? '', false)) {
|
||||
if (function_exists('interface_exists')) {
|
||||
if (! interface_exists($class ?? '', false)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
eval("\$is_a = \$object instanceof $class;");
|
||||
return $is_a;
|
||||
}
|
||||
if (function_exists('is_a')) {
|
||||
return is_a($object, $class ?? '');
|
||||
}
|
||||
return ((strtolower($class ?? '') == get_class($object))
|
||||
or (is_subclass_of($object, $class ?? '')));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a socket timeout for each chunk.
|
||||
* @param resource $handle Socket handle.
|
||||
* @param integer $timeout Limit in seconds.
|
||||
* @access public
|
||||
* @static
|
||||
*/
|
||||
function setTimeout($handle, $timeout) {
|
||||
if (function_exists('stream_set_timeout')) {
|
||||
stream_set_timeout($handle, $timeout ?? 0, 0);
|
||||
} elseif (function_exists('socket_set_timeout')) {
|
||||
socket_set_timeout($handle, $timeout ?? 0, 0);
|
||||
} elseif (function_exists('set_socket_timeout')) {
|
||||
set_socket_timeout($handle, $timeout, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
380
thirdparty/simpletest/cookies.php
vendored
380
thirdparty/simpletest/cookies.php
vendored
@ -1,380 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Base include file for SimpleTest
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
* @version $Id: cookies.php 1723 2008-04-08 00:34:10Z lastcraft $
|
||||
*/
|
||||
|
||||
/**#@+
|
||||
* include other SimpleTest class files
|
||||
*/
|
||||
require_once(dirname(__FILE__) . '/url.php');
|
||||
/**#@-*/
|
||||
|
||||
/**
|
||||
* Cookie data holder. Cookie rules are full of pretty
|
||||
* arbitary stuff. I have used...
|
||||
* http://wp.netscape.com/newsref/std/cookie_spec.html
|
||||
* http://www.cookiecentral.com/faq/
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
*/
|
||||
class SimpleCookie {
|
||||
var $_host;
|
||||
var $_name;
|
||||
var $_value;
|
||||
var $_path;
|
||||
var $_expiry;
|
||||
var $_is_secure;
|
||||
|
||||
/**
|
||||
* Constructor. Sets the stored values.
|
||||
* @param string $name Cookie key.
|
||||
* @param string $value Value of cookie.
|
||||
* @param string $path Cookie path if not host wide.
|
||||
* @param string $expiry Expiry date as string.
|
||||
* @param boolean $is_secure Currently ignored.
|
||||
*/
|
||||
function __construct($name, $value = false, $path = false, $expiry = false, $is_secure = false) {
|
||||
$this->_host = false;
|
||||
$this->_name = $name;
|
||||
$this->_value = $value;
|
||||
$this->_path = ($path ? $this->_fixPath($path) : "/");
|
||||
$this->_expiry = false;
|
||||
if (is_string($expiry)) {
|
||||
$this->_expiry = strtotime($expiry ?? '');
|
||||
} elseif (is_integer($expiry)) {
|
||||
$this->_expiry = $expiry;
|
||||
}
|
||||
$this->_is_secure = $is_secure;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the host. The cookie rules determine
|
||||
* that the first two parts are taken for
|
||||
* certain TLDs and three for others. If the
|
||||
* new host does not match these rules then the
|
||||
* call will fail.
|
||||
* @param string $host New hostname.
|
||||
* @return boolean True if hostname is valid.
|
||||
* @access public
|
||||
*/
|
||||
function setHost($host) {
|
||||
if ($host = $this->_truncateHost($host)) {
|
||||
$this->_host = $host;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for the truncated host to which this
|
||||
* cookie applies.
|
||||
* @return string Truncated hostname.
|
||||
* @access public
|
||||
*/
|
||||
function getHost() {
|
||||
return $this->_host;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for a cookie being valid for a host name.
|
||||
* @param string $host Host to test against.
|
||||
* @return boolean True if the cookie would be valid
|
||||
* here.
|
||||
*/
|
||||
function isValidHost($host) {
|
||||
return ($this->_truncateHost($host) === $this->getHost());
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts just the domain part that determines a
|
||||
* cookie's host validity.
|
||||
* @param string $host Host name to truncate.
|
||||
* @return string Domain or false on a bad host.
|
||||
* @access private
|
||||
*/
|
||||
function _truncateHost($host) {
|
||||
$tlds = SimpleUrl::getAllTopLevelDomains();
|
||||
if (preg_match('/[a-z\-]+\.(' . $tlds . ')$/i', $host ?? '', $matches)) {
|
||||
return $matches[0];
|
||||
} elseif (preg_match('/[a-z\-]+\.[a-z\-]+\.[a-z\-]+$/i', $host ?? '', $matches)) {
|
||||
return $matches[0];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for name.
|
||||
* @return string Cookie key.
|
||||
* @access public
|
||||
*/
|
||||
function getName() {
|
||||
return $this->_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for value. A deleted cookie will
|
||||
* have an empty string for this.
|
||||
* @return string Cookie value.
|
||||
* @access public
|
||||
*/
|
||||
function getValue() {
|
||||
return $this->_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for path.
|
||||
* @return string Valid cookie path.
|
||||
* @access public
|
||||
*/
|
||||
function getPath() {
|
||||
return $this->_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests a path to see if the cookie applies
|
||||
* there. The test path must be longer or
|
||||
* equal to the cookie path.
|
||||
* @param string $path Path to test against.
|
||||
* @return boolean True if cookie valid here.
|
||||
* @access public
|
||||
*/
|
||||
function isValidPath($path) {
|
||||
return (strncmp(
|
||||
$this->_fixPath($path) ?? '',
|
||||
$this->getPath() ?? '',
|
||||
strlen($this->getPath() ?? '')) == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for expiry.
|
||||
* @return string Expiry string.
|
||||
* @access public
|
||||
*/
|
||||
function getExpiry() {
|
||||
if (! $this->_expiry) {
|
||||
return false;
|
||||
}
|
||||
return gmdate("D, d M Y H:i:s", $this->_expiry) . " GMT";
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to see if cookie is expired against
|
||||
* the cookie format time or timestamp.
|
||||
* Will give true for a session cookie.
|
||||
* @param integer/string $now Time to test against. Result
|
||||
* will be false if this time
|
||||
* is later than the cookie expiry.
|
||||
* Can be either a timestamp integer
|
||||
* or a cookie format date.
|
||||
* @access public
|
||||
*/
|
||||
function isExpired($now) {
|
||||
if (! $this->_expiry) {
|
||||
return true;
|
||||
}
|
||||
if (is_string($now)) {
|
||||
$now = strtotime($now ?? '');
|
||||
}
|
||||
return ($this->_expiry < $now);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ages the cookie by the specified number of
|
||||
* seconds.
|
||||
* @param integer $interval In seconds.
|
||||
* @public
|
||||
*/
|
||||
function agePrematurely($interval) {
|
||||
if ($this->_expiry) {
|
||||
$this->_expiry -= $interval;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for the secure flag.
|
||||
* @return boolean True if cookie needs SSL.
|
||||
* @access public
|
||||
*/
|
||||
function isSecure() {
|
||||
return $this->_is_secure;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a trailing and leading slash to the path
|
||||
* if missing.
|
||||
* @param string $path Path to fix.
|
||||
* @access private
|
||||
*/
|
||||
function _fixPath($path) {
|
||||
if (substr($path ?? '', 0, 1) != '/') {
|
||||
$path = '/' . $path;
|
||||
}
|
||||
if (substr($path ?? '', -1, 1) != '/') {
|
||||
$path .= '/';
|
||||
}
|
||||
return $path;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Repository for cookies. This stuff is a
|
||||
* tiny bit browser dependent.
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
*/
|
||||
class SimpleCookieJar {
|
||||
var $_cookies;
|
||||
|
||||
/**
|
||||
* Constructor. Jar starts empty.
|
||||
* @access public
|
||||
*/
|
||||
function __construct() {
|
||||
$this->_cookies = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes expired and temporary cookies as if
|
||||
* the browser was closed and re-opened.
|
||||
* @param string/integer $now Time to test expiry against.
|
||||
* @access public
|
||||
*/
|
||||
function restartSession($date = false) {
|
||||
$surviving_cookies = array();
|
||||
for ($i = 0; $i < count($this->_cookies ?? []); $i++) {
|
||||
if (! $this->_cookies[$i]->getValue()) {
|
||||
continue;
|
||||
}
|
||||
if (! $this->_cookies[$i]->getExpiry()) {
|
||||
continue;
|
||||
}
|
||||
if ($date && $this->_cookies[$i]->isExpired($date)) {
|
||||
continue;
|
||||
}
|
||||
$surviving_cookies[] = $this->_cookies[$i];
|
||||
}
|
||||
$this->_cookies = $surviving_cookies;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ages all cookies in the cookie jar.
|
||||
* @param integer $interval The old session is moved
|
||||
* into the past by this number
|
||||
* of seconds. Cookies now over
|
||||
* age will be removed.
|
||||
* @access public
|
||||
*/
|
||||
function agePrematurely($interval) {
|
||||
for ($i = 0; $i < count($this->_cookies ?? []); $i++) {
|
||||
$this->_cookies[$i]->agePrematurely($interval);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an additional cookie. If a cookie has
|
||||
* the same name and path it is replaced.
|
||||
* @param string $name Cookie key.
|
||||
* @param string $value Value of cookie.
|
||||
* @param string $host Host upon which the cookie is valid.
|
||||
* @param string $path Cookie path if not host wide.
|
||||
* @param string $expiry Expiry date.
|
||||
* @access public
|
||||
*/
|
||||
function setCookie($name, $value, $host = false, $path = '/', $expiry = false) {
|
||||
$cookie = new SimpleCookie($name, $value, $path, $expiry);
|
||||
if ($host) {
|
||||
$cookie->setHost($host);
|
||||
}
|
||||
$this->_cookies[$this->_findFirstMatch($cookie)] = $cookie;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a matching cookie to write over or the
|
||||
* first empty slot if none.
|
||||
* @param SimpleCookie $cookie Cookie to write into jar.
|
||||
* @return integer Available slot.
|
||||
* @access private
|
||||
*/
|
||||
function _findFirstMatch($cookie) {
|
||||
for ($i = 0; $i < count($this->_cookies ?? []); $i++) {
|
||||
$is_match = $this->_isMatch(
|
||||
$cookie,
|
||||
$this->_cookies[$i]->getHost(),
|
||||
$this->_cookies[$i]->getPath(),
|
||||
$this->_cookies[$i]->getName());
|
||||
if ($is_match) {
|
||||
return $i;
|
||||
}
|
||||
}
|
||||
return count($this->_cookies ?? []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the most specific cookie value from the
|
||||
* browser cookies. Looks for the longest path that
|
||||
* matches.
|
||||
* @param string $host Host to search.
|
||||
* @param string $path Applicable path.
|
||||
* @param string $name Name of cookie to read.
|
||||
* @return string False if not present, else the
|
||||
* value as a string.
|
||||
* @access public
|
||||
*/
|
||||
function getCookieValue($host, $path, $name) {
|
||||
$longest_path = '';
|
||||
foreach ($this->_cookies as $cookie) {
|
||||
if ($this->_isMatch($cookie, $host, $path, $name)) {
|
||||
if (strlen($cookie->getPath() ?? '') > strlen($longest_path ?? '')) {
|
||||
$value = $cookie->getValue();
|
||||
$longest_path = $cookie->getPath();
|
||||
}
|
||||
}
|
||||
}
|
||||
return (isset($value) ? $value : false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests cookie for matching against search
|
||||
* criteria.
|
||||
* @param SimpleTest $cookie Cookie to test.
|
||||
* @param string $host Host must match.
|
||||
* @param string $path Cookie path must be shorter than
|
||||
* this path.
|
||||
* @param string $name Name must match.
|
||||
* @return boolean True if matched.
|
||||
* @access private
|
||||
*/
|
||||
function _isMatch($cookie, $host, $path, $name) {
|
||||
if ($cookie->getName() != $name) {
|
||||
return false;
|
||||
}
|
||||
if ($host && $cookie->getHost() && ! $cookie->isValidHost($host)) {
|
||||
return false;
|
||||
}
|
||||
if (! $cookie->isValidPath($path)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses a URL to sift relevant cookies by host and
|
||||
* path. Results are list of strings of form "name=value".
|
||||
* @param SimpleUrl $url Url to select by.
|
||||
* @return array Valid name and value pairs.
|
||||
* @access public
|
||||
*/
|
||||
function selectAsPairs($url) {
|
||||
$pairs = array();
|
||||
foreach ($this->_cookies as $cookie) {
|
||||
if ($this->_isMatch($cookie, $url->getHost(), $url->getPath(), $cookie->getName())) {
|
||||
$pairs[] = $cookie->getName() . '=' . $cookie->getValue();
|
||||
}
|
||||
}
|
||||
return $pairs;
|
||||
}
|
||||
}
|
||||
?>
|
552
thirdparty/simpletest/encoding.php
vendored
552
thirdparty/simpletest/encoding.php
vendored
@ -1,552 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* base include file for SimpleTest
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
* @version $Id: encoding.php 1723 2008-04-08 00:34:10Z lastcraft $
|
||||
*/
|
||||
|
||||
/**#@+
|
||||
* include other SimpleTest class files
|
||||
*/
|
||||
require_once(dirname(__FILE__) . '/socket.php');
|
||||
/**#@-*/
|
||||
|
||||
/**
|
||||
* Single post parameter.
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
*/
|
||||
class SimpleEncodedPair {
|
||||
var $_key;
|
||||
var $_value;
|
||||
|
||||
/**
|
||||
* Stashes the data for rendering later.
|
||||
* @param string $key Form element name.
|
||||
* @param string $value Data to send.
|
||||
*/
|
||||
function __construct($key, $value) {
|
||||
$this->_key = $key;
|
||||
$this->_value = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* The pair as a single string.
|
||||
* @return string Encoded pair.
|
||||
* @access public
|
||||
*/
|
||||
function asRequest() {
|
||||
return urlencode($this->_key ?? '') . '=' . urlencode($this->_value ?? '');
|
||||
}
|
||||
|
||||
/**
|
||||
* The MIME part as a string.
|
||||
* @return string MIME part encoding.
|
||||
* @access public
|
||||
*/
|
||||
function asMime() {
|
||||
$part = 'Content-Disposition: form-data; ';
|
||||
$part .= "name=\"" . $this->_key . "\"\r\n";
|
||||
$part .= "\r\n" . $this->_value;
|
||||
return $part;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this the value we are looking for?
|
||||
* @param string $key Identifier.
|
||||
* @return boolean True if matched.
|
||||
* @access public
|
||||
*/
|
||||
function isKey($key) {
|
||||
return $key == $this->_key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this the value we are looking for?
|
||||
* @return string Identifier.
|
||||
* @access public
|
||||
*/
|
||||
function getKey() {
|
||||
return $this->_key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this the value we are looking for?
|
||||
* @return string Content.
|
||||
* @access public
|
||||
*/
|
||||
function getValue() {
|
||||
return $this->_value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Single post parameter.
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
*/
|
||||
class SimpleAttachment {
|
||||
var $_key;
|
||||
var $_content;
|
||||
var $_filename;
|
||||
|
||||
/**
|
||||
* Stashes the data for rendering later.
|
||||
* @param string $key Key to add value to.
|
||||
* @param string $content Raw data.
|
||||
* @param hash $filename Original filename.
|
||||
*/
|
||||
function __construct($key, $content, $filename) {
|
||||
$this->_key = $key;
|
||||
$this->_content = $content;
|
||||
$this->_filename = $filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* The pair as a single string.
|
||||
* @return string Encoded pair.
|
||||
* @access public
|
||||
*/
|
||||
function asRequest() {
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* The MIME part as a string.
|
||||
* @return string MIME part encoding.
|
||||
* @access public
|
||||
*/
|
||||
function asMime() {
|
||||
$part = 'Content-Disposition: form-data; ';
|
||||
$part .= 'name="' . $this->_key . '"; ';
|
||||
$part .= 'filename="' . $this->_filename . '"';
|
||||
$part .= "\r\nContent-Type: " . $this->_deduceMimeType();
|
||||
$part .= "\r\n\r\n" . $this->_content;
|
||||
return $part;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to figure out the MIME type from the
|
||||
* file extension and the content.
|
||||
* @return string MIME type.
|
||||
* @access private
|
||||
*/
|
||||
function _deduceMimeType() {
|
||||
if ($this->_isOnlyAscii($this->_content)) {
|
||||
return 'text/plain';
|
||||
}
|
||||
return 'application/octet-stream';
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests each character is in the range 0-127.
|
||||
* @param string $ascii String to test.
|
||||
* @access private
|
||||
*/
|
||||
function _isOnlyAscii($ascii) {
|
||||
for ($i = 0, $length = strlen($ascii); $i < $length; $i++) {
|
||||
if (ord($ascii[$i] ?? '') > 127) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this the value we are looking for?
|
||||
* @param string $key Identifier.
|
||||
* @return boolean True if matched.
|
||||
* @access public
|
||||
*/
|
||||
function isKey($key) {
|
||||
return $key == $this->_key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this the value we are looking for?
|
||||
* @return string Identifier.
|
||||
* @access public
|
||||
*/
|
||||
function getKey() {
|
||||
return $this->_key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this the value we are looking for?
|
||||
* @return string Content.
|
||||
* @access public
|
||||
*/
|
||||
function getValue() {
|
||||
return $this->_filename;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bundle of GET/POST parameters. Can include
|
||||
* repeated parameters.
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
*/
|
||||
class SimpleEncoding {
|
||||
var $_request;
|
||||
|
||||
/**
|
||||
* Starts empty.
|
||||
* @param array $query Hash of parameters.
|
||||
* Multiple values are
|
||||
* as lists on a single key.
|
||||
* @access public
|
||||
*/
|
||||
function __construct($query = false) {
|
||||
if (! $query) {
|
||||
$query = array();
|
||||
}
|
||||
$this->clear();
|
||||
$this->merge($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Empties the request of parameters.
|
||||
* @access public
|
||||
*/
|
||||
function clear() {
|
||||
$this->_request = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a parameter to the query.
|
||||
* @param string $key Key to add value to.
|
||||
* @param string/array $value New data.
|
||||
* @access public
|
||||
*/
|
||||
function add($key, $value) {
|
||||
if ($value === false) {
|
||||
return;
|
||||
}
|
||||
if (is_array($value)) {
|
||||
foreach ($value as $item) {
|
||||
$this->_addPair($key, $item);
|
||||
}
|
||||
} else {
|
||||
$this->_addPair($key, $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new value into the request.
|
||||
* @param string $key Key to add value to.
|
||||
* @param string/array $value New data.
|
||||
* @access private
|
||||
*/
|
||||
function _addPair($key, $value) {
|
||||
$this->_request[] = new SimpleEncodedPair($key, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a MIME part to the query. Does nothing for a
|
||||
* form encoded packet.
|
||||
* @param string $key Key to add value to.
|
||||
* @param string $content Raw data.
|
||||
* @param hash $filename Original filename.
|
||||
* @access public
|
||||
*/
|
||||
function attach($key, $content, $filename) {
|
||||
$this->_request[] = new SimpleAttachment($key, $content, $filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a set of parameters to this query.
|
||||
* @param array/SimpleQueryString $query Multiple values are
|
||||
* as lists on a single key.
|
||||
* @access public
|
||||
*/
|
||||
function merge($query) {
|
||||
if (is_object($query)) {
|
||||
$this->_request = array_merge($this->_request, $query->getAll());
|
||||
} elseif (is_array($query)) {
|
||||
foreach ($query as $key => $value) {
|
||||
$this->add($key, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for single value.
|
||||
* @return string/array False if missing, string
|
||||
* if present and array if
|
||||
* multiple entries.
|
||||
* @access public
|
||||
*/
|
||||
function getValue($key) {
|
||||
$values = array();
|
||||
foreach ($this->_request as $pair) {
|
||||
if ($pair->isKey($key)) {
|
||||
$values[] = $pair->getValue();
|
||||
}
|
||||
}
|
||||
if (count($values ?? []) == 0) {
|
||||
return false;
|
||||
} elseif (count($values ?? []) == 1) {
|
||||
return $values[0];
|
||||
} else {
|
||||
return $values;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for listing of pairs.
|
||||
* @return array All pair objects.
|
||||
* @access public
|
||||
*/
|
||||
function getAll() {
|
||||
return $this->_request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the query string as a URL encoded
|
||||
* request part.
|
||||
* @return string Part of URL.
|
||||
* @access protected
|
||||
*/
|
||||
function _encode() {
|
||||
$statements = array();
|
||||
foreach ($this->_request as $pair) {
|
||||
if ($statement = $pair->asRequest()) {
|
||||
$statements[] = $statement;
|
||||
}
|
||||
}
|
||||
return implode('&', $statements);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bundle of GET parameters. Can include
|
||||
* repeated parameters.
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
*/
|
||||
class SimpleGetEncoding extends SimpleEncoding {
|
||||
|
||||
/**
|
||||
* Starts empty.
|
||||
* @param array $query Hash of parameters.
|
||||
* Multiple values are
|
||||
* as lists on a single key.
|
||||
* @access public
|
||||
*/
|
||||
function __construct($query = false) {
|
||||
parent::__construct($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* HTTP request method.
|
||||
* @return string Always GET.
|
||||
* @access public
|
||||
*/
|
||||
function getMethod() {
|
||||
return 'GET';
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes no extra headers.
|
||||
* @param SimpleSocket $socket Socket to write to.
|
||||
* @access public
|
||||
*/
|
||||
function writeHeadersTo(&$socket) {
|
||||
}
|
||||
|
||||
/**
|
||||
* No data is sent to the socket as the data is encoded into
|
||||
* the URL.
|
||||
* @param SimpleSocket $socket Socket to write to.
|
||||
* @access public
|
||||
*/
|
||||
function writeTo(&$socket) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the query string as a URL encoded
|
||||
* request part for attaching to a URL.
|
||||
* @return string Part of URL.
|
||||
* @access public
|
||||
*/
|
||||
function asUrlRequest() {
|
||||
return $this->_encode();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bundle of URL parameters for a HEAD request.
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
*/
|
||||
class SimpleHeadEncoding extends SimpleGetEncoding {
|
||||
|
||||
/**
|
||||
* Starts empty.
|
||||
* @param array $query Hash of parameters.
|
||||
* Multiple values are
|
||||
* as lists on a single key.
|
||||
* @access public
|
||||
*/
|
||||
function __construct($query = false) {
|
||||
parent::__construct($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* HTTP request method.
|
||||
* @return string Always HEAD.
|
||||
* @access public
|
||||
*/
|
||||
function getMethod() {
|
||||
return 'HEAD';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bundle of POST parameters. Can include
|
||||
* repeated parameters.
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
*/
|
||||
class SimplePostEncoding extends SimpleEncoding {
|
||||
|
||||
/**
|
||||
* Starts empty.
|
||||
* @param array $query Hash of parameters.
|
||||
* Multiple values are
|
||||
* as lists on a single key.
|
||||
* @access public
|
||||
*/
|
||||
function __construct($query = false) {
|
||||
if (is_array($query) and $this->hasMoreThanOneLevel($query)) {
|
||||
$query = $this->rewriteArrayWithMultipleLevels($query);
|
||||
}
|
||||
parent::__construct($query);
|
||||
}
|
||||
|
||||
function hasMoreThanOneLevel($query) {
|
||||
foreach ($query as $key => $value) {
|
||||
if (is_array($value)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function rewriteArrayWithMultipleLevels($query) {
|
||||
$query_ = array();
|
||||
foreach ($query as $key => $value) {
|
||||
if (is_array($value)) {
|
||||
foreach ($value as $sub_key => $sub_value) {
|
||||
$query_[$key."[".$sub_key."]"] = $sub_value;
|
||||
}
|
||||
} else {
|
||||
$query_[$key] = $value;
|
||||
}
|
||||
}
|
||||
if ($this->hasMoreThanOneLevel($query_)) {
|
||||
$query_ = $this->rewriteArrayWithMultipleLevels($query_);
|
||||
}
|
||||
|
||||
return $query_;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* HTTP request method.
|
||||
* @return string Always POST.
|
||||
* @access public
|
||||
*/
|
||||
function getMethod() {
|
||||
return 'POST';
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches the form headers down the socket.
|
||||
* @param SimpleSocket $socket Socket to write to.
|
||||
* @access public
|
||||
*/
|
||||
function writeHeadersTo(&$socket) {
|
||||
$socket->write("Content-Length: " . (integer)strlen($this->_encode() ?? '') . "\r\n");
|
||||
$socket->write("Content-Type: application/x-www-form-urlencoded\r\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches the form data down the socket.
|
||||
* @param SimpleSocket $socket Socket to write to.
|
||||
* @access public
|
||||
*/
|
||||
function writeTo(&$socket) {
|
||||
$socket->write($this->_encode());
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the query string as a URL encoded
|
||||
* request part for attaching to a URL.
|
||||
* @return string Part of URL.
|
||||
* @access public
|
||||
*/
|
||||
function asUrlRequest() {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bundle of POST parameters in the multipart
|
||||
* format. Can include file uploads.
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
*/
|
||||
class SimpleMultipartEncoding extends SimplePostEncoding {
|
||||
var $_boundary;
|
||||
|
||||
/**
|
||||
* Starts empty.
|
||||
* @param array $query Hash of parameters.
|
||||
* Multiple values are
|
||||
* as lists on a single key.
|
||||
* @access public
|
||||
*/
|
||||
function __construct($query = false, $boundary = false) {
|
||||
parent::__construct($query);
|
||||
$this->_boundary = ($boundary === false ? uniqid('st') : $boundary);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches the form headers down the socket.
|
||||
* @param SimpleSocket $socket Socket to write to.
|
||||
* @access public
|
||||
*/
|
||||
function writeHeadersTo(&$socket) {
|
||||
$socket->write("Content-Length: " . (integer)strlen($this->_encode() ?? '') . "\r\n");
|
||||
$socket->write("Content-Type: multipart/form-data, boundary=" . $this->_boundary . "\r\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches the form data down the socket.
|
||||
* @param SimpleSocket $socket Socket to write to.
|
||||
* @access public
|
||||
*/
|
||||
function writeTo(&$socket) {
|
||||
$socket->write($this->_encode());
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the query string as a URL encoded
|
||||
* request part.
|
||||
* @return string Part of URL.
|
||||
* @access public
|
||||
*/
|
||||
function _encode() {
|
||||
$stream = '';
|
||||
foreach ($this->_request as $pair) {
|
||||
$stream .= "--" . $this->_boundary . "\r\n";
|
||||
$stream .= $pair->asMime() . "\r\n";
|
||||
}
|
||||
$stream .= "--" . $this->_boundary . "--\r\n";
|
||||
return $stream;
|
||||
}
|
||||
}
|
||||
?>
|
355
thirdparty/simpletest/form.php
vendored
355
thirdparty/simpletest/form.php
vendored
@ -1,355 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Base include file for SimpleTest.
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
* @version $Id: form.php 1672 2008-03-02 04:47:34Z edwardzyang $
|
||||
*/
|
||||
|
||||
/**#@+
|
||||
* include SimpleTest files
|
||||
*/
|
||||
require_once(dirname(__FILE__) . '/tag.php');
|
||||
require_once(dirname(__FILE__) . '/encoding.php');
|
||||
require_once(dirname(__FILE__) . '/selector.php');
|
||||
/**#@-*/
|
||||
|
||||
/**
|
||||
* Form tag class to hold widget values.
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
*/
|
||||
class SimpleForm {
|
||||
var $_method;
|
||||
var $_action;
|
||||
var $_encoding;
|
||||
var $_default_target;
|
||||
var $_id;
|
||||
var $_buttons;
|
||||
var $_images;
|
||||
var $_widgets;
|
||||
var $_radios;
|
||||
var $_checkboxes;
|
||||
|
||||
/**
|
||||
* Starts with no held controls/widgets.
|
||||
* @param SimpleTag $tag Form tag to read.
|
||||
* @param SimplePage $page Holding page.
|
||||
*/
|
||||
function __construct($tag, &$page) {
|
||||
$this->_method = $tag->getAttribute('method');
|
||||
$this->_action = $this->_createAction($tag->getAttribute('action'), $page);
|
||||
$this->_encoding = $this->_setEncodingClass($tag);
|
||||
$this->_default_target = false;
|
||||
$this->_id = $tag->getAttribute('id');
|
||||
$this->_buttons = array();
|
||||
$this->_images = array();
|
||||
$this->_widgets = array();
|
||||
$this->_radios = array();
|
||||
$this->_checkboxes = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the request packet to be sent by the form.
|
||||
* @param SimpleTag $tag Form tag to read.
|
||||
* @return string Packet class.
|
||||
* @access private
|
||||
*/
|
||||
function _setEncodingClass($tag) {
|
||||
if (strtolower($tag->getAttribute('method') ?? '') == 'post') {
|
||||
if (strtolower($tag->getAttribute('enctype') ?? '') == 'multipart/form-data') {
|
||||
return 'SimpleMultipartEncoding';
|
||||
}
|
||||
return 'SimplePostEncoding';
|
||||
}
|
||||
return 'SimpleGetEncoding';
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the frame target within a frameset.
|
||||
* @param string $frame Name of frame.
|
||||
* @access public
|
||||
*/
|
||||
function setDefaultTarget($frame) {
|
||||
$this->_default_target = $frame;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for method of form submission.
|
||||
* @return string Either get or post.
|
||||
* @access public
|
||||
*/
|
||||
function getMethod() {
|
||||
return ($this->_method ? strtolower($this->_method) : 'get');
|
||||
}
|
||||
|
||||
/**
|
||||
* Combined action attribute with current location
|
||||
* to get an absolute form target.
|
||||
* @param string $action Action attribute from form tag.
|
||||
* @param SimpleUrl $base Page location.
|
||||
* @return SimpleUrl Absolute form target.
|
||||
*/
|
||||
function _createAction($action, &$page) {
|
||||
if (($action === '') || ($action === false)) {
|
||||
return $page->expandUrl($page->getUrl());
|
||||
}
|
||||
return $page->expandUrl(new SimpleUrl($action));;
|
||||
}
|
||||
|
||||
/**
|
||||
* Absolute URL of the target.
|
||||
* @return SimpleUrl URL target.
|
||||
* @access public
|
||||
*/
|
||||
function getAction() {
|
||||
$url = $this->_action;
|
||||
if ($this->_default_target && ! $url->getTarget()) {
|
||||
$url->setTarget($this->_default_target);
|
||||
}
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the encoding for the current values in the
|
||||
* form.
|
||||
* @return SimpleFormEncoding Request to submit.
|
||||
* @access private
|
||||
*/
|
||||
function _encode() {
|
||||
$class = $this->_encoding;
|
||||
$encoding = new $class();
|
||||
for ($i = 0, $count = count($this->_widgets); $i < $count; $i++) {
|
||||
$this->_widgets[$i]->write($encoding, 0, 0);
|
||||
}
|
||||
return $encoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* ID field of form for unique identification.
|
||||
* @return string Unique tag ID.
|
||||
* @access public
|
||||
*/
|
||||
function getId() {
|
||||
return $this->_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a tag contents to the form.
|
||||
* @param SimpleWidget $tag Input tag to add.
|
||||
* @access public
|
||||
*/
|
||||
function addWidget(&$tag) {
|
||||
if (strtolower($tag->getAttribute('type') ?? '') == 'submit') {
|
||||
$this->_buttons[] = &$tag;
|
||||
} elseif (strtolower($tag->getAttribute('type') ?? '') == 'image') {
|
||||
$this->_images[] = &$tag;
|
||||
} elseif ($tag->getName()) {
|
||||
$this->_setWidget($tag);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the widget into the form, grouping radio
|
||||
* buttons if any.
|
||||
* @param SimpleWidget $tag Incoming form control.
|
||||
* @access private
|
||||
*/
|
||||
function _setWidget(&$tag) {
|
||||
if (strtolower($tag->getAttribute('type') ?? '') == 'radio') {
|
||||
$this->_addRadioButton($tag);
|
||||
} elseif (strtolower($tag->getAttribute('type') ?? '') == 'checkbox') {
|
||||
$this->_addCheckbox($tag);
|
||||
} else {
|
||||
$this->_widgets[] = &$tag;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a radio button, building a group if necessary.
|
||||
* @param SimpleRadioButtonTag $tag Incoming form control.
|
||||
* @access private
|
||||
*/
|
||||
function _addRadioButton(&$tag) {
|
||||
if (! isset($this->_radios[$tag->getName()])) {
|
||||
$this->_widgets[] = new SimpleRadioGroup();
|
||||
$this->_radios[$tag->getName()] = count($this->_widgets ?? []) - 1;
|
||||
}
|
||||
$this->_widgets[$this->_radios[$tag->getName()]]->addWidget($tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a checkbox, making it a group on a repeated name.
|
||||
* @param SimpleCheckboxTag $tag Incoming form control.
|
||||
* @access private
|
||||
*/
|
||||
function _addCheckbox(&$tag) {
|
||||
if (! isset($this->_checkboxes[$tag->getName()])) {
|
||||
$this->_widgets[] = &$tag;
|
||||
$this->_checkboxes[$tag->getName()] = count($this->_widgets ?? []) - 1;
|
||||
} else {
|
||||
$index = $this->_checkboxes[$tag->getName()];
|
||||
if (! SimpleTestCompatibility::isA($this->_widgets[$index], 'SimpleCheckboxGroup')) {
|
||||
$previous = &$this->_widgets[$index];
|
||||
$this->_widgets[$index] = new SimpleCheckboxGroup();
|
||||
$this->_widgets[$index]->addWidget($previous);
|
||||
}
|
||||
$this->_widgets[$index]->addWidget($tag);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts current value from form.
|
||||
* @param SimpleSelector $selector Criteria to apply.
|
||||
* @return string/array Value(s) as string or null
|
||||
* if not set.
|
||||
* @access public
|
||||
*/
|
||||
function getValue($selector) {
|
||||
for ($i = 0, $count = count($this->_widgets); $i < $count; $i++) {
|
||||
if ($selector->isMatch($this->_widgets[$i])) {
|
||||
return $this->_widgets[$i]->getValue();
|
||||
}
|
||||
}
|
||||
foreach ($this->_buttons as $button) {
|
||||
if ($selector->isMatch($button)) {
|
||||
return $button->getValue();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a widget value within the form.
|
||||
* @param SimpleSelector $selector Criteria to apply.
|
||||
* @param string $value Value to input into the widget.
|
||||
* @return boolean True if value is legal, false
|
||||
* otherwise. If the field is not
|
||||
* present, nothing will be set.
|
||||
* @access public
|
||||
*/
|
||||
function setField($selector, $value, $position=false) {
|
||||
$success = false;
|
||||
$_position = 0;
|
||||
for ($i = 0, $count = count($this->_widgets); $i < $count; $i++) {
|
||||
if ($selector->isMatch($this->_widgets[$i])) {
|
||||
$_position++;
|
||||
if ($position === false or $_position === (int)$position) {
|
||||
if ($this->_widgets[$i]->setValue($value)) {
|
||||
$success = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by the page object to set widgets labels to
|
||||
* external label tags.
|
||||
* @param SimpleSelector $selector Criteria to apply.
|
||||
* @access public
|
||||
*/
|
||||
function attachLabelBySelector($selector, $label) {
|
||||
for ($i = 0, $count = count($this->_widgets); $i < $count; $i++) {
|
||||
if ($selector->isMatch($this->_widgets[$i])) {
|
||||
if (method_exists($this->_widgets[$i], 'setLabel')) {
|
||||
$this->_widgets[$i]->setLabel($label);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to see if a form has a submit button.
|
||||
* @param SimpleSelector $selector Criteria to apply.
|
||||
* @return boolean True if present.
|
||||
* @access public
|
||||
*/
|
||||
function hasSubmit($selector) {
|
||||
foreach ($this->_buttons as $button) {
|
||||
if ($selector->isMatch($button)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to see if a form has an image control.
|
||||
* @param SimpleSelector $selector Criteria to apply.
|
||||
* @return boolean True if present.
|
||||
* @access public
|
||||
*/
|
||||
function hasImage($selector) {
|
||||
foreach ($this->_images as $image) {
|
||||
if ($selector->isMatch($image)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the submit values for a selected button.
|
||||
* @param SimpleSelector $selector Criteria to apply.
|
||||
* @param hash $additional Additional data for the form.
|
||||
* @return SimpleEncoding Submitted values or false
|
||||
* if there is no such button
|
||||
* in the form.
|
||||
* @access public
|
||||
*/
|
||||
function submitButton($selector, $additional = false) {
|
||||
$additional = $additional ? $additional : array();
|
||||
foreach ($this->_buttons as $button) {
|
||||
if ($selector->isMatch($button)) {
|
||||
$encoding = $this->_encode();
|
||||
$button->write($encoding, 0, 0);
|
||||
if ($additional) {
|
||||
$encoding->merge($additional);
|
||||
}
|
||||
return $encoding;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the submit values for an image.
|
||||
* @param SimpleSelector $selector Criteria to apply.
|
||||
* @param integer $x X-coordinate of click.
|
||||
* @param integer $y Y-coordinate of click.
|
||||
* @param hash $additional Additional data for the form.
|
||||
* @return SimpleEncoding Submitted values or false
|
||||
* if there is no such button in the
|
||||
* form.
|
||||
* @access public
|
||||
*/
|
||||
function submitImage($selector, $x, $y, $additional = false) {
|
||||
$additional = $additional ? $additional : array();
|
||||
foreach ($this->_images as $image) {
|
||||
if ($selector->isMatch($image)) {
|
||||
$encoding = $this->_encode();
|
||||
$image->write($encoding, $x, $y);
|
||||
if ($additional) {
|
||||
$encoding->merge($additional);
|
||||
}
|
||||
return $encoding;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simply submits the form without the submit button
|
||||
* value. Used when there is only one button or it
|
||||
* is unimportant.
|
||||
* @return hash Submitted values.
|
||||
* @access public
|
||||
*/
|
||||
function submit() {
|
||||
return $this->_encode();
|
||||
}
|
||||
}
|
||||
?>
|
624
thirdparty/simpletest/http.php
vendored
624
thirdparty/simpletest/http.php
vendored
@ -1,624 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* base include file for SimpleTest
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
* @version $Id: http.php 1722 2008-04-07 19:30:56Z lastcraft $
|
||||
*/
|
||||
|
||||
/**#@+
|
||||
* include other SimpleTest class files
|
||||
*/
|
||||
require_once(dirname(__FILE__) . '/socket.php');
|
||||
require_once(dirname(__FILE__) . '/cookies.php');
|
||||
require_once(dirname(__FILE__) . '/url.php');
|
||||
/**#@-*/
|
||||
|
||||
/**
|
||||
* Creates HTTP headers for the end point of
|
||||
* a HTTP request.
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
*/
|
||||
class SimpleRoute {
|
||||
var $_url;
|
||||
|
||||
/**
|
||||
* Sets the target URL.
|
||||
* @param SimpleUrl $url URL as object.
|
||||
* @access public
|
||||
*/
|
||||
function __construct($url) {
|
||||
$this->_url = $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resource name.
|
||||
* @return SimpleUrl Current url.
|
||||
* @access protected
|
||||
*/
|
||||
function getUrl() {
|
||||
return $this->_url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the first line which is the actual request.
|
||||
* @param string $method HTTP request method, usually GET.
|
||||
* @return string Request line content.
|
||||
* @access protected
|
||||
*/
|
||||
function _getRequestLine($method) {
|
||||
return $method . ' ' . $this->_url->getPath() .
|
||||
$this->_url->getEncodedRequest() . ' HTTP/1.0';
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the host part of the request.
|
||||
* @return string Host line content.
|
||||
* @access protected
|
||||
*/
|
||||
function _getHostLine() {
|
||||
$line = 'Host: ' . $this->_url->getHost();
|
||||
if ($this->_url->getPort()) {
|
||||
$line .= ':' . $this->_url->getPort();
|
||||
}
|
||||
return $line;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a socket to the route.
|
||||
* @param string $method HTTP request method, usually GET.
|
||||
* @param integer $timeout Connection timeout.
|
||||
* @return SimpleSocket New socket.
|
||||
* @access public
|
||||
*/
|
||||
function &createConnection($method, $timeout) {
|
||||
$default_port = ('https' == $this->_url->getScheme()) ? 443 : 80;
|
||||
$socket = &$this->_createSocket(
|
||||
$this->_url->getScheme() ? $this->_url->getScheme() : 'http',
|
||||
$this->_url->getHost(),
|
||||
$this->_url->getPort() ? $this->_url->getPort() : $default_port,
|
||||
$timeout);
|
||||
if (! $socket->isError()) {
|
||||
$socket->write($this->_getRequestLine($method) . "\r\n");
|
||||
$socket->write($this->_getHostLine() . "\r\n");
|
||||
$socket->write("Connection: close\r\n");
|
||||
}
|
||||
return $socket;
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory for socket.
|
||||
* @param string $scheme Protocol to use.
|
||||
* @param string $host Hostname to connect to.
|
||||
* @param integer $port Remote port.
|
||||
* @param integer $timeout Connection timeout.
|
||||
* @return SimpleSocket/SimpleSecureSocket New socket.
|
||||
* @access protected
|
||||
*/
|
||||
function &_createSocket($scheme, $host, $port, $timeout) {
|
||||
if (in_array($scheme, array('https'))) {
|
||||
$socket = new SimpleSecureSocket($host, $port, $timeout);
|
||||
} else {
|
||||
$socket = new SimpleSocket($host, $port, $timeout);
|
||||
}
|
||||
return $socket;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates HTTP headers for the end point of
|
||||
* a HTTP request via a proxy server.
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
*/
|
||||
class SimpleProxyRoute extends SimpleRoute {
|
||||
var $_proxy;
|
||||
var $_username;
|
||||
var $_password;
|
||||
|
||||
/**
|
||||
* Stashes the proxy address.
|
||||
* @param SimpleUrl $url URL as object.
|
||||
* @param string $proxy Proxy URL.
|
||||
* @param string $username Username for autentication.
|
||||
* @param string $password Password for autentication.
|
||||
* @access public
|
||||
*/
|
||||
function __construct($url, $proxy, $username = false, $password = false) {
|
||||
parent::__construct($url);
|
||||
$this->_proxy = $proxy;
|
||||
$this->_username = $username;
|
||||
$this->_password = $password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the first line which is the actual request.
|
||||
* @param string $method HTTP request method, usually GET.
|
||||
* @param SimpleUrl $url URL as object.
|
||||
* @return string Request line content.
|
||||
* @access protected
|
||||
*/
|
||||
function _getRequestLine($method) {
|
||||
$url = $this->getUrl();
|
||||
$scheme = $url->getScheme() ? $url->getScheme() : 'http';
|
||||
$port = $url->getPort() ? ':' . $url->getPort() : '';
|
||||
return $method . ' ' . $scheme . '://' . $url->getHost() . $port .
|
||||
$url->getPath() . $url->getEncodedRequest() . ' HTTP/1.0';
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the host part of the request.
|
||||
* @param SimpleUrl $url URL as object.
|
||||
* @return string Host line content.
|
||||
* @access protected
|
||||
*/
|
||||
function _getHostLine() {
|
||||
$host = 'Host: ' . $this->_proxy->getHost();
|
||||
$port = $this->_proxy->getPort() ? $this->_proxy->getPort() : 8080;
|
||||
return "$host:$port";
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a socket to the route.
|
||||
* @param string $method HTTP request method, usually GET.
|
||||
* @param integer $timeout Connection timeout.
|
||||
* @return SimpleSocket New socket.
|
||||
* @access public
|
||||
*/
|
||||
function &createConnection($method, $timeout) {
|
||||
$socket = &$this->_createSocket(
|
||||
$this->_proxy->getScheme() ? $this->_proxy->getScheme() : 'http',
|
||||
$this->_proxy->getHost(),
|
||||
$this->_proxy->getPort() ? $this->_proxy->getPort() : 8080,
|
||||
$timeout);
|
||||
if ($socket->isError()) {
|
||||
return $socket;
|
||||
}
|
||||
$socket->write($this->_getRequestLine($method) . "\r\n");
|
||||
$socket->write($this->_getHostLine() . "\r\n");
|
||||
if ($this->_username && $this->_password) {
|
||||
$socket->write('Proxy-Authorization: Basic ' .
|
||||
base64_encode($this->_username . ':' . $this->_password) .
|
||||
"\r\n");
|
||||
}
|
||||
$socket->write("Connection: close\r\n");
|
||||
return $socket;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* HTTP request for a web page. Factory for
|
||||
* HttpResponse object.
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
*/
|
||||
class SimpleHttpRequest {
|
||||
var $_route;
|
||||
var $_encoding;
|
||||
var $_headers;
|
||||
var $_cookies;
|
||||
|
||||
/**
|
||||
* Builds the socket request from the different pieces.
|
||||
* These include proxy information, URL, cookies, headers,
|
||||
* request method and choice of encoding.
|
||||
* @param SimpleRoute $route Request route.
|
||||
* @param SimpleFormEncoding $encoding Content to send with
|
||||
* request.
|
||||
* @access public
|
||||
*/
|
||||
function __construct(&$route, $encoding) {
|
||||
$this->_route = &$route;
|
||||
$this->_encoding = $encoding;
|
||||
$this->_headers = array();
|
||||
$this->_cookies = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches the content to the route's socket.
|
||||
* @param integer $timeout Connection timeout.
|
||||
* @return SimpleHttpResponse A response which may only have
|
||||
* an error, but hopefully has a
|
||||
* complete web page.
|
||||
* @access public
|
||||
*/
|
||||
function &fetch($timeout) {
|
||||
$socket = &$this->_route->createConnection($this->_encoding->getMethod(), $timeout);
|
||||
if (! $socket->isError()) {
|
||||
$this->_dispatchRequest($socket, $this->_encoding);
|
||||
}
|
||||
$response = &$this->_createResponse($socket);
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the headers.
|
||||
* @param SimpleSocket $socket Open socket.
|
||||
* @param string $method HTTP request method,
|
||||
* usually GET.
|
||||
* @param SimpleFormEncoding $encoding Content to send with request.
|
||||
* @access private
|
||||
*/
|
||||
function _dispatchRequest(&$socket, $encoding) {
|
||||
foreach ($this->_headers as $header_line) {
|
||||
$socket->write($header_line . "\r\n");
|
||||
}
|
||||
if (count($this->_cookies ?? []) > 0) {
|
||||
$socket->write("Cookie: " . implode(";", $this->_cookies) . "\r\n");
|
||||
}
|
||||
$encoding->writeHeadersTo($socket);
|
||||
$socket->write("\r\n");
|
||||
$encoding->writeTo($socket);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a header line to the request.
|
||||
* @param string $header_line Text of full header line.
|
||||
* @access public
|
||||
*/
|
||||
function addHeaderLine($header_line) {
|
||||
$this->_headers[] = $header_line;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads all the relevant cookies from the
|
||||
* cookie jar.
|
||||
* @param SimpleCookieJar $jar Jar to read
|
||||
* @param SimpleUrl $url Url to use for scope.
|
||||
* @access public
|
||||
*/
|
||||
function readCookiesFromJar($jar, $url) {
|
||||
$this->_cookies = $jar->selectAsPairs($url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the socket in a response parser.
|
||||
* @param SimpleSocket $socket Responding socket.
|
||||
* @return SimpleHttpResponse Parsed response object.
|
||||
* @access protected
|
||||
*/
|
||||
function &_createResponse(&$socket) {
|
||||
$response = new SimpleHttpResponse(
|
||||
$socket,
|
||||
$this->_route->getUrl(),
|
||||
$this->_encoding);
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Collection of header lines in the response.
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
*/
|
||||
class SimpleHttpHeaders {
|
||||
var $_raw_headers;
|
||||
var $_response_code;
|
||||
var $_http_version;
|
||||
var $_mime_type;
|
||||
var $_location;
|
||||
var $_cookies;
|
||||
var $_authentication;
|
||||
var $_realm;
|
||||
|
||||
/**
|
||||
* Parses the incoming header block.
|
||||
* @param string $headers Header block.
|
||||
* @access public
|
||||
*/
|
||||
function __construct($headers) {
|
||||
$this->_raw_headers = $headers;
|
||||
$this->_response_code = false;
|
||||
$this->_http_version = false;
|
||||
$this->_mime_type = '';
|
||||
$this->_location = false;
|
||||
$this->_cookies = array();
|
||||
$this->_authentication = false;
|
||||
$this->_realm = false;
|
||||
foreach (preg_split("/\r\n/", $headers ?? '') as $header_line) {
|
||||
$this->_parseHeaderLine($header_line);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for parsed HTTP protocol version.
|
||||
* @return integer HTTP error code.
|
||||
* @access public
|
||||
*/
|
||||
function getHttpVersion() {
|
||||
return $this->_http_version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for raw header block.
|
||||
* @return string All headers as raw string.
|
||||
* @access public
|
||||
*/
|
||||
function getRaw() {
|
||||
return $this->_raw_headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for parsed HTTP error code.
|
||||
* @return integer HTTP error code.
|
||||
* @access public
|
||||
*/
|
||||
function getResponseCode() {
|
||||
return (integer)$this->_response_code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the redirected URL or false if
|
||||
* no redirection.
|
||||
* @return string URL or false for none.
|
||||
* @access public
|
||||
*/
|
||||
function getLocation() {
|
||||
return $this->_location;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to see if the response is a valid redirect.
|
||||
* @return boolean True if valid redirect.
|
||||
* @access public
|
||||
*/
|
||||
function isRedirect() {
|
||||
return in_array($this->_response_code, array(301, 302, 303, 307)) &&
|
||||
(boolean)$this->getLocation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to see if the response is an authentication
|
||||
* challenge.
|
||||
* @return boolean True if challenge.
|
||||
* @access public
|
||||
*/
|
||||
function isChallenge() {
|
||||
return ($this->_response_code == 401) &&
|
||||
(boolean)$this->_authentication &&
|
||||
(boolean)$this->_realm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for MIME type header information.
|
||||
* @return string MIME type.
|
||||
* @access public
|
||||
*/
|
||||
function getMimeType() {
|
||||
return $this->_mime_type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for authentication type.
|
||||
* @return string Type.
|
||||
* @access public
|
||||
*/
|
||||
function getAuthentication() {
|
||||
return $this->_authentication;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for security realm.
|
||||
* @return string Realm.
|
||||
* @access public
|
||||
*/
|
||||
function getRealm() {
|
||||
return $this->_realm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes new cookies to the cookie jar.
|
||||
* @param SimpleCookieJar $jar Jar to write to.
|
||||
* @param SimpleUrl $url Host and path to write under.
|
||||
* @access public
|
||||
*/
|
||||
function writeCookiesToJar(&$jar, $url) {
|
||||
foreach ($this->_cookies as $cookie) {
|
||||
$jar->setCookie(
|
||||
$cookie->getName(),
|
||||
$cookie->getValue(),
|
||||
$url->getHost(),
|
||||
$cookie->getPath(),
|
||||
$cookie->getExpiry());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called on each header line to accumulate the held
|
||||
* data within the class.
|
||||
* @param string $header_line One line of header.
|
||||
* @access protected
|
||||
*/
|
||||
function _parseHeaderLine($header_line) {
|
||||
if (preg_match('/HTTP\/(\d+\.\d+)\s+(\d+)/i', $header_line ?? '', $matches)) {
|
||||
$this->_http_version = $matches[1];
|
||||
$this->_response_code = $matches[2];
|
||||
}
|
||||
if (preg_match('/Content-type:\s*(.*)/i', $header_line ?? '', $matches)) {
|
||||
$this->_mime_type = trim($matches[1] ?? '');
|
||||
}
|
||||
if (preg_match('/Location:\s*(.*)/i', $header_line ?? '', $matches)) {
|
||||
$this->_location = trim($matches[1] ?? '');
|
||||
}
|
||||
if (preg_match('/Set-cookie:(.*)/i', $header_line ?? '', $matches)) {
|
||||
$this->_cookies[] = $this->_parseCookie($matches[1]);
|
||||
}
|
||||
if (preg_match('/WWW-Authenticate:\s+(\S+)\s+realm=\"(.*?)\"/i', $header_line ?? '', $matches)) {
|
||||
$this->_authentication = $matches[1];
|
||||
$this->_realm = trim($matches[2] ?? '');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the Set-cookie content.
|
||||
* @param string $cookie_line Text after "Set-cookie:"
|
||||
* @return SimpleCookie New cookie object.
|
||||
* @access private
|
||||
*/
|
||||
function _parseCookie($cookie_line) {
|
||||
$parts = preg_split('/;/', $cookie_line ?? '');
|
||||
$cookie = array();
|
||||
preg_match('/\s*(.*?)\s*=(.*)/', array_shift($parts) ?? '', $cookie);
|
||||
foreach ($parts as $part) {
|
||||
if (preg_match('/\s*(.*?)\s*=(.*)/', $part ?? '', $matches)) {
|
||||
$cookie[$matches[1]] = trim($matches[2] ?? '');
|
||||
}
|
||||
}
|
||||
return new SimpleCookie(
|
||||
$cookie[1],
|
||||
trim($cookie[2] ?? ''),
|
||||
isset($cookie["path"]) ? $cookie["path"] : "",
|
||||
isset($cookie["expires"]) ? $cookie["expires"] : false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic HTTP response.
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
*/
|
||||
class SimpleHttpResponse extends SimpleStickyError {
|
||||
var $_url;
|
||||
var $_encoding;
|
||||
var $_sent;
|
||||
var $_content;
|
||||
var $_headers;
|
||||
|
||||
/**
|
||||
* Constructor. Reads and parses the incoming
|
||||
* content and headers.
|
||||
* @param SimpleSocket $socket Network connection to fetch
|
||||
* response text from.
|
||||
* @param SimpleUrl $url Resource name.
|
||||
* @param mixed $encoding Record of content sent.
|
||||
* @access public
|
||||
*/
|
||||
function __construct(&$socket, $url, $encoding) {
|
||||
parent::__construct();
|
||||
$this->_url = $url;
|
||||
$this->_encoding = $encoding;
|
||||
$this->_sent = $socket->getSent();
|
||||
$this->_content = false;
|
||||
$raw = $this->_readAll($socket);
|
||||
if ($socket->isError()) {
|
||||
$this->_setError('Error reading socket [' . $socket->getError() . ']');
|
||||
return;
|
||||
}
|
||||
$this->_parse($raw);
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits up the headers and the rest of the content.
|
||||
* @param string $raw Content to parse.
|
||||
* @access private
|
||||
*/
|
||||
function _parse($raw) {
|
||||
if (! $raw) {
|
||||
$this->_setError('Nothing fetched');
|
||||
$this->_headers = new SimpleHttpHeaders('');
|
||||
} elseif (! strstr($raw ?? '', "\r\n\r\n")) {
|
||||
$this->_setError('Could not split headers from content');
|
||||
$this->_headers = new SimpleHttpHeaders($raw);
|
||||
} else {
|
||||
list($headers, $this->_content) = preg_split("/\r\n\r\n/", $raw ?? '', 2);
|
||||
$this->_headers = new SimpleHttpHeaders($headers);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Original request method.
|
||||
* @return string GET, POST or HEAD.
|
||||
* @access public
|
||||
*/
|
||||
function getMethod() {
|
||||
return $this->_encoding->getMethod();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resource name.
|
||||
* @return SimpleUrl Current url.
|
||||
* @access public
|
||||
*/
|
||||
function getUrl() {
|
||||
return $this->_url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Original request data.
|
||||
* @return mixed Sent content.
|
||||
* @access public
|
||||
*/
|
||||
function getRequestData() {
|
||||
return $this->_encoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Raw request that was sent down the wire.
|
||||
* @return string Bytes actually sent.
|
||||
* @access public
|
||||
*/
|
||||
function getSent() {
|
||||
return $this->_sent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for the content after the last
|
||||
* header line.
|
||||
* @return string All content.
|
||||
* @access public
|
||||
*/
|
||||
function getContent() {
|
||||
return $this->_content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for header block. The response is the
|
||||
* combination of this and the content.
|
||||
* @return SimpleHeaders Wrapped header block.
|
||||
* @access public
|
||||
*/
|
||||
function getHeaders() {
|
||||
return $this->_headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for any new cookies.
|
||||
* @return array List of new cookies.
|
||||
* @access public
|
||||
*/
|
||||
function getNewCookies() {
|
||||
return $this->_headers->getNewCookies();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the whole of the socket output into a
|
||||
* single string.
|
||||
* @param SimpleSocket $socket Unread socket.
|
||||
* @return string Raw output if successful
|
||||
* else false.
|
||||
* @access private
|
||||
*/
|
||||
function _readAll(&$socket) {
|
||||
$all = '';
|
||||
while (! $this->_isLastPacket($next = $socket->read())) {
|
||||
$all .= $next;
|
||||
}
|
||||
return $all;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to see if the packet from the socket is the
|
||||
* last one.
|
||||
* @param string $packet Chunk to interpret.
|
||||
* @return boolean True if empty or EOF.
|
||||
* @access private
|
||||
*/
|
||||
function _isLastPacket($packet) {
|
||||
if (is_string($packet)) {
|
||||
return $packet === '';
|
||||
}
|
||||
return ! $packet;
|
||||
}
|
||||
}
|
||||
?>
|
984
thirdparty/simpletest/page.php
vendored
984
thirdparty/simpletest/page.php
vendored
@ -1,984 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Base include file for SimpleTest
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
* @version $Id: page.php 1672 2008-03-02 04:47:34Z edwardzyang $
|
||||
*/
|
||||
|
||||
/**#@+
|
||||
* include other SimpleTest class files
|
||||
*/
|
||||
require_once(dirname(__FILE__) . '/http.php');
|
||||
require_once(dirname(__FILE__) . '/parser.php');
|
||||
require_once(dirname(__FILE__) . '/tag.php');
|
||||
require_once(dirname(__FILE__) . '/form.php');
|
||||
require_once(dirname(__FILE__) . '/selector.php');
|
||||
/**#@-*/
|
||||
|
||||
/**
|
||||
* Creates tags and widgets given HTML tag
|
||||
* attributes.
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
*/
|
||||
class SimpleTagBuilder {
|
||||
|
||||
/**
|
||||
* Factory for the tag objects. Creates the
|
||||
* appropriate tag object for the incoming tag name
|
||||
* and attributes.
|
||||
* @param string $name HTML tag name.
|
||||
* @param hash $attributes Element attributes.
|
||||
* @return SimpleTag Tag object.
|
||||
* @access public
|
||||
*/
|
||||
function createTag($name, $attributes) {
|
||||
static $map = array(
|
||||
'a' => 'SimpleAnchorTag',
|
||||
'title' => 'SimpleTitleTag',
|
||||
'base' => 'SimpleBaseTag',
|
||||
'button' => 'SimpleButtonTag',
|
||||
'textarea' => 'SimpleTextAreaTag',
|
||||
'option' => 'SimpleOptionTag',
|
||||
'label' => 'SimpleLabelTag',
|
||||
'form' => 'SimpleFormTag',
|
||||
'frame' => 'SimpleFrameTag');
|
||||
$attributes = $this->_keysToLowerCase($attributes);
|
||||
if (array_key_exists($name, $map ?? [])) {
|
||||
$tag_class = $map[$name];
|
||||
return new $tag_class($attributes);
|
||||
} elseif ($name == 'select') {
|
||||
return $this->_createSelectionTag($attributes);
|
||||
} elseif ($name == 'input') {
|
||||
return $this->_createInputTag($attributes);
|
||||
}
|
||||
return new SimpleTag($name, $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory for selection fields.
|
||||
* @param hash $attributes Element attributes.
|
||||
* @return SimpleTag Tag object.
|
||||
* @access protected
|
||||
*/
|
||||
function _createSelectionTag($attributes) {
|
||||
if (isset($attributes['multiple'])) {
|
||||
return new MultipleSelectionTag($attributes);
|
||||
}
|
||||
return new SimpleSelectionTag($attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory for input tags.
|
||||
* @param hash $attributes Element attributes.
|
||||
* @return SimpleTag Tag object.
|
||||
* @access protected
|
||||
*/
|
||||
function _createInputTag($attributes) {
|
||||
if (! isset($attributes['type'])) {
|
||||
return new SimpleTextTag($attributes);
|
||||
}
|
||||
$type = strtolower(trim($attributes['type'] ?? ''));
|
||||
$map = array(
|
||||
'submit' => 'SimpleSubmitTag',
|
||||
'image' => 'SimpleImageSubmitTag',
|
||||
'checkbox' => 'SimpleCheckboxTag',
|
||||
'radio' => 'SimpleRadioButtonTag',
|
||||
'text' => 'SimpleTextTag',
|
||||
'email' => 'SimpleTextTag',
|
||||
'hidden' => 'SimpleTextTag',
|
||||
'password' => 'SimpleTextTag',
|
||||
'file' => 'SimpleUploadTag');
|
||||
if (array_key_exists($type, $map ?? [])) {
|
||||
$tag_class = $map[$type];
|
||||
return new $tag_class($attributes);
|
||||
}
|
||||
return new SimpleTextTag($attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the keys lower case for case insensitive look-ups.
|
||||
* @param hash $map Hash to convert.
|
||||
* @return hash Unchanged values, but keys lower case.
|
||||
* @access private
|
||||
*/
|
||||
function _keysToLowerCase($map) {
|
||||
$lower = array();
|
||||
foreach ($map as $key => $value) {
|
||||
$lower[strtolower($key)] = $value;
|
||||
}
|
||||
return $lower;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* SAX event handler. Maintains a list of
|
||||
* open tags and dispatches them as they close.
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
*/
|
||||
class SimplePageBuilder extends SimpleSaxListener {
|
||||
var $_tags;
|
||||
var $_page;
|
||||
var $_private_content_tag;
|
||||
|
||||
/**
|
||||
* Sets the builder up empty.
|
||||
* @access public
|
||||
*/
|
||||
function __construct() {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Frees up any references so as to allow the PHP garbage
|
||||
* collection from unset() to work.
|
||||
* @access public
|
||||
*/
|
||||
function free() {
|
||||
unset($this->_tags);
|
||||
unset($this->_page);
|
||||
unset($this->_private_content_tags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the raw content and send events
|
||||
* into the page to be built.
|
||||
* @param $response SimpleHttpResponse Fetched response.
|
||||
* @return SimplePage Newly parsed page.
|
||||
* @access public
|
||||
*/
|
||||
function &parse($response) {
|
||||
$this->_tags = array();
|
||||
$this->_page = &$this->_createPage($response);
|
||||
$parser = &$this->_createParser($this);
|
||||
$parser->parse($response->getContent());
|
||||
$this->_page->acceptPageEnd();
|
||||
return $this->_page;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an empty page.
|
||||
* @return SimplePage New unparsed page.
|
||||
* @access protected
|
||||
*/
|
||||
function &_createPage($response) {
|
||||
$page = new SimplePage($response);
|
||||
return $page;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the parser used with the builder.
|
||||
* @param $listener SimpleSaxListener Target of parser.
|
||||
* @return SimpleSaxParser Parser to generate
|
||||
* events for the builder.
|
||||
* @access protected
|
||||
*/
|
||||
function &_createParser(&$listener) {
|
||||
$parser = new SimpleHtmlSaxParser($listener);
|
||||
return $parser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start of element event. Opens a new tag.
|
||||
* @param string $name Element name.
|
||||
* @param hash $attributes Attributes without content
|
||||
* are marked as true.
|
||||
* @return boolean False on parse error.
|
||||
* @access public
|
||||
*/
|
||||
function startElement($name, $attributes) {
|
||||
$factory = new SimpleTagBuilder();
|
||||
$tag = $factory->createTag($name, $attributes);
|
||||
if (! $tag) {
|
||||
return true;
|
||||
}
|
||||
if ($tag->getTagName() == 'label') {
|
||||
$this->_page->acceptLabelStart($tag);
|
||||
$this->_openTag($tag);
|
||||
return true;
|
||||
}
|
||||
if ($tag->getTagName() == 'form') {
|
||||
$this->_page->acceptFormStart($tag);
|
||||
return true;
|
||||
}
|
||||
if ($tag->getTagName() == 'frameset') {
|
||||
$this->_page->acceptFramesetStart($tag);
|
||||
return true;
|
||||
}
|
||||
if ($tag->getTagName() == 'frame') {
|
||||
$this->_page->acceptFrame($tag);
|
||||
return true;
|
||||
}
|
||||
if ($tag->isPrivateContent() && ! isset($this->_private_content_tag)) {
|
||||
$this->_private_content_tag = &$tag;
|
||||
}
|
||||
if ($tag->expectEndTag()) {
|
||||
$this->_openTag($tag);
|
||||
return true;
|
||||
}
|
||||
$this->_page->acceptTag($tag);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* End of element event.
|
||||
* @param string $name Element name.
|
||||
* @return boolean False on parse error.
|
||||
* @access public
|
||||
*/
|
||||
function endElement($name) {
|
||||
if ($name == 'label') {
|
||||
$this->_page->acceptLabelEnd();
|
||||
return true;
|
||||
}
|
||||
if ($name == 'form') {
|
||||
$this->_page->acceptFormEnd();
|
||||
return true;
|
||||
}
|
||||
if ($name == 'frameset') {
|
||||
$this->_page->acceptFramesetEnd();
|
||||
return true;
|
||||
}
|
||||
if ($this->_hasNamedTagOnOpenTagStack($name)) {
|
||||
$tag = array_pop($this->_tags[$name]);
|
||||
if ($tag->isPrivateContent() && $this->_private_content_tag->getTagName() == $name) {
|
||||
unset($this->_private_content_tag);
|
||||
}
|
||||
$this->_addContentTagToOpenTags($tag);
|
||||
$this->_page->acceptTag($tag);
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to see if there are any open tags awaiting
|
||||
* closure that match the tag name.
|
||||
* @param string $name Element name.
|
||||
* @return boolean True if any are still open.
|
||||
* @access private
|
||||
*/
|
||||
function _hasNamedTagOnOpenTagStack($name) {
|
||||
return isset($this->_tags[$name]) && (count($this->_tags[$name] ?? []) > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unparsed, but relevant data. The data is added
|
||||
* to every open tag.
|
||||
* @param string $text May include unparsed tags.
|
||||
* @return boolean False on parse error.
|
||||
* @access public
|
||||
*/
|
||||
function addContent($text) {
|
||||
if (isset($this->_private_content_tag)) {
|
||||
$this->_private_content_tag->addContent($text);
|
||||
} else {
|
||||
$this->_addContentToAllOpenTags($text);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Any content fills all currently open tags unless it
|
||||
* is part of an option tag.
|
||||
* @param string $text May include unparsed tags.
|
||||
* @access private
|
||||
*/
|
||||
function _addContentToAllOpenTags($text) {
|
||||
foreach (array_keys($this->_tags ?? []) as $name) {
|
||||
for ($i = 0, $count = count($this->_tags[$name]); $i < $count; $i++) {
|
||||
$this->_tags[$name][$i]->addContent($text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parsed data in tag form. The parsed tag is added
|
||||
* to every open tag. Used for adding options to select
|
||||
* fields only.
|
||||
* @param SimpleTag $tag Option tags only.
|
||||
* @access private
|
||||
*/
|
||||
function _addContentTagToOpenTags(&$tag) {
|
||||
if ($tag->getTagName() != 'option') {
|
||||
return;
|
||||
}
|
||||
foreach (array_keys($this->_tags ?? []) as $name) {
|
||||
for ($i = 0, $count = count($this->_tags[$name]); $i < $count; $i++) {
|
||||
$this->_tags[$name][$i]->addTag($tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a tag for receiving content. Multiple tags
|
||||
* will be receiving input at the same time.
|
||||
* @param SimpleTag $tag New content tag.
|
||||
* @access private
|
||||
*/
|
||||
function _openTag(&$tag) {
|
||||
$name = $tag->getTagName();
|
||||
if (! in_array($name, array_keys($this->_tags ?? []))) {
|
||||
$this->_tags[$name] = array();
|
||||
}
|
||||
$this->_tags[$name][] = &$tag;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A wrapper for a web page.
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
*/
|
||||
class SimplePage {
|
||||
var $_links;
|
||||
var $_title;
|
||||
var $_last_widget;
|
||||
var $_label;
|
||||
var $_left_over_labels;
|
||||
var $_open_forms;
|
||||
var $_complete_forms;
|
||||
var $_frameset;
|
||||
var $_frames;
|
||||
var $_frameset_nesting_level;
|
||||
var $_transport_error;
|
||||
var $_raw;
|
||||
var $_text;
|
||||
var $_sent;
|
||||
var $_headers;
|
||||
var $_method;
|
||||
var $_url;
|
||||
var $_base = false;
|
||||
var $_request_data;
|
||||
|
||||
/**
|
||||
* Parses a page ready to access it's contents.
|
||||
* @param SimpleHttpResponse $response Result of HTTP fetch.
|
||||
* @access public
|
||||
*/
|
||||
function __construct($response = false) {
|
||||
$this->_links = array();
|
||||
$this->_title = false;
|
||||
$this->_left_over_labels = array();
|
||||
$this->_open_forms = array();
|
||||
$this->_complete_forms = array();
|
||||
$this->_frameset = false;
|
||||
$this->_frames = array();
|
||||
$this->_frameset_nesting_level = 0;
|
||||
$this->_text = false;
|
||||
if ($response) {
|
||||
$this->_extractResponse($response);
|
||||
} else {
|
||||
$this->_noResponse();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts all of the response information.
|
||||
* @param SimpleHttpResponse $response Response being parsed.
|
||||
* @access private
|
||||
*/
|
||||
function _extractResponse($response) {
|
||||
$this->_transport_error = $response->getError();
|
||||
$this->_raw = $response->getContent();
|
||||
$this->_sent = $response->getSent();
|
||||
$this->_headers = $response->getHeaders();
|
||||
$this->_method = $response->getMethod();
|
||||
$this->_url = $response->getUrl();
|
||||
$this->_request_data = $response->getRequestData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up a missing response.
|
||||
* @access private
|
||||
*/
|
||||
function _noResponse() {
|
||||
$this->_transport_error = 'No page fetched yet';
|
||||
$this->_raw = false;
|
||||
$this->_sent = false;
|
||||
$this->_headers = false;
|
||||
$this->_method = 'GET';
|
||||
$this->_url = false;
|
||||
$this->_request_data = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Original request as bytes sent down the wire.
|
||||
* @return mixed Sent content.
|
||||
* @access public
|
||||
*/
|
||||
function getRequest() {
|
||||
return $this->_sent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for raw text of page.
|
||||
* @return string Raw unparsed content.
|
||||
* @access public
|
||||
*/
|
||||
function getRaw() {
|
||||
return $this->_raw;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for plain text of page as a text browser
|
||||
* would see it.
|
||||
* @return string Plain text of page.
|
||||
* @access public
|
||||
*/
|
||||
function getText() {
|
||||
if (! $this->_text) {
|
||||
$this->_text = SimpleHtmlSaxParser::normalise($this->_raw);
|
||||
}
|
||||
return $this->_text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for raw headers of page.
|
||||
* @return string Header block as text.
|
||||
* @access public
|
||||
*/
|
||||
function getHeaders() {
|
||||
if ($this->_headers) {
|
||||
return $this->_headers->getRaw();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Original request method.
|
||||
* @return string GET, POST or HEAD.
|
||||
* @access public
|
||||
*/
|
||||
function getMethod() {
|
||||
return $this->_method;
|
||||
}
|
||||
|
||||
/**
|
||||
* Original resource name.
|
||||
* @return SimpleUrl Current url.
|
||||
* @access public
|
||||
*/
|
||||
function getUrl() {
|
||||
return $this->_url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Base URL if set via BASE tag page url otherwise
|
||||
* @return SimpleUrl Base url.
|
||||
* @access public
|
||||
*/
|
||||
function getBaseUrl() {
|
||||
return $this->_base;
|
||||
}
|
||||
|
||||
/**
|
||||
* Original request data.
|
||||
* @return mixed Sent content.
|
||||
* @access public
|
||||
*/
|
||||
function getRequestData() {
|
||||
return $this->_request_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for last error.
|
||||
* @return string Error from last response.
|
||||
* @access public
|
||||
*/
|
||||
function getTransportError() {
|
||||
return $this->_transport_error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for current MIME type.
|
||||
* @return string MIME type as string; e.g. 'text/html'
|
||||
* @access public
|
||||
*/
|
||||
function getMimeType() {
|
||||
if ($this->_headers) {
|
||||
return $this->_headers->getMimeType();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for HTTP response code.
|
||||
* @return integer HTTP response code received.
|
||||
* @access public
|
||||
*/
|
||||
function getResponseCode() {
|
||||
if ($this->_headers) {
|
||||
return $this->_headers->getResponseCode();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for last Authentication type. Only valid
|
||||
* straight after a challenge (401).
|
||||
* @return string Description of challenge type.
|
||||
* @access public
|
||||
*/
|
||||
function getAuthentication() {
|
||||
if ($this->_headers) {
|
||||
return $this->_headers->getAuthentication();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for last Authentication realm. Only valid
|
||||
* straight after a challenge (401).
|
||||
* @return string Name of security realm.
|
||||
* @access public
|
||||
*/
|
||||
function getRealm() {
|
||||
if ($this->_headers) {
|
||||
return $this->_headers->getRealm();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for current frame focus. Will be
|
||||
* false as no frames.
|
||||
* @return array Always empty.
|
||||
* @access public
|
||||
*/
|
||||
function getFrameFocus() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the focus by index. The integer index starts from 1.
|
||||
* @param integer $choice Chosen frame.
|
||||
* @return boolean Always false.
|
||||
* @access public
|
||||
*/
|
||||
function setFrameFocusByIndex($choice) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the focus by name. Always fails for a leaf page.
|
||||
* @param string $name Chosen frame.
|
||||
* @return boolean False as no frames.
|
||||
* @access public
|
||||
*/
|
||||
function setFrameFocus($name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the frame focus. Does nothing for a leaf page.
|
||||
* @access public
|
||||
*/
|
||||
function clearFrameFocus() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a tag to the page.
|
||||
* @param SimpleTag $tag Tag to accept.
|
||||
* @access public
|
||||
*/
|
||||
function acceptTag(&$tag) {
|
||||
if ($tag->getTagName() == "a") {
|
||||
$this->_addLink($tag);
|
||||
} elseif ($tag->getTagName() == "base") {
|
||||
$this->_setBase($tag);
|
||||
} elseif ($tag->getTagName() == "title") {
|
||||
$this->_setTitle($tag);
|
||||
} elseif ($this->_isFormElement($tag->getTagName())) {
|
||||
for ($i = 0; $i < count($this->_open_forms ?? []); $i++) {
|
||||
$this->_open_forms[$i]->addWidget($tag);
|
||||
}
|
||||
$this->_last_widget = &$tag;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a label for a described widget.
|
||||
* @param SimpleFormTag $tag Tag to accept.
|
||||
* @access public
|
||||
*/
|
||||
function acceptLabelStart(&$tag) {
|
||||
$this->_label = &$tag;
|
||||
unset($this->_last_widget);
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the most recently opened label.
|
||||
* @access public
|
||||
*/
|
||||
function acceptLabelEnd() {
|
||||
if (isset($this->_label)) {
|
||||
if (isset($this->_last_widget)) {
|
||||
$this->_last_widget->setLabel($this->_label->getText());
|
||||
unset($this->_last_widget);
|
||||
} else {
|
||||
$this->_left_over_labels[] = SimpleTestCompatibility::copy($this->_label);
|
||||
}
|
||||
unset($this->_label);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests to see if a tag is a possible form
|
||||
* element.
|
||||
* @param string $name HTML element name.
|
||||
* @return boolean True if form element.
|
||||
* @access private
|
||||
*/
|
||||
function _isFormElement($name) {
|
||||
return in_array($name, array('input', 'button', 'textarea', 'select'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a form. New widgets go here.
|
||||
* @param SimpleFormTag $tag Tag to accept.
|
||||
* @access public
|
||||
*/
|
||||
function acceptFormStart(&$tag) {
|
||||
$this->_open_forms[] = new SimpleForm($tag, $this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the most recently opened form.
|
||||
* @access public
|
||||
*/
|
||||
function acceptFormEnd() {
|
||||
if (count($this->_open_forms ?? [])) {
|
||||
$this->_complete_forms[] = array_pop($this->_open_forms);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a frameset. A frameset may contain nested
|
||||
* frameset tags.
|
||||
* @param SimpleFramesetTag $tag Tag to accept.
|
||||
* @access public
|
||||
*/
|
||||
function acceptFramesetStart(&$tag) {
|
||||
if (! $this->_isLoadingFrames()) {
|
||||
$this->_frameset = &$tag;
|
||||
}
|
||||
$this->_frameset_nesting_level++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the most recently opened frameset.
|
||||
* @access public
|
||||
*/
|
||||
function acceptFramesetEnd() {
|
||||
if ($this->_isLoadingFrames()) {
|
||||
$this->_frameset_nesting_level--;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a single frame tag and stashes it in
|
||||
* the current frame set.
|
||||
* @param SimpleFrameTag $tag Tag to accept.
|
||||
* @access public
|
||||
*/
|
||||
function acceptFrame(&$tag) {
|
||||
if ($this->_isLoadingFrames()) {
|
||||
if ($tag->getAttribute('src')) {
|
||||
$this->_frames[] = &$tag;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to see if in the middle of reading
|
||||
* a frameset.
|
||||
* @return boolean True if inframeset.
|
||||
* @access private
|
||||
*/
|
||||
function _isLoadingFrames() {
|
||||
if (! $this->_frameset) {
|
||||
return false;
|
||||
}
|
||||
return ($this->_frameset_nesting_level > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to see if link is an absolute one.
|
||||
* @param string $url Url to test.
|
||||
* @return boolean True if absolute.
|
||||
* @access protected
|
||||
*/
|
||||
function _linkIsAbsolute($url) {
|
||||
$parsed = new SimpleUrl($url);
|
||||
return (boolean)($parsed->getScheme() && $parsed->getHost());
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a link to the page.
|
||||
* @param SimpleAnchorTag $tag Link to accept.
|
||||
* @access protected
|
||||
*/
|
||||
function _addLink($tag) {
|
||||
$this->_links[] = $tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marker for end of complete page. Any work in
|
||||
* progress can now be closed.
|
||||
* @access public
|
||||
*/
|
||||
function acceptPageEnd() {
|
||||
while (count($this->_open_forms ?? [])) {
|
||||
$this->_complete_forms[] = array_pop($this->_open_forms);
|
||||
}
|
||||
foreach ($this->_left_over_labels as $label) {
|
||||
for ($i = 0, $count = count($this->_complete_forms); $i < $count; $i++) {
|
||||
$this->_complete_forms[$i]->attachLabelBySelector(
|
||||
new SimpleById($label->getFor()),
|
||||
$label->getText());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for the presence of a frameset.
|
||||
* @return boolean True if frameset.
|
||||
* @access public
|
||||
*/
|
||||
function hasFrames() {
|
||||
return (boolean)$this->_frameset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for frame name and source URL for every frame that
|
||||
* will need to be loaded. Immediate children only.
|
||||
* @return boolean/array False if no frameset or
|
||||
* otherwise a hash of frame URLs.
|
||||
* The key is either a numerical
|
||||
* base one index or the name attribute.
|
||||
* @access public
|
||||
*/
|
||||
function getFrameset() {
|
||||
if (! $this->_frameset) {
|
||||
return false;
|
||||
}
|
||||
$urls = array();
|
||||
for ($i = 0; $i < count($this->_frames ?? []); $i++) {
|
||||
$name = $this->_frames[$i]->getAttribute('name');
|
||||
$url = new SimpleUrl($this->_frames[$i]->getAttribute('src'));
|
||||
$urls[$name ? $name : $i + 1] = $this->expandUrl($url);
|
||||
}
|
||||
return $urls;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches a list of loaded frames.
|
||||
* @return array/string Just the URL for a single page.
|
||||
* @access public
|
||||
*/
|
||||
function getFrames() {
|
||||
$url = $this->expandUrl($this->getUrl());
|
||||
return $url->asString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for a list of all links.
|
||||
* @return array List of urls with scheme of
|
||||
* http or https and hostname.
|
||||
* @access public
|
||||
*/
|
||||
function getUrls() {
|
||||
$all = array();
|
||||
foreach ($this->_links as $link) {
|
||||
$url = $this->_getUrlFromLink($link);
|
||||
$all[] = $url->asString();
|
||||
}
|
||||
return $all;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for URLs by the link label. Label will match
|
||||
* regardess of whitespace issues and case.
|
||||
* @param string $label Text of link.
|
||||
* @return array List of links with that label.
|
||||
* @access public
|
||||
*/
|
||||
function getUrlsByLabel($label) {
|
||||
$matches = array();
|
||||
foreach ($this->_links as $link) {
|
||||
if ($link->getText() == $label) {
|
||||
$matches[] = $this->_getUrlFromLink($link);
|
||||
}
|
||||
}
|
||||
return $matches;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for a URL by the id attribute.
|
||||
* @param string $id Id attribute of link.
|
||||
* @return SimpleUrl URL with that id of false if none.
|
||||
* @access public
|
||||
*/
|
||||
function getUrlById($id) {
|
||||
foreach ($this->_links as $link) {
|
||||
if ($link->getAttribute('id') === (string)$id) {
|
||||
return $this->_getUrlFromLink($link);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a link tag into a target URL.
|
||||
* @param SimpleAnchor $link Parsed link.
|
||||
* @return SimpleUrl URL with frame target if any.
|
||||
* @access private
|
||||
*/
|
||||
function _getUrlFromLink($link) {
|
||||
$url = $this->expandUrl($link->getHref());
|
||||
if ($link->getAttribute('target')) {
|
||||
$url->setTarget($link->getAttribute('target'));
|
||||
}
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expands expandomatic URLs into fully qualified
|
||||
* URLs.
|
||||
* @param SimpleUrl $url Relative URL.
|
||||
* @return SimpleUrl Absolute URL.
|
||||
* @access public
|
||||
*/
|
||||
function expandUrl($url) {
|
||||
if (! is_object($url)) {
|
||||
$url = new SimpleUrl($url);
|
||||
}
|
||||
$location = $this->getBaseUrl() ? $this->getBaseUrl() : new SimpleUrl();
|
||||
return $url->makeAbsolute($location->makeAbsolute($this->getUrl()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the base url for the page.
|
||||
* @param SimpleTag $tag Base URL for page.
|
||||
* @access protected
|
||||
*/
|
||||
function _setBase(&$tag) {
|
||||
$url = $tag->getAttribute('href');
|
||||
$this->_base = new SimpleUrl($url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the title tag contents.
|
||||
* @param SimpleTitleTag $tag Title of page.
|
||||
* @access protected
|
||||
*/
|
||||
function _setTitle(&$tag) {
|
||||
$this->_title = &$tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for parsed title.
|
||||
* @return string Title or false if no title is present.
|
||||
* @access public
|
||||
*/
|
||||
function getTitle() {
|
||||
if ($this->_title) {
|
||||
return $this->_title->getText();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a held form by button label. Will only
|
||||
* search correctly built forms.
|
||||
* @param SimpleSelector $selector Button finder.
|
||||
* @return SimpleForm Form object containing
|
||||
* the button.
|
||||
* @access public
|
||||
*/
|
||||
function &getFormBySubmit($selector) {
|
||||
for ($i = 0; $i < count($this->_complete_forms ?? []); $i++) {
|
||||
if ($this->_complete_forms[$i]->hasSubmit($selector)) {
|
||||
return $this->_complete_forms[$i];
|
||||
}
|
||||
}
|
||||
$null = null;
|
||||
return $null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a held form by image using a selector.
|
||||
* Will only search correctly built forms.
|
||||
* @param SimpleSelector $selector Image finder.
|
||||
* @return SimpleForm Form object containing
|
||||
* the image.
|
||||
* @access public
|
||||
*/
|
||||
function &getFormByImage($selector) {
|
||||
for ($i = 0; $i < count($this->_complete_forms ?? []); $i++) {
|
||||
if ($this->_complete_forms[$i]->hasImage($selector)) {
|
||||
return $this->_complete_forms[$i];
|
||||
}
|
||||
}
|
||||
$null = null;
|
||||
return $null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a held form by the form ID. A way of
|
||||
* identifying a specific form when we have control
|
||||
* of the HTML code.
|
||||
* @param string $id Form label.
|
||||
* @return SimpleForm Form object containing the matching ID.
|
||||
* @access public
|
||||
*/
|
||||
function &getFormById($id) {
|
||||
for ($i = 0; $i < count($this->_complete_forms ?? []); $i++) {
|
||||
if ($this->_complete_forms[$i]->getId() == $id) {
|
||||
return $this->_complete_forms[$i];
|
||||
}
|
||||
}
|
||||
$null = null;
|
||||
return $null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a field on each form in which the field is
|
||||
* available.
|
||||
* @param SimpleSelector $selector Field finder.
|
||||
* @param string $value Value to set field to.
|
||||
* @return boolean True if value is valid.
|
||||
* @access public
|
||||
*/
|
||||
function setField($selector, $value, $position=false) {
|
||||
$is_set = false;
|
||||
for ($i = 0; $i < count($this->_complete_forms ?? []); $i++) {
|
||||
if ($this->_complete_forms[$i]->setField($selector, $value, $position)) {
|
||||
$is_set = true;
|
||||
}
|
||||
}
|
||||
return $is_set;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for a form element value within a page.
|
||||
* @param SimpleSelector $selector Field finder.
|
||||
* @return string/boolean A string if the field is
|
||||
* present, false if unchecked
|
||||
* and null if missing.
|
||||
* @access public
|
||||
*/
|
||||
function getField($selector) {
|
||||
for ($i = 0; $i < count($this->_complete_forms ?? []); $i++) {
|
||||
$value = $this->_complete_forms[$i]->getValue($selector);
|
||||
if (isset($value)) {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
764
thirdparty/simpletest/parser.php
vendored
764
thirdparty/simpletest/parser.php
vendored
@ -1,764 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* base include file for SimpleTest
|
||||
* @package SimpleTest
|
||||
* @subpackage MockObjects
|
||||
* @version $Id: parser.php 1723 2008-04-08 00:34:10Z lastcraft $
|
||||
*/
|
||||
|
||||
/**#@+
|
||||
* Lexer mode stack constants
|
||||
*/
|
||||
foreach (array('LEXER_ENTER', 'LEXER_MATCHED',
|
||||
'LEXER_UNMATCHED', 'LEXER_EXIT',
|
||||
'LEXER_SPECIAL') as $i => $constant) {
|
||||
if (! defined($constant)) {
|
||||
define($constant, $i + 1);
|
||||
}
|
||||
}
|
||||
/**#@-*/
|
||||
|
||||
/**
|
||||
* Compounded regular expression. Any of
|
||||
* the contained patterns could match and
|
||||
* when one does, it's label is returned.
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
*/
|
||||
class ParallelRegex {
|
||||
var $_patterns;
|
||||
var $_labels;
|
||||
var $_regex;
|
||||
var $_case;
|
||||
|
||||
/**
|
||||
* Constructor. Starts with no patterns.
|
||||
* @param boolean $case True for case sensitive, false
|
||||
* for insensitive.
|
||||
* @access public
|
||||
*/
|
||||
function __construct($case) {
|
||||
$this->_case = $case;
|
||||
$this->_patterns = array();
|
||||
$this->_labels = array();
|
||||
$this->_regex = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a pattern with an optional label.
|
||||
* @param string $pattern Perl style regex, but ( and )
|
||||
* lose the usual meaning.
|
||||
* @param string $label Label of regex to be returned
|
||||
* on a match.
|
||||
* @access public
|
||||
*/
|
||||
function addPattern($pattern, $label = true) {
|
||||
$count = count($this->_patterns ?? []);
|
||||
$this->_patterns[$count] = $pattern;
|
||||
$this->_labels[$count] = $label;
|
||||
$this->_regex = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to match all patterns at once against
|
||||
* a string.
|
||||
* @param string $subject String to match against.
|
||||
* @param string $match First matched portion of
|
||||
* subject.
|
||||
* @return boolean True on success.
|
||||
* @access public
|
||||
*/
|
||||
function match($subject, &$match) {
|
||||
if (count($this->_patterns ?? []) == 0) {
|
||||
return false;
|
||||
}
|
||||
if (! preg_match($this->_getCompoundedRegex() ?? '', $subject ?? '', $matches)) {
|
||||
$match = '';
|
||||
return false;
|
||||
}
|
||||
$match = $matches[0];
|
||||
for ($i = 1; $i < count($matches ?? []); $i++) {
|
||||
if ($matches[$i]) {
|
||||
return $this->_labels[$i - 1];
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compounds the patterns into a single
|
||||
* regular expression separated with the
|
||||
* "or" operator. Caches the regex.
|
||||
* Will automatically escape (, ) and / tokens.
|
||||
* @param array $patterns List of patterns in order.
|
||||
* @access private
|
||||
*/
|
||||
function _getCompoundedRegex() {
|
||||
if ($this->_regex == null) {
|
||||
for ($i = 0, $count = count($this->_patterns); $i < $count; $i++) {
|
||||
$this->_patterns[$i] = '(' . str_replace(
|
||||
array('/', '(', ')'),
|
||||
array('\/', '\(', '\)'),
|
||||
$this->_patterns[$i] ?? '') . ')';
|
||||
}
|
||||
$this->_regex = "/" . implode("|", $this->_patterns) . "/" . $this->_getPerlMatchingFlags();
|
||||
}
|
||||
return $this->_regex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for perl regex mode flags to use.
|
||||
* @return string Perl regex flags.
|
||||
* @access private
|
||||
*/
|
||||
function _getPerlMatchingFlags() {
|
||||
return ($this->_case ? "msS" : "msSi");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* States for a stack machine.
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
*/
|
||||
class SimpleStateStack {
|
||||
var $_stack;
|
||||
|
||||
/**
|
||||
* Constructor. Starts in named state.
|
||||
* @param string $start Starting state name.
|
||||
* @access public
|
||||
*/
|
||||
function __construct($start) {
|
||||
$this->_stack = array($start);
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for current state.
|
||||
* @return string State.
|
||||
* @access public
|
||||
*/
|
||||
function getCurrent() {
|
||||
return $this->_stack[count($this->_stack) - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a state to the stack and sets it
|
||||
* to be the current state.
|
||||
* @param string $state New state.
|
||||
* @access public
|
||||
*/
|
||||
function enter($state) {
|
||||
array_push($this->_stack, $state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Leaves the current state and reverts
|
||||
* to the previous one.
|
||||
* @return boolean False if we drop off
|
||||
* the bottom of the list.
|
||||
* @access public
|
||||
*/
|
||||
function leave() {
|
||||
if (count($this->_stack ?? []) == 1) {
|
||||
return false;
|
||||
}
|
||||
array_pop($this->_stack);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Accepts text and breaks it into tokens.
|
||||
* Some optimisation to make the sure the
|
||||
* content is only scanned by the PHP regex
|
||||
* parser once. Lexer modes must not start
|
||||
* with leading underscores.
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
*/
|
||||
class SimpleLexer {
|
||||
var $_regexes;
|
||||
var $_parser;
|
||||
var $_mode;
|
||||
var $_mode_handlers;
|
||||
var $_case;
|
||||
|
||||
/**
|
||||
* Sets up the lexer in case insensitive matching
|
||||
* by default.
|
||||
* @param SimpleSaxParser $parser Handling strategy by
|
||||
* reference.
|
||||
* @param string $start Starting handler.
|
||||
* @param boolean $case True for case sensitive.
|
||||
* @access public
|
||||
*/
|
||||
function __construct(&$parser, $start = "accept", $case = false) {
|
||||
$this->_case = $case;
|
||||
$this->_regexes = array();
|
||||
$this->_parser = &$parser;
|
||||
$this->_mode = new SimpleStateStack($start);
|
||||
$this->_mode_handlers = array($start => $start);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a token search pattern for a particular
|
||||
* parsing mode. The pattern does not change the
|
||||
* current mode.
|
||||
* @param string $pattern Perl style regex, but ( and )
|
||||
* lose the usual meaning.
|
||||
* @param string $mode Should only apply this
|
||||
* pattern when dealing with
|
||||
* this type of input.
|
||||
* @access public
|
||||
*/
|
||||
function addPattern($pattern, $mode = "accept") {
|
||||
if (! isset($this->_regexes[$mode])) {
|
||||
$this->_regexes[$mode] = new ParallelRegex($this->_case);
|
||||
}
|
||||
$this->_regexes[$mode]->addPattern($pattern);
|
||||
if (! isset($this->_mode_handlers[$mode])) {
|
||||
$this->_mode_handlers[$mode] = $mode;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a pattern that will enter a new parsing
|
||||
* mode. Useful for entering parenthesis, strings,
|
||||
* tags, etc.
|
||||
* @param string $pattern Perl style regex, but ( and )
|
||||
* lose the usual meaning.
|
||||
* @param string $mode Should only apply this
|
||||
* pattern when dealing with
|
||||
* this type of input.
|
||||
* @param string $new_mode Change parsing to this new
|
||||
* nested mode.
|
||||
* @access public
|
||||
*/
|
||||
function addEntryPattern($pattern, $mode, $new_mode) {
|
||||
if (! isset($this->_regexes[$mode])) {
|
||||
$this->_regexes[$mode] = new ParallelRegex($this->_case);
|
||||
}
|
||||
$this->_regexes[$mode]->addPattern($pattern, $new_mode);
|
||||
if (! isset($this->_mode_handlers[$new_mode])) {
|
||||
$this->_mode_handlers[$new_mode] = $new_mode;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a pattern that will exit the current mode
|
||||
* and re-enter the previous one.
|
||||
* @param string $pattern Perl style regex, but ( and )
|
||||
* lose the usual meaning.
|
||||
* @param string $mode Mode to leave.
|
||||
* @access public
|
||||
*/
|
||||
function addExitPattern($pattern, $mode) {
|
||||
if (! isset($this->_regexes[$mode])) {
|
||||
$this->_regexes[$mode] = new ParallelRegex($this->_case);
|
||||
}
|
||||
$this->_regexes[$mode]->addPattern($pattern, "__exit");
|
||||
if (! isset($this->_mode_handlers[$mode])) {
|
||||
$this->_mode_handlers[$mode] = $mode;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a pattern that has a special mode. Acts as an entry
|
||||
* and exit pattern in one go, effectively calling a special
|
||||
* parser handler for this token only.
|
||||
* @param string $pattern Perl style regex, but ( and )
|
||||
* lose the usual meaning.
|
||||
* @param string $mode Should only apply this
|
||||
* pattern when dealing with
|
||||
* this type of input.
|
||||
* @param string $special Use this mode for this one token.
|
||||
* @access public
|
||||
*/
|
||||
function addSpecialPattern($pattern, $mode, $special) {
|
||||
if (! isset($this->_regexes[$mode])) {
|
||||
$this->_regexes[$mode] = new ParallelRegex($this->_case);
|
||||
}
|
||||
$this->_regexes[$mode]->addPattern($pattern, "_$special");
|
||||
if (! isset($this->_mode_handlers[$special])) {
|
||||
$this->_mode_handlers[$special] = $special;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a mapping from a mode to another handler.
|
||||
* @param string $mode Mode to be remapped.
|
||||
* @param string $handler New target handler.
|
||||
* @access public
|
||||
*/
|
||||
function mapHandler($mode, $handler) {
|
||||
$this->_mode_handlers[$mode] = $handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits the page text into tokens. Will fail
|
||||
* if the handlers report an error or if no
|
||||
* content is consumed. If successful then each
|
||||
* unparsed and parsed token invokes a call to the
|
||||
* held listener.
|
||||
* @param string $raw Raw HTML text.
|
||||
* @return boolean True on success, else false.
|
||||
* @access public
|
||||
*/
|
||||
function parse($raw) {
|
||||
if (! isset($this->_parser)) {
|
||||
return false;
|
||||
}
|
||||
$length = strlen($raw ?? '');
|
||||
while (is_array($parsed = $this->_reduce($raw))) {
|
||||
list($raw, $unmatched, $matched, $mode) = $parsed;
|
||||
if (! $this->_dispatchTokens($unmatched, $matched, $mode)) {
|
||||
return false;
|
||||
}
|
||||
if ($raw === '') {
|
||||
return true;
|
||||
}
|
||||
if (strlen($raw ?? '') == $length) {
|
||||
return false;
|
||||
}
|
||||
$length = strlen($raw ?? '');
|
||||
}
|
||||
if (! $parsed) {
|
||||
return false;
|
||||
}
|
||||
return $this->_invokeParser($raw, LEXER_UNMATCHED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the matched token and any leading unmatched
|
||||
* text to the parser changing the lexer to a new
|
||||
* mode if one is listed.
|
||||
* @param string $unmatched Unmatched leading portion.
|
||||
* @param string $matched Actual token match.
|
||||
* @param string $mode Mode after match. A boolean
|
||||
* false mode causes no change.
|
||||
* @return boolean False if there was any error
|
||||
* from the parser.
|
||||
* @access private
|
||||
*/
|
||||
function _dispatchTokens($unmatched, $matched, $mode = false) {
|
||||
if (! $this->_invokeParser($unmatched, LEXER_UNMATCHED)) {
|
||||
return false;
|
||||
}
|
||||
if (is_bool($mode)) {
|
||||
return $this->_invokeParser($matched, LEXER_MATCHED);
|
||||
}
|
||||
if ($this->_isModeEnd($mode)) {
|
||||
if (! $this->_invokeParser($matched, LEXER_EXIT)) {
|
||||
return false;
|
||||
}
|
||||
return $this->_mode->leave();
|
||||
}
|
||||
if ($this->_isSpecialMode($mode)) {
|
||||
$this->_mode->enter($this->_decodeSpecial($mode));
|
||||
if (! $this->_invokeParser($matched, LEXER_SPECIAL)) {
|
||||
return false;
|
||||
}
|
||||
return $this->_mode->leave();
|
||||
}
|
||||
$this->_mode->enter($mode);
|
||||
return $this->_invokeParser($matched, LEXER_ENTER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests to see if the new mode is actually to leave
|
||||
* the current mode and pop an item from the matching
|
||||
* mode stack.
|
||||
* @param string $mode Mode to test.
|
||||
* @return boolean True if this is the exit mode.
|
||||
* @access private
|
||||
*/
|
||||
function _isModeEnd($mode) {
|
||||
return ($mode === "__exit");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to see if the mode is one where this mode
|
||||
* is entered for this token only and automatically
|
||||
* leaves immediately afterwoods.
|
||||
* @param string $mode Mode to test.
|
||||
* @return boolean True if this is the exit mode.
|
||||
* @access private
|
||||
*/
|
||||
function _isSpecialMode($mode) {
|
||||
return (strncmp($mode ?? '', "_", 1) == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Strips the magic underscore marking single token
|
||||
* modes.
|
||||
* @param string $mode Mode to decode.
|
||||
* @return string Underlying mode name.
|
||||
* @access private
|
||||
*/
|
||||
function _decodeSpecial($mode) {
|
||||
return substr($mode ?? '', 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the parser method named after the current
|
||||
* mode. Empty content will be ignored. The lexer
|
||||
* has a parser handler for each mode in the lexer.
|
||||
* @param string $content Text parsed.
|
||||
* @param boolean $is_match Token is recognised rather
|
||||
* than unparsed data.
|
||||
* @access private
|
||||
*/
|
||||
function _invokeParser($content, $is_match) {
|
||||
if (($content === '') || ($content === false)) {
|
||||
return true;
|
||||
}
|
||||
$handler = $this->_mode_handlers[$this->_mode->getCurrent()];
|
||||
return $this->_parser->$handler($content, $is_match);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to match a chunk of text and if successful
|
||||
* removes the recognised chunk and any leading
|
||||
* unparsed data. Empty strings will not be matched.
|
||||
* @param string $raw The subject to parse. This is the
|
||||
* content that will be eaten.
|
||||
* @return array/boolean Three item list of unparsed
|
||||
* content followed by the
|
||||
* recognised token and finally the
|
||||
* action the parser is to take.
|
||||
* True if no match, false if there
|
||||
* is a parsing error.
|
||||
* @access private
|
||||
*/
|
||||
function _reduce($raw) {
|
||||
if ($action = $this->_regexes[$this->_mode->getCurrent()]->match($raw, $match)) {
|
||||
$unparsed_character_count = strpos($raw ?? '', $match ?? '');
|
||||
$unparsed = substr($raw ?? '', 0, $unparsed_character_count);
|
||||
$raw = substr($raw ?? '', $unparsed_character_count + strlen($match ?? ''));
|
||||
return array($raw, $unparsed, $match, $action);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Breaks HTML into SAX events.
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
*/
|
||||
class SimpleHtmlLexer extends SimpleLexer {
|
||||
|
||||
/**
|
||||
* Sets up the lexer with case insensitive matching
|
||||
* and adds the HTML handlers.
|
||||
* @param SimpleSaxParser $parser Handling strategy by
|
||||
* reference.
|
||||
* @access public
|
||||
*/
|
||||
function __construct(&$parser) {
|
||||
parent::__construct($parser, 'text');
|
||||
$this->mapHandler('text', 'acceptTextToken');
|
||||
$this->_addSkipping();
|
||||
foreach ($this->_getParsedTags() as $tag) {
|
||||
$this->_addTag($tag);
|
||||
}
|
||||
$this->_addInTagTokens();
|
||||
}
|
||||
|
||||
/**
|
||||
* List of parsed tags. Others are ignored.
|
||||
* @return array List of searched for tags.
|
||||
* @access private
|
||||
*/
|
||||
function _getParsedTags() {
|
||||
return array('a', 'base', 'title', 'form', 'input', 'button', 'textarea', 'select',
|
||||
'option', 'frameset', 'frame', 'label');
|
||||
}
|
||||
|
||||
/**
|
||||
* The lexer has to skip certain sections such
|
||||
* as server code, client code and styles.
|
||||
* @access private
|
||||
*/
|
||||
function _addSkipping() {
|
||||
$this->mapHandler('css', 'ignore');
|
||||
$this->addEntryPattern('<style', 'text', 'css');
|
||||
$this->addExitPattern('</style>', 'css');
|
||||
$this->mapHandler('js', 'ignore');
|
||||
$this->addEntryPattern('<script', 'text', 'js');
|
||||
$this->addExitPattern('</script>', 'js');
|
||||
$this->mapHandler('comment', 'ignore');
|
||||
$this->addEntryPattern('<!--', 'text', 'comment');
|
||||
$this->addExitPattern('-->', 'comment');
|
||||
}
|
||||
|
||||
/**
|
||||
* Pattern matches to start and end a tag.
|
||||
* @param string $tag Name of tag to scan for.
|
||||
* @access private
|
||||
*/
|
||||
function _addTag($tag) {
|
||||
$this->addSpecialPattern("</$tag>", 'text', 'acceptEndToken');
|
||||
$this->addEntryPattern("<$tag", 'text', 'tag');
|
||||
}
|
||||
|
||||
/**
|
||||
* Pattern matches to parse the inside of a tag
|
||||
* including the attributes and their quoting.
|
||||
* @access private
|
||||
*/
|
||||
function _addInTagTokens() {
|
||||
$this->mapHandler('tag', 'acceptStartToken');
|
||||
$this->addSpecialPattern('\s+', 'tag', 'ignore');
|
||||
$this->_addAttributeTokens();
|
||||
$this->addExitPattern('/>', 'tag');
|
||||
$this->addExitPattern('>', 'tag');
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches attributes that are either single quoted,
|
||||
* double quoted or unquoted.
|
||||
* @access private
|
||||
*/
|
||||
function _addAttributeTokens() {
|
||||
$this->mapHandler('dq_attribute', 'acceptAttributeToken');
|
||||
$this->addEntryPattern('=\s*"', 'tag', 'dq_attribute');
|
||||
$this->addPattern("\\\\\"", 'dq_attribute');
|
||||
$this->addExitPattern('"', 'dq_attribute');
|
||||
$this->mapHandler('sq_attribute', 'acceptAttributeToken');
|
||||
$this->addEntryPattern("=\s*'", 'tag', 'sq_attribute');
|
||||
$this->addPattern("\\\\'", 'sq_attribute');
|
||||
$this->addExitPattern("'", 'sq_attribute');
|
||||
$this->mapHandler('uq_attribute', 'acceptAttributeToken');
|
||||
$this->addSpecialPattern('=\s*[^>\s]*', 'tag', 'uq_attribute');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts HTML tokens into selected SAX events.
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
*/
|
||||
class SimpleHtmlSaxParser {
|
||||
var $_lexer;
|
||||
var $_listener;
|
||||
var $_tag;
|
||||
var $_attributes;
|
||||
var $_current_attribute;
|
||||
|
||||
/**
|
||||
* Sets the listener.
|
||||
* @param SimpleSaxListener $listener SAX event handler.
|
||||
* @access public
|
||||
*/
|
||||
function __construct(&$listener) {
|
||||
$this->_listener = &$listener;
|
||||
$this->_lexer = &$this->createLexer($this);
|
||||
$this->_tag = '';
|
||||
$this->_attributes = array();
|
||||
$this->_current_attribute = '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the content through the lexer which
|
||||
* should call back to the acceptors.
|
||||
* @param string $raw Page text to parse.
|
||||
* @return boolean False if parse error.
|
||||
* @access public
|
||||
*/
|
||||
function parse($raw) {
|
||||
return $this->_lexer->parse($raw);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the matching lexer. Starts in 'text' mode.
|
||||
* @param SimpleSaxParser $parser Event generator, usually $self.
|
||||
* @return SimpleLexer Lexer suitable for this parser.
|
||||
* @access public
|
||||
* @static
|
||||
*/
|
||||
function &createLexer(&$parser) {
|
||||
$lexer = new SimpleHtmlLexer($parser);
|
||||
return $lexer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accepts a token from the tag mode. If the
|
||||
* starting element completes then the element
|
||||
* is dispatched and the current attributes
|
||||
* set back to empty. The element or attribute
|
||||
* name is converted to lower case.
|
||||
* @param string $token Incoming characters.
|
||||
* @param integer $event Lexer event type.
|
||||
* @return boolean False if parse error.
|
||||
* @access public
|
||||
*/
|
||||
function acceptStartToken($token, $event) {
|
||||
if ($event == LEXER_ENTER) {
|
||||
$this->_tag = strtolower(substr($token ?? '', 1));
|
||||
return true;
|
||||
}
|
||||
if ($event == LEXER_EXIT) {
|
||||
$success = $this->_listener->startElement(
|
||||
$this->_tag,
|
||||
$this->_attributes);
|
||||
$this->_tag = '';
|
||||
$this->_attributes = array();
|
||||
return $success;
|
||||
}
|
||||
if ($token != '=') {
|
||||
$this->_current_attribute = strtolower(SimpleHtmlSaxParser::decodeHtml($token) ?? '');
|
||||
$this->_attributes[$this->_current_attribute] = '';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accepts a token from the end tag mode.
|
||||
* The element name is converted to lower case.
|
||||
* @param string $token Incoming characters.
|
||||
* @param integer $event Lexer event type.
|
||||
* @return boolean False if parse error.
|
||||
* @access public
|
||||
*/
|
||||
function acceptEndToken($token, $event) {
|
||||
if (! preg_match('/<\/(.*)>/', $token ?? '', $matches)) {
|
||||
return false;
|
||||
}
|
||||
return $this->_listener->endElement(strtolower($matches[1] ?? ''));
|
||||
}
|
||||
|
||||
/**
|
||||
* Part of the tag data.
|
||||
* @param string $token Incoming characters.
|
||||
* @param integer $event Lexer event type.
|
||||
* @return boolean False if parse error.
|
||||
* @access public
|
||||
*/
|
||||
function acceptAttributeToken($token, $event) {
|
||||
if ($this->_current_attribute) {
|
||||
if ($event == LEXER_UNMATCHED) {
|
||||
$this->_attributes[$this->_current_attribute] .=
|
||||
SimpleHtmlSaxParser::decodeHtml($token);
|
||||
}
|
||||
if ($event == LEXER_SPECIAL) {
|
||||
$this->_attributes[$this->_current_attribute] .=
|
||||
preg_replace('/^=\s*/' , '', SimpleHtmlSaxParser::decodeHtml($token) ?? '');
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* A character entity.
|
||||
* @param string $token Incoming characters.
|
||||
* @param integer $event Lexer event type.
|
||||
* @return boolean False if parse error.
|
||||
* @access public
|
||||
*/
|
||||
function acceptEntityToken($token, $event) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Character data between tags regarded as
|
||||
* important.
|
||||
* @param string $token Incoming characters.
|
||||
* @param integer $event Lexer event type.
|
||||
* @return boolean False if parse error.
|
||||
* @access public
|
||||
*/
|
||||
function acceptTextToken($token, $event) {
|
||||
return $this->_listener->addContent($token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Incoming data to be ignored.
|
||||
* @param string $token Incoming characters.
|
||||
* @param integer $event Lexer event type.
|
||||
* @return boolean False if parse error.
|
||||
* @access public
|
||||
*/
|
||||
function ignore($token, $event) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes any HTML entities.
|
||||
* @param string $html Incoming HTML.
|
||||
* @return string Outgoing plain text.
|
||||
* @access public
|
||||
* @static
|
||||
*/
|
||||
static function decodeHtml($html) {
|
||||
return html_entity_decode($html ?? '', ENT_QUOTES);
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns HTML into text browser visible text. Images
|
||||
* are converted to their alt text and tags are supressed.
|
||||
* Entities are converted to their visible representation.
|
||||
* @param string $html HTML to convert.
|
||||
* @return string Plain text.
|
||||
* @access public
|
||||
* @static
|
||||
*/
|
||||
static function normalise($html) {
|
||||
$text = preg_replace('|<!--.*?-->|', '', $html ?? '');
|
||||
$text = preg_replace('|<script[^>]*>.*?</script>|', '', $text ?? '');
|
||||
$text = preg_replace('|<img[^>]*alt\s*=\s*"([^"]*)"[^>]*>|', ' \1 ', $text ?? '');
|
||||
$text = preg_replace('|<img[^>]*alt\s*=\s*\'([^\']*)\'[^>]*>|', ' \1 ', $text ?? '');
|
||||
$text = preg_replace('|<img[^>]*alt\s*=\s*([a-zA-Z_]+)[^>]*>|', ' \1 ', $text ?? '');
|
||||
$text = preg_replace('|<[^>]*>|', '', $text ?? '');
|
||||
$text = SimpleHtmlSaxParser::decodeHtml($text);
|
||||
$text = preg_replace('|\s+|', ' ', $text ?? '');
|
||||
return trim(trim($text ?? ''), "\xA0"); // TODO: The \xAO is a . Add a test for this.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* SAX event handler.
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
* @abstract
|
||||
*/
|
||||
class SimpleSaxListener {
|
||||
|
||||
/**
|
||||
* Sets the document to write to.
|
||||
* @access public
|
||||
*/
|
||||
function __construct() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Start of element event.
|
||||
* @param string $name Element name.
|
||||
* @param hash $attributes Name value pairs.
|
||||
* Attributes without content
|
||||
* are marked as true.
|
||||
* @return boolean False on parse error.
|
||||
* @access public
|
||||
*/
|
||||
function startElement($name, $attributes) {
|
||||
}
|
||||
|
||||
/**
|
||||
* End of element event.
|
||||
* @param string $name Element name.
|
||||
* @return boolean False on parse error.
|
||||
* @access public
|
||||
*/
|
||||
function endElement($name) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Unparsed, but relevant data.
|
||||
* @param string $text May include unparsed tags.
|
||||
* @return boolean False on parse error.
|
||||
* @access public
|
||||
*/
|
||||
function addContent($text) {
|
||||
}
|
||||
}
|
||||
|
137
thirdparty/simpletest/selector.php
vendored
137
thirdparty/simpletest/selector.php
vendored
@ -1,137 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Base include file for SimpleTest.
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
* @version $Id: selector.php 1723 2008-04-08 00:34:10Z lastcraft $
|
||||
*/
|
||||
|
||||
/**#@+
|
||||
* include SimpleTest files
|
||||
*/
|
||||
require_once(dirname(__FILE__) . '/tag.php');
|
||||
require_once(dirname(__FILE__) . '/encoding.php');
|
||||
/**#@-*/
|
||||
|
||||
/**
|
||||
* Used to extract form elements for testing against.
|
||||
* Searches by name attribute.
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
*/
|
||||
class SimpleByName {
|
||||
var $_name;
|
||||
|
||||
/**
|
||||
* Stashes the name for later comparison.
|
||||
* @param string $name Name attribute to match.
|
||||
*/
|
||||
function __construct($name) {
|
||||
$this->_name = $name;
|
||||
}
|
||||
|
||||
function getName() {
|
||||
return $this->_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares with name attribute of widget.
|
||||
* @param SimpleWidget $widget Control to compare.
|
||||
* @access public
|
||||
*/
|
||||
function isMatch($widget) {
|
||||
return ($widget->getName() == $this->_name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to extract form elements for testing against.
|
||||
* Searches by visible label or alt text.
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
*/
|
||||
class SimpleByLabel {
|
||||
var $_label;
|
||||
|
||||
/**
|
||||
* Stashes the name for later comparison.
|
||||
* @param string $label Visible text to match.
|
||||
*/
|
||||
function __construct($label) {
|
||||
$this->_label = $label;
|
||||
}
|
||||
|
||||
/**
|
||||
* Comparison. Compares visible text of widget or
|
||||
* related label.
|
||||
* @param SimpleWidget $widget Control to compare.
|
||||
* @access public
|
||||
*/
|
||||
function isMatch($widget) {
|
||||
if (! method_exists($widget, 'isLabel')) {
|
||||
return false;
|
||||
}
|
||||
return $widget->isLabel($this->_label);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to extract form elements for testing against.
|
||||
* Searches dy id attribute.
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
*/
|
||||
class SimpleById {
|
||||
var $_id;
|
||||
|
||||
/**
|
||||
* Stashes the name for later comparison.
|
||||
* @param string $id ID atribute to match.
|
||||
*/
|
||||
function __construct($id) {
|
||||
$this->_id = $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Comparison. Compares id attribute of widget.
|
||||
* @param SimpleWidget $widget Control to compare.
|
||||
* @access public
|
||||
*/
|
||||
function isMatch($widget) {
|
||||
return $widget->isId($this->_id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to extract form elements for testing against.
|
||||
* Searches by visible label, name or alt text.
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
*/
|
||||
class SimpleByLabelOrName {
|
||||
var $_label;
|
||||
|
||||
/**
|
||||
* Stashes the name/label for later comparison.
|
||||
* @param string $label Visible text to match.
|
||||
*/
|
||||
function __construct($label) {
|
||||
$this->_label = $label;
|
||||
}
|
||||
|
||||
/**
|
||||
* Comparison. Compares visible text of widget or
|
||||
* related label or name.
|
||||
* @param SimpleWidget $widget Control to compare.
|
||||
* @access public
|
||||
*/
|
||||
function isMatch($widget) {
|
||||
if (method_exists($widget, 'isLabel')) {
|
||||
if ($widget->isLabel($this->_label)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return ($widget->getName() == $this->_label);
|
||||
}
|
||||
}
|
||||
?>
|
216
thirdparty/simpletest/socket.php
vendored
216
thirdparty/simpletest/socket.php
vendored
@ -1,216 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* base include file for SimpleTest
|
||||
* @package SimpleTest
|
||||
* @subpackage MockObjects
|
||||
* @version $Id: socket.php 1723 2008-04-08 00:34:10Z lastcraft $
|
||||
*/
|
||||
|
||||
/**#@+
|
||||
* include SimpleTest files
|
||||
*/
|
||||
require_once(dirname(__FILE__) . '/compatibility.php');
|
||||
/**#@-*/
|
||||
|
||||
/**
|
||||
* Stashes an error for later. Useful for constructors
|
||||
* until PHP gets exceptions.
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
*/
|
||||
class SimpleStickyError {
|
||||
var $_error = 'Constructor not chained';
|
||||
|
||||
/**
|
||||
* Sets the error to empty.
|
||||
* @access public
|
||||
*/
|
||||
function __construct() {
|
||||
$this->_clearError();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for an outstanding error.
|
||||
* @return boolean True if there is an error.
|
||||
* @access public
|
||||
*/
|
||||
function isError() {
|
||||
return ($this->_error != '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for an outstanding error.
|
||||
* @return string Empty string if no error otherwise
|
||||
* the error message.
|
||||
* @access public
|
||||
*/
|
||||
function getError() {
|
||||
return $this->_error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the internal error.
|
||||
* @param string Error message to stash.
|
||||
* @access protected
|
||||
*/
|
||||
function _setError($error) {
|
||||
$this->_error = $error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the error state to no error.
|
||||
* @access protected
|
||||
*/
|
||||
function _clearError() {
|
||||
$this->_setError('');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for TCP/IP socket.
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
*/
|
||||
class SimpleSocket extends SimpleStickyError {
|
||||
var $_handle;
|
||||
var $_is_open = false;
|
||||
var $_sent = '';
|
||||
var $lock_size;
|
||||
|
||||
/**
|
||||
* Opens a socket for reading and writing.
|
||||
* @param string $host Hostname to send request to.
|
||||
* @param integer $port Port on remote machine to open.
|
||||
* @param integer $timeout Connection timeout in seconds.
|
||||
* @param integer $block_size Size of chunk to read.
|
||||
* @access public
|
||||
*/
|
||||
function __construct($host, $port, $timeout, $block_size = 255) {
|
||||
parent::__construct();
|
||||
if (! ($this->_handle = $this->_openSocket($host, $port, $error_number, $error, $timeout))) {
|
||||
$this->_setError("Cannot open [$host:$port] with [$error] within [$timeout] seconds");
|
||||
return;
|
||||
}
|
||||
$this->_is_open = true;
|
||||
$this->_block_size = $block_size;
|
||||
SimpleTestCompatibility::setTimeout($this->_handle, $timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes some data to the socket and saves alocal copy.
|
||||
* @param string $message String to send to socket.
|
||||
* @return boolean True if successful.
|
||||
* @access public
|
||||
*/
|
||||
function write($message) {
|
||||
if ($this->isError() || ! $this->isOpen()) {
|
||||
return false;
|
||||
}
|
||||
$count = fwrite($this->_handle, $message ?? '');
|
||||
if (! $count) {
|
||||
if ($count === false) {
|
||||
$this->_setError('Cannot write to socket');
|
||||
$this->close();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
fflush($this->_handle);
|
||||
$this->_sent .= $message;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads data from the socket. The error suppresion
|
||||
* is a workaround for PHP4 always throwing a warning
|
||||
* with a secure socket.
|
||||
* @return integer/boolean Incoming bytes. False
|
||||
* on error.
|
||||
* @access public
|
||||
*/
|
||||
function read() {
|
||||
if ($this->isError() || ! $this->isOpen()) {
|
||||
return false;
|
||||
}
|
||||
$raw = @fread($this->_handle, $this->_block_size ?? 0);
|
||||
if ($raw === false) {
|
||||
$this->_setError('Cannot read from socket');
|
||||
$this->close();
|
||||
}
|
||||
return $raw;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for socket open state.
|
||||
* @return boolean True if open.
|
||||
* @access public
|
||||
*/
|
||||
function isOpen() {
|
||||
return $this->_is_open;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the socket preventing further reads.
|
||||
* Cannot be reopened once closed.
|
||||
* @return boolean True if successful.
|
||||
* @access public
|
||||
*/
|
||||
function close() {
|
||||
$this->_is_open = false;
|
||||
return fclose($this->_handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for content so far.
|
||||
* @return string Bytes sent only.
|
||||
* @access public
|
||||
*/
|
||||
function getSent() {
|
||||
return $this->_sent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually opens the low level socket.
|
||||
* @param string $host Host to connect to.
|
||||
* @param integer $port Port on host.
|
||||
* @param integer $error_number Recipient of error code.
|
||||
* @param string $error Recipoent of error message.
|
||||
* @param integer $timeout Maximum time to wait for connection.
|
||||
* @access protected
|
||||
*/
|
||||
function _openSocket($host, $port, &$error_number, &$error, $timeout) {
|
||||
return @fsockopen($host ?? '', $port ?? 0, $error_number, $error, $timeout);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for TCP/IP socket over TLS.
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
*/
|
||||
class SimpleSecureSocket extends SimpleSocket {
|
||||
|
||||
/**
|
||||
* Opens a secure socket for reading and writing.
|
||||
* @param string $host Hostname to send request to.
|
||||
* @param integer $port Port on remote machine to open.
|
||||
* @param integer $timeout Connection timeout in seconds.
|
||||
* @access public
|
||||
*/
|
||||
function __construct($host, $port, $timeout) {
|
||||
parent::__construct($host, $port, $timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually opens the low level socket.
|
||||
* @param string $host Host to connect to.
|
||||
* @param integer $port Port on host.
|
||||
* @param integer $error_number Recipient of error code.
|
||||
* @param string $error Recipient of error message.
|
||||
* @param integer $timeout Maximum time to wait for connection.
|
||||
* @access protected
|
||||
*/
|
||||
function _openSocket($host, $port, &$error_number, &$error, $timeout) {
|
||||
return parent::_openSocket("tls://$host", $port, $error_number, $error, $timeout);
|
||||
}
|
||||
}
|
||||
?>
|
1417
thirdparty/simpletest/tag.php
vendored
1417
thirdparty/simpletest/tag.php
vendored
@ -1,1417 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Base include file for SimpleTest.
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
* @version $Id: tag.php 1723 2008-04-08 00:34:10Z lastcraft $
|
||||
*/
|
||||
|
||||
/**#@+
|
||||
* include SimpleTest files
|
||||
*/
|
||||
require_once(dirname(__FILE__) . '/parser.php');
|
||||
require_once(dirname(__FILE__) . '/encoding.php');
|
||||
/**#@-*/
|
||||
|
||||
/**
|
||||
* HTML or XML tag.
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
*/
|
||||
class SimpleTag {
|
||||
var $_name;
|
||||
var $_attributes;
|
||||
var $_content;
|
||||
|
||||
/**
|
||||
* Starts with a named tag with attributes only.
|
||||
* @param string $name Tag name.
|
||||
* @param hash $attributes Attribute names and
|
||||
* string values. Note that
|
||||
* the keys must have been
|
||||
* converted to lower case.
|
||||
*/
|
||||
function __construct($name, $attributes) {
|
||||
$this->_name = strtolower(trim($name ?? ''));
|
||||
$this->_attributes = $attributes;
|
||||
$this->_content = '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if the tag can have both start and
|
||||
* end tags with content in between.
|
||||
* @return boolean True if content allowed.
|
||||
* @access public
|
||||
*/
|
||||
function expectEndTag() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* The current tag should not swallow all content for
|
||||
* itself as it's searchable page content. Private
|
||||
* content tags are usually widgets that contain default
|
||||
* values.
|
||||
* @return boolean False as content is available
|
||||
* to other tags by default.
|
||||
* @access public
|
||||
*/
|
||||
function isPrivateContent() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends string content to the current content.
|
||||
* @param string $content Additional text.
|
||||
* @access public
|
||||
*/
|
||||
function addContent($content) {
|
||||
$this->_content .= (string)$content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an enclosed tag to the content.
|
||||
* @param SimpleTag $tag New tag.
|
||||
* @access public
|
||||
*/
|
||||
function addTag(&$tag) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for tag name.
|
||||
* @return string Name of tag.
|
||||
* @access public
|
||||
*/
|
||||
function getTagName() {
|
||||
return $this->_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* List of legal child elements.
|
||||
* @return array List of element names.
|
||||
* @access public
|
||||
*/
|
||||
function getChildElements() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for an attribute.
|
||||
* @param string $label Attribute name.
|
||||
* @return string Attribute value.
|
||||
* @access public
|
||||
*/
|
||||
function getAttribute($label) {
|
||||
$label = strtolower($label ?? '');
|
||||
if (! isset($this->_attributes[$label])) {
|
||||
return false;
|
||||
}
|
||||
return (string)$this->_attributes[$label];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an attribute.
|
||||
* @param string $label Attribute name.
|
||||
* @return string $value New attribute value.
|
||||
* @access protected
|
||||
*/
|
||||
function _setAttribute($label, $value) {
|
||||
$this->_attributes[strtolower($label)] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for the whole content so far.
|
||||
* @return string Content as big raw string.
|
||||
* @access public
|
||||
*/
|
||||
function getContent() {
|
||||
return $this->_content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for content reduced to visible text. Acts
|
||||
* like a text mode browser, normalising space and
|
||||
* reducing images to their alt text.
|
||||
* @return string Content as plain text.
|
||||
* @access public
|
||||
*/
|
||||
function getText() {
|
||||
return SimpleHtmlSaxParser::normalise($this->_content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to see if id attribute matches.
|
||||
* @param string $id ID to test against.
|
||||
* @return boolean True on match.
|
||||
* @access public
|
||||
*/
|
||||
function isId($id) {
|
||||
return ($this->getAttribute('id') == $id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Base url.
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
*/
|
||||
class SimpleBaseTag extends SimpleTag {
|
||||
|
||||
/**
|
||||
* Starts with a named tag with attributes only.
|
||||
* @param hash $attributes Attribute names and
|
||||
* string values.
|
||||
*/
|
||||
function __construct($attributes) {
|
||||
parent::__construct('base', $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Base tag is not a block tag.
|
||||
* @return boolean false
|
||||
* @access public
|
||||
*/
|
||||
function expectEndTag() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Page title.
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
*/
|
||||
class SimpleTitleTag extends SimpleTag {
|
||||
|
||||
/**
|
||||
* Starts with a named tag with attributes only.
|
||||
* @param hash $attributes Attribute names and
|
||||
* string values.
|
||||
*/
|
||||
function __construct($attributes) {
|
||||
parent::__construct('title', $attributes);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Link.
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
*/
|
||||
class SimpleAnchorTag extends SimpleTag {
|
||||
|
||||
/**
|
||||
* Starts with a named tag with attributes only.
|
||||
* @param hash $attributes Attribute names and
|
||||
* string values.
|
||||
*/
|
||||
function __construct($attributes) {
|
||||
parent::__construct('a', $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for URL as string.
|
||||
* @return string Coerced as string.
|
||||
* @access public
|
||||
*/
|
||||
function getHref() {
|
||||
$url = $this->getAttribute('href');
|
||||
if (is_bool($url)) {
|
||||
$url = '';
|
||||
}
|
||||
return $url;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Form element.
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
*/
|
||||
class SimpleWidget extends SimpleTag {
|
||||
var $_value;
|
||||
var $_label;
|
||||
var $_is_set;
|
||||
|
||||
/**
|
||||
* Starts with a named tag with attributes only.
|
||||
* @param string $name Tag name.
|
||||
* @param hash $attributes Attribute names and
|
||||
* string values.
|
||||
*/
|
||||
function __construct($name, $attributes) {
|
||||
parent::__construct($name, $attributes);
|
||||
$this->_value = false;
|
||||
$this->_label = false;
|
||||
$this->_is_set = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for name submitted as the key in
|
||||
* GET/POST variables hash.
|
||||
* @return string Parsed value.
|
||||
* @access public
|
||||
*/
|
||||
function getName() {
|
||||
return $this->getAttribute('name');
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for default value parsed with the tag.
|
||||
* @return string Parsed value.
|
||||
* @access public
|
||||
*/
|
||||
function getDefault() {
|
||||
return $this->getAttribute('value');
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for currently set value or default if
|
||||
* none.
|
||||
* @return string Value set by form or default
|
||||
* if none.
|
||||
* @access public
|
||||
*/
|
||||
function getValue() {
|
||||
if (! $this->_is_set) {
|
||||
return $this->getDefault();
|
||||
}
|
||||
return $this->_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current form element value.
|
||||
* @param string $value New value.
|
||||
* @return boolean True if allowed.
|
||||
* @access public
|
||||
*/
|
||||
function setValue($value) {
|
||||
$this->_value = $value;
|
||||
$this->_is_set = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the form element value back to the
|
||||
* default.
|
||||
* @access public
|
||||
*/
|
||||
function resetValue() {
|
||||
$this->_is_set = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows setting of a label externally, say by a
|
||||
* label tag.
|
||||
* @param string $label Label to attach.
|
||||
* @access public
|
||||
*/
|
||||
function setLabel($label) {
|
||||
$this->_label = trim($label ?? '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads external or internal label.
|
||||
* @param string $label Label to test.
|
||||
* @return boolean True is match.
|
||||
* @access public
|
||||
*/
|
||||
function isLabel($label) {
|
||||
return $this->_label == trim($label ?? '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches the value into the form encoded packet.
|
||||
* @param SimpleEncoding $encoding Form packet.
|
||||
* @access public
|
||||
*/
|
||||
function write(&$encoding, $x, $y) {
|
||||
if ($this->getName()) {
|
||||
$encoding->add($this->getName(), $this->getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Text, password and hidden field.
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
*/
|
||||
class SimpleTextTag extends SimpleWidget {
|
||||
|
||||
/**
|
||||
* Starts with a named tag with attributes only.
|
||||
* @param hash $attributes Attribute names and
|
||||
* string values.
|
||||
*/
|
||||
function __construct($attributes) {
|
||||
parent::__construct('input', $attributes);
|
||||
if ($this->getAttribute('value') === false) {
|
||||
$this->_setAttribute('value', '');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tag contains no content.
|
||||
* @return boolean False.
|
||||
* @access public
|
||||
*/
|
||||
function expectEndTag() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current form element value. Cannot
|
||||
* change the value of a hidden field.
|
||||
* @param string $value New value.
|
||||
* @return boolean True if allowed.
|
||||
* @access public
|
||||
*/
|
||||
function setValue($value) {
|
||||
if ($this->getAttribute('type') == 'hidden') {
|
||||
return false;
|
||||
}
|
||||
return parent::setValue($value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit button as input tag.
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
*/
|
||||
class SimpleSubmitTag extends SimpleWidget {
|
||||
|
||||
/**
|
||||
* Starts with a named tag with attributes only.
|
||||
* @param hash $attributes Attribute names and
|
||||
* string values.
|
||||
*/
|
||||
function __construct($attributes) {
|
||||
parent::__construct('input', $attributes);
|
||||
if ($this->getAttribute('value') === false) {
|
||||
$this->_setAttribute('value', 'Submit');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tag contains no end element.
|
||||
* @return boolean False.
|
||||
* @access public
|
||||
*/
|
||||
function expectEndTag() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables the setting of the button value.
|
||||
* @param string $value Ignored.
|
||||
* @return boolean True if allowed.
|
||||
* @access public
|
||||
*/
|
||||
function setValue($value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Value of browser visible text.
|
||||
* @return string Visible label.
|
||||
* @access public
|
||||
*/
|
||||
function getLabel() {
|
||||
return $this->getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for a label match when searching.
|
||||
* @param string $label Label to test.
|
||||
* @return boolean True on match.
|
||||
* @access public
|
||||
*/
|
||||
function isLabel($label) {
|
||||
return trim($label ?? '') == trim($this->getLabel() ?? '');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Image button as input tag.
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
*/
|
||||
class SimpleImageSubmitTag extends SimpleWidget {
|
||||
|
||||
/**
|
||||
* Starts with a named tag with attributes only.
|
||||
* @param hash $attributes Attribute names and
|
||||
* string values.
|
||||
*/
|
||||
function __construct($attributes) {
|
||||
parent::__construct('input', $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tag contains no end element.
|
||||
* @return boolean False.
|
||||
* @access public
|
||||
*/
|
||||
function expectEndTag() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables the setting of the button value.
|
||||
* @param string $value Ignored.
|
||||
* @return boolean True if allowed.
|
||||
* @access public
|
||||
*/
|
||||
function setValue($value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Value of browser visible text.
|
||||
* @return string Visible label.
|
||||
* @access public
|
||||
*/
|
||||
function getLabel() {
|
||||
if ($this->getAttribute('title')) {
|
||||
return $this->getAttribute('title');
|
||||
}
|
||||
return $this->getAttribute('alt');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for a label match when searching.
|
||||
* @param string $label Label to test.
|
||||
* @return boolean True on match.
|
||||
* @access public
|
||||
*/
|
||||
function isLabel($label) {
|
||||
return trim($label ?? '') == trim($this->getLabel() ?? '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches the value into the form encoded packet.
|
||||
* @param SimpleEncoding $encoding Form packet.
|
||||
* @param integer $x X coordinate of click.
|
||||
* @param integer $y Y coordinate of click.
|
||||
* @access public
|
||||
*/
|
||||
function write(&$encoding, $x, $y) {
|
||||
if ($this->getName()) {
|
||||
$encoding->add($this->getName() . '.x', $x);
|
||||
$encoding->add($this->getName() . '.y', $y);
|
||||
} else {
|
||||
$encoding->add('x', $x);
|
||||
$encoding->add('y', $y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit button as button tag.
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
*/
|
||||
class SimpleButtonTag extends SimpleWidget {
|
||||
|
||||
/**
|
||||
* Starts with a named tag with attributes only.
|
||||
* Defaults are very browser dependent.
|
||||
* @param hash $attributes Attribute names and
|
||||
* string values.
|
||||
*/
|
||||
function __construct($attributes) {
|
||||
parent::__construct('button', $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if the tag can have both start and
|
||||
* end tags with content in between.
|
||||
* @return boolean True if content allowed.
|
||||
* @access public
|
||||
*/
|
||||
function expectEndTag() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables the setting of the button value.
|
||||
* @param string $value Ignored.
|
||||
* @return boolean True if allowed.
|
||||
* @access public
|
||||
*/
|
||||
function setValue($value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Value of browser visible text.
|
||||
* @return string Visible label.
|
||||
* @access public
|
||||
*/
|
||||
function getLabel() {
|
||||
return $this->getContent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for a label match when searching.
|
||||
* @param string $label Label to test.
|
||||
* @return boolean True on match.
|
||||
* @access public
|
||||
*/
|
||||
function isLabel($label) {
|
||||
return trim($label ?? '') == trim($this->getLabel() ?? '');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Content tag for text area.
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
*/
|
||||
class SimpleTextAreaTag extends SimpleWidget {
|
||||
|
||||
/**
|
||||
* Starts with a named tag with attributes only.
|
||||
* @param hash $attributes Attribute names and
|
||||
* string values.
|
||||
*/
|
||||
function __construct($attributes) {
|
||||
parent::__construct('textarea', $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for starting value.
|
||||
* @return string Parsed value.
|
||||
* @access public
|
||||
*/
|
||||
function getDefault() {
|
||||
return $this->_wrap(SimpleHtmlSaxParser::decodeHtml($this->getContent()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies word wrapping if needed.
|
||||
* @param string $value New value.
|
||||
* @return boolean True if allowed.
|
||||
* @access public
|
||||
*/
|
||||
function setValue($value) {
|
||||
return parent::setValue($this->_wrap($value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to see if text should be wrapped.
|
||||
* @return boolean True if wrapping on.
|
||||
* @access private
|
||||
*/
|
||||
function _wrapIsEnabled() {
|
||||
if ($this->getAttribute('cols')) {
|
||||
$wrap = $this->getAttribute('wrap');
|
||||
if (($wrap == 'physical') || ($wrap == 'hard')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the formatting that is peculiar to
|
||||
* this tag. There is strange behaviour in this
|
||||
* one, including stripping a leading new line.
|
||||
* Go figure. I am using Firefox as a guide.
|
||||
* @param string $text Text to wrap.
|
||||
* @return string Text wrapped with carriage
|
||||
* returns and line feeds
|
||||
* @access private
|
||||
*/
|
||||
function _wrap($text) {
|
||||
$text = str_replace("\r\r\n", "\r\n", str_replace("\n", "\r\n", $text ?? '') ?? '');
|
||||
$text = str_replace("\r\n\n", "\r\n", str_replace("\r", "\r\n", $text ?? '') ?? '');
|
||||
if (strncmp($text ?? '', "\r\n", strlen("\r\n")) == 0) {
|
||||
$text = substr($text ?? '', strlen("\r\n"));
|
||||
}
|
||||
if ($this->_wrapIsEnabled()) {
|
||||
return wordwrap(
|
||||
$text ?? '',
|
||||
(integer)$this->getAttribute('cols'),
|
||||
"\r\n");
|
||||
}
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* The content of textarea is not part of the page.
|
||||
* @return boolean True.
|
||||
* @access public
|
||||
*/
|
||||
function isPrivateContent() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* File upload widget.
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
*/
|
||||
class SimpleUploadTag extends SimpleWidget {
|
||||
|
||||
/**
|
||||
* Starts with attributes only.
|
||||
* @param hash $attributes Attribute names and
|
||||
* string values.
|
||||
*/
|
||||
function __construct($attributes) {
|
||||
parent::__construct('input', $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tag contains no content.
|
||||
* @return boolean False.
|
||||
* @access public
|
||||
*/
|
||||
function expectEndTag() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches the value into the form encoded packet.
|
||||
* @param SimpleEncoding $encoding Form packet.
|
||||
* @access public
|
||||
*/
|
||||
function write(&$encoding, $x, $y) {
|
||||
if (! file_exists($this->getValue() ?? '')) {
|
||||
return;
|
||||
}
|
||||
$encoding->attach(
|
||||
$this->getName(),
|
||||
implode('', file($this->getValue() ?? '')),
|
||||
basename($this->getValue() ?? ''));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop down widget.
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
*/
|
||||
class SimpleSelectionTag extends SimpleWidget {
|
||||
var $_options;
|
||||
var $_choice;
|
||||
|
||||
/**
|
||||
* Starts with attributes only.
|
||||
* @param hash $attributes Attribute names and
|
||||
* string values.
|
||||
*/
|
||||
function __construct($attributes) {
|
||||
parent::__construct('select', $attributes);
|
||||
$this->_options = array();
|
||||
$this->_choice = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an option tag to a selection field.
|
||||
* @param SimpleOptionTag $tag New option.
|
||||
* @access public
|
||||
*/
|
||||
function addTag(&$tag) {
|
||||
if ($tag->getTagName() == 'option') {
|
||||
$this->_options[] = &$tag;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Text within the selection element is ignored.
|
||||
* @param string $content Ignored.
|
||||
* @access public
|
||||
*/
|
||||
function addContent($content) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans options for defaults. If none, then
|
||||
* the first option is selected.
|
||||
* @return string Selected field.
|
||||
* @access public
|
||||
*/
|
||||
function getDefault() {
|
||||
for ($i = 0, $count = count($this->_options); $i < $count; $i++) {
|
||||
if ($this->_options[$i]->getAttribute('selected') !== false) {
|
||||
return $this->_options[$i]->getDefault();
|
||||
}
|
||||
}
|
||||
if ($count > 0) {
|
||||
return $this->_options[0]->getDefault();
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Can only set allowed values.
|
||||
* @param string $value New choice.
|
||||
* @return boolean True if allowed.
|
||||
* @access public
|
||||
*/
|
||||
function setValue($value) {
|
||||
for ($i = 0, $count = count($this->_options); $i < $count; $i++) {
|
||||
if ($this->_options[$i]->isValue($value)) {
|
||||
$this->_choice = $i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for current selection value.
|
||||
* @return string Value attribute or
|
||||
* content of opton.
|
||||
* @access public
|
||||
*/
|
||||
function getValue() {
|
||||
if ($this->_choice === false) {
|
||||
return $this->getDefault();
|
||||
}
|
||||
return $this->_options[$this->_choice]->getValue();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop down widget.
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
*/
|
||||
class MultipleSelectionTag extends SimpleWidget {
|
||||
var $_options;
|
||||
var $_values;
|
||||
|
||||
/**
|
||||
* Starts with attributes only.
|
||||
* @param hash $attributes Attribute names and
|
||||
* string values.
|
||||
*/
|
||||
function __construct($attributes) {
|
||||
parent::__construct('select', $attributes);
|
||||
$this->_options = array();
|
||||
$this->_values = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an option tag to a selection field.
|
||||
* @param SimpleOptionTag $tag New option.
|
||||
* @access public
|
||||
*/
|
||||
function addTag(&$tag) {
|
||||
if ($tag->getTagName() == 'option') {
|
||||
$this->_options[] = &$tag;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Text within the selection element is ignored.
|
||||
* @param string $content Ignored.
|
||||
* @access public
|
||||
*/
|
||||
function addContent($content) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans options for defaults to populate the
|
||||
* value array().
|
||||
* @return array Selected fields.
|
||||
* @access public
|
||||
*/
|
||||
function getDefault() {
|
||||
$default = array();
|
||||
for ($i = 0, $count = count($this->_options); $i < $count; $i++) {
|
||||
if ($this->_options[$i]->getAttribute('selected') !== false) {
|
||||
$default[] = $this->_options[$i]->getDefault();
|
||||
}
|
||||
}
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can only set allowed values. Any illegal value
|
||||
* will result in a failure, but all correct values
|
||||
* will be set.
|
||||
* @param array $desired New choices.
|
||||
* @return boolean True if all allowed.
|
||||
* @access public
|
||||
*/
|
||||
function setValue($desired) {
|
||||
$achieved = array();
|
||||
foreach ($desired as $value) {
|
||||
$success = false;
|
||||
for ($i = 0, $count = count($this->_options); $i < $count; $i++) {
|
||||
if ($this->_options[$i]->isValue($value)) {
|
||||
$achieved[] = $this->_options[$i]->getValue();
|
||||
$success = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (! $success) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
$this->_values = $achieved;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for current selection value.
|
||||
* @return array List of currently set options.
|
||||
* @access public
|
||||
*/
|
||||
function getValue() {
|
||||
if ($this->_values === false) {
|
||||
return $this->getDefault();
|
||||
}
|
||||
return $this->_values;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Option for selection field.
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
*/
|
||||
class SimpleOptionTag extends SimpleWidget {
|
||||
|
||||
/**
|
||||
* Stashes the attributes.
|
||||
*/
|
||||
function __construct($attributes) {
|
||||
parent::__construct('option', $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Does nothing.
|
||||
* @param string $value Ignored.
|
||||
* @return boolean Not allowed.
|
||||
* @access public
|
||||
*/
|
||||
function setValue($value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to see if a value matches the option.
|
||||
* @param string $compare Value to compare with.
|
||||
* @return boolean True if possible match.
|
||||
* @access public
|
||||
*/
|
||||
function isValue($compare) {
|
||||
$compare = trim($compare ?? '');
|
||||
if (trim($this->getValue() ?? '') == $compare) {
|
||||
return true;
|
||||
}
|
||||
return trim($this->getContent() ?? '') == $compare;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for starting value. Will be set to
|
||||
* the option label if no value exists.
|
||||
* @return string Parsed value.
|
||||
* @access public
|
||||
*/
|
||||
function getDefault() {
|
||||
if ($this->getAttribute('value') === false) {
|
||||
return $this->getContent();
|
||||
}
|
||||
return $this->getAttribute('value');
|
||||
}
|
||||
|
||||
/**
|
||||
* The content of options is not part of the page.
|
||||
* @return boolean True.
|
||||
* @access public
|
||||
*/
|
||||
function isPrivateContent() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Radio button.
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
*/
|
||||
class SimpleRadioButtonTag extends SimpleWidget {
|
||||
|
||||
/**
|
||||
* Stashes the attributes.
|
||||
* @param array $attributes Hash of attributes.
|
||||
*/
|
||||
function __construct($attributes) {
|
||||
parent::__construct('input', $attributes);
|
||||
if ($this->getAttribute('value') === false) {
|
||||
$this->_setAttribute('value', 'on');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tag contains no content.
|
||||
* @return boolean False.
|
||||
* @access public
|
||||
*/
|
||||
function expectEndTag() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* The only allowed value sn the one in the
|
||||
* "value" attribute.
|
||||
* @param string $value New value.
|
||||
* @return boolean True if allowed.
|
||||
* @access public
|
||||
*/
|
||||
function setValue($value) {
|
||||
if ($value === false) {
|
||||
return parent::setValue($value);
|
||||
}
|
||||
if ($value != $this->getAttribute('value')) {
|
||||
return false;
|
||||
}
|
||||
return parent::setValue($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for starting value.
|
||||
* @return string Parsed value.
|
||||
* @access public
|
||||
*/
|
||||
function getDefault() {
|
||||
if ($this->getAttribute('checked') !== false) {
|
||||
return $this->getAttribute('value');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checkbox widget.
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
*/
|
||||
class SimpleCheckboxTag extends SimpleWidget {
|
||||
|
||||
/**
|
||||
* Starts with attributes only.
|
||||
* @param hash $attributes Attribute names and
|
||||
* string values.
|
||||
*/
|
||||
function __construct($attributes) {
|
||||
parent::__construct('input', $attributes);
|
||||
if ($this->getAttribute('value') === false) {
|
||||
$this->_setAttribute('value', 'on');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tag contains no content.
|
||||
* @return boolean False.
|
||||
* @access public
|
||||
*/
|
||||
function expectEndTag() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* The only allowed value in the one in the
|
||||
* "value" attribute. The default for this
|
||||
* attribute is "on". If this widget is set to
|
||||
* true, then the usual value will be taken.
|
||||
* @param string $value New value.
|
||||
* @return boolean True if allowed.
|
||||
* @access public
|
||||
*/
|
||||
function setValue($value) {
|
||||
if ($value === false) {
|
||||
return parent::setValue($value);
|
||||
}
|
||||
if ($value === true) {
|
||||
return parent::setValue($this->getAttribute('value'));
|
||||
}
|
||||
if ($value != $this->getAttribute('value')) {
|
||||
return false;
|
||||
}
|
||||
return parent::setValue($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for starting value. The default
|
||||
* value is "on".
|
||||
* @return string Parsed value.
|
||||
* @access public
|
||||
*/
|
||||
function getDefault() {
|
||||
if ($this->getAttribute('checked') !== false) {
|
||||
return $this->getAttribute('value');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A group of multiple widgets with some shared behaviour.
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
*/
|
||||
class SimpleTagGroup {
|
||||
var $_widgets = array();
|
||||
|
||||
/**
|
||||
* Adds a tag to the group.
|
||||
* @param SimpleWidget $widget
|
||||
* @access public
|
||||
*/
|
||||
function addWidget(&$widget) {
|
||||
$this->_widgets[] = &$widget;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor to widget set.
|
||||
* @return array All widgets.
|
||||
* @access protected
|
||||
*/
|
||||
function &_getWidgets() {
|
||||
return $this->_widgets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for an attribute.
|
||||
* @param string $label Attribute name.
|
||||
* @return boolean Always false.
|
||||
* @access public
|
||||
*/
|
||||
function getAttribute($label) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the name for the widget from the first
|
||||
* member.
|
||||
* @return string Name of widget.
|
||||
* @access public
|
||||
*/
|
||||
function getName() {
|
||||
if (count($this->_widgets ?? []) > 0) {
|
||||
return $this->_widgets[0]->getName();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans the widgets for one with the appropriate
|
||||
* ID field.
|
||||
* @param string $id ID value to try.
|
||||
* @return boolean True if matched.
|
||||
* @access public
|
||||
*/
|
||||
function isId($id) {
|
||||
for ($i = 0, $count = count($this->_widgets); $i < $count; $i++) {
|
||||
if ($this->_widgets[$i]->isId($id)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans the widgets for one with the appropriate
|
||||
* attached label.
|
||||
* @param string $label Attached label to try.
|
||||
* @return boolean True if matched.
|
||||
* @access public
|
||||
*/
|
||||
function isLabel($label) {
|
||||
for ($i = 0, $count = count($this->_widgets); $i < $count; $i++) {
|
||||
if ($this->_widgets[$i]->isLabel($label)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches the value into the form encoded packet.
|
||||
* @param SimpleEncoding $encoding Form packet.
|
||||
* @access public
|
||||
*/
|
||||
function write(&$encoding) {
|
||||
$encoding->add($this->getName(), $this->getValue());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A group of tags with the same name within a form.
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
*/
|
||||
class SimpleCheckboxGroup extends SimpleTagGroup {
|
||||
|
||||
/**
|
||||
* Accessor for current selected widget or false
|
||||
* if none.
|
||||
* @return string/array Widget values or false if none.
|
||||
* @access public
|
||||
*/
|
||||
function getValue() {
|
||||
$values = array();
|
||||
$widgets = &$this->_getWidgets();
|
||||
for ($i = 0, $count = count($widgets); $i < $count; $i++) {
|
||||
if ($widgets[$i]->getValue() !== false) {
|
||||
$values[] = $widgets[$i]->getValue();
|
||||
}
|
||||
}
|
||||
return $this->_coerceValues($values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for starting value that is active.
|
||||
* @return string/array Widget values or false if none.
|
||||
* @access public
|
||||
*/
|
||||
function getDefault() {
|
||||
$values = array();
|
||||
$widgets = &$this->_getWidgets();
|
||||
for ($i = 0, $count = count($widgets); $i < $count; $i++) {
|
||||
if ($widgets[$i]->getDefault() !== false) {
|
||||
$values[] = $widgets[$i]->getDefault();
|
||||
}
|
||||
}
|
||||
return $this->_coerceValues($values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for current set values.
|
||||
* @param string/array/boolean $values Either a single string, a
|
||||
* hash or false for nothing set.
|
||||
* @return boolean True if all values can be set.
|
||||
* @access public
|
||||
*/
|
||||
function setValue($values) {
|
||||
$values = $this->_makeArray($values);
|
||||
if (! $this->_valuesArePossible($values)) {
|
||||
return false;
|
||||
}
|
||||
$widgets = &$this->_getWidgets();
|
||||
for ($i = 0, $count = count($widgets); $i < $count; $i++) {
|
||||
$possible = $widgets[$i]->getAttribute('value');
|
||||
if (in_array($widgets[$i]->getAttribute('value'), $values ?? [])) {
|
||||
$widgets[$i]->setValue($possible);
|
||||
} else {
|
||||
$widgets[$i]->setValue(false);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests to see if a possible value set is legal.
|
||||
* @param string/array/boolean $values Either a single string, a
|
||||
* hash or false for nothing set.
|
||||
* @return boolean False if trying to set a
|
||||
* missing value.
|
||||
* @access private
|
||||
*/
|
||||
function _valuesArePossible($values) {
|
||||
$matches = array();
|
||||
$widgets = &$this->_getWidgets();
|
||||
for ($i = 0, $count = count($widgets); $i < $count; $i++) {
|
||||
$possible = $widgets[$i]->getAttribute('value');
|
||||
if (in_array($possible, $values ?? [])) {
|
||||
$matches[] = $possible;
|
||||
}
|
||||
}
|
||||
return ($values == $matches);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the output to an appropriate format. This means
|
||||
* that no values is false, a single value is just that
|
||||
* value and only two or more are contained in an array.
|
||||
* @param array $values List of values of widgets.
|
||||
* @return string/array/boolean Expected format for a tag.
|
||||
* @access private
|
||||
*/
|
||||
function _coerceValues($values) {
|
||||
if (count($values ?? []) == 0) {
|
||||
return false;
|
||||
} elseif (count($values ?? []) == 1) {
|
||||
return $values[0];
|
||||
} else {
|
||||
return $values;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts false or string into array. The opposite of
|
||||
* the coercian method.
|
||||
* @param string/array/boolean $value A single item is converted
|
||||
* to a one item list. False
|
||||
* gives an empty list.
|
||||
* @return array List of values, possibly empty.
|
||||
* @access private
|
||||
*/
|
||||
function _makeArray($value) {
|
||||
if ($value === false) {
|
||||
return array();
|
||||
}
|
||||
if (is_string($value)) {
|
||||
return array($value);
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A group of tags with the same name within a form.
|
||||
* Used for radio buttons.
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
*/
|
||||
class SimpleRadioGroup extends SimpleTagGroup {
|
||||
|
||||
/**
|
||||
* Each tag is tried in turn until one is
|
||||
* successfully set. The others will be
|
||||
* unchecked if successful.
|
||||
* @param string $value New value.
|
||||
* @return boolean True if any allowed.
|
||||
* @access public
|
||||
*/
|
||||
function setValue($value) {
|
||||
if (! $this->_valueIsPossible($value)) {
|
||||
return false;
|
||||
}
|
||||
$index = false;
|
||||
$widgets = &$this->_getWidgets();
|
||||
for ($i = 0, $count = count($widgets); $i < $count; $i++) {
|
||||
if (! $widgets[$i]->setValue($value)) {
|
||||
$widgets[$i]->setValue(false);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests to see if a value is allowed.
|
||||
* @param string Attempted value.
|
||||
* @return boolean True if a valid value.
|
||||
* @access private
|
||||
*/
|
||||
function _valueIsPossible($value) {
|
||||
$widgets = &$this->_getWidgets();
|
||||
for ($i = 0, $count = count($widgets); $i < $count; $i++) {
|
||||
if ($widgets[$i]->getAttribute('value') == $value) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for current selected widget or false
|
||||
* if none.
|
||||
* @return string/boolean Value attribute or
|
||||
* content of opton.
|
||||
* @access public
|
||||
*/
|
||||
function getValue() {
|
||||
$widgets = &$this->_getWidgets();
|
||||
for ($i = 0, $count = count($widgets); $i < $count; $i++) {
|
||||
if ($widgets[$i]->getValue() !== false) {
|
||||
return $widgets[$i]->getValue();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for starting value that is active.
|
||||
* @return string/boolean Value of first checked
|
||||
* widget or false if none.
|
||||
* @access public
|
||||
*/
|
||||
function getDefault() {
|
||||
$widgets = &$this->_getWidgets();
|
||||
for ($i = 0, $count = count($widgets); $i < $count; $i++) {
|
||||
if ($widgets[$i]->getDefault() !== false) {
|
||||
return $widgets[$i]->getDefault();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tag to keep track of labels.
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
*/
|
||||
class SimpleLabelTag extends SimpleTag {
|
||||
|
||||
/**
|
||||
* Starts with a named tag with attributes only.
|
||||
* @param hash $attributes Attribute names and
|
||||
* string values.
|
||||
*/
|
||||
function __construct($attributes) {
|
||||
parent::__construct('label', $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Access for the ID to attach the label to.
|
||||
* @return string For attribute.
|
||||
* @access public
|
||||
*/
|
||||
function getFor() {
|
||||
return $this->getAttribute('for');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tag to aid parsing the form.
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
*/
|
||||
class SimpleFormTag extends SimpleTag {
|
||||
|
||||
/**
|
||||
* Starts with a named tag with attributes only.
|
||||
* @param hash $attributes Attribute names and
|
||||
* string values.
|
||||
*/
|
||||
function __construct($attributes) {
|
||||
parent::__construct('form', $attributes);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tag to aid parsing the frames in a page.
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
*/
|
||||
class SimpleFrameTag extends SimpleTag {
|
||||
|
||||
/**
|
||||
* Starts with a named tag with attributes only.
|
||||
* @param hash $attributes Attribute names and
|
||||
* string values.
|
||||
*/
|
||||
function __construct($attributes) {
|
||||
parent::__construct('frame', $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tag contains no content.
|
||||
* @return boolean False.
|
||||
* @access public
|
||||
*/
|
||||
function expectEndTag() {
|
||||
return false;
|
||||
}
|
||||
}
|
531
thirdparty/simpletest/url.php
vendored
531
thirdparty/simpletest/url.php
vendored
@ -1,531 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* base include file for SimpleTest
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
* @version $Id: url.php 1723 2008-04-08 00:34:10Z lastcraft $
|
||||
*/
|
||||
|
||||
/**#@+
|
||||
* include other SimpleTest class files
|
||||
*/
|
||||
require_once(dirname(__FILE__) . '/encoding.php');
|
||||
/**#@-*/
|
||||
|
||||
/**
|
||||
* URL parser to replace parse_url() PHP function which
|
||||
* got broken in PHP 4.3.0. Adds some browser specific
|
||||
* functionality such as expandomatics.
|
||||
* Guesses a bit trying to separate the host from
|
||||
* the path and tries to keep a raw, possibly unparsable,
|
||||
* request string as long as possible.
|
||||
* @package SimpleTest
|
||||
* @subpackage WebTester
|
||||
*/
|
||||
class SimpleUrl {
|
||||
var $_scheme;
|
||||
var $_username;
|
||||
var $_password;
|
||||
var $_host;
|
||||
var $_port;
|
||||
var $_path;
|
||||
var $_request;
|
||||
var $_fragment;
|
||||
var $_x;
|
||||
var $_y;
|
||||
var $_target;
|
||||
var $_raw = false;
|
||||
|
||||
/**
|
||||
* Constructor. Parses URL into sections.
|
||||
* @param string $url Incoming URL.
|
||||
* @access public
|
||||
*/
|
||||
function __construct($url = '') {
|
||||
list($x, $y) = $this->_chompCoordinates($url);
|
||||
$this->setCoordinates($x, $y);
|
||||
$this->_scheme = $this->_chompScheme($url);
|
||||
list($this->_username, $this->_password) = $this->_chompLogin($url);
|
||||
$this->_host = $this->_chompHost($url);
|
||||
$this->_port = false;
|
||||
if (preg_match('/(.*?):(.*)/', $this->_host ?? '', $host_parts)) {
|
||||
$this->_host = $host_parts[1];
|
||||
$this->_port = (integer)$host_parts[2];
|
||||
}
|
||||
$this->_path = $this->_chompPath($url);
|
||||
$this->_request = $this->_parseRequest($this->_chompRequest($url));
|
||||
$this->_fragment = (strncmp($url ?? '', "#", 1) == 0 ? substr($url, 1) : false);
|
||||
$this->_target = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the X, Y coordinate pair from an image map.
|
||||
* @param string $url URL so far. The coordinates will be
|
||||
* removed.
|
||||
* @return array X, Y as a pair of integers.
|
||||
* @access private
|
||||
*/
|
||||
function _chompCoordinates(&$url) {
|
||||
if (preg_match('/(.*)\?(\d+),(\d+)$/', $url ?? '', $matches)) {
|
||||
$url = $matches[1];
|
||||
return array((integer)$matches[2], (integer)$matches[3]);
|
||||
}
|
||||
return array(false, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the scheme part of an incoming URL.
|
||||
* @param string $url URL so far. The scheme will be
|
||||
* removed.
|
||||
* @return string Scheme part or false.
|
||||
* @access private
|
||||
*/
|
||||
function _chompScheme(&$url) {
|
||||
if (preg_match('/^([^\/:]*):(\/\/)(.*)/', $url ?? '', $matches)) {
|
||||
$url = $matches[2] . $matches[3];
|
||||
return $matches[1];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the username and password from the
|
||||
* incoming URL. The // prefix will be reattached
|
||||
* to the URL after the doublet is extracted.
|
||||
* @param string $url URL so far. The username and
|
||||
* password are removed.
|
||||
* @return array Two item list of username and
|
||||
* password. Will urldecode() them.
|
||||
* @access private
|
||||
*/
|
||||
function _chompLogin(&$url) {
|
||||
$prefix = '';
|
||||
if (preg_match('/^(\/\/)(.*)/', $url ?? '', $matches)) {
|
||||
$prefix = $matches[1];
|
||||
$url = $matches[2];
|
||||
}
|
||||
if (preg_match('/^([^\/]*)@(.*)/', $url ?? '', $matches)) {
|
||||
$url = $prefix . $matches[2];
|
||||
$parts = preg_split('/:/', $matches[1] ?? '');
|
||||
return array(
|
||||
urldecode($parts[0] ?? ''),
|
||||
isset($parts[1]) ? urldecode($parts[1]) : false);
|
||||
}
|
||||
$url = $prefix . $url;
|
||||
return array(false, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the host part of an incoming URL.
|
||||
* Includes the port number part. Will extract
|
||||
* the host if it starts with // or it has
|
||||
* a top level domain or it has at least two
|
||||
* dots.
|
||||
* @param string $url URL so far. The host will be
|
||||
* removed.
|
||||
* @return string Host part guess or false.
|
||||
* @access private
|
||||
*/
|
||||
function _chompHost(&$url) {
|
||||
if (preg_match('/^(\/\/)(.*?)(\/.*|\?.*|#.*|$)/', $url ?? '', $matches)) {
|
||||
$url = $matches[3];
|
||||
return $matches[2];
|
||||
}
|
||||
if (preg_match('/(.*?)(\.\.\/|\.\/|\/|\?|#|$)(.*)/', $url ?? '', $matches)) {
|
||||
$tlds = SimpleUrl::getAllTopLevelDomains();
|
||||
if (preg_match('/[a-z0-9\-]+\.(' . $tlds . ')/i', $matches[1] ?? '')) {
|
||||
$url = $matches[2] . $matches[3];
|
||||
return $matches[1];
|
||||
} elseif (preg_match('/[a-z0-9\-]+\.[a-z0-9\-]+\.[a-z0-9\-]+/i', $matches[1] ?? '')) {
|
||||
$url = $matches[2] . $matches[3];
|
||||
return $matches[1];
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the path information from the incoming
|
||||
* URL. Strips this path from the URL.
|
||||
* @param string $url URL so far. The host will be
|
||||
* removed.
|
||||
* @return string Path part or '/'.
|
||||
* @access private
|
||||
*/
|
||||
function _chompPath(&$url) {
|
||||
if (preg_match('/(.*?)(\?|#|$)(.*)/', $url ?? '', $matches)) {
|
||||
$url = $matches[2] . $matches[3];
|
||||
return ($matches[1] ? $matches[1] : '');
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Strips off the request data.
|
||||
* @param string $url URL so far. The request will be
|
||||
* removed.
|
||||
* @return string Raw request part.
|
||||
* @access private
|
||||
*/
|
||||
function _chompRequest(&$url) {
|
||||
if (preg_match('/\?(.*?)(#|$)(.*)/', $url ?? '', $matches)) {
|
||||
$url = $matches[2] . $matches[3];
|
||||
return $matches[1];
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Breaks the request down into an object.
|
||||
* @param string $raw Raw request.
|
||||
* @return SimpleFormEncoding Parsed data.
|
||||
* @access private
|
||||
*/
|
||||
function _parseRequest($raw) {
|
||||
$this->_raw = $raw;
|
||||
$request = new SimpleGetEncoding();
|
||||
foreach (preg_split('/&/', $raw ?? '') as $pair) {
|
||||
if (preg_match('/(.*?)=(.*)/', $pair ?? '', $matches)) {
|
||||
$request->add($matches[1], urldecode($matches[2] ?? ''));
|
||||
} elseif ($pair) {
|
||||
$request->add($pair, '');
|
||||
}
|
||||
}
|
||||
return $request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for protocol part.
|
||||
* @param string $default Value to use if not present.
|
||||
* @return string Scheme name, e.g "http".
|
||||
* @access public
|
||||
*/
|
||||
function getScheme($default = false) {
|
||||
return $this->_scheme ? $this->_scheme : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for user name.
|
||||
* @return string Username preceding host.
|
||||
* @access public
|
||||
*/
|
||||
function getUsername() {
|
||||
return $this->_username;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for password.
|
||||
* @return string Password preceding host.
|
||||
* @access public
|
||||
*/
|
||||
function getPassword() {
|
||||
return $this->_password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for hostname and port.
|
||||
* @param string $default Value to use if not present.
|
||||
* @return string Hostname only.
|
||||
* @access public
|
||||
*/
|
||||
function getHost($default = false) {
|
||||
return $this->_host ? $this->_host : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for top level domain.
|
||||
* @return string Last part of host.
|
||||
* @access public
|
||||
*/
|
||||
function getTld() {
|
||||
$path_parts = pathinfo($this->getHost() ?? '');
|
||||
return (isset($path_parts['extension']) ? $path_parts['extension'] : false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for port number.
|
||||
* @return integer TCP/IP port number.
|
||||
* @access public
|
||||
*/
|
||||
function getPort() {
|
||||
return $this->_port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for path.
|
||||
* @return string Full path including leading slash if implied.
|
||||
* @access public
|
||||
*/
|
||||
function getPath() {
|
||||
if (! $this->_path && $this->_host) {
|
||||
return '/';
|
||||
}
|
||||
return $this->_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for page if any. This may be a
|
||||
* directory name if ambiguious.
|
||||
* @return Page name.
|
||||
* @access public
|
||||
*/
|
||||
function getPage() {
|
||||
if (! preg_match('/([^\/]*?)$/', $this->getPath() ?? '', $matches)) {
|
||||
return false;
|
||||
}
|
||||
return $matches[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the path to the page.
|
||||
* @return string Path less the page.
|
||||
* @access public
|
||||
*/
|
||||
function getBasePath() {
|
||||
if (! preg_match('/(.*\/)[^\/]*?$/', $this->getPath() ?? '', $matches)) {
|
||||
return false;
|
||||
}
|
||||
return $matches[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for fragment at end of URL after the "#".
|
||||
* @return string Part after "#".
|
||||
* @access public
|
||||
*/
|
||||
function getFragment() {
|
||||
return $this->_fragment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets image coordinates. Set to false to clear
|
||||
* them.
|
||||
* @param integer $x Horizontal position.
|
||||
* @param integer $y Vertical position.
|
||||
* @access public
|
||||
*/
|
||||
function setCoordinates($x = false, $y = false) {
|
||||
if (($x === false) || ($y === false)) {
|
||||
$this->_x = $this->_y = false;
|
||||
return;
|
||||
}
|
||||
$this->_x = (integer)$x;
|
||||
$this->_y = (integer)$y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for horizontal image coordinate.
|
||||
* @return integer X value.
|
||||
* @access public
|
||||
*/
|
||||
function getX() {
|
||||
return $this->_x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for vertical image coordinate.
|
||||
* @return integer Y value.
|
||||
* @access public
|
||||
*/
|
||||
function getY() {
|
||||
return $this->_y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for current request parameters
|
||||
* in URL string form. Will return teh original request
|
||||
* if at all possible even if it doesn't make much
|
||||
* sense.
|
||||
* @return string Form is string "?a=1&b=2", etc.
|
||||
* @access public
|
||||
*/
|
||||
function getEncodedRequest() {
|
||||
if ($this->_raw) {
|
||||
$encoded = $this->_raw;
|
||||
} else {
|
||||
$encoded = $this->_request->asUrlRequest();
|
||||
}
|
||||
if ($encoded) {
|
||||
return '?' . preg_replace('/^\?/', '', $encoded ?? '');
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an additional parameter to the request.
|
||||
* @param string $key Name of parameter.
|
||||
* @param string $value Value as string.
|
||||
* @access public
|
||||
*/
|
||||
function addRequestParameter($key, $value) {
|
||||
$this->_raw = false;
|
||||
$this->_request->add($key, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds additional parameters to the request.
|
||||
* @param hash/SimpleFormEncoding $parameters Additional
|
||||
* parameters.
|
||||
* @access public
|
||||
*/
|
||||
function addRequestParameters($parameters) {
|
||||
$this->_raw = false;
|
||||
$this->_request->merge($parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears down all parameters.
|
||||
* @access public
|
||||
*/
|
||||
function clearRequest() {
|
||||
$this->_raw = false;
|
||||
$this->_request = new SimpleGetEncoding();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the frame target if present. Although
|
||||
* not strictly part of the URL specification it
|
||||
* acts as similarily to the browser.
|
||||
* @return boolean/string Frame name or false if none.
|
||||
* @access public
|
||||
*/
|
||||
function getTarget() {
|
||||
return $this->_target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches a frame target.
|
||||
* @param string $frame Name of frame.
|
||||
* @access public
|
||||
*/
|
||||
function setTarget($frame) {
|
||||
$this->_raw = false;
|
||||
$this->_target = $frame;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the URL back into a string.
|
||||
* @return string URL in canonical form.
|
||||
* @access public
|
||||
*/
|
||||
function asString() {
|
||||
$path = $this->_path;
|
||||
$scheme = $identity = $host = $port = $encoded = $fragment = '';
|
||||
if ($this->_username && $this->_password) {
|
||||
$identity = $this->_username . ':' . $this->_password . '@';
|
||||
}
|
||||
if ($this->getHost()) {
|
||||
$scheme = $this->getScheme() ? $this->getScheme() : 'http';
|
||||
$scheme .= "://";
|
||||
$host = $this->getHost();
|
||||
}
|
||||
if ($this->getPort() && $this->getPort() != 80 ) {
|
||||
$port = ':'.$this->getPort();
|
||||
}
|
||||
if (substr($this->_path ?? '', 0, 1) == '/') {
|
||||
$path = $this->normalisePath($this->_path);
|
||||
}
|
||||
$encoded = $this->getEncodedRequest();
|
||||
$fragment = $this->getFragment() ? '#'. $this->getFragment() : '';
|
||||
$coords = $this->getX() === false ? '' : '?' . $this->getX() . ',' . $this->getY();
|
||||
return "$scheme$identity$host$port$path$encoded$fragment$coords";
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces unknown sections to turn a relative
|
||||
* URL into an absolute one. The base URL can
|
||||
* be either a string or a SimpleUrl object.
|
||||
* @param string/SimpleUrl $base Base URL.
|
||||
* @access public
|
||||
*/
|
||||
function makeAbsolute($base) {
|
||||
if (! is_object($base)) {
|
||||
$base = new SimpleUrl($base);
|
||||
}
|
||||
if ($this->getHost()) {
|
||||
$scheme = $this->getScheme();
|
||||
$host = $this->getHost();
|
||||
$port = $this->getPort() ? ':' . $this->getPort() : '';
|
||||
$identity = $this->getIdentity() ? $this->getIdentity() . '@' : '';
|
||||
if (! $identity) {
|
||||
$identity = $base->getIdentity() ? $base->getIdentity() . '@' : '';
|
||||
}
|
||||
} else {
|
||||
$scheme = $base->getScheme();
|
||||
$host = $base->getHost();
|
||||
$port = $base->getPort() ? ':' . $base->getPort() : '';
|
||||
$identity = $base->getIdentity() ? $base->getIdentity() . '@' : '';
|
||||
}
|
||||
$path = $this->normalisePath($this->_extractAbsolutePath($base));
|
||||
$encoded = $this->getEncodedRequest();
|
||||
$fragment = $this->getFragment() ? '#'. $this->getFragment() : '';
|
||||
$coords = $this->getX() === false ? '' : '?' . $this->getX() . ',' . $this->getY();
|
||||
return new SimpleUrl("$scheme://$identity$host$port$path$encoded$fragment$coords");
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces unknown sections of the path with base parts
|
||||
* to return a complete absolute one.
|
||||
* @param string/SimpleUrl $base Base URL.
|
||||
* @param string Absolute path.
|
||||
* @access private
|
||||
*/
|
||||
function _extractAbsolutePath($base) {
|
||||
if ($this->getHost()) {
|
||||
return $this->_path;
|
||||
}
|
||||
if (! $this->_isRelativePath($this->_path)) {
|
||||
return $this->_path;
|
||||
}
|
||||
if ($this->_path) {
|
||||
return $base->getBasePath() . $this->_path;
|
||||
}
|
||||
return $base->getPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple test to see if a path part is relative.
|
||||
* @param string $path Path to test.
|
||||
* @return boolean True if starts with a "/".
|
||||
* @access private
|
||||
*/
|
||||
function _isRelativePath($path) {
|
||||
return (substr($path ?? '', 0, 1) != '/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the username and password for use in rendering
|
||||
* a URL.
|
||||
* @return string/boolean Form of username:password or false.
|
||||
* @access public
|
||||
*/
|
||||
function getIdentity() {
|
||||
if ($this->_username && $this->_password) {
|
||||
return $this->_username . ':' . $this->_password;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces . and .. sections of the path.
|
||||
* @param string $path Unoptimised path.
|
||||
* @return string Path with dots removed if possible.
|
||||
* @access public
|
||||
*/
|
||||
function normalisePath($path) {
|
||||
$path = preg_replace('|/\./|', '/', $path ?? '');
|
||||
return preg_replace('|/[^/]+/\.\./|', '/', $path ?? '');
|
||||
}
|
||||
|
||||
/**
|
||||
* A pipe seperated list of all TLDs that result in two part
|
||||
* domain names.
|
||||
* @return string Pipe separated list.
|
||||
* @access public
|
||||
* @static
|
||||
*/
|
||||
function getAllTopLevelDomains() {
|
||||
return 'com|edu|net|org|gov|mil|int|biz|info|name|pro|aero|coop|museum';
|
||||
}
|
||||
}
|
||||
?>
|
Loading…
Reference in New Issue
Block a user