diff --git a/.gitignore b/.gitignore
index dfbe013f5..5992f93fc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,4 +2,5 @@
/.project
/.buildpath
/composer.phar
-/vendor
\ No newline at end of file
+/vendor
+/nbproject
diff --git a/README.md b/README.md
index 50788a490..9dae242e9 100644
--- a/README.md
+++ b/README.md
@@ -12,8 +12,9 @@ Installation / Usage
1. Download the [`composer.phar`](http://getcomposer.org/composer.phar) executable or use the installer.
-
+ ``` sh
$ curl -s http://getcomposer.org/installer | php
+ ```
2. Create a composer.json defining your dependencies. Note that this example is
diff --git a/bin/compile b/bin/compile
old mode 100755
new mode 100644
diff --git a/bin/composer b/bin/composer
old mode 100755
new mode 100644
diff --git a/composer.lock b/composer.lock
index 953c1a7a2..da880d14a 100644
--- a/composer.lock
+++ b/composer.lock
@@ -1,6 +1,10 @@
{
"hash": "9c243b2c15fdc7c3e35c5200d704ba53",
"packages": [
+ {
+ "package": "symfony\/process",
+ "version": "2.1.0-dev"
+ },
{
"package": "symfony\/finder",
"version": "2.1.0-dev"
@@ -8,10 +12,6 @@
{
"package": "symfony\/console",
"version": "2.1.0-dev"
- },
- {
- "package": "symfony\/process",
- "version": "2.1.0-dev"
}
]
}
\ No newline at end of file
diff --git a/src/Composer/Command/InstallCommand.php b/src/Composer/Command/InstallCommand.php
index 26e1a5ba2..d656ad5d1 100644
--- a/src/Composer/Command/InstallCommand.php
+++ b/src/Composer/Command/InstallCommand.php
@@ -46,7 +46,7 @@ class InstallCommand extends Command
->setName('install')
->setDescription('Parses the composer.json file and downloads the needed dependencies.')
->setDefinition(array(
- new InputOption('dev', null, InputOption::VALUE_NONE, 'Forces installation from package sources when possible, including VCS information.'),
+ new InputOption('prefer-source', null, InputOption::VALUE_NONE, 'Forces installation from package sources when possible, including VCS information.'),
new InputOption('dry-run', null, InputOption::VALUE_NONE, 'Outputs the operations but will not execute anything (implicitly enables --verbose).'),
new InputOption('no-install-recommends', null, InputOption::VALUE_NONE, 'Do not install recommended packages (ignored when installing from an existing lock file).'),
new InputOption('install-suggests', null, InputOption::VALUE_NONE, 'Also install suggested packages (ignored when installing from an existing lock file).'),
@@ -73,7 +73,7 @@ EOT
$io,
$composer,
$eventDispatcher,
- (Boolean)$input->getOption('dev'),
+ (Boolean)$input->getOption('prefer-source'),
(Boolean)$input->getOption('dry-run'),
(Boolean)$input->getOption('verbose'),
(Boolean)$input->getOption('no-install-recommends'),
diff --git a/src/Composer/Command/SelfUpdateCommand.php b/src/Composer/Command/SelfUpdateCommand.php
index 50ad96c6d..febc78295 100644
--- a/src/Composer/Command/SelfUpdateCommand.php
+++ b/src/Composer/Command/SelfUpdateCommand.php
@@ -13,6 +13,7 @@
namespace Composer\Command;
use Composer\Composer;
+use Composer\Util\StreamContextFactory;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
@@ -39,7 +40,9 @@ EOT
protected function execute(InputInterface $input, OutputInterface $output)
{
- $latest = trim(file_get_contents('http://getcomposer.org/version'));
+ $ctx = StreamContextFactory::getContext();
+
+ $latest = trim(file_get_contents('http://getcomposer.org/version'), false, $ctx);
if (Composer::VERSION !== $latest) {
$output->writeln(sprintf("Updating to version %s.", $latest));
@@ -47,7 +50,7 @@ EOT
$remoteFilename = 'http://getcomposer.org/composer.phar';
$localFilename = $_SERVER['argv'][0];
- file_put_contents($localFilename, file_get_contents($remoteFilename));
+ copy($remoteFilename, $localFilename, $ctx);
} else {
$output->writeln("You are using the latest composer version.");
}
diff --git a/src/Composer/Command/UpdateCommand.php b/src/Composer/Command/UpdateCommand.php
index 3c2b394c0..7e1e8e0c1 100644
--- a/src/Composer/Command/UpdateCommand.php
+++ b/src/Composer/Command/UpdateCommand.php
@@ -35,7 +35,7 @@ class UpdateCommand extends Command
->setName('update')
->setDescription('Updates your dependencies to the latest version, and updates the composer.lock file.')
->setDefinition(array(
- new InputOption('dev', null, InputOption::VALUE_NONE, 'Forces installation from package sources when possible, including VCS information.'),
+ new InputOption('prefer-source', null, InputOption::VALUE_NONE, 'Forces installation from package sources when possible, including VCS information.'),
new InputOption('dry-run', null, InputOption::VALUE_NONE, 'Outputs the operations but will not execute anything (implicitly enables --verbose).'),
new InputOption('no-install-recommends', null, InputOption::VALUE_NONE, 'Do not install recommended packages.'),
new InputOption('install-suggests', null, InputOption::VALUE_NONE, 'Also install suggested packages.'),
@@ -63,7 +63,7 @@ EOT
$io,
$composer,
$eventDispatcher,
- (Boolean)$input->getOption('dev'),
+ (Boolean)$input->getOption('prefer-source'),
(Boolean)$input->getOption('dry-run'),
(Boolean)$input->getOption('verbose'),
(Boolean)$input->getOption('no-install-recommends'),
diff --git a/src/Composer/Compiler.php b/src/Composer/Compiler.php
index 4898fd795..c21b66248 100644
--- a/src/Composer/Compiler.php
+++ b/src/Composer/Compiler.php
@@ -79,7 +79,8 @@ class Compiler
$phar->stopBuffering();
- $phar->compressFiles(\Phar::GZ);
+ // disabled for interoperability with systems without gzip ext
+ // $phar->compressFiles(\Phar::GZ);
$this->addFile($phar, new \SplFileInfo(__DIR__.'/../../LICENSE'), false);
diff --git a/src/Composer/Json/JsonFile.php b/src/Composer/Json/JsonFile.php
index 67a60c1ea..ecd286d78 100644
--- a/src/Composer/Json/JsonFile.php
+++ b/src/Composer/Json/JsonFile.php
@@ -14,6 +14,7 @@ namespace Composer\Json;
use Composer\Repository\RepositoryManager;
use Composer\Composer;
+use Composer\Util\StreamContextFactory;
/**
* Reads/writes json files.
@@ -59,11 +60,12 @@ class JsonFile
*/
public function read()
{
- $context = stream_context_create(array(
- 'http' => array('header' => 'User-Agent: Composer/'.Composer::VERSION."\r\n")
- ));
+ $ctx = StreamContextFactory::getContext(array(
+ 'http' => array(
+ 'header' => 'User-Agent: Composer/'.Composer::VERSION."\r\n"
+ )));
- $json = file_get_contents($this->path, false, $context);
+ $json = file_get_contents($this->path, false, $ctx);
if (!$json) {
throw new \RuntimeException('Could not read '.$this->path.', you are probably offline');
}
@@ -76,8 +78,9 @@ class JsonFile
*
* @param array $hash writes hash into json file
* @param Boolean $prettyPrint If true, output is pretty-printed
+ * @param Boolean $unescapeUnicode If true, unicode chars in output are unescaped
*/
- public function write(array $hash, $prettyPrint = true)
+ public function write(array $hash, $prettyPrint = true, $unescapeUnicode = true)
{
$dir = dirname($this->path);
if (!is_dir($dir)) {
@@ -92,7 +95,7 @@ class JsonFile
);
}
}
- file_put_contents($this->path, static::encode($hash, $prettyPrint));
+ file_put_contents($this->path, static::encode($hash, $prettyPrint, $unescapeUnicode));
}
/**
@@ -103,17 +106,23 @@ class JsonFile
*
* @param array $hash Data to encode into a formatted JSON string
* @param Boolean $prettyPrint If true, output is pretty-printed
+ * @param Boolean $unescapeUnicode If true, unicode chars in output are unescaped
* @return string Indented version of the original JSON string
*/
- static public function encode(array $hash, $prettyPrint = true)
+ static public function encode(array $hash, $prettyPrint = true, $unescapeUnicode = true)
{
- if ($prettyPrint && defined('JSON_PRETTY_PRINT')) {
- return json_encode($hash, JSON_PRETTY_PRINT);
+ if (version_compare(PHP_VERSION, '5.4', '>=')) {
+ $options = $prettyPrint ? JSON_PRETTY_PRINT : 0;
+ if ($unescapeUnicode) {
+ $options |= JSON_UNESCAPED_UNICODE;
+ }
+
+ return json_encode($hash, $options);
}
$json = json_encode($hash);
- if (!$prettyPrint) {
+ if (!$prettyPrint && !$unescapeUnicode) {
return $json;
}
@@ -122,21 +131,43 @@ class JsonFile
$strLen = strlen($json);
$indentStr = ' ';
$newLine = "\n";
- $prevChar = '';
$outOfQuotes = true;
+ $buffer = '';
+ $noescape = true;
for ($i = 0; $i <= $strLen; $i++) {
// Grab the next character in the string
$char = substr($json, $i, 1);
// Are we inside a quoted string?
- if ('"' === $char && ('\\' !== $prevChar || '\\\\' === substr($json, $i-2, 2))) {
+ if ('"' === $char && $noescape) {
$outOfQuotes = !$outOfQuotes;
- } elseif (':' === $char && $outOfQuotes) {
+ }
+
+ if (!$outOfQuotes) {
+ $buffer .= $char;
+ $noescape = '\\' === $char ? !$noescape : true;
+ continue;
+ } elseif ('' !== $buffer) {
+ if ($unescapeUnicode && function_exists('mb_convert_encoding')) {
+ // http://stackoverflow.com/questions/2934563/how-to-decode-unicode-escape-sequences-like-u00ed-to-proper-utf-8-encoded-cha
+ $result .= preg_replace_callback('/\\\\u([0-9a-f]{4})/i', function($match) {
+ return mb_convert_encoding(pack('H*', $match[1]), 'UTF-8', 'UCS-2BE');
+ }, $buffer.$char);
+ } else {
+ $result .= $buffer.$char;
+ }
+
+ $buffer = '';
+ continue;
+ }
+
+ if (':' === $char) {
// Add a space after the : character
$char .= ' ';
- } elseif (('}' === $char || ']' === $char) && $outOfQuotes) {
+ } elseif (('}' === $char || ']' === $char)) {
$pos--;
+ $prevChar = substr($json, $i - 1, 1);
if ('{' !== $prevChar && '[' !== $prevChar) {
// If this character is the end of an element,
@@ -151,12 +182,11 @@ class JsonFile
}
}
- // Add the character to the result string
$result .= $char;
// If the last character was the beginning of an element,
// output a new line and indent the next line
- if ((',' === $char || '{' === $char || '[' === $char) && $outOfQuotes) {
+ if (',' === $char || '{' === $char || '[' === $char) {
$result .= $newLine;
if ('{' === $char || '[' === $char) {
@@ -167,8 +197,6 @@ class JsonFile
$result .= $indentStr;
}
}
-
- $prevChar = $char;
}
return $result;
diff --git a/src/Composer/Package/Dumper/ArrayDumper.php b/src/Composer/Package/Dumper/ArrayDumper.php
index ee2a0b376..f372929b1 100644
--- a/src/Composer/Package/Dumper/ArrayDumper.php
+++ b/src/Composer/Package/Dumper/ArrayDumper.php
@@ -24,11 +24,16 @@ class ArrayDumper
{
$keys = array(
'binaries' => 'bin',
+ 'scripts',
'type',
'names',
'extra',
'installationSource' => 'installation-source',
'license',
+ 'authors',
+ 'description',
+ 'homepage',
+ 'keywords',
'autoload',
'repositories',
);
@@ -41,6 +46,10 @@ class ArrayDumper
$data['target-dir'] = $package->getTargetDir();
}
+ if ($package->getReleaseDate()) {
+ $data['time'] = $package->getReleaseDate()->format('Y-m-d H:i:s');
+ }
+
if ($package->getSourceType()) {
$data['source']['type'] = $package->getSourceType();
$data['source']['url'] = $package->getSourceUrl();
diff --git a/src/Composer/Package/MemoryPackage.php b/src/Composer/Package/MemoryPackage.php
index 02222e43b..d1e63e102 100644
--- a/src/Composer/Package/MemoryPackage.php
+++ b/src/Composer/Package/MemoryPackage.php
@@ -438,7 +438,7 @@ class MemoryPackage extends BasePackage
*
* @param DateTime $releaseDate
*/
- public function setReleasedate(\DateTime $releaseDate)
+ public function setReleaseDate(\DateTime $releaseDate)
{
$this->releaseDate = $releaseDate;
}
diff --git a/src/Composer/Repository/PearRepository.php b/src/Composer/Repository/PearRepository.php
index 51c1722b4..edf5b9e3b 100644
--- a/src/Composer/Repository/PearRepository.php
+++ b/src/Composer/Repository/PearRepository.php
@@ -13,6 +13,7 @@
namespace Composer\Repository;
use Composer\Package\Loader\ArrayLoader;
+use Composer\Util\StreamContextFactory;
/**
* @author Benjamin Eberlei
@@ -20,7 +21,8 @@ use Composer\Package\Loader\ArrayLoader;
*/
class PearRepository extends ArrayRepository
{
- protected $url;
+ private $url;
+ private $streamContext;
public function __construct(array $config)
{
@@ -31,7 +33,7 @@ class PearRepository extends ArrayRepository
throw new \UnexpectedValueException('Invalid url given for PEAR repository: '.$config['url']);
}
- $this->url = $config['url'];
+ $this->url = rtrim($config['url'], '/');
}
protected function initialize()
@@ -41,6 +43,7 @@ class PearRepository extends ArrayRepository
set_error_handler(function($severity, $message, $file, $line) {
throw new \ErrorException($message, $severity, $severity, $file, $line);
});
+ $this->streamContext = StreamContextFactory::getContext();
$this->fetchFromServer();
restore_error_handler();
}
@@ -51,24 +54,62 @@ class PearRepository extends ArrayRepository
$categories = $categoryXML->getElementsByTagName("c");
foreach ($categories as $category) {
- $categoryLink = $category->getAttribute("xlink:href");
- $categoryLink = str_replace("info.xml", "packages.xml", $categoryLink);
- if ('/' !== substr($categoryLink, 0, 1)) {
- $categoryLink = '/' . $categoryLink;
+ $link = '/' . ltrim($category->getAttribute("xlink:href"), '/');
+ try {
+ $packagesLink = str_replace("info.xml", "packagesinfo.xml", $link);
+ $this->fetchPear2Packages($this->url . $packagesLink);
+ } catch (\ErrorException $e) {
+ if (false === strpos($e->getMessage(), '404')) {
+ throw $e;
+ }
+ $categoryLink = str_replace("info.xml", "packages.xml", $link);
+ $this->fetchPearPackages($this->url . $categoryLink);
}
- $packagesXML = $this->requestXml($this->url . $categoryLink);
- $packages = $packagesXML->getElementsByTagName('p');
- $loader = new ArrayLoader();
- foreach ($packages as $package) {
- $packageName = $package->nodeValue;
+ }
+ }
- $packageLink = $package->getAttribute('xlink:href');
- $releaseLink = $this->url . str_replace("/rest/p/", "/rest/r/", $packageLink);
- $allReleasesLink = $releaseLink . "/allreleases2.xml";
+ /**
+ * @param string $categoryLink
+ * @throws ErrorException
+ * @throws InvalidArgumentException
+ */
+ private function fetchPearPackages($categoryLink)
+ {
+ $packagesXML = $this->requestXml($categoryLink);
+ $packages = $packagesXML->getElementsByTagName('p');
+ $loader = new ArrayLoader();
+ foreach ($packages as $package) {
+ $packageName = $package->nodeValue;
+
+ $packageLink = $package->getAttribute('xlink:href');
+ $releaseLink = $this->url . str_replace("/rest/p/", "/rest/r/", $packageLink);
+ $allReleasesLink = $releaseLink . "/allreleases2.xml";
+
+ try {
+ $releasesXML = $this->requestXml($allReleasesLink);
+ } catch (\ErrorException $e) {
+ if (strpos($e->getMessage(), '404')) {
+ continue;
+ }
+ throw $e;
+ }
+
+ $releases = $releasesXML->getElementsByTagName('r');
+
+ foreach ($releases as $release) {
+ /* @var $release \DOMElement */
+ $pearVersion = $release->getElementsByTagName('v')->item(0)->nodeValue;
+
+ $packageData = array(
+ 'name' => $packageName,
+ 'type' => 'library',
+ 'dist' => array('type' => 'pear', 'url' => $this->url.'/get/'.$packageName.'-'.$pearVersion.".tgz"),
+ 'version' => $pearVersion,
+ );
try {
- $releasesXML = $this->requestXml($allReleasesLink);
+ $deps = file_get_contents($releaseLink . "/deps.".$pearVersion.".txt", false, $this->streamContext);
} catch (\ErrorException $e) {
if (strpos($e->getMessage(), '404')) {
continue;
@@ -76,54 +117,155 @@ class PearRepository extends ArrayRepository
throw $e;
}
- $releases = $releasesXML->getElementsByTagName('r');
+ $packageData += $this->parseDependencies($deps);
- foreach ($releases as $release) {
- /* @var $release DOMElement */
- $pearVersion = $release->getElementsByTagName('v')->item(0)->nodeValue;
+ try {
+ $this->addPackage($loader->load($packageData));
+ } catch (\UnexpectedValueException $e) {
+ continue;
+ }
+ }
+ }
+ }
- $packageData = array(
- 'name' => $packageName,
- 'type' => 'library',
- 'dist' => array('type' => 'pear', 'url' => $this->url.'/get/'.$packageName.'-'.$pearVersion.".tgz"),
- 'version' => $pearVersion,
+ /**
+ * @param array $data
+ * @return string
+ */
+ private function parseVersion(array $data)
+ {
+ if (!isset($data['min']) && !isset($data['max'])) {
+ return '*';
+ }
+ $versions = array();
+ if (isset($data['min'])) {
+ $versions[] = '>=' . $data['min'];
+ }
+ if (isset($data['max'])) {
+ $versions[] = '<=' . $data['max'];
+ }
+ return implode(',', $versions);
+ }
+
+ /**
+ * @todo Improve dependencies resolution of pear packages.
+ * @param array $options
+ * @return array
+ */
+ private function parseDependenciesOptions(array $depsOptions)
+ {
+ $data = array();
+ foreach ($depsOptions as $name => $options) {
+ // make sure single deps are wrapped in an array
+ if (isset($options['name'])) {
+ $options = array($options);
+ }
+ if ('php' == $name) {
+ $data[$name] = $this->parseVersion($options);
+ } elseif ('package' == $name) {
+ foreach ($options as $key => $value) {
+ if (is_array($value)) {
+ $dataKey = $value['name'];
+ $data[$dataKey] = $this->parseVersion($value);
+ }
+ }
+ } elseif ('extension' == $name) {
+ foreach ($options as $key => $value) {
+ $dataKey = 'ext-' . $value['name'];
+ $data[$dataKey] = $this->parseVersion($value);
+ }
+ }
+ }
+ return $data;
+ }
+
+ /**
+ * @param string $deps
+ * @return array
+ * @throws InvalidArgumentException
+ */
+ private function parseDependencies($deps)
+ {
+ if (preg_match('((O:([0-9])+:"([^"]+)"))', $deps, $matches)) {
+ if (strlen($matches[3]) == $matches[2]) {
+ throw new \InvalidArgumentException("Invalid dependency data, it contains serialized objects.");
+ }
+ }
+ $deps = (array) @unserialize($deps);
+ unset($deps['required']['pearinstaller']);
+
+ $depsData = array();
+ if (!empty($deps['required'])) {
+ $depsData['require'] = $this->parseDependenciesOptions($deps['required']);
+ }
+
+ if (!empty($deps['optional'])) {
+ $depsData['suggest'] = $this->parseDependenciesOptions($deps['optional']);
+ }
+
+ return $depsData;
+ }
+
+ /**
+ * @param string $packagesLink
+ * @return void
+ * @throws InvalidArgumentException
+ */
+ private function fetchPear2Packages($packagesLink)
+ {
+ $loader = new ArrayLoader();
+ $packagesXml = $this->requestXml($packagesLink);
+ $informations = $packagesXml->getElementsByTagName('pi');
+ foreach ($informations as $information) {
+ $package = $information->getElementsByTagName('p')->item(0);
+
+ $packageName = $package->getElementsByTagName('n')->item(0)->nodeValue;
+ $packageData = array(
+ 'name' => $packageName,
+ 'type' => 'library'
+ );
+ $packageKeys = array('l' => 'license', 'd' => 'description');
+ foreach ($packageKeys as $pear => $composer) {
+ if ($package->getElementsByTagName($pear)->length > 0
+ && ($pear = $package->getElementsByTagName($pear)->item(0)->nodeValue)) {
+ $packageData[$composer] = $pear;
+ }
+ }
+
+ $depsData = array();
+ foreach ($information->getElementsByTagName('deps') as $depElement) {
+ $depsVersion = $depElement->getElementsByTagName('v')->item(0)->nodeValue;
+ $depsData[$depsVersion] = $this->parseDependencies(
+ $depElement->getElementsByTagName('d')->item(0)->nodeValue
+ );
+ }
+
+ $releases = $information->getElementsByTagName('a')->item(0);
+ if (!$releases) {
+ continue;
+ }
+
+ $releases = $releases->getElementsByTagName('r');
+ $packageUrl = $this->url . '/get/' . $packageName;
+ foreach ($releases as $release) {
+ $version = $release->getElementsByTagName('v')->item(0)->nodeValue;
+ $releaseData = array(
+ 'dist' => array(
+ 'type' => 'pear',
+ 'url' => $packageUrl . '-' . $version . '.tgz'
+ ),
+ 'version' => $version
+ );
+ if (isset($depsData[$version])) {
+ $releaseData += $depsData[$version];
+ }
+
+ try {
+ $this->addPackage(
+ $loader->load($packageData + $releaseData)
);
-
- try {
- $deps = file_get_contents($releaseLink . "/deps.".$pearVersion.".txt");
- } catch (\ErrorException $e) {
- if (strpos($e->getMessage(), '404')) {
- continue;
- }
- throw $e;
- }
-
- if (preg_match('((O:([0-9])+:"([^"]+)"))', $deps, $matches)) {
- if (strlen($matches[3]) == $matches[2]) {
- throw new \InvalidArgumentException("Invalid dependency data, it contains serialized objects.");
- }
- }
- $deps = unserialize($deps);
- if (isset($deps['required']['package'])) {
-
- if (isset($deps['required']['package']['name'])) {
- $deps['required']['package'] = array($deps['required']['package']);
- }
-
- foreach ($deps['required']['package'] as $dependency) {
- if (isset($dependency['min'])) {
- $packageData['require'][$dependency['name']] = '>='.$dependency['min'];
- } else {
- $packageData['require'][$dependency['name']] = '>=0.0.0';
- }
- }
- }
-
- try {
- $this->addPackage($loader->load($packageData));
- } catch (\UnexpectedValueException $e) {
- continue;
- }
+ } catch (\UnexpectedValueException $e) {
+ continue;
}
}
}
@@ -135,7 +277,7 @@ class PearRepository extends ArrayRepository
*/
private function requestXml($url)
{
- $content = file_get_contents($url);
+ $content = file_get_contents($url, false, $this->streamContext);
if (!$content) {
throw new \UnexpectedValueException('The PEAR channel at '.$url.' did not respond.');
}
diff --git a/src/Composer/Util/RemoteFilesystem.php b/src/Composer/Util/RemoteFilesystem.php
index 54aca9356..036c0b2fa 100644
--- a/src/Composer/Util/RemoteFilesystem.php
+++ b/src/Composer/Util/RemoteFilesystem.php
@@ -87,37 +87,20 @@ class RemoteFilesystem
$this->fileName = $fileName;
$this->progress = $progess;
- // Handle system proxy
- $params = array('http' => array());
-
- if (isset($_SERVER['HTTP_PROXY'])) {
- // http(s):// is not supported in proxy
- $proxy = str_replace(array('http://', 'https://'), array('tcp://', 'ssl://'), $_SERVER['HTTP_PROXY']);
-
- if (0 === strpos($proxy, 'ssl:') && !extension_loaded('openssl')) {
- throw new \RuntimeException('You must enable the openssl extension to use a proxy over https');
- }
-
- $params['http'] = array(
- 'proxy' => $proxy,
- 'request_fulluri' => true,
- );
- }
-
// add authorization in context
+ $options = array();
if ($this->io->hasAuthorization($originUrl)) {
- $auth = $this->io->getAuthorization($originUrl);
- $authStr = base64_encode($auth['username'] . ':' . $auth['password']);
- $params['http'] = array_merge($params['http'], array('header' => "Authorization: Basic $authStr\r\n"));
+ $auth = $this->io->getAuthorization($originUrl);
+ $authStr = base64_encode($auth['username'] . ':' . $auth['password']);
+ $options['http']['header'] = "Authorization: Basic $authStr\r\n";
} else if (null !== $this->io->getLastUsername()) {
$authStr = base64_encode($this->io->getLastUsername() . ':' . $this->io->getLastPassword());
- $params['http'] = array('header' => "Authorization: Basic $authStr\r\n");
+ $options['http'] = array('header' => "Authorization: Basic $authStr\r\n");
$this->io->setAuthorization($originUrl, $this->io->getLastUsername(), $this->io->getLastPassword());
}
- $ctx = stream_context_create($params);
- stream_context_set_params($ctx, array("notification" => array($this, 'callbackGet')));
+ $ctx = StreamContextFactory::getContext($options, array('notification' => array($this, 'callbackGet')));
if ($this->progress) {
$this->io->overwrite(" Downloading: connection...", false);
diff --git a/src/Composer/Util/StreamContextFactory.php b/src/Composer/Util/StreamContextFactory.php
new file mode 100644
index 000000000..4ea31da4d
--- /dev/null
+++ b/src/Composer/Util/StreamContextFactory.php
@@ -0,0 +1,56 @@
+
+ * Jordi Boggiano
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer\Util;
+
+/**
+ * Allows the creation of a basic context supporting http proxy
+ *
+ * @author Jordan Alliot
+ */
+final class StreamContextFactory
+{
+ /**
+ * Creates a context supporting HTTP proxies
+ *
+ * @param array $defaultOptions Options to merge with the default
+ * @param array $defaultParams Parameters to specify on the context
+ * @return resource Default context
+ * @throws \RuntimeException if https proxy required and OpenSSL uninstalled
+ */
+ static public function getContext(array $defaultOptions = array(), array $defaultParams = array())
+ {
+ $options = array('http' => array());
+
+ // Handle system proxy
+ if (isset($_SERVER['HTTP_PROXY']) || isset($_SERVER['http_proxy'])) {
+ // Some systems seem to rely on a lowercased version instead...
+ $proxy = isset($_SERVER['HTTP_PROXY']) ? $_SERVER['HTTP_PROXY'] : $_SERVER['http_proxy'];
+
+ // http(s):// is not supported in proxy
+ $proxy = str_replace(array('http://', 'https://'), array('tcp://', 'ssl://'), $proxy);
+
+ if (0 === strpos($proxy, 'ssl:') && !extension_loaded('openssl')) {
+ throw new \RuntimeException('You must enable the openssl extension to use a proxy over https');
+ }
+
+ $options['http'] = array(
+ 'proxy' => $proxy,
+ 'request_fulluri' => true,
+ );
+ }
+
+ $options = array_merge_recursive($options, $defaultOptions);
+
+ return stream_context_create($options, $defaultParams);
+ }
+}
diff --git a/tests/Composer/Test/Json/JsonFileTest.php b/tests/Composer/Test/Json/JsonFileTest.php
index 4ebf7b0c5..4ed016fa6 100644
--- a/tests/Composer/Test/Json/JsonFileTest.php
+++ b/tests/Composer/Test/Json/JsonFileTest.php
@@ -121,6 +121,26 @@ class JsonFileTest extends \PHPUnit_Framework_TestCase
$this->assertJsonFormat($json, $data);
}
+ public function testEscape()
+ {
+ $data = array("Metadata\\\"" => 'src/');
+ $json = '{
+ "Metadata\\\\\\"": "src\/"
+}';
+
+ $this->assertJsonFormat($json, $data);
+ }
+
+ public function testUnicode()
+ {
+ $data = array("Žluťoučký \" kůň" => "úpěl ďábelské ódy za €");
+ $json = '{
+ "Žluťoučký \" kůň": "úpěl ďábelské ódy za €"
+}';
+
+ $this->assertJsonFormat($json, $data);
+ }
+
private function expectParseException($text, $json)
{
try {
diff --git a/tests/Composer/Test/Package/Dumper/ArrayDumperTest.php b/tests/Composer/Test/Package/Dumper/ArrayDumperTest.php
new file mode 100644
index 000000000..eab3450bf
--- /dev/null
+++ b/tests/Composer/Test/Package/Dumper/ArrayDumperTest.php
@@ -0,0 +1,66 @@
+
+ * Jordi Boggiano
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer\Test\Package\Dumper;
+
+use Composer\Package\Dumper\ArrayDumper;
+use Composer\Package\MemoryPackage;
+
+class ArrayDumperTest extends \PHPUnit_Framework_TestCase
+{
+ public function setUp()
+ {
+ $this->dumper = new ArrayDumper();
+ }
+
+ public function testRequiredInformations()
+ {
+ $package = new MemoryPackage('foo', '1.0.0.0', '1.0');
+
+ $config = $this->dumper->dump($package);
+ $this->assertEquals(array('name', 'version', 'version_normalized', 'type', 'names'), array_keys($config));
+ }
+
+ /**
+ * @dataProvider getKeys
+ */
+ public function testKeys($key, $value, $expectedValue = null, $method = null)
+ {
+ $package = new MemoryPackage('foo', '1.0.0.0', '1.0');
+
+ $setter = 'set'.ucfirst($method ?: $key);
+ $package->$setter($value);
+
+ $config = $this->dumper->dump($package);
+ $this->assertArrayHasKey($key, $config);
+
+ $expectedValue = $expectedValue ?: $value;
+ $this->assertSame($expectedValue, $config[$key]);
+ }
+
+ public function getKeys()
+ {
+ return array(
+ array('time', new \DateTime('2012-02-01'), '2012-02-01 00:00:00', 'ReleaseDate'),
+ array('authors', array('Nils Adermann ', 'Jordi Boggiano ')),
+ array('homepage', 'http://getcomposer.org'),
+ array('description', 'Package Manager'),
+ array('keywords', array('package', 'dependency', 'autoload')),
+ array('bin', array('bin/composer'), null, 'binaries'),
+ array('license', array('MIT')),
+ array('autoload', array('psr-0' => array('Composer' => 'src/'))),
+ array('repositories', array('packagist' => false)),
+ array('scripts', array('post-update-cmd' => 'MyVendor\\MyClass::postUpdate')),
+ array('extra', array('class' => 'MyVendor\\Installer')),
+ );
+ }
+}