Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add appveyor.yml for C.I. on Windows #15575

Merged
merged 2 commits into from Aug 26, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -5,3 +5,4 @@ phpunit.xml
composer.phar
package.tar
/packages.json
/.phpunit
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wouldn't it be cleaner to just have phpunit in require-dev? Or is it blocked because of it's dep on yml component?
Would also be easier for first time contributors, now that phpunit on pear, and pear itself, is dead.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Certainly not, see #13934 (comment)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this would not work properly for component builds unless we add it in all components too and make the logic locating phpunit much more complex. It would make things even worse by forbidding to test components against Yaml 3.0 as PHPUnit would force using the 2.x component
The issue we want to solve here is that some optional PHPUnit feature (not used in Symfony) make it require the Yaml component 2.x and we don't want that dependencies to be able to test our Yaml component properly in any version.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, clear

side:

make the logic locating phpunit much more complex

well.. that doesn't sounds valid, if it is in require dev it will always be in one location, so you can scrap the logic involved for that and rather focus on other things, but if it is not possible this is besides the point. nothing to see here 🚶

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@andrerom I'm talking about the logic to locate PHPunit in our CI setup, which is running the testsuite both for the fullstack repo and for each component. If it is in the require_dev section of each component, it means we have a different PHPunit location for each component

36 changes: 17 additions & 19 deletions .travis.yml
Expand Up @@ -3,9 +3,9 @@ language: php
sudo: false

addons:
apt_packages:
- parallel
- language-pack-fr-base
apt_packages:
- parallel
- language-pack-fr-base

matrix:
include:
Expand All @@ -31,24 +31,22 @@ env:
before_install:
- composer self-update
- if [[ "$TRAVIS_PHP_VERSION" != "hhvm" ]]; then echo "memory_limit = -1" >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini; fi;
- if [[ "$TRAVIS_PHP_VERSION" != "7.0" ]] && [[ "$TRAVIS_PHP_VERSION" != "hhvm" ]]; then phpenv config-rm xdebug.ini; fi;
- if [[ "$TRAVIS_PHP_VERSION" != "7.0" ]] && [[ "$TRAVIS_PHP_VERSION" != "hhvm" ]]; then echo "extension = mongo.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi;
- if [[ "$TRAVIS_PHP_VERSION" != "7.0" ]] && [[ "$TRAVIS_PHP_VERSION" != "hhvm" ]] && [ $(php -r "echo PHP_MINOR_VERSION;") -le 4 ]; then echo "extension = apc.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi;
- if [[ "$TRAVIS_PHP_VERSION" != "7.0" ]] && [[ "$TRAVIS_PHP_VERSION" != "hhvm" ]]; then (pecl install -f memcached-2.1.0 && echo "extension = memcache.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini) || echo "Let's continue without memcache extension"; fi;
- if [[ "$TRAVIS_PHP_VERSION" != "7.0" ]] && [[ "$TRAVIS_PHP_VERSION" != "hhvm" ]]; then php -i; fi;
# Build a standalone phpunit without symfony/yaml and that works around https://github.com/sebastianbergmann/phpunit-mock-objects/issues/223
- (mkdir phpunit && cd phpunit && wget https://github.com/sebastianbergmann/phpunit/archive/4.7.zip && unzip 4.7.zip && cd phpunit-4.7 && composer remove --no-update symfony/yaml && composer require --prefer-source phpunit/phpunit-mock-objects '2.3.0')
- export PHPUNIT="$(readlink -f ./phpunit/phpunit-4.7/phpunit) --colors=always"
# Set the COMPOSER_ROOT_VERSION to the right version according to the branch being built
- if [ "$TRAVIS_BRANCH" = "master" ]; then export COMPOSER_ROOT_VERSION=dev-master; else export COMPOSER_ROOT_VERSION="$TRAVIS_BRANCH".x-dev; fi;
- if [[ "$TRAVIS_PHP_VERSION" = 5.* ]]; then phpenv config-rm xdebug.ini; fi;
- if [[ "$TRAVIS_PHP_VERSION" = 5.* ]]; then echo "extension = mongo.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi;
- if [[ "$TRAVIS_PHP_VERSION" =~ 5.[34] ]]; then echo "extension = apc.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi;
- if [[ "$TRAVIS_PHP_VERSION" = 5.* ]]; then (pecl install -f memcached-2.1.0 && echo "extension = memcache.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini) || echo "Let's continue without memcache extension"; fi;
- if [[ "$TRAVIS_PHP_VERSION" != "hhvm" ]]; then php -i; fi;
- ./phpunit install
- export PHPUNIT="$(readlink -f ./phpunit)"

install:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you should keep this comment alongside the instruction IMO

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure, the line is quit straightforward...

- if [ "$TRAVIS_BRANCH" = "master" ]; then export COMPOSER_ROOT_VERSION=dev-master; else export COMPOSER_ROOT_VERSION="$TRAVIS_BRANCH".x-dev; fi;
- if [ "$deps" = "no" ]; then composer --prefer-source install; fi;
- components=$(find src/Symfony -mindepth 3 -type f -name phpunit.xml.dist -printf '%h\n')
- if [ "$deps" != "no" ]; then php .travis.php $TRAVIS_COMMIT_RANGE $TRAVIS_BRANCH $components; fi;
- COMPONENTS=$(find src/Symfony -mindepth 3 -type f -name phpunit.xml.dist -printf '%h\n')
- if [ "$deps" != "no" ]; then php .travis.php $TRAVIS_COMMIT_RANGE $TRAVIS_BRANCH $COMPONENTS; fi;

script:
- if [ "$deps" = "no" ]; then echo "$components" | parallel --gnu --keep-order 'echo -e "\\nRunning {} tests"; $PHPUNIT --exclude-group tty,benchmark,intl-data {} || (echo -e "\\e[41mKO\\e[0m {}" && $(exit 1));'; fi;
- if [ "$deps" = "no" ]; then echo -e "\\nRunning tests requiring tty"; $PHPUNIT --group tty || (echo -e "\\e[41mKO\\e[0m tty group" && $(exit 1)); fi;
- if [ "$deps" = "high" ]; then echo "$components" | parallel --gnu --keep-order -j10% 'echo -e "\\nRunning {} tests"; cd {}; composer --prefer-source update; $PHPUNIT --exclude-group tty,benchmark,intl-data || (echo -e "\\e[41mKO\\e[0m {}" && $(exit 1));'; fi;
- if [ "$deps" = "low" ]; then echo "$components" | parallel --gnu --keep-order -j10% 'echo -e "\\nRunning {} tests"; cd {}; composer --prefer-source --prefer-lowest --prefer-stable update; $PHPUNIT --exclude-group tty,benchmark,intl-data || (echo -e "\\e[41mKO\\e[0m {}" && $(exit 1));'; fi;
- if [ "$deps" = "no" ]; then echo "$COMPONENTS" | parallel --gnu --keep-order 'echo -e "\\nRunning {} tests"; $PHPUNIT --exclude-group tty,benchmark,intl-data {}'; fi;
- if [ "$deps" = "no" ]; then echo -e "\\nRunning tests requiring tty"; $PHPUNIT --group tty; fi;
- if [ "$deps" = "high" ]; then echo "$COMPONENTS" | parallel --gnu --keep-order -j10% 'echo -e "\\nRunning {} tests"; cd {}; composer --prefer-source update; $PHPUNIT --exclude-group tty,benchmark,intl-data'; fi;
- if [ "$deps" = "low" ]; then echo "$COMPONENTS" | parallel --gnu --keep-order -j10% 'echo -e "\\nRunning {} tests"; cd {}; composer --prefer-source --prefer-lowest --prefer-stable update; $PHPUNIT --exclude-group tty,benchmark,intl-data'; fi;
53 changes: 53 additions & 0 deletions appveyor.yml
@@ -0,0 +1,53 @@
build: false
shallow_clone: true
platform: x86
clone_folder: c:\projects\symfony

environment:
matrix:
- PHP_EXT: 1
- PHP_EXT: 0

cache:
- c:\php -> appveyor.yml
- .phpunit -> phpunit
- vendor -> composer.json

init:
- SET PATH=c:\php;%PATH%
- SET COMPOSER_NO_INTERACTION=1
- SET SYMFONY_DEPRECATIONS_HELPER=weak
- SET PHP=1

install:
- IF EXIST c:\php (SET PHP=0) ELSE (mkdir c:\php)
- cd c:\php
- IF %PHP%==1 appveyor DownloadFile http://windows.php.net/downloads/releases/archives/php-5.4.43-nts-Win32-VC9-x86.zip
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nicolas-grekas why testing against PHP 5.4 which is nearly EOL rather than against PHP 5.6 ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because I couldn't find a .dll for intl + icu 51.2 for 5.5 (vc9-nts-x86)

- IF %PHP%==1 7z x php-5.4.43-nts-Win32-VC9-x86.zip -y > 7z.log
- IF %PHP%==1 appveyor DownloadFile http://nebm.ist.utl.pt/~glopes/misc/intl_win/ICU-51.2-dlls.zip
- IF %PHP%==1 7z x ICU-51.2-dlls.zip -y > 7z.log
- IF %PHP%==1 cd ext
- IF %PHP%==1 appveyor DownloadFile http://nebm.ist.utl.pt/~glopes/misc/intl_win/php_intl-3.0.0-5.4-nts-vc9-x86.zip
- IF %PHP%==1 7z x php_intl-3.0.0-5.4-nts-vc9-x86.zip -y > 7z.log
- IF %PHP%==1 appveyor DownloadFile http://windows.php.net/downloads/pecl/releases/apc/3.1.13/php_apc-3.1.13-5.4-nts-vc9-x86.zip
- IF %PHP%==1 7z x php_apc-3.1.13-5.4-nts-vc9-x86.zip -y > 7z.log
- IF %PHP%==1 cd ..
- IF %PHP%==1 echo @php %%~dp0composer.phar %%* > composer.bat
- appveyor DownloadFile https://getcomposer.org/composer.phar
- copy php.ini-production php.ini /Y
- echo date.timezone="UTC" >> php.ini
- echo extension_dir=ext >> php.ini
- echo extension=php_openssl.dll >> php.ini
- IF %PHP_EXT%==1 echo extension=php_apc.dll >> php.ini
- IF %PHP_EXT%==1 echo extension=php_intl.dll >> php.ini
- IF %PHP_EXT%==1 echo extension=php_mbstring.dll >> php.ini
- IF %PHP_EXT%==1 echo extension=php_fileinfo.dll >> php.ini
- IF %PHP_EXT%==1 echo extension=php_pdo_sqlite.dll >> php.ini
- cd c:\projects\symfony
- php phpunit install
- IF %APPVEYOR_REPO_BRANCH%==master (SET COMPOSER_ROOT_VERSION=dev-master) ELSE (SET COMPOSER_ROOT_VERSION=%APPVEYOR_REPO_BRANCH%.x-dev)
- composer update --prefer-dist --no-progress --ansi

test_script:
- cd c:\projects\symfony
- php phpunit symfony --exclude-group benchmark,intl-data
116 changes: 116 additions & 0 deletions phpunit
@@ -0,0 +1,116 @@
#!/usr/bin/env php
<?php

error_reporting(-1);

$PHPUNIT_VERSION = 4.8;
$PHPUNIT_DIR = __DIR__.'/.phpunit';

if (!file_exists("$PHPUNIT_DIR/phpunit-$PHPUNIT_VERSION/phpunit")) {
# Build a standalone phpunit without symfony/yaml

$oldPwd = getcwd();
mkdir($PHPUNIT_DIR);
chdir($PHPUNIT_DIR);
if (extension_loaded('openssl') && ini_get('allow_url_fopen')) {
stream_copy_to_stream(fopen("https://github.com/sebastianbergmann/phpunit/archive/$PHPUNIT_VERSION.zip", 'rb'), fopen("$PHPUNIT_VERSION.zip", 'wb'));
} else {
passthru("wget https://github.com/sebastianbergmann/phpunit/archive/$PHPUNIT_VERSION.zip");
}
$zip = new ZipArchive();
$zip->open("$PHPUNIT_VERSION.zip");
$zip->extractTo(getcwd());
$zip->close();
chdir("phpunit-$PHPUNIT_VERSION");
passthru("composer remove --no-update symfony/yaml");
passthru("composer install --prefer-source --no-progress --ansi");
chdir($oldPwd);
}

$cmd = array_map('escapeshellarg', $argv);
$exit = 0;

if (isset($argv[1]) && 'symfony' === $argv[1]) {
# Find Symfony components in plain php for Windows portability

$finder = new RecursiveDirectoryIterator('src/Symfony', FilesystemIterator::KEY_AS_FILENAME | FilesystemIterator::UNIX_PATHS);
$finder = new RecursiveIteratorIterator($finder);
$finder->setMaxDepth(3);

array_shift($cmd);
$cmd[0] = "php $PHPUNIT_DIR/phpunit-$PHPUNIT_VERSION/phpunit --colors=always";
$procs = array();

foreach ($finder as $file => $fileInfo) {
if ('phpunit.xml.dist' === $file) {
$component = dirname($fileInfo->getPathname());

# Run phpunit tests in parallel

$c = escapeshellarg($component);

if ($proc = proc_open(implode(' ', $cmd)." $c > $c/phpunit.stdout 2> $c/phpunit.stderr", array(), $pipes)) {
$procs[$component] = $proc;
} else {
$exit = 1;
echo "\033[41mKO\033[0m $component\n\n";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you should not put colors here if the terminal does not support them (by default, Windows does not)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't want to manage color portability here. This script is complex enough. Nobody forces anyone to use the tool.

}
}
}

foreach ($procs as $component => $proc) {
$procStatus = proc_close($proc);

foreach (array('out', 'err') as $file) {
$file = "$component/phpunit.std$file";
$h = fopen($file, 'rb');
while (false !== $line = fgets($h)) {
echo preg_replace_callback(
'/\033\[[0-9]++(?:;[0-9]++)++m/',
function ($m) {return str_replace(';', "m\033[", $m[0]);},
$line
);
}
fclose($h);
unlink($file);
}

if ($procStatus) {
$exit = 1;
echo "\033[41mKO\033[0m $component\n\n";
} else {
echo "\033[32mOK\033[0m $component\n\n";
}
}

} elseif (!isset($argv[1]) || 'install' !== $argv[1]) {
# Run regular phpunit in a subprocess

$cmd[0] = "php $PHPUNIT_DIR/phpunit-$PHPUNIT_VERSION/phpunit --colors=always";

$errFile = tempnam(sys_get_temp_dir(), 'phpunit.stderr.');
if ($proc = proc_open(implode(' ', $cmd).' 2> '.escapeshellarg($errFile), array(1 => array('pipe', 'w')), $pipes)) {
while (false !== $line = fgets($pipes[1])) {
echo $line;
}
fclose($pipes[1]);
$exit = proc_close($proc);

$h = fopen($errFile, 'rb');
while (false !== $line = fgets($h)) {
echo $line;
}
fclose($h);
unlink($errFile);
}

if (file_exists($component = array_pop($argv))) {
if ($exit) {
echo "\033[41mKO\033[0m $component\n\n";
} else {
echo "\033[32mOK\033[0m $component\n\n";
}
}
}

exit($exit);
4 changes: 4 additions & 0 deletions src/Symfony/Component/Console/Tests/ApplicationTest.php
Expand Up @@ -491,6 +491,10 @@ public function testRenderException()

public function testRenderExceptionWithDoubleWidthCharacters()
{
if (!function_exists('mb_strwidth')) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not using @requires extension mbstring here too ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a reminder that only mb_strwidth is required here and that this could be replaced as mb_strwidth is broken anyway, see e.g. https://github.com/nicolas-grekas/Patchwork-UTF8/blob/master/class/Patchwork/Utf8.php#L602 for a working implementation of string width on unicode terminals.

$this->markTestSkipped('The "mb_strwidth" function is not available');
}

$application = $this->getMock('Symfony\Component\Console\Application', array('getTerminalWidth'));
$application->setAutoExit(false);
$application->expects($this->any())
Expand Down
45 changes: 34 additions & 11 deletions src/Symfony/Component/DomCrawler/Crawler.php
Expand Up @@ -156,20 +156,43 @@ public function addHtmlContent($content, $charset = 'UTF-8')
$dom = new \DOMDocument('1.0', $charset);
$dom->validateOnParse = true;

if (function_exists('mb_convert_encoding')) {
$hasError = false;
set_error_handler(function () use (&$hasError) {
$hasError = true;
});
$tmpContent = @mb_convert_encoding($content, 'HTML-ENTITIES', $charset);

restore_error_handler();

if (!$hasError) {
$content = $tmpContent;
set_error_handler(function () {throw new \Exception();});

try {
// Convert charset to HTML-entities to work around bugs in DOMDocument::loadHTML()

if (function_exists('mb_convert_encoding')) {
$content = mb_convert_encoding($content, 'HTML-ENTITIES', $charset);
} elseif (function_exists('iconv')) {
$content = preg_replace_callback(
'/[\x80-\xFF]+/',
function ($m) {
$m = unpack('C*', $m[0]);
$i = 1;
$entities = '';

while (isset($m[$i])) {
if (0xF0 <= $m[$i]) {
$c = (($m[$i++] - 0xF0) << 18) + (($m[$i++] - 0x80) << 12) + (($m[$i++] - 0x80) << 6) + $m[$i++] - 0x80;
} elseif (0xE0 <= $m[$i]) {
$c = (($m[$i++] - 0xE0) << 12) + (($m[$i++] - 0x80) << 6) + $m[$i++] - 0x80;
} else {
$c = (($m[$i++] - 0xC0) << 6) + $m[$i++] - 0x80;
}

$entities .= '&#'.$c.';';
}

return $entities;
},
iconv($charset, 'UTF-8', $content)
);
}
} catch (\Exception $e) {
}

restore_error_handler();

if ('' !== trim($content)) {
@$dom->loadHTML($content);
}
Expand Down
4 changes: 3 additions & 1 deletion src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php
Expand Up @@ -80,6 +80,7 @@ public function testAddHtmlContent()

/**
* @covers Symfony\Component\DomCrawler\Crawler::addHtmlContent
* @requires extension mbstring
*/
public function testAddHtmlContentCharset()
{
Expand Down Expand Up @@ -114,6 +115,7 @@ public function testAddHtmlContentUnsupportedCharset()

/**
* @covers Symfony\Component\DomCrawler\Crawler::addHtmlContent
* @requires extension mbstring
*/
public function testAddHtmlContentCharsetGbk()
{
Expand Down Expand Up @@ -234,7 +236,7 @@ public function testAddContent()
$this->assertEquals('中文', $crawler->filterXPath('//span')->text(), '->addContent() guess wrong charset');

$crawler = new Crawler();
$crawler->addContent(mb_convert_encoding('<html><head><meta charset="Shift_JIS"></head><body>日本語</body></html>', 'SJIS', 'UTF-8'));
$crawler->addContent(iconv('UTF-8', 'SJIS', '<html><head><meta charset="Shift_JIS"></head><body>日本語</body></html>'));
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

iconv is built-in into php, whereas mbstring is an optional extension

$this->assertEquals('日本語', $crawler->filterXPath('//body')->text(), '->addContent() can recognize "Shift_JIS" in html5 meta charset tag');
}

Expand Down
36 changes: 8 additions & 28 deletions src/Symfony/Component/Filesystem/Tests/FilesystemTest.php
Expand Up @@ -34,16 +34,13 @@ class FilesystemTest extends \PHPUnit_Framework_TestCase

public static function setUpBeforeClass()
{
if ('\\' === DIRECTORY_SEPARATOR) {
self::$symlinkOnWindows = true;
$originDir = tempnam(sys_get_temp_dir(), 'sl');
$targetDir = tempnam(sys_get_temp_dir(), 'sl');
if (true !== @symlink($originDir, $targetDir)) {
$report = error_get_last();
if (is_array($report) && false !== strpos($report['message'], 'error code(1314)')) {
self::$symlinkOnWindows = false;
}
if ('\\' === DIRECTORY_SEPARATOR && null === self::$symlinkOnWindows) {
$target = tempnam(sys_get_temp_dir(), 'sl');
$link = sys_get_temp_dir().'/sl'.microtime(true).mt_rand();
if (self::$symlinkOnWindows = @symlink($target, $link)) {
unlink($link);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this never worked

}
unlink($target);
}
}

Expand All @@ -58,27 +55,10 @@ protected function setUp()

protected function tearDown()
{
$this->clean($this->workspace);
$this->filesystem->remove($this->workspace);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clean was broken, fs->remove not, let's use the code that works

umask($this->umask);
}

/**
* @param string $file
*/
private function clean($file)
{
if (is_dir($file) && !is_link($file)) {
$dir = new \FilesystemIterator($file);
foreach ($dir as $childFile) {
$this->clean($childFile);
}

rmdir($file);
} else {
unlink($file);
}
}

public function testCopyCreatesNewFile()
{
$sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file';
Expand Down Expand Up @@ -1035,7 +1015,7 @@ private function markAsSkippedIfSymlinkIsMissing()
$this->markTestSkipped('symlink is not supported');
}

if ('\\' === DIRECTORY_SEPARATOR && false === self::$symlinkOnWindows) {
if (false === self::$symlinkOnWindows) {
$this->markTestSkipped('symlink requires "Create symbolic links" privilege on Windows');
}
}
Expand Down
Expand Up @@ -60,9 +60,6 @@ public function testSeek($path, $seekable, $contains, $message = null)
$i->seek(1);
$actual[] = $i->getPathname();

$i->seek(2);
$actual[] = $i->getPathname();

$this->assertEquals($contains, $actual);
}

Expand All @@ -73,7 +70,6 @@ public function getPaths()
// ftp
$contains = array(
'ftp://ftp.mozilla.org'.DIRECTORY_SEPARATOR.'README',
'ftp://ftp.mozilla.org'.DIRECTORY_SEPARATOR.'index.html',
'ftp://ftp.mozilla.org'.DIRECTORY_SEPARATOR.'pub',
);
$data[] = array('ftp://ftp.mozilla.org/', false, $contains);
Expand Down